summaryrefslogtreecommitdiff
path: root/usr/src/uts
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts')
-rw-r--r--usr/src/uts/Makefile.mapfile43
-rw-r--r--usr/src/uts/Makefile.targ6
-rw-r--r--usr/src/uts/Makefile.uts16
-rw-r--r--usr/src/uts/common/Makefile.files77
-rw-r--r--usr/src/uts/common/Makefile.rules119
-rw-r--r--usr/src/uts/common/brand/lx/autofs/lx_autofs.c3152
-rw-r--r--usr/src/uts/common/brand/lx/autofs/lxautofs.conf14
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps.h222
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_node.c1014
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c1071
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c1600
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd.h244
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c368
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_node.c1003
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c860
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_vnops.c1519
-rw-r--r--usr/src/uts/common/brand/lx/dtrace/lx_systrace.c499
-rw-r--r--usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf27
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_netlink.c1686
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_ptm.c1188
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_ptm.conf27
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c2630
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_misc.c1124
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_pid.c395
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_ptrace.c2638
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_signal.c50
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c1221
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_proc.h351
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prsubr.c852
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c377
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvnops.c7344
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs.h511
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h162
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h680
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_fcntl.h161
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_futex.h121
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_impl.h52
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_ldt.h91
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_misc.h119
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_ptm.h44
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_sched.h60
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_siginfo.h190
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_signal.h32
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_socket.h434
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h306
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_types.h144
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_access.c223
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_aio.c45
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_brk.c57
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_chmod.c107
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_chown.c180
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_clone.c143
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_close.c57
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_cpu.c36
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_dup.c53
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_epoll.c279
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fadvise.c103
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fallocate.c251
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fcntl.c660
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_futex.c1104
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getcwd.c50
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getdents.c354
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getpid.c79
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getrandom.c33
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_id.c509
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_ioctl.c1769
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_ioprio.c66
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_kill.c402
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_link.c194
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_lseek.c81
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_miscsys.c282
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mkdir.c38
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c121
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mount.c657
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_open.c260
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_personality.c112
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_pgrp.c189
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_pipe.c200
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_poll.c762
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_prctl.c211
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rename.c39
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rlimit.c575
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rw.c949
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sched.c524
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_socket.c3966
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_stat.c474
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sync.c86
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c218
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_thread_area.c197
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_timer.c388
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_umask.c25
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_uname.c82
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_wait.c377
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_xattr.c371
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h196
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c452
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c348
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c1802
-rw-r--r--usr/src/uts/common/brand/sn1/sn1_brand.c103
-rw-r--r--usr/src/uts/common/brand/sn1/sn1_brand.h7
-rw-r--r--usr/src/uts/common/brand/solaris10/s10_brand.c103
-rw-r--r--usr/src/uts/common/brand/solaris10/s10_brand.h6
-rw-r--r--usr/src/uts/common/conf/param.c12
-rw-r--r--usr/src/uts/common/contract/process.c24
-rw-r--r--usr/src/uts/common/crypto/api/kcf_random.c4
-rw-r--r--usr/src/uts/common/crypto/core/kcf_sched.c6
-rw-r--r--usr/src/uts/common/ctf/ctf_mod.c11
-rw-r--r--usr/src/uts/common/disp/cmt.c8
-rw-r--r--usr/src/uts/common/disp/cpucaps.c285
-rw-r--r--usr/src/uts/common/disp/disp.c26
-rw-r--r--usr/src/uts/common/disp/fx.c12
-rw-r--r--usr/src/uts/common/disp/rt.c9
-rw-r--r--usr/src/uts/common/disp/rt_dptbl.c4
-rw-r--r--usr/src/uts/common/disp/thread.c136
-rw-r--r--usr/src/uts/common/disp/thread_intr.c37
-rw-r--r--usr/src/uts/common/dtrace/dtrace.c23
-rw-r--r--usr/src/uts/common/dtrace/sdt_subr.c33
-rw-r--r--usr/src/uts/common/exec/aout/aout.c5
-rw-r--r--usr/src/uts/common/exec/elf/elf.c436
-rw-r--r--usr/src/uts/common/exec/intp/intp.c35
-rw-r--r--usr/src/uts/common/exec/java/java.c3
-rw-r--r--usr/src/uts/common/exec/shbin/shbin.c5
-rw-r--r--usr/src/uts/common/fs/dev/sdev_netops.c259
-rw-r--r--usr/src/uts/common/fs/dev/sdev_plugin.c913
-rw-r--r--usr/src/uts/common/fs/dev/sdev_subr.c209
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vfsops.c23
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vnops.c10
-rw-r--r--usr/src/uts/common/fs/dev/sdev_zvolops.c6
-rw-r--r--usr/src/uts/common/fs/dnlc.c44
-rw-r--r--usr/src/uts/common/fs/fem.c688
-rw-r--r--usr/src/uts/common/fs/fifofs/fifosubr.c7
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c640
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c127
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c614
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c1441
-rw-r--r--usr/src/uts/common/fs/lookup.c445
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_subr.c524
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_vfsops.c367
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_vnops.c3099
-rw-r--r--usr/src/uts/common/fs/lxproc/lxproc.h278
-rw-r--r--usr/src/uts/common/fs/nfs/nfs3_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs3_vnops.c14
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_srv_ns.c5
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_vnops.c48
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_auth.c26
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_server.c6
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_vnops.c24
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_dir.c5
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_vnops.c5
-rw-r--r--usr/src/uts/common/fs/portfs/port.c36
-rw-r--r--usr/src/uts/common/fs/proc/prargv.c441
-rw-r--r--usr/src/uts/common/fs/proc/prcontrol.c16
-rw-r--r--usr/src/uts/common/fs/proc/prdata.h5
-rw-r--r--usr/src/uts/common/fs/proc/prsubr.c9
-rw-r--r--usr/src/uts/common/fs/proc/prvnops.c162
-rw-r--r--usr/src/uts/common/fs/sockfs/sockcommon.c8
-rw-r--r--usr/src/uts/common/fs/sockfs/sockcommon_sops.c50
-rw-r--r--usr/src/uts/common/fs/sockfs/sockcommon_subr.c71
-rw-r--r--usr/src/uts/common/fs/sockfs/sockfilter.c24
-rw-r--r--usr/src/uts/common/fs/sockfs/sockfilter_impl.h2
-rw-r--r--usr/src/uts/common/fs/sockfs/socksubr.c9
-rw-r--r--usr/src/uts/common/fs/sockfs/socksyscalls.c120
-rw-r--r--usr/src/uts/common/fs/sockfs/socktpi_impl.h3
-rw-r--r--usr/src/uts/common/fs/swapfs/swap_subr.c6
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_dir.c61
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_subr.c299
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_tnode.c70
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_vfsops.c307
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_vnops.c99
-rw-r--r--usr/src/uts/common/fs/udfs/udf_dir.c6
-rw-r--r--usr/src/uts/common/fs/udfs/udf_vnops.c14
-rw-r--r--usr/src/uts/common/fs/ufs/ufs_vnops.c27
-rw-r--r--usr/src/uts/common/fs/vfs.c13
-rw-r--r--usr/src/uts/common/fs/vnode.c460
-rw-r--r--usr/src/uts/common/fs/zfs/arc.c9
-rw-r--r--usr/src/uts/common/fs/zfs/dbuf.c15
-rw-r--r--usr/src/uts/common/fs/zfs/dmu.c1
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_send.c8
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_tx.c4
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dir.c3
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_pool.c1
-rw-r--r--usr/src/uts/common/fs/zfs/metaslab.c8
-rw-r--r--usr/src/uts/common/fs/zfs/sa.c12
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_impl.h1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_zone.h63
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zio.h2
-rw-r--r--usr/src/uts/common/fs/zfs/txg.c3
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c13
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_queue.c12
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_dir.c7
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c62
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c12
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vnops.c60
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_zone.c1336
-rw-r--r--usr/src/uts/common/fs/zfs/zil.c14
-rw-r--r--usr/src/uts/common/fs/zfs/zio.c5
-rw-r--r--usr/src/uts/common/fs/zfs/zvol.c103
-rw-r--r--usr/src/uts/common/inet/bpf.h49
-rw-r--r--usr/src/uts/common/inet/bpf_filter.c (renamed from usr/src/uts/common/io/bpf/bpf_filter.c)46
-rw-r--r--usr/src/uts/common/inet/inet_hash.h37
-rw-r--r--usr/src/uts/common/inet/ip/conn_opt.c22
-rw-r--r--usr/src/uts/common/inet/ip/icmp.c125
-rw-r--r--usr/src/uts/common/inet/ip/icmp_opt_data.c6
-rw-r--r--usr/src/uts/common/inet/ip/ip.c12
-rw-r--r--usr/src/uts/common/inet/ip/ip_attr.c5
-rw-r--r--usr/src/uts/common/inet/ip/ip_if.c21
-rw-r--r--usr/src/uts/common/inet/ip/ip_squeue.c2
-rw-r--r--usr/src/uts/common/inet/ip/ipclassifier.c165
-rw-r--r--usr/src/uts/common/inet/ip/ipsecesp.c3
-rw-r--r--usr/src/uts/common/inet/ipclassifier.h4
-rw-r--r--usr/src/uts/common/inet/ipf/ip_fil_solaris.c136
-rw-r--r--usr/src/uts/common/inet/ipf/ipf.conf5
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/ipf_stack.h10
-rw-r--r--usr/src/uts/common/inet/ipf/solaris.c1
-rw-r--r--usr/src/uts/common/inet/ipnet/ipnet.c17
-rw-r--r--usr/src/uts/common/inet/iptun/iptun.c2
-rw-r--r--usr/src/uts/common/inet/rawip_impl.h6
-rw-r--r--usr/src/uts/common/inet/sockmods/datafilt.c116
-rw-r--r--usr/src/uts/common/inet/sockmods/sockmod_pfp.c11
-rw-r--r--usr/src/uts/common/inet/squeue.c100
-rw-r--r--usr/src/uts/common/inet/tcp.h10
-rw-r--r--usr/src/uts/common/inet/tcp/tcp.c20
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_bind.c227
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_opt_data.c148
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_socket.c11
-rw-r--r--usr/src/uts/common/inet/tcp_impl.h24
-rw-r--r--usr/src/uts/common/inet/udp/udp.c165
-rw-r--r--usr/src/uts/common/inet/udp/udp_opt_data.c4
-rw-r--r--usr/src/uts/common/inet/udp_impl.h7
-rw-r--r--usr/src/uts/common/io/aggr/aggr_port.c8
-rw-r--r--usr/src/uts/common/io/aggr/aggr_recv.c36
-rw-r--r--usr/src/uts/common/io/axf/ax88172reg.h163
-rw-r--r--usr/src/uts/common/io/axf/axf_usbgem.c1539
-rw-r--r--usr/src/uts/common/io/bpf/bpf_wrap.c35
-rw-r--r--usr/src/uts/common/io/cons.c16
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3.c2
-rw-r--r--usr/src/uts/common/io/devpoll.c50
-rw-r--r--usr/src/uts/common/io/dld/dld_drv.c9
-rw-r--r--usr/src/uts/common/io/dld/dld_proto.c87
-rw-r--r--usr/src/uts/common/io/dld/dld_str.c104
-rw-r--r--usr/src/uts/common/io/dls/dls.c128
-rw-r--r--usr/src/uts/common/io/dls/dls_link.c72
-rw-r--r--usr/src/uts/common/io/dls/dls_mgmt.c259
-rw-r--r--usr/src/uts/common/io/dls/dls_stat.c172
-rw-r--r--usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE32
-rw-r--r--usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/dr_sas/dr_sas.c5506
-rw-r--r--usr/src/uts/common/io/dr_sas/dr_sas.conf15
-rw-r--r--usr/src/uts/common/io/dr_sas/dr_sas.h1766
-rw-r--r--usr/src/uts/common/io/dr_sas/dr_sas_list.h212
-rw-r--r--usr/src/uts/common/io/eventfd.c30
-rw-r--r--usr/src/uts/common/io/fibre-channel/impl/fctl.c6
-rw-r--r--usr/src/uts/common/io/gsqueue/gsqueue.c612
-rw-r--r--usr/src/uts/common/io/inotify.c1504
-rw-r--r--usr/src/uts/common/io/inotify.conf16
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_main.c8
-rw-r--r--usr/src/uts/common/io/ksocket/ksocket.c14
-rw-r--r--usr/src/uts/common/io/ksocket/ksocket_impl.h6
-rw-r--r--usr/src/uts/common/io/ksyms.c9
-rw-r--r--usr/src/uts/common/io/mac/mac.c3
-rw-r--r--usr/src/uts/common/io/mac/mac_client.c68
-rw-r--r--usr/src/uts/common/io/mac/mac_datapath_setup.c21
-rw-r--r--usr/src/uts/common/io/mac/mac_protect.c3
-rw-r--r--usr/src/uts/common/io/mac/mac_provider.c8
-rw-r--r--usr/src/uts/common/io/mac/mac_sched.c16
-rw-r--r--usr/src/uts/common/io/mac/mac_stat.c9
-rw-r--r--usr/src/uts/common/io/mem.c13
-rw-r--r--usr/src/uts/common/io/mr_sas/mr_sas.conf8
-rw-r--r--usr/src/uts/common/io/nfp/THIRDPARTYLICENSE19
-rw-r--r--usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/nfp/autoversion.h21
-rw-r--r--usr/src/uts/common/io/nfp/drvlist.c19
-rw-r--r--usr/src/uts/common/io/nfp/hostif.c1192
-rw-r--r--usr/src/uts/common/io/nfp/i21285.c310
-rw-r--r--usr/src/uts/common/io/nfp/i21285.h43
-rw-r--r--usr/src/uts/common/io/nfp/i21555.c423
-rw-r--r--usr/src/uts/common/io/nfp/i21555.h51
-rw-r--r--usr/src/uts/common/io/nfp/i21555d.c28
-rw-r--r--usr/src/uts/common/io/nfp/nfdev-common.h141
-rw-r--r--usr/src/uts/common/io/nfp/nfdev-solaris.h37
-rw-r--r--usr/src/uts/common/io/nfp/nfp.h113
-rw-r--r--usr/src/uts/common/io/nfp/nfp_cmd.h68
-rw-r--r--usr/src/uts/common/io/nfp/nfp_common.h68
-rw-r--r--usr/src/uts/common/io/nfp/nfp_error.h48
-rw-r--r--usr/src/uts/common/io/nfp/nfp_hostif.h54
-rw-r--r--usr/src/uts/common/io/nfp/nfp_ifvers.c51
-rw-r--r--usr/src/uts/common/io/nfp/nfp_osif.h105
-rw-r--r--usr/src/uts/common/io/nfp/nfpci.h171
-rw-r--r--usr/src/uts/common/io/nfp/osif.c184
-rw-r--r--usr/src/uts/common/io/overlay/overlay.c2184
-rw-r--r--usr/src/uts/common/io/overlay/overlay.conf16
-rw-r--r--usr/src/uts/common/io/overlay/overlay.mapfile46
-rw-r--r--usr/src/uts/common/io/overlay/overlay_fm.c82
-rw-r--r--usr/src/uts/common/io/overlay/overlay_mux.c354
-rw-r--r--usr/src/uts/common/io/overlay/overlay_plugin.c281
-rw-r--r--usr/src/uts/common/io/overlay/overlay_prop.c122
-rw-r--r--usr/src/uts/common/io/overlay/overlay_target.c1651
-rw-r--r--usr/src/uts/common/io/overlay/plugins/overlay_vxlan.c372
-rw-r--r--usr/src/uts/common/io/physmem.c8
-rw-r--r--usr/src/uts/common/io/pseudo.conf9
-rw-r--r--usr/src/uts/common/io/pseudonex.c26
-rw-r--r--usr/src/uts/common/io/ptm.c47
-rw-r--r--usr/src/uts/common/io/random.c5
-rw-r--r--usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c4
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt.c441
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt.conf16
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss.c1119
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss_simple.c212
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_commands.c325
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_device.c215
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_hba.c1073
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_interrupts.c233
-rw-r--r--usr/src/uts/common/io/scsi/adapters/smrt/smrt_logvol.c312
-rw-r--r--usr/src/uts/common/io/scsi/targets/sd.c79
-rw-r--r--usr/src/uts/common/io/signalfd.c533
-rw-r--r--usr/src/uts/common/io/stream.c10
-rw-r--r--usr/src/uts/common/io/udmf/dm9601reg.h348
-rw-r--r--usr/src/uts/common/io/udmf/udmf_usbgem.c1036
-rw-r--r--usr/src/uts/common/io/upf/adm8511reg.h205
-rw-r--r--usr/src/uts/common/io/upf/upf_usbgem.c1213
-rw-r--r--usr/src/uts/common/io/urf/rtl8150reg.h218
-rw-r--r--usr/src/uts/common/io/urf/urf_usbgem.c1039
-rw-r--r--usr/src/uts/common/io/usbgem/usbgem.c6389
-rw-r--r--usr/src/uts/common/io/usbgem/usbgem.h428
-rw-r--r--usr/src/uts/common/io/usbgem/usbgem_mii.h242
-rw-r--r--usr/src/uts/common/io/vioif/vioif.c101
-rw-r--r--usr/src/uts/common/io/vnd/frameio.c464
-rw-r--r--usr/src/uts/common/io/vnd/vnd.c5802
-rw-r--r--usr/src/uts/common/io/vnd/vnd.conf16
-rw-r--r--usr/src/uts/common/io/vnic/vnic_dev.c87
-rw-r--r--usr/src/uts/common/io/vscan/vscan_svc.c4
-rw-r--r--usr/src/uts/common/io/zfd.c1154
-rw-r--r--usr/src/uts/common/mapfiles/README68
-rw-r--r--usr/src/uts/common/mapfiles/ddi.mapfile190
-rw-r--r--usr/src/uts/common/mapfiles/dtrace.mapfile.awk34
-rw-r--r--usr/src/uts/common/mapfiles/kernel.mapfile41
-rw-r--r--usr/src/uts/common/mapfiles/mac.mapfile55
-rw-r--r--usr/src/uts/common/mapfiles/random.mapfile37
-rw-r--r--usr/src/uts/common/netinet/in.h7
-rw-r--r--usr/src/uts/common/netinet/udp.h14
-rw-r--r--usr/src/uts/common/os/brand.c182
-rw-r--r--usr/src/uts/common/os/clock_highres.c59
-rw-r--r--usr/src/uts/common/os/contract.c6
-rw-r--r--usr/src/uts/common/os/cred.c8
-rw-r--r--usr/src/uts/common/os/ddi_intr_irm.c2
-rw-r--r--usr/src/uts/common/os/exec.c119
-rw-r--r--usr/src/uts/common/os/exit.c198
-rw-r--r--usr/src/uts/common/os/fio.c3
-rw-r--r--usr/src/uts/common/os/flock.c10
-rw-r--r--usr/src/uts/common/os/fork.c102
-rw-r--r--usr/src/uts/common/os/grow.c35
-rw-r--r--usr/src/uts/common/os/id_space.c159
-rw-r--r--usr/src/uts/common/os/ipc.c26
-rw-r--r--usr/src/uts/common/os/klpd.c12
-rw-r--r--usr/src/uts/common/os/kmem.c318
-rw-r--r--usr/src/uts/common/os/logsubr.c4
-rw-r--r--usr/src/uts/common/os/lwp.c129
-rw-r--r--usr/src/uts/common/os/main.c14
-rw-r--r--usr/src/uts/common/os/mmapobj.c13
-rw-r--r--usr/src/uts/common/os/netstack.c24
-rw-r--r--usr/src/uts/common/os/pid.c27
-rw-r--r--usr/src/uts/common/os/policy.c34
-rw-r--r--usr/src/uts/common/os/priv_defs8
-rw-r--r--usr/src/uts/common/os/sched.c15
-rw-r--r--usr/src/uts/common/os/shm.c41
-rw-r--r--usr/src/uts/common/os/sig.c93
-rw-r--r--usr/src/uts/common/os/smb_subr.c8
-rw-r--r--usr/src/uts/common/os/streamio.c48
-rw-r--r--usr/src/uts/common/os/sysent.c38
-rw-r--r--usr/src/uts/common/os/timers.c49
-rw-r--r--usr/src/uts/common/os/vmem.c2
-rw-r--r--usr/src/uts/common/os/zone.c744
-rw-r--r--usr/src/uts/common/refhash/refhash.c (renamed from usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas_hash.c)6
-rw-r--r--usr/src/uts/common/sys/Makefile17
-rw-r--r--usr/src/uts/common/sys/aggr_impl.h3
-rw-r--r--usr/src/uts/common/sys/auxv.h37
-rw-r--r--usr/src/uts/common/sys/brand.h95
-rw-r--r--usr/src/uts/common/sys/buf.h8
-rw-r--r--usr/src/uts/common/sys/contract/process.h6
-rw-r--r--usr/src/uts/common/sys/cpucaps.h5
-rw-r--r--usr/src/uts/common/sys/cpucaps_impl.h6
-rw-r--r--usr/src/uts/common/sys/cred.h1
-rw-r--r--usr/src/uts/common/sys/ctf_api.h103
-rw-r--r--usr/src/uts/common/sys/dktp/dadk.h2
-rw-r--r--usr/src/uts/common/sys/dld.h7
-rw-r--r--usr/src/uts/common/sys/dld_impl.h5
-rw-r--r--usr/src/uts/common/sys/dld_ioc.h3
-rw-r--r--usr/src/uts/common/sys/dlpi.h12
-rw-r--r--usr/src/uts/common/sys/dls.h12
-rw-r--r--usr/src/uts/common/sys/dls_impl.h11
-rw-r--r--usr/src/uts/common/sys/dls_mgmt.h9
-rw-r--r--usr/src/uts/common/sys/elf.h46
-rw-r--r--usr/src/uts/common/sys/exec.h26
-rw-r--r--usr/src/uts/common/sys/frameio.h107
-rw-r--r--usr/src/uts/common/sys/fs/hyprlofs.h91
-rw-r--r--usr/src/uts/common/sys/fs/hyprlofs_info.h174
-rw-r--r--usr/src/uts/common/sys/fs/sdev_impl.h61
-rw-r--r--usr/src/uts/common/sys/fs/sdev_plugin.h106
-rw-r--r--usr/src/uts/common/sys/fs/tmp.h60
-rw-r--r--usr/src/uts/common/sys/fx.h10
-rw-r--r--usr/src/uts/common/sys/gsqueue.h65
-rw-r--r--usr/src/uts/common/sys/id_space.h5
-rw-r--r--usr/src/uts/common/sys/inotify.h153
-rw-r--r--usr/src/uts/common/sys/ipc_impl.h2
-rw-r--r--usr/src/uts/common/sys/iso/signal_iso.h3
-rw-r--r--usr/src/uts/common/sys/klwp.h11
-rw-r--r--usr/src/uts/common/sys/kmem_impl.h1
-rw-r--r--usr/src/uts/common/sys/ksocket.h5
-rw-r--r--usr/src/uts/common/sys/limits.h32
-rw-r--r--usr/src/uts/common/sys/mac.h12
-rw-r--r--usr/src/uts/common/sys/mac_client.h3
-rw-r--r--usr/src/uts/common/sys/mac_client_impl.h6
-rw-r--r--usr/src/uts/common/sys/mac_client_priv.h3
-rw-r--r--usr/src/uts/common/sys/mac_impl.h7
-rw-r--r--usr/src/uts/common/sys/mac_provider.h4
-rw-r--r--usr/src/uts/common/sys/mman.h1
-rw-r--r--usr/src/uts/common/sys/mntent.h2
-rw-r--r--usr/src/uts/common/sys/netconfig.h3
-rw-r--r--usr/src/uts/common/sys/neti.h2
-rw-r--r--usr/src/uts/common/sys/netstack.h9
-rw-r--r--usr/src/uts/common/sys/overlay.h96
-rw-r--r--usr/src/uts/common/sys/overlay_common.h65
-rw-r--r--usr/src/uts/common/sys/overlay_impl.h205
-rw-r--r--usr/src/uts/common/sys/overlay_plugin.h324
-rw-r--r--usr/src/uts/common/sys/overlay_target.h292
-rw-r--r--usr/src/uts/common/sys/param.h2
-rw-r--r--usr/src/uts/common/sys/policy.h2
-rw-r--r--usr/src/uts/common/sys/proc.h14
-rw-r--r--usr/src/uts/common/sys/procfs.h2
-rw-r--r--usr/src/uts/common/sys/ptms.h19
-rw-r--r--usr/src/uts/common/sys/refhash.h (renamed from usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_hash.h)8
-rw-r--r--usr/src/uts/common/sys/resource.h2
-rw-r--r--usr/src/uts/common/sys/rt.h13
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h4
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/smrt/smrt.h586
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/smrt/smrt_ciss.h339
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/smrt/smrt_scsi.h105
-rw-r--r--usr/src/uts/common/sys/shm.h5
-rw-r--r--usr/src/uts/common/sys/shm_impl.h17
-rw-r--r--usr/src/uts/common/sys/signal.h5
-rw-r--r--usr/src/uts/common/sys/signalfd.h8
-rw-r--r--usr/src/uts/common/sys/socket.h10
-rw-r--r--usr/src/uts/common/sys/socketvar.h42
-rw-r--r--usr/src/uts/common/sys/sockfilter.h10
-rw-r--r--usr/src/uts/common/sys/squeue.h14
-rw-r--r--usr/src/uts/common/sys/squeue_impl.h2
-rw-r--r--usr/src/uts/common/sys/stream.h8
-rw-r--r--usr/src/uts/common/sys/sysevent.h3
-rw-r--r--usr/src/uts/common/sys/sysevent/datalink.h54
-rw-r--r--usr/src/uts/common/sys/sysevent/eventdefs.h4
-rw-r--r--usr/src/uts/common/sys/systrace.h13
-rw-r--r--usr/src/uts/common/sys/termios.h18
-rw-r--r--usr/src/uts/common/sys/thread.h16
-rw-r--r--usr/src/uts/common/sys/time.h9
-rw-r--r--usr/src/uts/common/sys/uadmin.h3
-rw-r--r--usr/src/uts/common/sys/uio.h12
-rw-r--r--usr/src/uts/common/sys/user.h7
-rw-r--r--usr/src/uts/common/sys/vm_usage.h5
-rw-r--r--usr/src/uts/common/sys/vmsystm.h5
-rw-r--r--usr/src/uts/common/sys/vnd.h141
-rw-r--r--usr/src/uts/common/sys/vnd_errno.h72
-rw-r--r--usr/src/uts/common/sys/vnic_impl.h3
-rw-r--r--usr/src/uts/common/sys/vnode.h74
-rw-r--r--usr/src/uts/common/sys/vxlan.h47
-rw-r--r--usr/src/uts/common/sys/zfd.h78
-rw-r--r--usr/src/uts/common/sys/zone.h119
-rw-r--r--usr/src/uts/common/syscall/brandsys.c8
-rw-r--r--usr/src/uts/common/syscall/fcntl.c6
-rw-r--r--usr/src/uts/common/syscall/memcntl.c8
-rw-r--r--usr/src/uts/common/syscall/open.c8
-rw-r--r--usr/src/uts/common/syscall/poll.c350
-rw-r--r--usr/src/uts/common/syscall/rusagesys.c14
-rw-r--r--usr/src/uts/common/syscall/rw.c222
-rw-r--r--usr/src/uts/common/syscall/sendfile.c19
-rw-r--r--usr/src/uts/common/syscall/stat.c2
-rw-r--r--usr/src/uts/common/syscall/sysconfig.c24
-rw-r--r--usr/src/uts/common/syscall/uadmin.c6
-rw-r--r--usr/src/uts/common/vm/hat.h10
-rw-r--r--usr/src/uts/common/vm/seg_kmem.c3
-rw-r--r--usr/src/uts/common/vm/seg_kmem.h3
-rw-r--r--usr/src/uts/common/vm/seg_umap.c453
-rw-r--r--usr/src/uts/common/vm/seg_umap.h42
-rw-r--r--usr/src/uts/common/vm/seg_vn.c11
-rw-r--r--usr/src/uts/common/vm/vm_as.c20
-rw-r--r--usr/src/uts/common/vm/vm_pvn.c28
-rw-r--r--usr/src/uts/common/vm/vm_swap.c27
-rw-r--r--usr/src/uts/common/vm/vm_usage.c387
-rw-r--r--usr/src/uts/i86pc/Makefile.files2
-rw-r--r--usr/src/uts/i86pc/acpi_drv/Makefile4
-rw-r--r--usr/src/uts/i86pc/acpidev/Makefile3
-rw-r--r--usr/src/uts/i86pc/acpinex/Makefile4
-rw-r--r--usr/src/uts/i86pc/acpippm/Makefile4
-rw-r--r--usr/src/uts/i86pc/amd_iommu/Makefile3
-rw-r--r--usr/src/uts/i86pc/amd_opteron/Makefile3
-rw-r--r--usr/src/uts/i86pc/apix/Makefile2
-rw-r--r--usr/src/uts/i86pc/cpr/Makefile2
-rw-r--r--usr/src/uts/i86pc/cpudrv/Makefile2
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_startkern.c41
-rw-r--r--usr/src/uts/i86pc/drmach_acpi/Makefile4
-rw-r--r--usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c85
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpisleep.c8
-rw-r--r--usr/src/uts/i86pc/io/psm/psm_common.c3
-rw-r--r--usr/src/uts/i86pc/io/xsvc/xsvc.c17
-rw-r--r--usr/src/uts/i86pc/isa/Makefile4
-rw-r--r--usr/src/uts/i86pc/ml/comm_page.s88
-rw-r--r--usr/src/uts/i86pc/ml/offsets.in5
-rw-r--r--usr/src/uts/i86pc/ml/syscall_asm.s30
-rw-r--r--usr/src/uts/i86pc/ml/syscall_asm_amd64.s158
-rw-r--r--usr/src/uts/i86pc/npe/Makefile3
-rw-r--r--usr/src/uts/i86pc/os/cpr_impl.c14
-rw-r--r--usr/src/uts/i86pc/os/cpuid.c28
-rw-r--r--usr/src/uts/i86pc/os/ibft.c6
-rw-r--r--usr/src/uts/i86pc/os/lgrpplat.c14
-rw-r--r--usr/src/uts/i86pc/os/mlsetup.c10
-rw-r--r--usr/src/uts/i86pc/os/mp_startup.c30
-rw-r--r--usr/src/uts/i86pc/os/timestamp.c161
-rw-r--r--usr/src/uts/i86pc/os/trap.c14
-rw-r--r--usr/src/uts/i86pc/pci-ide/Makefile3
-rw-r--r--usr/src/uts/i86pc/pci/Makefile3
-rw-r--r--usr/src/uts/i86pc/pcplusmp/Makefile4
-rw-r--r--usr/src/uts/i86pc/sys/acpidev.h3
-rw-r--r--usr/src/uts/i86pc/sys/apic.h2
-rw-r--r--usr/src/uts/i86pc/sys/comm_page.h100
-rw-r--r--usr/src/uts/i86pc/sys/machparam.h5
-rw-r--r--usr/src/uts/i86pc/sys/tsc.h28
-rw-r--r--usr/src/uts/i86pc/tzmon/Makefile3
-rw-r--r--usr/src/uts/i86pc/unix/Makefile14
-rw-r--r--usr/src/uts/i86pc/uppc/Makefile3
-rw-r--r--usr/src/uts/i86pc/vm/hat_i86.c79
-rw-r--r--usr/src/uts/i86pc/vm/vm_machdep.c7
-rw-r--r--usr/src/uts/i86xpv/amd_opteron/Makefile2
-rw-r--r--usr/src/uts/i86xpv/isa/Makefile4
-rw-r--r--usr/src/uts/i86xpv/npe/Makefile3
-rw-r--r--usr/src/uts/i86xpv/os/xpv_timestamp.c6
-rw-r--r--usr/src/uts/i86xpv/pci-ide/Makefile3
-rw-r--r--usr/src/uts/i86xpv/pci/Makefile3
-rw-r--r--usr/src/uts/i86xpv/rootnex/Makefile3
-rw-r--r--usr/src/uts/i86xpv/unix/Makefile2
-rw-r--r--usr/src/uts/i86xpv/xpv_psm/Makefile3
-rw-r--r--usr/src/uts/i86xpv/xpv_uppc/Makefile4
-rw-r--r--usr/src/uts/intel/Makefile6
-rw-r--r--usr/src/uts/intel/Makefile.files164
-rw-r--r--usr/src/uts/intel/Makefile.intel37
-rw-r--r--usr/src/uts/intel/Makefile.rules7
-rw-r--r--usr/src/uts/intel/acpica/Makefile3
-rw-r--r--usr/src/uts/intel/axf/Makefile73
-rw-r--r--usr/src/uts/intel/bpf/Makefile2
-rw-r--r--usr/src/uts/intel/brand/common/brand_asm.h2
-rw-r--r--usr/src/uts/intel/brand/lx/lx_archdep.c1720
-rw-r--r--usr/src/uts/intel/core_pcbe/Makefile4
-rw-r--r--usr/src/uts/intel/datafilt/Makefile74
-rw-r--r--usr/src/uts/intel/dev/Makefile1
-rw-r--r--usr/src/uts/intel/dld/Makefile1
-rw-r--r--usr/src/uts/intel/dls/Makefile1
-rw-r--r--usr/src/uts/intel/dr_sas/Makefile90
-rw-r--r--usr/src/uts/intel/dtrace/fasttrap_isa.c15
-rw-r--r--usr/src/uts/intel/e1000g/Makefile2
-rw-r--r--usr/src/uts/intel/elfexec/Makefile6
-rw-r--r--usr/src/uts/intel/genassym/Makefile85
-rw-r--r--usr/src/uts/intel/genassym/offsets.in43
-rw-r--r--usr/src/uts/intel/gsqueue/Makefile49
-rw-r--r--usr/src/uts/intel/hyprlofs/Makefile83
-rw-r--r--usr/src/uts/intel/i40e/Makefile3
-rw-r--r--usr/src/uts/intel/ia32/ml/i86_subr.s17
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s12
-rw-r--r--usr/src/uts/intel/ia32/ml/swtch.s104
-rw-r--r--usr/src/uts/intel/ia32/os/archdep.c26
-rw-r--r--usr/src/uts/intel/ia32/os/comm_page_util.c62
-rw-r--r--usr/src/uts/intel/ia32/os/desctbls.c28
-rw-r--r--usr/src/uts/intel/ia32/os/sendsig.c78
-rw-r--r--usr/src/uts/intel/ia32/sys/comm_page_util.h33
-rw-r--r--usr/src/uts/intel/ia32/syscall/getcontext.c72
-rw-r--r--usr/src/uts/intel/icmp/Makefile3
-rw-r--r--usr/src/uts/intel/igb/Makefile2
-rw-r--r--usr/src/uts/intel/inotify/Makefile70
-rw-r--r--usr/src/uts/intel/io/acpica/acpi_enum.c99
-rw-r--r--usr/src/uts/intel/io/acpica/acpica.c26
-rw-r--r--usr/src/uts/intel/io/acpica/ahids.c245
-rw-r--r--usr/src/uts/intel/io/acpica/changes.txt8900
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbcmds.c822
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbdisply.c1031
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbexec.c1105
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbfileio.c577
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbhistry.c220
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbinput.c1081
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbmethod.c525
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbnames.c934
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbstats.c549
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbutils.c526
-rw-r--r--usr/src/uts/intel/io/acpica/debugger/dbxface.c536
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c628
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmcstyle.c877
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmdeferred.c264
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmnames.c45
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmobject.c589
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmopcode.c696
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmresrc.c61
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c136
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmresrcl2.c750
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c102
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmutils.c91
-rw-r--r--usr/src/uts/intel/io/acpica/disassembler/dmwalk.c316
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsargs.c48
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c71
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsdebug.c250
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsfield.c253
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsinit.c86
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c270
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c61
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsobject.c109
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c106
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dsutils.c122
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dswexec.c92
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dswload.c86
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dswload2.c78
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dswscope.c7
-rw-r--r--usr/src/uts/intel/io/acpica/dispatcher/dswstate.c28
-rw-r--r--usr/src/uts/intel/io/acpica/events/evevent.c46
-rw-r--r--usr/src/uts/intel/io/acpica/events/evglock.c24
-rw-r--r--usr/src/uts/intel/io/acpica/events/evgpe.c231
-rw-r--r--usr/src/uts/intel/io/acpica/events/evgpeblk.c70
-rw-r--r--usr/src/uts/intel/io/acpica/events/evgpeinit.c63
-rw-r--r--usr/src/uts/intel/io/acpica/events/evgpeutil.c112
-rw-r--r--usr/src/uts/intel/io/acpica/events/evhandler.c606
-rw-r--r--usr/src/uts/intel/io/acpica/events/evmisc.c208
-rw-r--r--usr/src/uts/intel/io/acpica/events/evregion.c876
-rw-r--r--usr/src/uts/intel/io/acpica/events/evrgnini.c124
-rw-r--r--usr/src/uts/intel/io/acpica/events/evsci.c99
-rw-r--r--usr/src/uts/intel/io/acpica/events/evxface.c1008
-rw-r--r--usr/src/uts/intel/io/acpica/events/evxfevnt.c88
-rw-r--r--usr/src/uts/intel/io/acpica/events/evxfgpe.c265
-rw-r--r--usr/src/uts/intel/io/acpica/events/evxfregn.c46
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exconcat.c460
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exconfig.c148
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exconvrt.c66
-rw-r--r--usr/src/uts/intel/io/acpica/executer/excreate.c73
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exdebug.c128
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exdump.c378
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exfield.c238
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exfldio.c146
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exmisc.c308
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exmutex.c81
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exnames.c26
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exoparg1.c176
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exoparg2.c78
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exoparg3.c50
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exoparg6.c41
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exprep.c116
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exregion.c65
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exresnte.c38
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exresolv.c69
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exresop.c66
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exstore.c283
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exstoren.c34
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exstorob.c30
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exsystem.c34
-rw-r--r--usr/src/uts/intel/io/acpica/executer/extrace.c427
-rw-r--r--usr/src/uts/intel/io/acpica/executer/exutils.c194
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwacpi.c41
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwesleep.c259
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwgpe.c117
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwpci.c30
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwregs.c78
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwsleep.c421
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwtimer.c31
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwvalid.c21
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwxface.c252
-rw-r--r--usr/src/uts/intel/io/acpica/hardware/hwxfsleep.c501
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsaccess.c48
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsalloc.c35
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsarguments.c305
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsconvert.c566
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsdump.c264
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c12
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nseval.c277
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsinit.c252
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsload.c62
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsnames.c317
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsobject.c40
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsparse.c69
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nspredef.c1045
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsprepkg.c788
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsrepair.c620
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsrepair2.c420
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nssearch.c47
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsutils.c177
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nswalk.c47
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsxfeval.c370
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsxfname.c112
-rw-r--r--usr/src/uts/intel/io/acpica/namespace/nsxfobj.c17
-rw-r--r--usr/src/uts/intel/io/acpica/osl.c45
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psargs.c213
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psloop.c687
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psobject.c686
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psopcode.c200
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psopinfo.c284
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psparse.c73
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psscope.c14
-rw-r--r--usr/src/uts/intel/io/acpica/parser/pstree.c20
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psutils.c39
-rw-r--r--usr/src/uts/intel/io/acpica/parser/pswalk.c4
-rw-r--r--usr/src/uts/intel/io/acpica/parser/psxface.c172
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsaddr.c41
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rscalc.c154
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rscreate.c207
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsdump.c559
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsdumpinfo.c360
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsinfo.c65
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsio.c6
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsirq.c64
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rslist.c94
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsmemory.c6
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsmisc.c342
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsserial.c439
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsutils.c148
-rw-r--r--usr/src/uts/intel/io/acpica/resources/rsxface.c198
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbdata.c869
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbfadt.c476
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbfind.c47
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbinstal.c809
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbprint.c272
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbutils.c493
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbxface.c261
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbxfload.c538
-rw-r--r--usr/src/uts/intel/io/acpica/tables/tbxfroot.c90
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utaddress.c324
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utalloc.c165
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utascii.c161
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utbuffer.c362
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utcache.c48
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utclib.c441
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utcopy.c176
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utdebug.c401
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utdecode.c251
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utdelete.c241
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/uterror.c325
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/uteval.c31
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utexcep.c179
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utglobal.c230
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/uthex.c111
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utids.c156
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utinit.c217
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utlock.c5
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utmath.c42
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utmisc.c1008
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utmutex.c70
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utnonansi.c667
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utobject.c61
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utosi.c186
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utownerid.c239
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utpredef.c452
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utprint.c812
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utresrc.c349
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utstate.c86
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utstring.c277
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/uttrack.c268
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utuuid.c103
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utxface.c546
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utxferror.c251
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utxfinit.c340
-rw-r--r--usr/src/uts/intel/io/acpica/utilities/utxfmutex.c211
-rw-r--r--usr/src/uts/intel/io/dktp/dcdev/dadk.c48
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmivars.h1
-rw-r--r--usr/src/uts/intel/io/pci/pci_resource.c14
-rw-r--r--usr/src/uts/intel/io/vmxnet/buildNumber.h12
-rw-r--r--usr/src/uts/intel/io/vmxnet/includeCheck.h159
-rw-r--r--usr/src/uts/intel/io/vmxnet/net.h220
-rw-r--r--usr/src/uts/intel/io/vmxnet/net_sg.h84
-rw-r--r--usr/src/uts/intel/io/vmxnet/vm_basic_types.h1037
-rw-r--r--usr/src/uts/intel/io/vmxnet/vm_device_version.h246
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmnet_def.h91
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet.c2438
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet.conf24
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet2_def.h436
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet_def.h184
-rw-r--r--usr/src/uts/intel/ipf/Makefile2
-rw-r--r--usr/src/uts/intel/ipf/ipf.global-objs.debug648
-rw-r--r--usr/src/uts/intel/iptun/Makefile1
-rw-r--r--usr/src/uts/intel/ixgbe/Makefile2
-rw-r--r--usr/src/uts/intel/kdi/kdi_idt.c3
-rw-r--r--usr/src/uts/intel/lx_brand/Makefile111
-rw-r--r--usr/src/uts/intel/lx_brand/Makefile.rules100
-rw-r--r--usr/src/uts/intel/lx_cgroup/Makefile57
-rw-r--r--usr/src/uts/intel/lx_cgroup/Makefile.rules21
-rw-r--r--usr/src/uts/intel/lx_devfs/Makefile57
-rw-r--r--usr/src/uts/intel/lx_devfs/Makefile.rules21
-rw-r--r--usr/src/uts/intel/lx_netlink/Makefile77
-rw-r--r--usr/src/uts/intel/lx_proc/Makefile117
-rw-r--r--usr/src/uts/intel/lx_proc/Makefile.rules38
-rw-r--r--usr/src/uts/intel/lx_ptm/Makefile91
-rw-r--r--usr/src/uts/intel/lx_sysfs/Makefile66
-rw-r--r--usr/src/uts/intel/lx_sysfs/Makefile.rules21
-rw-r--r--usr/src/uts/intel/lx_systrace/Makefile80
-rw-r--r--usr/src/uts/intel/lxautofs/Makefile114
-rw-r--r--usr/src/uts/intel/lxautofs/Makefile.rules38
-rw-r--r--usr/src/uts/intel/lxprocfs/Makefile88
-rw-r--r--usr/src/uts/intel/mac/Makefile1
-rw-r--r--usr/src/uts/intel/mac_ether/Makefile1
-rw-r--r--usr/src/uts/intel/mac_ib/Makefile1
-rw-r--r--usr/src/uts/intel/mac_wifi/Makefile1
-rw-r--r--usr/src/uts/intel/nfp/Makefile82
-rw-r--r--usr/src/uts/intel/opteron_pcbe/Makefile2
-rw-r--r--usr/src/uts/intel/os/driver_aliases1
-rw-r--r--usr/src/uts/intel/os/name_to_major1
-rw-r--r--usr/src/uts/intel/overlay/Makefile54
-rw-r--r--usr/src/uts/intel/p123_pcbe/Makefile4
-rw-r--r--usr/src/uts/intel/p4_pcbe/Makefile2
-rw-r--r--usr/src/uts/intel/pci_autoconfig/Makefile4
-rw-r--r--usr/src/uts/intel/power/Makefile4
-rw-r--r--usr/src/uts/intel/promif/prom_emul.c6
-rw-r--r--usr/src/uts/intel/smrt/Makefile72
-rw-r--r--usr/src/uts/intel/sockpfp/Makefile2
-rw-r--r--usr/src/uts/intel/spdsock/Makefile2
-rw-r--r--usr/src/uts/intel/sys/acpi/acapps.h118
-rw-r--r--usr/src/uts/intel/sys/acpi/acbuffer.h255
-rw-r--r--usr/src/uts/intel/sys/acpi/acclib.h167
-rw-r--r--usr/src/uts/intel/sys/acpi/accommon.h5
-rw-r--r--usr/src/uts/intel/sys/acpi/acconfig.h73
-rw-r--r--usr/src/uts/intel/sys/acpi/acdebug.h166
-rw-r--r--usr/src/uts/intel/sys/acpi/acdisasm.h542
-rw-r--r--usr/src/uts/intel/sys/acpi/acdispat.h27
-rw-r--r--usr/src/uts/intel/sys/acpi/acevents.h88
-rw-r--r--usr/src/uts/intel/sys/acpi/acexcep.h442
-rw-r--r--usr/src/uts/intel/sys/acpi/acglobal.h446
-rw-r--r--usr/src/uts/intel/sys/acpi/achware.h60
-rw-r--r--usr/src/uts/intel/sys/acpi/acinterp.h56
-rw-r--r--usr/src/uts/intel/sys/acpi/aclocal.h321
-rw-r--r--usr/src/uts/intel/sys/acpi/acmacros.h284
-rw-r--r--usr/src/uts/intel/sys/acpi/acnames.h41
-rw-r--r--usr/src/uts/intel/sys/acpi/acnamesp.h152
-rw-r--r--usr/src/uts/intel/sys/acpi/acobject.h53
-rw-r--r--usr/src/uts/intel/sys/acpi/acopcode.h28
-rw-r--r--usr/src/uts/intel/sys/acpi/acoutput.h222
-rw-r--r--usr/src/uts/intel/sys/acpi/acparser.h55
-rw-r--r--usr/src/uts/intel/sys/acpi/acpi.h3
-rw-r--r--usr/src/uts/intel/sys/acpi/acpi_pci.h10
-rw-r--r--usr/src/uts/intel/sys/acpi/acpiosxf.h219
-rw-r--r--usr/src/uts/intel/sys/acpi/acpixf.h874
-rw-r--r--usr/src/uts/intel/sys/acpi/acpredef.h1323
-rw-r--r--usr/src/uts/intel/sys/acpi/acresrc.h138
-rw-r--r--usr/src/uts/intel/sys/acpi/acrestyp.h295
-rw-r--r--usr/src/uts/intel/sys/acpi/acstruct.h55
-rw-r--r--usr/src/uts/intel/sys/acpi/actables.h126
-rw-r--r--usr/src/uts/intel/sys/acpi/actbl.h122
-rw-r--r--usr/src/uts/intel/sys/acpi/actbl1.h486
-rw-r--r--usr/src/uts/intel/sys/acpi/actbl2.h657
-rw-r--r--usr/src/uts/intel/sys/acpi/actbl3.h925
-rw-r--r--usr/src/uts/intel/sys/acpi/actypes.h339
-rw-r--r--usr/src/uts/intel/sys/acpi/acutils.h572
-rw-r--r--usr/src/uts/intel/sys/acpi/acuuid.h90
-rw-r--r--usr/src/uts/intel/sys/acpi/amlcode.h49
-rw-r--r--usr/src/uts/intel/sys/acpi/amlresrc.h218
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/accygwin.h15
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acdragonfly.h128
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acdragonflyex.h84
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acefi.h208
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acefiex.h855
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acenv.h154
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acenvex.h75
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acfreebsd.h14
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acgcc.h21
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/achaiku.h106
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acintel.h2
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/aclinux.h169
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/aclinuxex.h158
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acmacosx.h58
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acmsvc.h70
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acmsvcex.h54
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acnetbsd.h25
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acos2.h2
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acqnx.h74
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acsolaris.h23
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acwin.h2
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acwin64.h3
-rw-r--r--usr/src/uts/intel/sys/acpi/platform/acwinex.h52
-rw-r--r--usr/src/uts/intel/sys/machbrand.h12
-rw-r--r--usr/src/uts/intel/sys/machlock.h5
-rw-r--r--usr/src/uts/intel/sys/segments.h9
-rw-r--r--usr/src/uts/intel/sys/ucontext.h15
-rw-r--r--usr/src/uts/intel/sys/x86_archext.h19
-rw-r--r--usr/src/uts/intel/udmf/Makefile74
-rw-r--r--usr/src/uts/intel/upf/Makefile74
-rw-r--r--usr/src/uts/intel/urf/Makefile74
-rw-r--r--usr/src/uts/intel/usbgem/Makefile84
-rw-r--r--usr/src/uts/intel/vmxnet/Makefile93
-rw-r--r--usr/src/uts/intel/vnd/Makefile56
-rw-r--r--usr/src/uts/intel/vxlan/Makefile51
-rw-r--r--usr/src/uts/intel/zfd/Makefile48
-rw-r--r--usr/src/uts/sparc/Makefile.sparc5
-rw-r--r--usr/src/uts/sparc/bpf/Makefile2
-rw-r--r--usr/src/uts/sparc/datafilt/Makefile73
-rw-r--r--usr/src/uts/sparc/dld/Makefile1
-rw-r--r--usr/src/uts/sparc/dls/Makefile1
-rw-r--r--usr/src/uts/sparc/icmp/Makefile3
-rw-r--r--usr/src/uts/sparc/inotify/Makefile70
-rw-r--r--usr/src/uts/sparc/ipf/ipf.global-objs.debug644
-rw-r--r--usr/src/uts/sparc/iptun/Makefile1
-rw-r--r--usr/src/uts/sparc/mac/Makefile1
-rw-r--r--usr/src/uts/sparc/mac_ether/Makefile1
-rw-r--r--usr/src/uts/sparc/mac_ib/Makefile1
-rw-r--r--usr/src/uts/sparc/mac_wifi/Makefile1
-rw-r--r--usr/src/uts/sparc/sockpfp/Makefile2
-rw-r--r--usr/src/uts/sparc/spdsock/Makefile5
-rw-r--r--usr/src/uts/sparc/syscall/getcontext.c31
-rw-r--r--usr/src/uts/sparc/zfd/Makefile50
-rw-r--r--usr/src/uts/sun4/brand/common/brand_solaris.s2
-rw-r--r--usr/src/uts/sun4/ml/offsets.in2
-rw-r--r--usr/src/uts/sun4/os/machdep.c11
-rw-r--r--usr/src/uts/sun4u/io/pci/pcisch.c5
911 files changed, 178081 insertions, 28481 deletions
diff --git a/usr/src/uts/Makefile.mapfile b/usr/src/uts/Makefile.mapfile
new file mode 100644
index 0000000000..45ab0ae22b
--- /dev/null
+++ b/usr/src/uts/Makefile.mapfile
@@ -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 2016 Joyent, Inc.
+#
+
+#
+# This Makefile should be included in individual kernel modules to opt
+# into a -z defs world. Note, this should be included after a MAPFILES
+# entry is defined. See uts/common/README.mapfiles for more information.
+#
+
+#
+# Mapfile base
+#
+MAPBASE = $(UTSBASE)/common/mapfiles
+DTRACE_MAPFILE = $(OBJS_DIR)/dtrace.mapfile
+
+#
+# Always append -z defs to the LD FLAGS and append all mapfiles.
+#
+LDFLAGS += -z defs $(MAPFILES:%=-M $(MAPBASE)/%.mapfile) -M $(DTRACE_MAPFILE)
+
+#
+# Definitions and rulesto assemble the DTrace probe mapfile. There's no
+# good way to automatically do this, hence we have a slightly gross
+# series of automated tools. This does mean that we have a bit more work
+# to do, but also means that probes can be added arbitrarily without
+# having to manually edit mapfiles.
+#
+DTRACE_AWK_FILE = $(MAPBASE)/dtrace.mapfile.awk
+
+$(OBJS_DIR)/dtrace.mapfile: $(OBJECTS) $(DTRACE_AWK_FILE)
+ $(NM) -u $(OBJECTS) | $(AWK) -f $(DTRACE_AWK_FILE) > $@
diff --git a/usr/src/uts/Makefile.targ b/usr/src/uts/Makefile.targ
index 034817dc31..72ca278100 100644
--- a/usr/src/uts/Makefile.targ
+++ b/usr/src/uts/Makefile.targ
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015, Joyent, Inc.
#
# This Makefiles contains the common targets and definitions for
# all kernels. It is to be included in the Makefiles for specific
@@ -50,7 +51,7 @@ $(OBJECTS): $(INLINES)
# ELFSIGN_MOD is defined in the individual KCF plug-in modules Makefiles,
# and will sign the ELF objects using elfsign(1).
#
-$(BINARY): $(OBJECTS)
+$(BINARY): $(OBJECTS) $(DTRACE_MAPFILE)
$(LD) -r $(LDFLAGS) -o $@ $(OBJECTS)
$(CTFMERGE_UNIQUIFY_AGAINST_GENUNIX)
$(POST_PROCESS)
@@ -205,6 +206,9 @@ $(ROOT_FONT_DIR)/%: $(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_FONT_DIR) FRC
$(ROOT_MAC_DIR)/%: $(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_MAC_DIR) FRC
$(INS.file)
+$(ROOT_OVERLAY_DIR)/%: $(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_OVERLAY_DIR) FRC
+ $(INS.file)
+
$(USR_DRV_DIR)/%: $(OBJS_DIR)/% $(USR_DRV_DIR) FRC
$(INS.file)
diff --git a/usr/src/uts/Makefile.uts b/usr/src/uts/Makefile.uts
index 6a418b34e7..273949fb74 100644
--- a/usr/src/uts/Makefile.uts
+++ b/usr/src/uts/Makefile.uts
@@ -24,6 +24,7 @@
# Copyright (c) 2011 Bayard G. Bell. All rights reserved.
# Copyright (c) 2011 by Delphix. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
+# Copyright 2015 Joyent, Inc.
#
#
@@ -213,6 +214,7 @@ AS_CPPFLAGS = $(ALWAYS_DEFS) $(ALL_DEFS) $(CONFIG_DEFS) $(AS_DEFS) \
# Override the default, the kernel is squeaky clean
CERRWARN = -errtags=yes -errwarn=%all
+$(__GNUC4)CERRWARN += -_gcc=-Wno-address
CERRWARN += -_gcc=-Wno-missing-braces
CERRWARN += -_gcc=-Wno-sign-compare
@@ -412,6 +414,7 @@ ROOT_FONT_DIR_32 = $(ROOT_MOD_DIR)/fonts
ROOT_DACF_DIR_32 = $(ROOT_MOD_DIR)/dacf
ROOT_CRYPTO_DIR_32 = $(ROOT_MOD_DIR)/crypto
ROOT_MAC_DIR_32 = $(ROOT_MOD_DIR)/mac
+ROOT_OVERLAY_DIR_32 = $(ROOT_MOD_DIR)/overlay
ROOT_KICONV_DIR_32 = $(ROOT_MOD_DIR)/kiconv
ROOT_KERN_DIR_64 = $(ROOT_MOD_DIR)/$(SUBDIR64)
@@ -439,6 +442,7 @@ ROOT_FONT_DIR_64 = $(ROOT_MOD_DIR)/fonts/$(SUBDIR64)
ROOT_DACF_DIR_64 = $(ROOT_MOD_DIR)/dacf/$(SUBDIR64)
ROOT_CRYPTO_DIR_64 = $(ROOT_MOD_DIR)/crypto/$(SUBDIR64)
ROOT_MAC_DIR_64 = $(ROOT_MOD_DIR)/mac/$(SUBDIR64)
+ROOT_OVERLAY_DIR_64 = $(ROOT_MOD_DIR)/overlay/$(SUBDIR64)
ROOT_KICONV_DIR_64 = $(ROOT_MOD_DIR)/kiconv/$(SUBDIR64)
ROOT_KERN_DIR = $(ROOT_KERN_DIR_$(CLASS))
@@ -466,6 +470,7 @@ ROOT_FONT_DIR = $(ROOT_FONT_DIR_$(CLASS))
ROOT_DACF_DIR = $(ROOT_DACF_DIR_$(CLASS))
ROOT_CRYPTO_DIR = $(ROOT_CRYPTO_DIR_$(CLASS))
ROOT_MAC_DIR = $(ROOT_MAC_DIR_$(CLASS))
+ROOT_OVERLAY_DIR = $(ROOT_OVERLAY_DIR_$(CLASS))
ROOT_KICONV_DIR = $(ROOT_KICONV_DIR_$(CLASS))
ROOT_MOD_DIRS_32 = $(ROOT_BRAND_DIR_32) $(ROOT_DRV_DIR_32)
@@ -483,6 +488,7 @@ ROOT_MOD_DIRS_32 += $(ROOT_EMLXS_FW_DIR_32)
ROOT_MOD_DIRS_32 += $(ROOT_CPU_DIR_32) $(ROOT_FONT_DIR_32)
ROOT_MOD_DIRS_32 += $(ROOT_TOD_DIR_32) $(ROOT_DACF_DIR_32)
ROOT_MOD_DIRS_32 += $(ROOT_CRYPTO_DIR_32) $(ROOT_MAC_DIR_32)
+ROOT_MOD_DIRS_32 += $(ROOT_OVERLAY_DIR_32)
ROOT_MOD_DIRS_32 += $(ROOT_KICONV_DIR_32)
USR_MOD_DIR = $(ROOT)/usr/kernel
@@ -575,7 +581,7 @@ PARALLEL_KMODS = $(DRV_KMODS) $(EXEC_KMODS) $(FS_KMODS) $(SCHED_KMODS) \
$(MMU_KMODS) $(DACF_KMODS) $(EXPORT_KMODS) $(IPP_KMODS) \
$(CRYPTO_KMODS) $(PCBE_KMODS) \
$(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS) \
- $(BRAND_KMODS) $(KICONV_KMODS) \
+ $(BRAND_KMODS) $(KICONV_KMODS) $(OVERLAY_KMODS) \
$(SOCKET_KMODS)
KMODS = $(GENUNIX_KMODS) $(PARALLEL_KMODS)
@@ -629,3 +635,11 @@ PRIVS_DEF = $(SRC)/uts/common/os/priv_defs
#
USBDEVS_AWK = $(SRC)/uts/common/io/usb/usbdevs2h.awk
USBDEVS_DATA = $(SRC)/uts/common/io/usb/usbdevs
+
+
+#
+# If we're using the newer CTF tools, then we need to make sure that we
+# are building with the private -X option to ctfconvert which allows us
+# to fixup the struct cpu to account for machcpu.
+#
+$(ENABLE_NEW_CTF)$(_GNUC)CTFCVTFLAGS += -X
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index fc68987bbe..a19f386b12 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -24,6 +24,7 @@
# Copyright (c) 2011, 2014 by Delphix. All rights reserved.
# Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2016 Joyent, Inc.
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
# Copyright 2016 Joyent, Inc.
# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
@@ -283,6 +284,7 @@ GENUNIX_OBJS += \
rctl.o \
rctlsys.o \
readlink.o \
+ refhash.o \
refstr.o \
rename.o \
resolvepath.o \
@@ -304,6 +306,7 @@ GENUNIX_OBJS += \
seg_map.o \
seg_vn.o \
seg_spt.o \
+ seg_umap.o \
semaphore.o \
sendfile.o \
session.o \
@@ -429,6 +432,8 @@ PROFILE_OBJS += profile.o
SYSTRACE_OBJS += systrace.o
+LX_SYSTRACE_OBJS += lx_systrace.o
+
LOCKSTAT_OBJS += lockstat.o
FASTTRAP_OBJS += fasttrap.o fasttrap_isa.o
@@ -493,6 +498,10 @@ PTSL_OBJS += tty_pts.o
PTM_OBJS += ptm.o
+LX_PTM_OBJS += lx_ptm.o
+
+LX_NETLINK_OBJS += lx_netlink.o
+
MII_OBJS += mii.o mii_cicada.o mii_natsemi.o mii_intel.o mii_qualsemi.o \
mii_marvell.o mii_realtek.o mii_other.o
@@ -550,6 +559,7 @@ IP_SCTP_OBJS = sctp.o sctp_opt_data.o sctp_output.o \
sctp_addr.o tn_ipopt.o tnet.o ip_netinfo.o \
sctp_misc.o
IP_ILB_OBJS = ilb.o ilb_nat.o ilb_conn.o ilb_alg_hash.o ilb_alg_rr.o
+IP_COMM_OBJS = inet_hash.o
IP_OBJS += igmp.o ipmp.o ip.o ip6.o ip6_asp.o ip6_if.o ip6_ire.o \
ip6_rts.o ip_if.o ip_ire.o ip_listutils.o ip_mroute.o \
@@ -560,12 +570,14 @@ IP_OBJS += igmp.o ipmp.o ip.o ip6.o ip6_asp.o ip6_if.o ip6_ire.o \
ip_helper_stream.o ip_tunables.o \
ip_output.o ip_input.o ip6_input.o ip6_output.o ip_arp.o \
conn_opt.o ip_attr.o ip_dce.o \
+ bpf_filter.o \
$(IP_ICMP_OBJS) \
$(IP_RTS_OBJS) \
$(IP_TCP_OBJS) \
$(IP_UDP_OBJS) \
$(IP_SCTP_OBJS) \
- $(IP_ILB_OBJS)
+ $(IP_ILB_OBJS) \
+ $(IP_COMM_OBJS)
IP6_OBJS += ip6ddi.o
@@ -583,6 +595,8 @@ IPSECESP_OBJS += ipsecespddi.o ipsecesp.o
IPSECAH_OBJS += ipsecahddi.o ipsecah.o sadb.o
+DATAFILT_OBJS += datafilt.o
+
SPPP_OBJS += sppp.o sppp_dlpi.o sppp_mod.o s_common.o
SPPPTUN_OBJS += sppptun.o sppptun_mod.o
@@ -636,7 +650,7 @@ TL_OBJS += tl.o
DUMP_OBJS += dump.o
-BPF_OBJS += bpf.o bpf_filter.o bpf_mod.o bpf_dlt.o bpf_mac.o
+BPF_OBJS += bpf.o bpf_wrap.o bpf_mod.o bpf_dlt.o bpf_mac.o
CLONE_OBJS += clone.o
@@ -680,6 +694,15 @@ NET80211_OBJS += net80211.o net80211_proto.o net80211_input.o \
VNIC_OBJS += vnic_ctl.o vnic_dev.o
+OVERLAY_OBJS += overlay.o overlay_fm.o overlay_mux.o overlay_plugin.o \
+ overlay_prop.o overlay_target.o
+
+OVERLAY_VXLAN_OBJS += overlay_vxlan.o
+
+VND_OBJS += vnd.o frameio.o
+
+GSQUEUE_OBJS += gsqueue.o
+
SIMNET_OBJS += simnet.o
IB_OBJS += ibnex.o ibnex_ioctl.o ibnex_hca.o
@@ -998,6 +1021,8 @@ SIGNALFD_OBJS += signalfd.o
I8042_OBJS += i8042.o
+INOTIFY_OBJS += inotify.o
+
KB8042_OBJS += \
at_keyprocess.o \
kb8042.o \
@@ -1072,6 +1097,8 @@ QLGE_OBJS += qlge.o qlge_dbg.o qlge_flash.o qlge_fm.o qlge_gld.o qlge_mpi.o
ZCONS_OBJS += zcons.o
+ZFD_OBJS += zfd.o
+
NV_SATA_OBJS += nv_sata.o
SI3124_OBJS += si3124.o
@@ -1125,8 +1152,7 @@ DEVFS_OBJS += devfs_subr.o devfs_vfsops.o devfs_vnops.o
DEV_OBJS += sdev_subr.o sdev_vfsops.o sdev_vnops.o \
sdev_ptsops.o sdev_zvolops.o sdev_comm.o \
sdev_profile.o sdev_ncache.o sdev_netops.o \
- sdev_ipnetops.o \
- sdev_vtops.o
+ sdev_ipnetops.o sdev_vtops.o sdev_plugin.o
CTFS_OBJS += ctfs_all.o ctfs_cdir.o ctfs_ctl.o ctfs_event.o \
ctfs_latest.o ctfs_root.o ctfs_sym.o ctfs_tdir.o ctfs_tmpl.o
@@ -1143,8 +1169,13 @@ PIPE_OBJS += pipe.o
HSFS_OBJS += hsfs_node.o hsfs_subr.o hsfs_vfsops.o hsfs_vnops.o \
hsfs_susp.o hsfs_rrip.o hsfs_susp_subr.o
+HYPRLOFS_OBJS += hyprlofs_dir.o hyprlofs_subr.o \
+ hyprlofs_vnops.o hyprlofs_vfsops.o
+
LOFS_OBJS += lofs_subr.o lofs_vfsops.o lofs_vnops.o
+LXPROC_OBJS += lxpr_subr.o lxpr_vfsops.o lxpr_vnops.o
+
NAMEFS_OBJS += namevfs.o namevno.o
NFS_OBJS += nfs_client.o nfs_common.o nfs_dump.o \
@@ -1296,8 +1327,8 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
PCFS_OBJS += pc_alloc.o pc_dir.o pc_node.o pc_subr.o \
pc_vfsops.o pc_vnops.o
-PROC_OBJS += prcontrol.o prioctl.o prsubr.o prusrio.o \
- prvfsops.o prvnops.o
+PROC_OBJS += prargv.o prcontrol.o prioctl.o prsubr.o \
+ prusrio.o prvfsops.o prvnops.o
MNTFS_OBJS += mntvfsops.o mntvnops.o
@@ -1439,6 +1470,7 @@ ZFS_COMMON_OBJS += \
zfs_fuid.o \
zfs_sa.o \
zfs_znode.o \
+ zfs_zone.o \
zil.o \
zio.o \
zio_checksum.o \
@@ -1885,7 +1917,7 @@ ZYD_OBJS += zyd.o zyd_usb.o zyd_hw.o zyd_fw.o
MXFE_OBJS += mxfe.o
-MPTSAS_OBJS += mptsas.o mptsas_hash.o mptsas_impl.o mptsas_init.o \
+MPTSAS_OBJS += mptsas.o mptsas_impl.o mptsas_init.o \
mptsas_raid.o mptsas_smhba.o
SFE_OBJS += sfe.o sfe_util.o
@@ -2119,6 +2151,11 @@ MEGA_SAS_OBJS = megaraid_sas.o
MR_SAS_OBJS = ld_pd_map.o mr_sas.o mr_sas_tbolt.o mr_sas_list.o
#
+# DR_SAS module
+#
+DR_SAS_OBJS = dr_sas.o
+
+#
# CPQARY3 module
#
CPQARY3_OBJS = cpqary3.o cpqary3_noe.o cpqary3_talk2ctlr.o \
@@ -2127,6 +2164,18 @@ CPQARY3_OBJS = cpqary3.o cpqary3_noe.o cpqary3_talk2ctlr.o \
cpqary3_bd.o
#
+# HP Smart Array driver module (smrt)
+#
+SMRT_OBJS = smrt.o \
+ smrt_device.o \
+ smrt_interrupts.o \
+ smrt_commands.o \
+ smrt_logvol.o \
+ smrt_hba.o \
+ smrt_ciss_simple.o \
+ smrt_ciss.o
+
+#
# ISCSI_INITIATOR module
#
ISCSI_INITIATOR_OBJS = chap.o iscsi_io.o iscsi_thread.o \
@@ -2157,6 +2206,20 @@ NULLDRIVER_OBJS = nulldriver.o
TPM_OBJS = tpm.o tpm_hcall.o
#
+# USB Fast ethernet drivers
+#
+USBGEM_OBJS = usbgem.o
+AXF_OBJS = axf_usbgem.o
+UDMF_OBJS = udmf_usbgem.o
+URF_OBJS = urf_usbgem.o
+UPF_OBJS = upf_usbgem.o
+
+#
+# NFP objects
+#
+NFP_OBJS = hostif.o osif.o drvlist.o i21555.o i21285.o i21555d.o
+
+#
# BNXE objects
#
BNXE_OBJS += bnxe_cfg.o \
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index d4fbb8c276..0504ac065b 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -23,6 +23,7 @@
# Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2016 Garrett D'Amore <garrett@damore.org>
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2016 Joyent, Inc.
# Copyright 2013 Saso Kiselkov. All rights reserved.
# Copyright 2016 Joyent, Inc.
#
@@ -97,6 +98,10 @@ $(OBJS_DIR)/%.o: $(COMMONBASE)/avl/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(COMMONBASE)/inet/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(COMMONBASE)/ucode/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -245,10 +250,18 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/hsfs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/hyprlofs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/lofs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/lxproc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/mntfs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -760,6 +773,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/drm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/dr_sas/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/efe/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -988,6 +1005,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/net80211/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nfp/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nge/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1007,6 +1028,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nxge/npi/%.c
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nxge/%.s
$(COMPILE.s) -o $@ $<
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/overlay/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/overlay/plugins/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pci-ide/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1103,6 +1132,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/scsi/adapters/scsi_vhci/fops/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/scsi/adapters/smrt/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/fibre-channel/ulp/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1143,6 +1176,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/sdcard/targets/sdcard/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/gsqueue/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/sfe/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1155,6 +1192,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/softmac/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vnd/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/uath/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1279,6 +1320,30 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/usba10/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usbgem/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/axf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/udf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/udmf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/upf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/urf/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vuidmice/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1471,9 +1536,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vioblk/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(COMMONBASE)/idspace/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vioif/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+
#
# krtld must refer to its own bzero/bcopy until the kernel is fully linked
#
@@ -1542,6 +1612,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/pcmcia/pcs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/refhash/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/rpc/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1665,6 +1739,9 @@ $(LINTS_DIR)/%.ln: $(COMMONBASE)/acl/%.c
$(LINTS_DIR)/%.ln: $(COMMONBASE)/avl/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(COMMONBASE)/inet/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(COMMONBASE)/ucode/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -1800,9 +1877,15 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/fifofs/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/hsfs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/hyprlofs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/lofs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/lxproc/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/mntfs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2148,6 +2231,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/dmfe/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/drm/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/dr_sas/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/efe/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2319,6 +2405,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mwl/mwl_fw/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/net80211/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/nfp/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/nge/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2334,6 +2423,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/nxge/%.s
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/nxge/npi/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/overlay/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/overlay/plugins/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pci-ide/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2400,6 +2495,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/scsi/adapters/scsi_vhci/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/scsi/adapters/scsi_vhci/fops/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/scsi/adapters/smrt/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/fibre-channel/ulp/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2541,6 +2639,21 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usba/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usba10/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usbgem/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/axf/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/udmf/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/upf/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/urf/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/vuidmice/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2694,6 +2807,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/pcmcia/nexus/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/pcmcia/pcs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/refhash/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/rpc/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
@@ -2787,3 +2903,6 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/skd/%.c
$(LINTS_DIR)/%.ln: $(COMMONBASE)/fsreparse/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(COMMONBASE)/idspace/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
new file mode 100644
index 0000000000..c55fc6d95f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
@@ -0,0 +1,3152 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * See the big theory statement in ../sys/lx_autofs.h
+ */
+
+#include <fs/fs_subr.h>
+#include <sys/stat.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#include <sys/dirent.h>
+#include <sys/fs/fifonode.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/sunddi.h>
+#include <sys/conf.h>
+#include <sys/sdt.h>
+
+#include <sys/sysmacros.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+
+#include <sys/dnlc.h>
+#include <nfs/rnode.h>
+#include <nfs/rnode4.h>
+#include <sys/lx_autofs_impl.h>
+#include <sys/lx_types.h>
+
+/*
+ * External functions
+ */
+extern uintptr_t space_fetch(char *key);
+extern int space_store(char *key, uintptr_t ptr);
+extern int umount2_engine(vfs_t *, int, cred_t *, int);
+
+/*
+ * Globals
+ */
+static vfsops_t *lx_autofs_vfsops;
+static vnodeops_t *lx_autofs_vn_ops = NULL;
+static int lx_autofs_fstype;
+static major_t lx_autofs_major;
+static minor_t lx_autofs_minor = 0;
+static dev_info_t *lx_autofs_dip = NULL;
+
+#define LX_AUTOFS_DEV_VERSION_MAJOR 1
+#define LX_AUTOFS_DEV_VERSION_MINOR 0
+
+/* The Linux autofs superblock magic number */
+#define LX_AUTOFS_SB_MAGIC 0x0187
+
+/* Linux autofs mount types */
+#define LX_AUTOFS_TYPE_INDIRECT 1
+#define LX_AUTOFS_TYPE_DIRECT 2
+#define LX_AUTOFS_TYPE_OFFSET 4
+
+/* Structure passed for autofs dev ioctls */
+typedef struct lx_autofs_dv_ioctl {
+ uint32_t lad_ver_major;
+ uint32_t lad_ver_minor;
+ uint32_t lad_size;
+ uint32_t lad_ioctlfd;
+ uint32_t lad_arg1;
+ uint32_t lad_arg2;
+ char lad_path[0];
+} lx_autofs_dv_ioctl_t;
+
+/*
+ * Support functions
+ */
+static void
+lx_autofs_strfree(char *str)
+{
+ kmem_free(str, strlen(str) + 1);
+}
+
+static char *
+lx_autofs_strdup(char *str)
+{
+ int n = strlen(str);
+ char *ptr = kmem_alloc(n + 1, KM_SLEEP);
+ bcopy(str, ptr, n + 1);
+ return (ptr);
+}
+
+static int
+lx_autofs_str_to_int(char *str, int *val)
+{
+ long res;
+
+ if (str == NULL)
+ return (-1);
+
+ if ((ddi_strtol(str, NULL, 10, &res) != 0) ||
+ (res < INT_MIN) || (res > INT_MAX))
+ return (-1);
+
+ *val = res;
+ return (0);
+}
+
+static void
+ls_autofs_stack_init(list_t *lp)
+{
+ list_create(lp,
+ sizeof (stack_elem_t), offsetof(stack_elem_t, se_list));
+}
+
+static void
+lx_autofs_stack_fini(list_t *lp)
+{
+ ASSERT(list_head(lp) == NULL);
+ list_destroy(lp);
+}
+
+static void
+lx_autofs_stack_push(list_t *lp, caddr_t ptr1, caddr_t ptr2, caddr_t ptr3)
+{
+ stack_elem_t *se;
+
+ se = kmem_alloc(sizeof (*se), KM_SLEEP);
+ se->se_ptr1 = ptr1;
+ se->se_ptr2 = ptr2;
+ se->se_ptr3 = ptr3;
+ list_insert_head(lp, se);
+}
+
+static int
+lx_autofs_stack_pop(list_t *lp, caddr_t *ptr1, caddr_t *ptr2, caddr_t *ptr3)
+{
+ stack_elem_t *se;
+
+ if ((se = list_head(lp)) == NULL)
+ return (-1);
+ list_remove(lp, se);
+ if (ptr1 != NULL)
+ *ptr1 = se->se_ptr1;
+ if (ptr2 != NULL)
+ *ptr2 = se->se_ptr2;
+ if (ptr3 != NULL)
+ *ptr3 = se->se_ptr3;
+ kmem_free(se, sizeof (*se));
+ return (0);
+}
+
+static vnode_t *
+lx_autofs_fifo_peer_vp(vnode_t *vp)
+{
+ fifonode_t *fnp = VTOF(vp);
+ fifonode_t *fn_dest = fnp->fn_dest;
+ return (FTOV(fn_dest));
+}
+
+static vnode_t *
+lx_autofs_vn_alloc(vfs_t *vfsp, vnode_t *uvp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *vp, *vp_old;
+
+ /* Allocate a new vnode structure in case we need it. */
+ vp = vn_alloc(KM_SLEEP);
+ vn_setops(vp, lx_autofs_vn_ops);
+ VN_SET_VFS_TYPE_DEV(vp, vfsp, uvp->v_type, uvp->v_rdev);
+ vp->v_data = uvp;
+ ASSERT(vp->v_count == 1);
+
+ /*
+ * Take a hold on the vfs structure. This is how unmount will
+ * determine if there are any active vnodes in the file system.
+ */
+ VFS_HOLD(vfsp);
+
+ /*
+ * Check if we already have a vnode allocated for this underlying
+ * vnode_t.
+ */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_vn_hash,
+ (mod_hash_key_t)uvp, (mod_hash_val_t *)&vp_old) != 0) {
+
+ /*
+ * Didn't find an existing node.
+ * Add this node to the hash and return.
+ */
+ VERIFY(mod_hash_insert(data->lav_vn_hash,
+ (mod_hash_key_t)uvp,
+ (mod_hash_val_t)vp) == 0);
+ mutex_exit(&data->lav_lock);
+ return (vp);
+ }
+
+ /* Get a hold on the existing vnode and free up the one we allocated. */
+ VN_HOLD(vp_old);
+ mutex_exit(&data->lav_lock);
+
+ /* Free up the new vnode we allocated. */
+ VN_RELE(uvp);
+ VFS_RELE(vfsp);
+ vn_invalid(vp);
+ vn_free(vp);
+
+ return (vp_old);
+}
+
+static void
+lx_autofs_vn_free(vnode_t *vp)
+{
+ vfs_t *vfsp = vp->v_vfsp;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *uvp = vp->v_data;
+ vnode_t *vp_tmp;
+
+ ASSERT(MUTEX_HELD((&data->lav_lock)));
+ ASSERT(MUTEX_HELD((&vp->v_lock)));
+
+ ASSERT(vp->v_count == 0);
+
+ /* We're about to free this vnode so take it out of the hash. */
+ (void) mod_hash_remove(data->lav_vn_hash,
+ (mod_hash_key_t)uvp, (mod_hash_val_t)&vp_tmp);
+
+ /*
+ * No one else can lookup this vnode any more so there's no need
+ * to hold locks.
+ */
+ mutex_exit(&data->lav_lock);
+ mutex_exit(&vp->v_lock);
+
+ /* Release the underlying vnode. */
+ VN_RELE(uvp);
+ VFS_RELE(vfsp);
+ vn_invalid(vp);
+ vn_free(vp);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_alloc(lx_autofs_vfs_t *data, boolean_t *is_dup, boolean_t expire,
+ char *nm)
+{
+ lx_autofs_automnt_req_t *laar, *laar_dup;
+
+ /* Pre-allocate a new automounter request before grabbing locks. */
+ laar = kmem_zalloc(sizeof (*laar), KM_SLEEP);
+ mutex_init(&laar->laar_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&laar->laar_cv, NULL, CV_DEFAULT, NULL);
+ laar->laar_ref = 1;
+
+ if (data->lav_min_proto == 5) {
+ laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS5;
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ if (expire) {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_EXPIRE_INDIR;
+ } else {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_MISSING_INDIR;
+ }
+ } else {
+ if (expire) {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_EXPIRE_DIRECT;
+ } else {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_MISSING_DIRECT;
+ }
+ }
+ laar->laar_pkt_size = sizeof (lx_autofs_v5_pkt_t);
+
+ laar->laar_pkt.lap_v5.lap_dev = data->lav_dev;
+ laar->laar_pkt.lap_v5.lap_ino = data->lav_ino;
+ /*
+ * Note that we're currently not filling in the other v5 pkt
+ * fields (pid, uid, etc.) since they don't appear to be used
+ * by the automounter. We can fill those in later if it proves
+ * necessary.
+ */
+
+ /*
+ * For indirect mounts the token expected by the automounter is
+ * the name of the directory entry to look up (not the entire
+ * path that is being accessed.) For direct mounts the Linux
+ * kernel passes a dummy name, so this is just as good.
+ */
+ laar->laar_pkt.lap_v5.lap_name_len = strlen(nm);
+ if (laar->laar_pkt.lap_v5.lap_name_len >
+ (sizeof (laar->laar_pkt.lap_v5.lap_name) - 1)) {
+ zcmn_err(getzoneid(), CE_NOTE,
+ "invalid autofs automnt req: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+ }
+ (void) strlcpy(laar->laar_pkt.lap_v5.lap_name, nm,
+ sizeof (laar->laar_pkt.lap_v5.lap_name));
+
+ } else if (expire) {
+ zcmn_err(getzoneid(), CE_WARN,
+ "unsupported expire protocol request: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+
+ } else {
+ ASSERT(expire == B_FALSE);
+
+ /* Older protocol pkt (really v2) */
+ laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS2;
+ laar->laar_pkt.lap_type = LX_AUTOFS_PTYPE_MISSING;
+ laar->laar_pkt_size = sizeof (lx_autofs_v2_pkt_t);
+
+ /*
+ * The token expected by the linux automount is the name of
+ * the directory entry to look up. (And not the entire
+ * path that is being accessed.)
+ */
+ laar->laar_pkt.lap_v2.lap_name_len = strlen(nm);
+ if (laar->laar_pkt.lap_v2.lap_name_len >
+ (sizeof (laar->laar_pkt.lap_v2.lap_name) - 1)) {
+ zcmn_err(getzoneid(), CE_NOTE,
+ "invalid autofs lookup: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+ }
+ (void) strlcpy(laar->laar_pkt.lap_v2.lap_name, nm,
+ sizeof (laar->laar_pkt.lap_v2.lap_name));
+ }
+
+ /* Assign a unique id for this request. */
+ laar->laar_pkt.lap_id = id_alloc(data->lav_ids);
+
+ /* Check for an outstanding request for this path. */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_path_hash,
+ (mod_hash_key_t)nm, (mod_hash_val_t *)&laar_dup) == 0) {
+ /*
+ * There's already an outstanding request for this
+ * path so we don't need a new one.
+ */
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+ laar = laar_dup;
+
+ /* Bump the ref count on the old request. */
+ atomic_add_int(&laar->laar_ref, 1);
+
+ *is_dup = 1;
+ } else {
+ /* Add it to the hashes. */
+ VERIFY(mod_hash_insert(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)laar) == 0);
+ VERIFY(mod_hash_insert(data->lav_path_hash,
+ (mod_hash_key_t)lx_autofs_strdup(nm),
+ (mod_hash_val_t)laar) == 0);
+
+ *is_dup = 0;
+ }
+ mutex_exit(&data->lav_lock);
+
+ return (laar);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_find(lx_autofs_vfs_t *data, int id)
+{
+ lx_autofs_automnt_req_t *laar;
+
+ /* Check for an outstanding request for this id. */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_id_hash, (mod_hash_key_t)(uintptr_t)id,
+ (mod_hash_val_t *)&laar) != 0) {
+ mutex_exit(&data->lav_lock);
+ return (NULL);
+ }
+ atomic_add_int(&laar->laar_ref, 1);
+ mutex_exit(&data->lav_lock);
+ return (laar);
+}
+
+static void
+lx_autofs_la_complete(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ lx_autofs_automnt_req_t *laar_tmp;
+
+ /* Remove this request from the hashes so no one can look it up. */
+ mutex_enter(&data->lav_lock);
+ (void) mod_hash_remove(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)&laar_tmp);
+ if (data->lav_min_proto == 5) {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ } else {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Mark this requst as complete and wakeup anyone waiting on it. */
+ mutex_enter(&laar->laar_lock);
+ laar->laar_complete = 1;
+ cv_broadcast(&laar->laar_cv);
+ mutex_exit(&laar->laar_lock);
+}
+
+static void
+lx_autofs_la_release(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ ASSERT(!MUTEX_HELD(&laar->laar_lock));
+ if (atomic_add_int_nv(&laar->laar_ref, -1) > 0)
+ return;
+ ASSERT(laar->laar_ref == 0);
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+}
+
+static void
+lx_autofs_la_abort(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ lx_autofs_automnt_req_t *laar_tmp;
+
+ /*
+ * This is a little tricky. We're aborting the wait for this
+ * request. So if anyone else is waiting for this request we
+ * can't free it, but if no one else is waiting for the request
+ * we should free it.
+ */
+ mutex_enter(&data->lav_lock);
+ if (atomic_add_int_nv(&laar->laar_ref, -1) > 0) {
+ mutex_exit(&data->lav_lock);
+ return;
+ }
+ ASSERT(laar->laar_ref == 0);
+
+ /* Remove this request from the hashes so no one can look it up. */
+ (void) mod_hash_remove(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)&laar_tmp);
+ if (data->lav_min_proto == 5) {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ } else {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* It's ok to free this now because the ref count was zero. */
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+}
+
+static int
+lx_autofs_fifo_lookup(pid_t pgrp, int fd, file_t **fpp_wr, file_t **fpp_rd)
+{
+ proc_t *prp;
+ uf_info_t *fip;
+ uf_entry_t *ufp_wr, *ufp_rd = NULL;
+ file_t *fp_wr, *fp_rd = NULL;
+ vnode_t *vp_wr, *vp_rd;
+ int i;
+
+ /*
+ * sprlock() is zone aware, so assuming this mount call was
+ * initiated by a process in a zone, if it tries to specify
+ * a pgrp outside of it's zone this call will fail.
+ *
+ * Also, we want to grab hold of the main automounter process
+ * and its going to be the group leader for pgrp, so its
+ * pid will be equal to pgrp.
+ */
+ prp = sprlock(pgrp);
+ if (prp == NULL)
+ return (-1);
+ mutex_exit(&prp->p_lock);
+
+ /* Now we want to access the processes open file descriptors. */
+ fip = P_FINFO(prp);
+ mutex_enter(&fip->fi_lock);
+
+ /* Sanity check fifo write fd. */
+ if (fd >= fip->fi_nfiles) {
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /* Get a pointer to the write fifo. */
+ UF_ENTER(ufp_wr, fip, fd);
+ if (((fp_wr = ufp_wr->uf_file) == NULL) ||
+ ((vp_wr = fp_wr->f_vnode) == NULL) || (vp_wr->v_type != VFIFO)) {
+ /* Invalid fifo fd. */
+ UF_EXIT(ufp_wr);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * Now we need to find the read end of the fifo (for reasons
+ * explained below.) We assume that the read end of the fifo
+ * is in the same process as the write end.
+ */
+ vp_rd = lx_autofs_fifo_peer_vp(fp_wr->f_vnode);
+ for (i = 0; i < fip->fi_nfiles; i++) {
+ if (i == fd)
+ continue;
+ UF_ENTER(ufp_rd, fip, i);
+ if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+ (fp_rd->f_vnode == vp_rd))
+ break;
+ UF_EXIT(ufp_rd);
+ }
+ if (i == fip->fi_nfiles) {
+ /* Didn't find it. */
+ UF_EXIT(ufp_wr);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * We need to drop fi_lock before we can try to acquire f_tlock
+ * the good news is that the file pointers are protected because
+ * we're still holding uf_lock.
+ */
+ mutex_exit(&fip->fi_lock);
+
+ /*
+ * Here we bump the open counts on the fifos. The reason
+ * that we do this is because when we go to write to the
+ * fifo we want to ensure that they are actually open (and
+ * not in the process of being closed) without having to
+ * stop the automounter. (If the write end of the fifo
+ * were closed and we tried to write to it we would panic.
+ * If the read end of the fifo was closed and we tried to
+ * write to the other end, the process that invoked the
+ * lookup operation would get an unexpected SIGPIPE.)
+ */
+ mutex_enter(&fp_wr->f_tlock);
+ fp_wr->f_count++;
+ ASSERT(fp_wr->f_count >= 2);
+ mutex_exit(&fp_wr->f_tlock);
+
+ mutex_enter(&fp_rd->f_tlock);
+ fp_rd->f_count++;
+ ASSERT(fp_rd->f_count >= 2);
+ mutex_exit(&fp_rd->f_tlock);
+
+ /* Release all our locks. */
+ UF_EXIT(ufp_wr);
+ UF_EXIT(ufp_rd);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+
+ /* Return the file pointers. */
+ *fpp_rd = fp_rd;
+ *fpp_wr = fp_wr;
+ return (0);
+}
+
+static uint_t
+/*ARGSUSED*/
+lx_autofs_fifo_close_cb(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
+{
+ int *id = (int *)arg;
+ /* Return the key and terminate the walk. */
+ *id = (uintptr_t)key;
+ return (MH_WALK_TERMINATE);
+}
+
+static void
+lx_autofs_fifo_close(lx_autofs_vfs_t *data)
+{
+ /*
+ * Close the fifo to prevent any future requests from
+ * getting sent to the automounter.
+ */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr != NULL) {
+ (void) closef(data->lav_fifo_wr);
+ data->lav_fifo_wr = NULL;
+ }
+ if (data->lav_fifo_rd != NULL) {
+ (void) closef(data->lav_fifo_rd);
+ data->lav_fifo_rd = NULL;
+ }
+ mutex_exit(&data->lav_lock);
+
+ /*
+ * Wakeup any threads currently waiting for the automounter
+ * note that it's possible for multiple threads to have entered
+ * this function and to be doing the work below simultaneously.
+ */
+ for (;;) {
+ lx_autofs_automnt_req_t *laar;
+ int id;
+
+ /* Lookup the first entry in the hash. */
+ id = -1;
+ mod_hash_walk(data->lav_id_hash,
+ lx_autofs_fifo_close_cb, &id);
+ if (id == -1) {
+ /* No more id's in the hash. */
+ break;
+ }
+ if ((laar = lx_autofs_la_find(data, id)) == NULL) {
+ /* Someone else beat us to it. */
+ continue;
+ }
+
+ /* Mark the request as complete and release it. */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ }
+}
+
+static int
+lx_autofs_fifo_verify_rd(lx_autofs_vfs_t *data)
+{
+ proc_t *prp;
+ uf_info_t *fip;
+ uf_entry_t *ufp_rd = NULL;
+ file_t *fp_rd = NULL;
+ vnode_t *vp_rd;
+ int i;
+
+ ASSERT(MUTEX_HELD((&data->lav_lock)));
+
+ /* Check if we've already been shut down. */
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ return (-1);
+ }
+ vp_rd = lx_autofs_fifo_peer_vp(data->lav_fifo_wr->f_vnode);
+
+ /*
+ * sprlock() is zone aware, so assuming this mount call was
+ * initiated by a process in a zone, if it tries to specify
+ * a pgrp outside of it's zone this call will fail.
+ *
+ * Also, we want to grab hold of the main automounter process
+ * and its going to be the group leader for pgrp, so its
+ * pid will be equal to pgrp.
+ */
+ prp = sprlock(data->lav_pgrp);
+ if (prp == NULL)
+ return (-1);
+ mutex_exit(&prp->p_lock);
+
+ /* Now we want to access the processes open file descriptors. */
+ fip = P_FINFO(prp);
+ mutex_enter(&fip->fi_lock);
+
+ /*
+ * Now we need to find the read end of the fifo (for reasons
+ * explained below.) We assume that the read end of the fifo
+ * is in the same process as the write end.
+ */
+ for (i = 0; i < fip->fi_nfiles; i++) {
+ UF_ENTER(ufp_rd, fip, i);
+ if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+ (fp_rd->f_vnode == vp_rd))
+ break;
+ UF_EXIT(ufp_rd);
+ }
+ if (i == fip->fi_nfiles) {
+ /* Didn't find it. */
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * Seems the automounter still has the read end of the fifo
+ * open, we're done here. Release all our locks and exit.
+ */
+ mutex_exit(&fip->fi_lock);
+ UF_EXIT(ufp_rd);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+
+ return (0);
+}
+
+static int
+lx_autofs_fifo_write(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laarp)
+{
+ struct uio uio;
+ struct iovec iov;
+ file_t *fp_wr, *fp_rd;
+ int error;
+
+ /*
+ * The catch here is we need to make sure _we_ don't close
+ * the the fifo while writing to it. (Another thread could come
+ * along and realize the automounter process is gone and close
+ * the fifo. To do this we bump the open count before we
+ * write to the fifo.
+ */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ return (ENOENT);
+ }
+ fp_wr = data->lav_fifo_wr;
+ fp_rd = data->lav_fifo_rd;
+
+ /* Bump the open count on the write fifo. */
+ mutex_enter(&fp_wr->f_tlock);
+ fp_wr->f_count++;
+ mutex_exit(&fp_wr->f_tlock);
+
+ /* Bump the open count on the read fifo. */
+ mutex_enter(&fp_rd->f_tlock);
+ fp_rd->f_count++;
+ mutex_exit(&fp_rd->f_tlock);
+
+ mutex_exit(&data->lav_lock);
+
+ iov.iov_base = (caddr_t)&laarp->laar_pkt;
+ iov.iov_len = laarp->laar_pkt_size;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_loffset = 0;
+ uio.uio_segflg = (short)UIO_SYSSPACE;
+ uio.uio_resid = laarp->laar_pkt_size;
+ uio.uio_llimit = 0;
+ uio.uio_fmode = FWRITE | FNDELAY | FNONBLOCK;
+
+ error = VOP_WRITE(fp_wr->f_vnode, &uio, 0, kcred, NULL);
+ (void) closef(fp_wr);
+ (void) closef(fp_rd);
+
+ /*
+ * After every write we verify that the automounter still has
+ * these files open.
+ */
+ mutex_enter(&data->lav_lock);
+ if (lx_autofs_fifo_verify_rd(data) != 0) {
+ /*
+ * Something happened to the automounter.
+ * Close down the communication pipe we setup.
+ */
+ mutex_exit(&data->lav_lock);
+ lx_autofs_fifo_close(data);
+ if (error != 0)
+ return (error);
+ return (ENOENT);
+ }
+ mutex_exit(&data->lav_lock);
+
+ return (error);
+}
+
+static int
+lx_autofs_bs_readdir(vnode_t *dvp, list_t *dir_stack, list_t *file_stack)
+{
+ struct iovec iov;
+ struct uio uio;
+ dirent64_t *dp, *dbuf;
+ vnode_t *vp;
+ size_t dlen, dbuflen;
+ int eof, error, ndirents = 64;
+ char *nm;
+
+ dlen = ndirents * (sizeof (*dbuf));
+ dbuf = kmem_alloc(dlen, KM_SLEEP);
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_fmode = 0;
+ uio.uio_extflg = UIO_COPY_CACHED;
+ uio.uio_loffset = 0;
+ uio.uio_llimit = MAXOFFSET_T;
+
+ eof = 0;
+ error = 0;
+ while (!error && !eof) {
+ uio.uio_resid = dlen;
+ iov.iov_base = (char *)dbuf;
+ iov.iov_len = dlen;
+
+ (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+ if (VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0) != 0) {
+ VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+ kmem_free(dbuf, dlen);
+ return (-1);
+ }
+ VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+
+ if ((dbuflen = dlen - uio.uio_resid) == 0) {
+ /* We're done. */
+ break;
+ }
+
+ for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
+ dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+
+ nm = dp->d_name;
+
+ if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
+ continue;
+
+ if (VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, kcred,
+ NULL, NULL, NULL) != 0) {
+ kmem_free(dbuf, dlen);
+ return (-1);
+ }
+ if (vp->v_type == VDIR) {
+ if (dir_stack != NULL) {
+ lx_autofs_stack_push(dir_stack,
+ (caddr_t)dvp,
+ (caddr_t)vp, lx_autofs_strdup(nm));
+ } else {
+ VN_RELE(vp);
+ }
+ } else {
+ if (file_stack != NULL) {
+ lx_autofs_stack_push(file_stack,
+ (caddr_t)dvp,
+ (caddr_t)vp, lx_autofs_strdup(nm));
+ } else {
+ VN_RELE(vp);
+ }
+ }
+ }
+ }
+ kmem_free(dbuf, dlen);
+ return (0);
+}
+
+static void
+lx_autofs_bs_destroy(vnode_t *dvp, char *path)
+{
+ list_t search_stack;
+ list_t dir_stack;
+ list_t file_stack;
+ vnode_t *pdvp, *vp;
+ char *dpath, *fpath;
+ int ret;
+
+ if (VOP_LOOKUP(dvp, path, &vp, NULL, 0, NULL, kcred,
+ NULL, NULL, NULL) != 0) {
+ /* A directory entry with this name doesn't actually exist. */
+ return;
+ }
+
+ if ((vp->v_type & VDIR) == 0) {
+ /* Easy, the directory entry is a file so delete it. */
+ VN_RELE(vp);
+ (void) VOP_REMOVE(dvp, path, kcred, NULL, 0);
+ return;
+ }
+
+ /*
+ * The directory entry is a subdirectory, now we have a bit more
+ * work to do. (We'll have to recurse into the sub directory.)
+ * It would have been much easier to do this recursively but kernel
+ * stacks are notoriously small.
+ */
+ ls_autofs_stack_init(&search_stack);
+ ls_autofs_stack_init(&dir_stack);
+ ls_autofs_stack_init(&file_stack);
+
+ /* Save our newfound subdirectory into a list. */
+ lx_autofs_stack_push(&search_stack, (caddr_t)dvp, (caddr_t)vp,
+ lx_autofs_strdup(path));
+
+ /* Do a recursive depth first search into the subdirectories. */
+ while (lx_autofs_stack_pop(&search_stack,
+ (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+ /* Get a list of the subdirectories in this directory. */
+ if (lx_autofs_bs_readdir(dvp, &search_stack, NULL) != 0)
+ goto exit;
+
+ /* Save the current directory a separate stack. */
+ lx_autofs_stack_push(&dir_stack, (caddr_t)pdvp, (caddr_t)dvp,
+ dpath);
+ }
+
+ /*
+ * Now dir_stack contains a list of directories, the deepest paths
+ * are at the top of the list. So let's go through and process them.
+ */
+ while (lx_autofs_stack_pop(&dir_stack,
+ (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+ /* Get a list of the files in this directory. */
+ if (lx_autofs_bs_readdir(dvp, NULL, &file_stack) != 0) {
+ VN_RELE(dvp);
+ lx_autofs_strfree(dpath);
+ goto exit;
+ }
+
+ /* Delete all the files in this directory. */
+ while (lx_autofs_stack_pop(&file_stack,
+ NULL, (caddr_t *)&vp, &fpath) == 0) {
+ VN_RELE(vp)
+ ret = VOP_REMOVE(dvp, fpath, kcred, NULL, 0);
+ lx_autofs_strfree(fpath);
+ if (ret != 0) {
+ lx_autofs_strfree(dpath);
+ goto exit;
+ }
+ }
+
+ /* Delete this directory. */
+ VN_RELE(dvp);
+ ret = VOP_RMDIR(pdvp, dpath, pdvp, kcred, NULL, 0);
+ lx_autofs_strfree(dpath);
+ if (ret != 0)
+ goto exit;
+ }
+
+exit:
+ while (
+ (lx_autofs_stack_pop(&search_stack, NULL, (caddr_t *)&vp,
+ &path) == 0) ||
+ (lx_autofs_stack_pop(&dir_stack, NULL, (caddr_t *)&vp,
+ &path) == 0) ||
+ (lx_autofs_stack_pop(&file_stack, NULL, (caddr_t *)&vp,
+ &path) == 0)) {
+ VN_RELE(vp);
+ lx_autofs_strfree(path);
+ }
+ lx_autofs_stack_fini(&search_stack);
+ lx_autofs_stack_fini(&dir_stack);
+ lx_autofs_stack_fini(&file_stack);
+}
+
+static vnode_t *
+lx_autofs_bs_create(vnode_t *dvp, char *bs_name)
+{
+ vnode_t *vp;
+ vattr_t vattr;
+
+ /*
+ * After looking at the mkdir syscall path it seems we don't need
+ * to initialize all of the vattr_t structure.
+ */
+ bzero(&vattr, sizeof (vattr));
+ vattr.va_type = VDIR;
+ vattr.va_mode = 0755; /* u+rwx,og=rx */
+ vattr.va_mask = AT_TYPE|AT_MODE;
+
+ if (VOP_MKDIR(dvp, bs_name, &vattr, &vp, kcred, NULL, 0, NULL) != 0)
+ return (NULL);
+ return (vp);
+}
+
+static int
+lx_autofs_automounter_call(vnode_t *dvp, char *nm)
+{
+ lx_autofs_automnt_req_t *laar;
+ lx_autofs_vfs_t *data;
+ int error;
+ boolean_t is_dup;
+
+ /* Get a pointer to the vfs mount data. */
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+ /* The automounter only supports queries in the root directory. */
+ if (dvp != data->lav_root)
+ return (ENOENT);
+
+ /*
+ * Check if the current process is in the automounters process
+ * group. (If it is, the current process is either the autmounter
+ * itself or one of it's forked child processes.) If so, don't
+ * redirect this call back into the automounter because we'll
+ * hang.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp == curproc->p_pgrp) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ /* Verify that the automount process pipe still exists. */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ return (ENOENT);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Allocate an automounter request structure. */
+ if ((laar = lx_autofs_la_alloc(data, &is_dup, B_FALSE,
+ nm)) == NULL)
+ return (ENOENT);
+
+ /*
+ * If we were the first one to allocate this request then we
+ * need to send it to the automounter.
+ */
+ if ((!is_dup) &&
+ ((error = lx_autofs_fifo_write(data, laar)) != 0)) {
+ /*
+ * Unable to send the request to the automounter.
+ * Unblock any other threads waiting on the request
+ * and release the request.
+ */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ return (error);
+ }
+
+ /* Wait for someone to signal us that this request has completed. */
+ mutex_enter(&laar->laar_lock);
+ while (!laar->laar_complete) {
+ if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+ /* We got a signal, abort this call. */
+ mutex_exit(&laar->laar_lock);
+ lx_autofs_la_abort(data, laar);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&laar->laar_lock);
+
+ if (laar->laar_result == LXACR_READY) {
+ /*
+ * Mount succeeded, keep track for future expire calls.
+ *
+ * See vfs lav_vn_hash. Is this something we could use for
+ * iterating mounts under this autofs? Used by
+ * lx_autofs_vn_alloc
+ */
+ lx_autofs_mntent_t *mp;
+
+ mp = kmem_zalloc(sizeof (lx_autofs_mntent_t), KM_SLEEP);
+ mp->lxafme_len = strlen(nm) + 1;
+ mp->lxafme_path = kmem_zalloc(mp->lxafme_len, KM_SLEEP);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ (void) strlcpy(mp->lxafme_path, nm, mp->lxafme_len);
+
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ }
+
+ lx_autofs_la_release(data, laar);
+
+ return (0);
+}
+
+/*
+ * Same preliminary checks as in lx_autofs_unmount.
+ */
+static boolean_t
+lx_autofs_may_unmount(vfs_t *vfsp, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+
+ if (secpolicy_fs_unmount(cr, vfsp) != 0)
+ return (B_FALSE);
+
+ /*
+ * We should never have a reference count of less than 2: one for the
+ * caller, one for the root vnode.
+ */
+ ASSERT(vfsp->vfs_count >= 2);
+
+ /* If there are any outstanding vnodes, we can't unmount. */
+ if (vfsp->vfs_count > 2)
+ return (B_FALSE);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ ASSERT(data->lav_root->v_vfsp == vfsp);
+
+ /* Check for any remaining holds on the root vnode. */
+ if (data->lav_root->v_count > 1)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static vfs_t *
+lx_autofs_get_mountvfs(char *fs_mntpt, int *cnt)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ vfs_t *fnd_vfs = NULL;
+ int fsmplen;
+ int acnt = 0;
+
+ fsmplen = strlen(fs_mntpt);
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ *cnt = 0;
+ return (NULL);
+ }
+
+ do {
+ /* Skip mounts we shouldn't show. */
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt;
+
+ mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+ if (strncmp(fs_mntpt, mntpt, fsmplen) == 0 &&
+ (mntpt[fsmplen] == '\0' || mntpt[fsmplen] == '/')) {
+ /*
+ * We'll return the first one we find but don't
+ * return a mount that is actually autofs (i.e.
+ * autofs direct or offset mount).
+ */
+ if (vfsp->vfs_op == lx_autofs_vfsops) {
+ acnt++;
+ } else if (fnd_vfs == NULL) {
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ }
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ *cnt = acnt;
+ return (fnd_vfs);
+}
+
+/*
+ * Unmount all autofs offset mounts below the given path.
+ */
+static boolean_t
+lx_autofs_umount_offset(char *fs_mntpt, struct cred *cr)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ boolean_t busy = B_FALSE;
+ int fsmplen = strlen(fs_mntpt);
+
+restart:
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return (B_FALSE);
+ }
+
+ do {
+ char *mntpt;
+ lx_autofs_vfs_t *data;
+
+ /* Skip mounts we should ignore. */
+ if ((vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+ if (strncmp(fs_mntpt, mntpt, fsmplen) != 0 ||
+ (mntpt[fsmplen] != '\0' && mntpt[fsmplen] != '/')) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * Something got mounted over the autofs mountpoint
+ * after we checked that this inidrect hierarchy was
+ * not busy.
+ */
+ busy = B_TRUE;
+ break;
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if (data->lav_mnttype != LXAMT_OFFSET) {
+ /*
+ * Something mounted a non-offset autofs fs under this
+ * indirect mnt!
+ */
+ busy = B_TRUE;
+ break;
+ }
+
+ /*
+ * Attempt to umount - set busy if fails.
+ *
+ * umount2_engine will call VFS_RELE, so we need to take an
+ * extra hold to match the behavior during the normal umount
+ * path.
+ *
+ * We also need to drop the list lock to prevent deadlock
+ * during umount.
+ */
+ VFS_HOLD(vfsp);
+ vfs_list_unlock();
+ if (umount2_engine(vfsp, 0, cr, 0) != 0) {
+ busy = B_TRUE;
+ goto errexit;
+ }
+
+ /* Retake list lock and look for more. */
+ goto restart;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+errexit:
+ return (busy);
+}
+
+
+/*
+ * Note that lx_autofs_automounter_call() only supports queries in the root
+ * directory, so all mntent names are relative to that.
+ */
+static int
+lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+ lx_autofs_mntent_t *mp;
+ lx_autofs_automnt_req_t *laar;
+ boolean_t is_dup;
+ vfs_t *fnd_vfs;
+ int autofs_cnt;
+ boolean_t busy = B_FALSE;
+ char exp_path[MAXPATHLEN];
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /*
+ * We process only the first element (i.e. do not do multi). This
+ * works fine for the automounter.
+ */
+ mutex_enter(&data->lav_lock);
+ mp = (lx_autofs_mntent_t *)list_remove_head(&data->lav_mnt_list);
+ mutex_exit(&data->lav_lock);
+ if (mp == NULL) {
+ if (data->lav_mnttype == LXAMT_OFFSET) {
+ /*
+ * During restart the automounter will openmount each
+ * offset mount for management. It won't closemount the
+ * offset mount until we expire it, even though nothing
+ * is mounted over that offset. We handle this as a
+ * special expiration case.
+ */
+ int cnt;
+
+ mutex_enter(&data->lav_lock);
+ cnt = data->lav_openmnt_cnt;
+ mutex_exit(&data->lav_lock);
+
+ if (cnt == 1 && vn_ismntpt(data->lav_root) == 0) {
+ char *mntpt = (char *)
+ refstr_value(vfsp->vfs_mntpt);
+ char *nm = ZONE_PATH_TRANSLATE(mntpt, curzone);
+
+ mp = kmem_zalloc(sizeof (lx_autofs_mntent_t),
+ KM_SLEEP);
+ mp->lxafme_len = strlen(nm) + 1;
+ mp->lxafme_path = kmem_zalloc(mp->lxafme_len,
+ KM_SLEEP);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ (void) strlcpy(mp->lxafme_path, nm,
+ mp->lxafme_len);
+
+ goto exp_offset;
+ }
+ }
+
+ return (EAGAIN);
+ }
+
+ /*
+ * We only return an expired mount if it is inactive for the full
+ * timeout. This reduces overly aggressive umount/mount activity.
+ */
+ if (data->lav_timeout > 0) {
+ uint64_t now = TICK_TO_SEC(ddi_get_lbolt64());
+
+ if ((now - mp->lxafme_ts) < data->lav_timeout) {
+ /* put it back at the end of the line */
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ return (EAGAIN);
+ }
+ }
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ (void) snprintf(exp_path, sizeof (exp_path), "%s/%s",
+ (char *)refstr_value(vfsp->vfs_mntpt), mp->lxafme_path);
+ } else {
+ (void) strlcpy(exp_path, (char *)refstr_value(vfsp->vfs_mntpt),
+ sizeof (exp_path));
+ }
+
+ fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt);
+ if (fnd_vfs != NULL) {
+ boolean_t skip = B_FALSE;
+ vfssw_t *vfssw;
+
+ /*
+ * If it's an NFS file system (typical) then we check in
+ * advance to see if it can be unmounted, otherwise, proceed.
+ * The fs-specific umount attempted by the automounter will
+ * either succeed or fail. Both are valid outcomes but checking
+ * now for nfs will save a bunch of work by the automounter
+ * if the fs is busy.
+ *
+ * Unfortunately, for NFS the vfs_fstype is the same for all
+ * versions of NFS, so we need to check the vfs_op member to
+ * determine which version of NFS we're dealing with.
+ */
+ if (!skip && (vfssw = vfs_getvfssw("nfs4")) != NULL) {
+ if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+ (void) dnlc_purge_vfsp(fnd_vfs, 0);
+ if (check_rtable4(fnd_vfs))
+ busy = B_TRUE;
+ skip = B_TRUE;
+ }
+ vfs_unrefvfssw(vfssw);
+ }
+
+ if (!skip && (vfssw = vfs_getvfssw("nfs3")) != NULL) {
+ if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+ (void) dnlc_purge_vfsp(fnd_vfs, 0);
+ if (check_rtable(fnd_vfs))
+ busy = B_TRUE;
+ }
+ vfs_unrefvfssw(vfssw);
+ }
+
+ VFS_RELE(fnd_vfs);
+
+ } else if (autofs_cnt > 0) {
+ /*
+ * The automounter is asking us to expire and we pulled this
+ * name from our vfs mountpoint list, but if
+ * lx_autofs_get_mountvfs returns null then that means we
+ * didn't find a non-autofs mount under this name. Thus, the
+ * name could be a subdirectory under an autofs toplevel
+ * indirect mount with one or more offset mounts below.
+ * autofs_cnt will indicate how many autofs mounts exist below
+ * this subdirectory name.
+ *
+ * The automounter will take care of unmounting any fs mounted
+ * over one of these offset mounts (i.e. offset is like a
+ * direct mount which the automounter will manage) but the
+ * automounter will not unmount the actual autofs offset mount
+ * itself, so we have to do that before we can expire the
+ * top-level subrectory name.
+ */
+ busy = lx_autofs_umount_offset(exp_path, cr);
+ }
+
+ if (busy) {
+ /*
+ * Can't unmount this one right now, put it at the end of the
+ * list and return. The caller will return EAGAIN for the
+ * expire ioctl and the automounter will check again later.
+ */
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ return (EAGAIN);
+ }
+
+ /*
+ * See lx_autofs_automounter_call. We want to send a msg up the pipe
+ * to the automounter in a similar way.
+ */
+
+exp_offset:
+ /* Verify that the automount process pipe still exists. */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ goto err_free;
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Allocate an automounter expire structure. */
+ if ((laar = lx_autofs_la_alloc(data, &is_dup, B_TRUE,
+ mp->lxafme_path)) == NULL)
+ goto err_free;
+
+ /*
+ * If we were the first one to allocate this request then we
+ * need to send it to the automounter.
+ */
+ if (!is_dup && lx_autofs_fifo_write(data, laar) != 0) {
+ /*
+ * Unable to send the request to the automounter.
+ * Unblock any other threads waiting on the request
+ * and release the request.
+ */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ goto err_free;
+ }
+
+ /* Wait for someone to signal us that this request has completed. */
+ mutex_enter(&laar->laar_lock);
+ while (!laar->laar_complete) {
+ if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+ /* We got a signal, abort this request. */
+ mutex_exit(&laar->laar_lock);
+ lx_autofs_la_abort(data, laar);
+ goto err_free;
+ }
+ }
+ mutex_exit(&laar->laar_lock);
+
+ /*
+ * If it failed or if the file system is still mounted after we get the
+ * response from our expire msg, then that means the automounter tried
+ * to unmount it but failed because the file system is busy, so we put
+ * this entry back on our list to try to expire it again later.
+ */
+ fnd_vfs = NULL;
+ if (laar->laar_result == LXACR_FAIL ||
+ (fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt)) != NULL ||
+ autofs_cnt > 0) {
+ if (fnd_vfs != NULL)
+ VFS_RELE(fnd_vfs);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ } else {
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ }
+
+ lx_autofs_la_release(data, laar);
+ return (0);
+
+err_free:
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_ack(int reqid, vfs_t *vfsp, enum lx_autofs_callres result)
+{
+ lx_autofs_vfs_t *data;
+ lx_autofs_automnt_req_t *laar;
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if ((laar = lx_autofs_la_find(data, reqid)) == NULL)
+ return (ENXIO);
+
+ /* Mark the request as complete and release it. */
+ laar->laar_result = result;
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ return (0);
+}
+
+static int
+lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+ int id = arg;
+ int v;
+ int err;
+
+ /*
+ * Be strict.
+ * We only accept ioctls from the automounter process group.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp != curproc->p_pgrp) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ switch (cmd) {
+ case LX_AUTOFS_IOC_READY:
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_READY)) != 0)
+ return (err);
+ return (0);
+
+ case LX_AUTOFS_IOC_FAIL:
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_FAIL)) != 0)
+ return (err);
+ return (0);
+
+ case LX_AUTOFS_IOC_CATATONIC:
+ /* The automounter is shutting down. */
+ lx_autofs_fifo_close(data);
+ return (0);
+
+ case LX_AUTOFS_IOC_PROTOVER:
+ v = LX_AUTOFS_PROTO_VERS5;
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_PROTOSUBVER:
+ v = LX_AUTOFS_PROTO_SUBVERSION;
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_ASKUMOUNT:
+ /*
+ * This is asking if autofs can be unmounted, not asking to
+ * actually unmount it. We return 1 if it is busy or 0 if it
+ * can be unmounted.
+ */
+ v = 1;
+ if (lx_autofs_may_unmount(vp->v_vfsp, cr))
+ v = 0;
+
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_SETTIMEOUT:
+ if (copyin((caddr_t)arg, &data->lav_timeout, sizeof (ulong_t))
+ != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_EXPIRE:
+ return (ENOTSUP);
+
+ case LX_AUTOFS_IOC_EXPIRE_MULTI:
+ lx_autofs_expire(vp->v_vfsp, cr);
+ return (EAGAIN);
+
+ default:
+ ASSERT(0);
+ return (ENOTSUP);
+ }
+}
+
+static int
+lx_autofs_parse_mntopt(vfs_t *vfsp, lx_autofs_vfs_t *data)
+{
+ char *fd_str, *pgrp_str, *minproto_str, *maxproto_str;
+ int fd, pgrp, minproto, maxproto;
+ file_t *fp_wr, *fp_rd;
+
+ /* Require these options to be present. */
+ if ((vfs_optionisset(vfsp, LX_MNTOPT_FD, &fd_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_PGRP, &pgrp_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_MINPROTO, &minproto_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_MAXPROTO, &maxproto_str) != 1))
+ return (EINVAL);
+
+ /* Get the values for each parameter. */
+ if ((lx_autofs_str_to_int(fd_str, &fd) != 0) ||
+ (lx_autofs_str_to_int(pgrp_str, &pgrp) != 0) ||
+ (lx_autofs_str_to_int(minproto_str, &minproto) != 0) ||
+ (lx_autofs_str_to_int(maxproto_str, &maxproto) != 0))
+ return (EINVAL);
+
+ /*
+ * We primarily support v2 & v5 of the linux kernel automounter
+ * protocol. The userland daemon typically needs v5. We'll reject
+ * unsupported ioctls later if we get one.
+ */
+ if ((minproto > 5) || (maxproto < 2))
+ return (EINVAL);
+
+ /*
+ * Now we need to lookup the fifos we'll be using
+ * to talk to the userland automounter process.
+ */
+ if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+ /*
+ * The automounter doesn't always have the same id as the pgrp.
+ * This happens when it is started via one of the various
+ * service managers. In this case the fifo lookup will fail
+ * so we retry with our own pid.
+ */
+ int pid = (int)curproc->p_pid;
+
+ if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0)
+ return (EINVAL);
+ }
+
+ if (vfs_optionisset(vfsp, LX_MNTOPT_INDIRECT, NULL)) {
+ data->lav_mnttype = LXAMT_INDIR;
+ }
+ if (vfs_optionisset(vfsp, LX_MNTOPT_DIRECT, NULL)) {
+ if (data->lav_mnttype != LXAMT_NONE)
+ return (EINVAL);
+ data->lav_mnttype = LXAMT_DIRECT;
+ }
+ if (vfs_optionisset(vfsp, LX_MNTOPT_OFFSET, NULL)) {
+ if (data->lav_mnttype != LXAMT_NONE)
+ return (EINVAL);
+ data->lav_mnttype = LXAMT_OFFSET;
+ }
+ /* The automounter does test mounts with none of the options */
+ if (data->lav_mnttype == LXAMT_NONE)
+ data->lav_mnttype = LXAMT_DIRECT;
+
+ /* Save the mount options and fifo pointers. */
+ data->lav_fd = fd;
+ data->lav_min_proto = minproto;
+ data->lav_pgrp = pgrp;
+ data->lav_fifo_rd = fp_rd;
+ data->lav_fifo_wr = fp_wr;
+ return (0);
+}
+
+static uint64_t
+s2l_dev(dev_t dev)
+{
+ major_t maj = getmajor(dev);
+ minor_t min = getminor(dev);
+
+ return (LX_MAKEDEVICE(maj, min));
+}
+
+/*
+ * VFS entry points
+ */
+static int
+lx_autofs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ lx_autofs_vfs_t *data;
+ dev_t dev;
+ char name[40];
+ int error;
+ vattr_t va;
+
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
+ return (EBUSY);
+
+ /* We don't support mounts in the global zone. */
+ if (getzoneid() == GLOBAL_ZONEID)
+ return (EPERM);
+
+ /*
+ * Offset mounts will occur below the top-level mountpoint so we
+ * need to allow for autofs mounts even though mvp is an autofs.
+ */
+
+ /* Allocate a vfs struct. */
+ data = kmem_zalloc(sizeof (lx_autofs_vfs_t), KM_SLEEP);
+
+ /* Parse mount options. */
+ if ((error = lx_autofs_parse_mntopt(vfsp, data)) != 0) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (error);
+ }
+
+ /* Initialize the backing store. */
+ lx_autofs_bs_destroy(mvp, LX_AUTOFS_BS_DIR);
+ data->lav_bs_vp = lx_autofs_bs_create(mvp, LX_AUTOFS_BS_DIR);
+ if (data->lav_bs_vp == NULL) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (EBUSY);
+ }
+ data->lav_bs_name = LX_AUTOFS_BS_DIR;
+
+ /* Get the backing store inode for use in v5 protocol msgs */
+ va.va_mask = AT_STAT;
+ if ((error = VOP_GETATTR(data->lav_bs_vp, &va, 0, cr, NULL)) != 0) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (error);
+ }
+ data->lav_ino = va.va_nodeid;
+
+ /* We have to hold the underlying vnode we're mounted on. */
+ data->lav_mvp = mvp;
+ VN_HOLD(mvp);
+
+ /* Initialize vfs fields */
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lx_autofs_fstype;
+ vfsp->vfs_data = data;
+
+ /* Invent a dev_t (sigh) */
+ do {
+ dev = makedevice(lx_autofs_major,
+ atomic_add_32_nv(&lx_autofs_minor, 1) & L_MAXMIN32);
+ } while (vfs_devismounted(dev));
+ vfsp->vfs_dev = dev;
+ vfs_make_fsid(&vfsp->vfs_fsid, dev, lx_autofs_fstype);
+
+ data->lav_dev = s2l_dev(vfsp->vfs_dev);
+
+ /* Create an id space arena for automounter requests. */
+ (void) snprintf(name, sizeof (name), "lx_autofs_id_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_ids = id_space_create(name, 1, INT_MAX);
+
+ /* Create hashes to keep track of automounter requests. */
+ mutex_init(&data->lav_lock, NULL, MUTEX_DEFAULT, NULL);
+ (void) snprintf(name, sizeof (name), "lx_autofs_path_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_path_hash = mod_hash_create_strhash(name,
+ LX_AUTOFS_VFS_PATH_HASH_SIZE, mod_hash_null_valdtor);
+ (void) snprintf(name, sizeof (name), "lx_autofs_id_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_id_hash = mod_hash_create_idhash(name,
+ LX_AUTOFS_VFS_ID_HASH_SIZE, mod_hash_null_valdtor);
+
+ /* Create a hash to keep track of vnodes. */
+ (void) snprintf(name, sizeof (name), "lx_autofs_vn_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_vn_hash = mod_hash_create_ptrhash(name,
+ LX_AUTOFS_VFS_VN_HASH_SIZE, mod_hash_null_valdtor,
+ sizeof (vnode_t));
+
+ list_create(&data->lav_mnt_list, sizeof (lx_autofs_mntent_t),
+ offsetof(lx_autofs_mntent_t, lxafme_lst));
+
+ /* Create root vnode */
+ data->lav_root = lx_autofs_vn_alloc(vfsp, data->lav_bs_vp);
+
+ data->lav_root->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP;
+
+ /*
+ * For a direct mountpoint we need to allow a filesystem to be
+ * mounted overtop of this autofs mount. Otherwise, disallow that.
+ */
+ if (data->lav_mnttype == LXAMT_INDIR)
+ data->lav_root->v_flag |= VNOMOUNT;
+
+ return (0);
+}
+
+static int
+lx_autofs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+
+ if (secpolicy_fs_unmount(cr, vfsp) != 0)
+ return (EPERM);
+
+ /* We do not currently support forced unmounts. */
+ if (flag & MS_FORCE)
+ return (ENOTSUP);
+
+ /*
+ * We should never have a reference count of less than 2: one for the
+ * caller, one for the root vnode.
+ */
+ ASSERT(vfsp->vfs_count >= 2);
+
+ /* If there are any outstanding vnodes, we can't unmount. */
+ if (vfsp->vfs_count > 2)
+ return (EBUSY);
+
+ /* Check for any remaining holds on the root vnode. */
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ ASSERT(data->lav_root->v_vfsp == vfsp);
+ if (data->lav_root->v_count > 1)
+ return (EBUSY);
+
+ /* Close the fifo to the automount process. */
+ if (data->lav_fifo_wr != NULL)
+ (void) closef(data->lav_fifo_wr);
+ if (data->lav_fifo_rd != NULL)
+ (void) closef(data->lav_fifo_rd);
+
+ /*
+ * We have to release our hold on our root vnode before we can
+ * delete the backing store. (Since the root vnode is linked
+ * to the backing store.)
+ */
+ VN_RELE(data->lav_root);
+
+ /* Cleanup the backing store. */
+ lx_autofs_bs_destroy(data->lav_mvp, data->lav_bs_name);
+ VN_RELE(data->lav_mvp);
+
+ /*
+ * Delete all listed mounts.
+ */
+ for (;;) {
+ lx_autofs_mntent_t *mp;
+
+ mp = list_remove_head(&data->lav_mnt_list);
+ if (mp == NULL)
+ break;
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ }
+
+ /* Cleanup out remaining data structures. */
+ mod_hash_destroy_strhash(data->lav_path_hash);
+ mod_hash_destroy_idhash(data->lav_id_hash);
+ mod_hash_destroy_ptrhash(data->lav_vn_hash);
+ id_space_destroy(data->lav_ids);
+ list_destroy(&data->lav_mnt_list);
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+
+ return (0);
+}
+
+static int
+lx_autofs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ *vpp = data->lav_root;
+ VN_HOLD(*vpp);
+
+ return (0);
+}
+
+static int
+lx_autofs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *urvp = data->lav_root->v_data;
+ dev32_t d32;
+ int error;
+
+ if ((error = VFS_STATVFS(urvp->v_vfsp, sp)) != 0)
+ return (error);
+
+ /* Update some of values before returning. */
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ (void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
+ sizeof (sp->f_basetype));
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+ return (0);
+}
+
+static const fs_operation_def_t lx_autofs_vfstops[] = {
+ { VFSNAME_MOUNT, { .vfs_mount = lx_autofs_mount } },
+ { VFSNAME_UNMOUNT, { .vfs_unmount = lx_autofs_unmount } },
+ { VFSNAME_ROOT, { .vfs_root = lx_autofs_root } },
+ { VFSNAME_STATVFS, { .vfs_statvfs = lx_autofs_statvfs } },
+ { NULL, NULL }
+};
+
+/*
+ * VOP entry points - simple passthrough
+ *
+ * For most VOP entry points we can simply pass the request on to
+ * the underlying filesystem we're mounted on.
+ */
+static int
+lx_autofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_CLOSE(uvp, flag, count, offset, cr, ctp));
+}
+
+static int
+lx_autofs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ctp, int flags)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_READDIR(uvp, uiop, cr, eofp, ctp, flags));
+}
+
+static int
+lx_autofs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_ACCESS(uvp, mode, flags, cr, ctp));
+}
+
+static int
+lx_autofs_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_RWLOCK(uvp, write_lock, ctp));
+}
+
+static void
+lx_autofs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ VOP_RWUNLOCK(uvp, write_lock, ctp);
+}
+
+/*
+ * Check if attempting to access a 'direct' mount and if so, call the
+ * automounter to perform the mount. Once the mount occurs, the new filesystem
+ * will be mounted overtop of this autofs mountpoint and we will no longer
+ * come through this path.
+ */
+static vnode_t *
+lx_autofs_do_direct(vnode_t *vp)
+{
+ vfs_t *vfsp = vp->v_vfsp;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *nvp;
+ boolean_t skip_am_call = B_FALSE;
+
+ if (data->lav_mnttype == LXAMT_INDIR)
+ return (NULL);
+
+ /*
+ * Check if the current process is in the automounter's process group.
+ * If it is, the current process is either the automounter itself or
+ * one of it's children. If so, don't call back into the automounter.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp == curproc->p_pgrp) {
+ skip_am_call = B_TRUE;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * It is possible there is already a new fs mounted on top of our vnode.
+ * This can happen if the caller first did a lookup of a file name
+ * using our vnode as the directory vp. The lookup would trigger the
+ * autofs mount on top of ourself, but if the caller then uses our
+ * vnode to do a getattr on the directory, it will use the autofs
+ * vnode and not the newly mounted vnode. We need to skip re-calling
+ * the automounter for this case.
+ */
+ if (!skip_am_call && vn_mountedvfs(vp) == NULL) {
+ char tbuf[MAXPATHLEN];
+ char *nm;
+
+ (void) strlcpy(tbuf, (char *)refstr_value(vfsp->vfs_mntpt),
+ sizeof (tbuf));
+ nm = tbuf + strlen(tbuf);
+ while (*nm != '/' && nm != tbuf)
+ nm--;
+ if (*nm == '/')
+ nm++;
+ (void) lx_autofs_automounter_call(vp, nm);
+ }
+
+ /*
+ * We need to take an extra hold on our vp (which is the autofs
+ * root vp) to account for the rele done in traverse. traverse will
+ * take a hold on the new vp so the caller is responsible for calling
+ * VN_RELE on the returned vp.
+ */
+ VN_HOLD(vp);
+ nvp = vp;
+ if (traverse(&nvp) != 0) {
+ VN_RELE(nvp);
+ return (NULL);
+ }
+
+ /* Confirm that we have a non-autofs fs mounted now */
+ if (nvp->v_op == lx_autofs_vn_ops) {
+ VN_RELE(nvp);
+ return (NULL);
+ }
+
+ return (nvp);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ctp, int flags)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *nvp;
+
+ /* handle direct mount here */
+ if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+ int error;
+
+ error = VOP_RMDIR(nvp, nm, cdir, cr, ctp, flags);
+ VN_RELE(nvp);
+ return (error);
+ }
+
+ /*
+ * cdir is the calling processes current directory.
+ * If cdir is lx_autofs vnode then get its real underlying
+ * vnode ptr. (It seems like the only thing cdir is
+ * ever used for is to make sure the user doesn't delete
+ * their current directory.)
+ */
+ if (vn_matchops(cdir, lx_autofs_vn_ops)) {
+ vnode_t *ucdir = cdir->v_data;
+ return (VOP_RMDIR(udvp, nm, ucdir, cr, ctp, flags));
+ }
+
+ return (VOP_RMDIR(udvp, nm, cdir, cr, ctp, flags));
+}
+
+/*
+ * VOP entry points - special passthrough
+ *
+ * For some VOP entry points we will first pass the request on to
+ * the underlying filesystem we're mounted on. If there's an error
+ * then we immediately return the error, but if the request succeeds
+ * we have to do some extra work before returning.
+ */
+static int
+lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ctp)
+{
+ vnode_t *ovp = *vpp;
+ vnode_t *uvp = ovp->v_data;
+ int error;
+
+ /* direct mounts were handled by the lookup to get *vpp */
+
+ if ((error = VOP_OPEN(&uvp, flag, cr, ctp)) != 0)
+ return (error);
+
+ /* Check for clone opens. */
+ if (uvp == ovp->v_data)
+ return (0);
+
+ /* Deal with clone opens by returning a new vnode. */
+ *vpp = lx_autofs_vn_alloc(ovp->v_vfsp, uvp);
+ VN_RELE(ovp);
+ return (0);
+}
+
+static int
+lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ vnode_t *dvp;
+ int error;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+
+ if ((dvp = lx_autofs_do_direct(vp)) != NULL) {
+ uvp = dvp;
+ }
+
+ error = VOP_GETATTR(uvp, vap, flags, cr, ctp);
+
+ if (dvp != NULL) {
+ /* we operated on the direct mounted fs */
+ VN_RELE(dvp);
+ if (error == 0) {
+ /*
+ * During automounter restart recovery the automounter
+ * will fstat the fd provided in the setpipe ioctl. It
+ * uses the resulting inode & dev to correlate future
+ * autofs fifo requests to the correct entry. Thus, we
+ * have to update the attributes with our own id's.
+ */
+ vap->va_fsid = data->lav_dev;
+ vap->va_nodeid = data->lav_ino;
+ }
+ } else if (error == 0) {
+ /* Update the attributes with our filesystem id. */
+ vap->va_fsid = data->lav_dev;
+ }
+
+ return (error);
+}
+
+static int
+lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp,
+ cred_t *cr, caller_context_t *ctp, int flags, vsecattr_t *vsecp)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *nvp;
+ int error;
+
+ if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+ udvp = nvp;
+ }
+
+ error = VOP_MKDIR(udvp, nm, vap, vpp, cr, ctp, flags, vsecp);
+
+ if (nvp != NULL) {
+ /* we operated on the direct mounted fs */
+ VN_RELE(nvp);
+ } else if (error == 0) {
+ vnode_t *uvp = NULL;
+
+ /* Update the attributes with our filesystem id. */
+ vap->va_fsid = dvp->v_vfsp->vfs_dev;
+
+ /* Allocate our new vnode. */
+ uvp = *vpp;
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ }
+
+ return (error);
+}
+
+/*
+ * VOP entry points - custom
+ */
+/*ARGSUSED*/
+static void
+lx_autofs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ctp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+
+ /*
+ * We need to hold the vfs lock because if we're going to free
+ * this vnode we have to prevent anyone from looking it up
+ * in the vnode hash.
+ */
+ mutex_enter(&data->lav_lock);
+ mutex_enter(&vp->v_lock);
+
+ if (vp->v_count < 1) {
+ panic("lx_autofs_inactive: bad v_count");
+ /*NOTREACHED*/
+ }
+
+ /* Drop the temporary hold by vn_rele now. */
+ if (--vp->v_count > 0) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&data->lav_lock);
+ return;
+ }
+
+ /*
+ * No one should have been blocked on this lock because we're
+ * about to free this vnode.
+ */
+ lx_autofs_vn_free(vp);
+}
+
+static int
+lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ctp,
+ int *direntflags, pathname_t *realpnp)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *uvp = NULL;
+ lx_autofs_vfs_t *data;
+ int error = ENOENT;
+
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+ /*
+ * For an indirect mount first try to lookup if this path component
+ * already exists.
+ */
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+ ctp, direntflags, realpnp)) == 0) {
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ return (0);
+ }
+ }
+
+ /* Only query the automounter if the path does not exist. */
+ if (error != ENOENT)
+ return (error);
+
+ if (data->lav_catatonic)
+ return (ENOENT);
+
+ /* Save the uid/gid for the requestor ioctl. */
+ data->lav_uid = crgetuid(cr);
+ data->lav_gid = crgetgid(cr);
+
+ /* Refer the lookup to the automounter. */
+ if ((error = lx_autofs_automounter_call(dvp, nm)) != 0)
+ return (error);
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ /*
+ * Indirect mount. The automounter call should have mounted
+ * something on nm. Retry the lookup operation.
+ */
+ if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+ ctp, direntflags, realpnp)) == 0) {
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ return (0);
+ }
+ } else {
+ /*
+ * Direct or offset mount. The automounter call should have
+ * covered our 'dvp' with a new filesystem. Traverse into the
+ * new mount and retry the lookup.
+ *
+ * We need to take an extra hold on our vp (which is the autofs
+ * root vp) to acount for the rele done in traverse. Our caller
+ * will also do a rele on the original dvp and that would leave
+ * us one ref short on our autofs root vnode.
+ */
+ VN_HOLD(dvp);
+ if ((error = traverse(&dvp)) != 0) {
+ VN_RELE(dvp);
+ return (error);
+ }
+
+ error = VOP_LOOKUP(dvp, nm, vpp, pnp, flags, rdir, cr, ctp,
+ direntflags, realpnp);
+
+ /* release the traverse hold */
+ VN_RELE(dvp);
+ }
+ return (error);
+}
+
+static int
+lx_autofs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
+ int *rvalp, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+
+ /* Intercept our ioctls. */
+ switch ((uint_t)cmd) {
+ case LX_AUTOFS_IOC_READY:
+ case LX_AUTOFS_IOC_FAIL:
+ case LX_AUTOFS_IOC_CATATONIC:
+ case LX_AUTOFS_IOC_PROTOVER:
+ case LX_AUTOFS_IOC_SETTIMEOUT:
+ case LX_AUTOFS_IOC_EXPIRE:
+ case LX_AUTOFS_IOC_EXPIRE_MULTI:
+ case LX_AUTOFS_IOC_PROTOSUBVER:
+ case LX_AUTOFS_IOC_ASKUMOUNT:
+ return (lx_autofs_automounter_ioctl(vp, cmd, arg, cr));
+ }
+
+ /* Pass any remaining ioctl on. */
+ return (VOP_IOCTL(uvp, cmd, arg, mode, cr, rvalp, ctp));
+}
+
+/*
+ * VOP entry points definitions
+ */
+static const fs_operation_def_t lx_autofs_tops_root[] = {
+ { VOPNAME_OPEN, { .vop_open = lx_autofs_open } },
+ { VOPNAME_CLOSE, { .vop_close = lx_autofs_close } },
+ { VOPNAME_IOCTL, { .vop_ioctl = lx_autofs_ioctl } },
+ { VOPNAME_RWLOCK, { .vop_rwlock = lx_autofs_rwlock } },
+ { VOPNAME_RWUNLOCK, { .vop_rwunlock = lx_autofs_rwunlock } },
+ { VOPNAME_GETATTR, { .vop_getattr = lx_autofs_getattr } },
+ { VOPNAME_ACCESS, { .vop_access = lx_autofs_access } },
+ { VOPNAME_READDIR, { .vop_readdir = lx_autofs_readdir } },
+ { VOPNAME_LOOKUP, { .vop_lookup = lx_autofs_lookup } },
+ { VOPNAME_INACTIVE, { .vop_inactive = lx_autofs_inactive } },
+ { VOPNAME_MKDIR, { .vop_mkdir = lx_autofs_mkdir } },
+ { VOPNAME_RMDIR, { .vop_rmdir = lx_autofs_rmdir } },
+ { NULL }
+};
+
+/*
+ * DEV-specific entry points
+ */
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+static int
+lx_autofs_dev_validate_cmd(intptr_t arg, lx_autofs_dv_ioctl_t *dcmd)
+{
+ if (copyin((caddr_t)arg, dcmd, sizeof (lx_autofs_dv_ioctl_t)) != 0)
+ return (EFAULT);
+
+ if (dcmd->lad_ver_major != LX_AUTOFS_DEV_VERSION_MAJOR ||
+ dcmd->lad_ver_minor > LX_AUTOFS_DEV_VERSION_MINOR)
+ return (EINVAL);
+
+ DTRACE_PROBE1(lx__dev__cmd, void *, dcmd);
+
+ /* Fill in the version for return */
+ dcmd->lad_ver_major = LX_AUTOFS_DEV_VERSION_MAJOR;
+ dcmd->lad_ver_minor = LX_AUTOFS_DEV_VERSION_MINOR;
+ return (0);
+}
+
+static vfs_t *
+lx_autofs_dev_getvfs_bypath(char *fs_mntpt)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return (NULL);
+ }
+
+ do {
+ if (vfsp->vfs_op == lx_autofs_vfsops) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(fs_mntpt, ZONE_PATH_TRANSLATE(mntpt, zone))
+ == 0) {
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ return (fnd_vfs);
+}
+
+static int
+lx_autofs_dev_fd_preamble(intptr_t arg, lx_autofs_dv_ioctl_t *dc, vfs_t **vfspp)
+{
+ int err;
+ lx_autofs_vfs_t *data;
+ file_t *fp;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, dc)) != 0)
+ return (err);
+
+ if ((fp = getf(dc->lad_ioctlfd)) == NULL)
+ return (EBADF);
+
+ vfsp = fp->f_vnode->v_vfsp;
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if (data->lav_root->v_count <= 1) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ VFS_HOLD(vfsp);
+ *vfspp = vfsp;
+
+ releasef(dc->lad_ioctlfd);
+ return (0);
+}
+
+static int
+lx_autofs_dev_vers(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_VERS5;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protosubver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_SUBVERSION;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_get_path_cmd(intptr_t arg, lx_autofs_dv_ioctl_t **dcp)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd, *dc;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (dcmd.lad_size <= sizeof (dcmd) ||
+ dcmd.lad_size > (sizeof (dcmd) + MAXPATHLEN))
+ return (EINVAL);
+
+ dc = kmem_alloc(dcmd.lad_size, KM_SLEEP);
+
+ /* re-copyin the full struct with the path */
+ if (copyin((caddr_t)arg, dc, dcmd.lad_size) != 0) {
+ kmem_free(dc, dcmd.lad_size);
+ return (EFAULT);
+ }
+ dc->lad_size = dcmd.lad_size;
+
+ if (dc->lad_path[0] != '/' ||
+ dc->lad_path[dcmd.lad_size - sizeof (dcmd) - 1] != '\0') {
+ kmem_free(dc, dcmd.lad_size);
+ return (EINVAL);
+ }
+
+ *dcp = dc;
+ return (0);
+}
+
+static int
+lx_autofs_dev_openmount(intptr_t arg)
+{
+ int err;
+ int fd;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ if ((vfsp = lx_autofs_dev_getvfs_bypath(dc->lad_path)) == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ /* lad_arg1 is the dev number of the mnt but we don't check that */
+
+ /*
+ * Do an "open" on the root vnode. To fully simulate "open" we also add
+ * a hold on the root vnode itself since lx_autofs_open will only open
+ * (and hold) the underlying vnode.
+ */
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ VN_HOLD(data->lav_root);
+ if ((err = fassign(&data->lav_root, FWRITE|FREAD, &fd)) != 0) {
+ VN_RELE(data->lav_root);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt++;
+ mutex_exit(&data->lav_lock);
+
+ dc->lad_ioctlfd = fd;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+ (void) closeandsetf(fd, NULL);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+ VFS_RELE(vfsp);
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_closemount(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* "close" the vnode */
+ if ((err = closeandsetf(dcmd.lad_ioctlfd, NULL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ ASSERT(data->lav_openmnt_cnt > 0);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ready(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_READY)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_fail(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_FAIL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+/*
+ * Update the fifo pipe information we use to talk to the automounter. The
+ * ioctl is used when the automounter restarts. This logic is similar to the
+ * handling done in lx_autofs_parse_mntopt() when the filesytem is first
+ * mounted.
+ */
+static int
+lx_autofs_dev_setpipefd(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+ int fd, pgrp;
+ file_t *fp_wr, *fp_rd;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ mutex_enter(&pidlock);
+ pgrp = curproc->p_pgrp;
+ mutex_exit(&pidlock);
+ fd = dcmd.lad_arg1;
+
+ /* Lookup the new fifos. See comment in lx_autofs_parse_mntopt. */
+ if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+ int pid = (int)curproc->p_pid;
+
+ if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0) {
+ VFS_RELE(vfsp);
+ return (EINVAL);
+ }
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* Close the old fifos. */
+ if (data->lav_fifo_wr != NULL)
+ (void) closef(data->lav_fifo_wr);
+ if (data->lav_fifo_rd != NULL)
+ (void) closef(data->lav_fifo_rd);
+
+ data->lav_fd = fd;
+ data->lav_pgrp = pgrp;
+ data->lav_fifo_rd = fp_rd;
+ data->lav_fifo_wr = fp_wr;
+ /*
+ * Not explicitly in the ioctl spec. but necessary for correct recovery
+ */
+ data->lav_catatonic = B_FALSE;
+
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_catatonic(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_catatonic = B_TRUE;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_expire(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ /* If it succeeds in expiring then we don't want to return EAGAIN */
+ if ((err = lx_autofs_expire(vfsp, kcred)) == 0) {
+ VFS_RELE(vfsp);
+ return (0);
+ }
+
+ VFS_RELE(vfsp);
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_dev_timeout(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_timeout = dcmd.lad_arg1;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_requestor(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ struct vfs *vfslist;
+ zone_t *zone = curzone;
+ lx_autofs_vfs_t *data;
+ uid_t uid;
+ gid_t gid;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ do {
+ /* Skip mounts we shouldn't show. */
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * Found an indirect mount (probably
+ * NFS) so we need to get the vfs it's
+ * mounted onto.
+ */
+ vnode_t *vn = vfsp->vfs_vnodecovered;
+ vfsp = vn->v_vfsp;
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * autofs doesn't manage this
+ * path.
+ */
+ break;
+ }
+ }
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ data = (lx_autofs_vfs_t *)fnd_vfs->vfs_data;
+ uid = data->lav_uid;
+ gid = data->lav_gid;
+ VFS_RELE(fnd_vfs);
+
+ dc->lad_arg1 = uid;
+ dc->lad_arg2 = gid;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ismntpt(intptr_t arg)
+{
+ int err = 0;
+ lx_autofs_dv_ioctl_t *dc;
+ struct vfs *vfslist;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ /*
+ * The automounter will always pass a path. It can also either pass an
+ * ioctlfd or, if it's -1, arg1 can be an LX_AUTOFS_TYPE_* value. We
+ * currently don't need those for our algorithm.
+ */
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ do {
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ /*
+ * To handle direct mounts (on top of an autofs
+ * mount), we must prefer non-autofs vfs for
+ * this request.
+ */
+ if (fnd_vfs != NULL)
+ VFS_RELE(fnd_vfs);
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+
+ if (fnd_vfs->vfs_op != lx_autofs_vfsops)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ /*
+ * arg1 is device number, arg2 is superblock magic number
+ * The superblock value only matters if autofs or not.
+ */
+ dc->lad_arg1 = fnd_vfs->vfs_dev;
+ if (fnd_vfs->vfs_op == lx_autofs_vfsops) {
+ dc->lad_arg2 = LX_AUTOFS_SB_MAGIC;
+ } else {
+ dc->lad_arg2 = ~LX_AUTOFS_SB_MAGIC;
+ }
+
+ VFS_RELE(fnd_vfs);
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+
+ /*
+ * We have to return 1 if it is a mount point. The lx ioctl autofs
+ * translator will convert a negative value back to a positive,
+ * non-error return value.
+ */
+ return (-1);
+}
+
+static int
+lx_autofs_dev_askumount(intptr_t arg)
+{
+ int err;
+ int v;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if (lx_autofs_may_unmount(vfsp, kcred)) {
+ v = 0;
+ } else {
+ v = 1;
+ }
+ VFS_RELE(vfsp);
+
+ dcmd.lad_arg1 = v;
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ switch (cmd) {
+ case LX_AUTOFS_DEV_IOC_VERSION_CMD:
+ return (lx_autofs_dev_vers(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOVER_CMD:
+ return (lx_autofs_dev_protver(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD:
+ return (lx_autofs_dev_protosubver(arg));
+
+ case LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD:
+ return (lx_autofs_dev_openmount(arg));
+
+ case LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD:
+ return (lx_autofs_dev_closemount(arg));
+
+ case LX_AUTOFS_DEV_IOC_READY_CMD:
+ return (lx_autofs_dev_ready(arg));
+
+ case LX_AUTOFS_DEV_IOC_FAIL_CMD:
+ return (lx_autofs_dev_fail(arg));
+
+ case LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD:
+ return (lx_autofs_dev_setpipefd(arg));
+
+ case LX_AUTOFS_DEV_IOC_CATATONIC_CMD:
+ return (lx_autofs_dev_catatonic(arg));
+
+ case LX_AUTOFS_DEV_IOC_TIMEOUT_CMD:
+ return (lx_autofs_dev_timeout(arg));
+
+ case LX_AUTOFS_DEV_IOC_REQUESTER_CMD:
+ return (lx_autofs_dev_requestor(arg));
+
+ case LX_AUTOFS_DEV_IOC_EXPIRE_CMD:
+ return (lx_autofs_dev_expire(arg));
+
+ case LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD:
+ return (lx_autofs_dev_askumount(arg));
+
+ case LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD:
+ return (lx_autofs_dev_ismntpt(arg));
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * lx_autofs_init() gets invoked via the mod_install() call in
+ * this module's _init() routine. Therefore, the code that cleans
+ * up the structures we allocate below is actually found in
+ * our _fini() routine.
+ */
+/* ARGSUSED */
+static int
+lx_autofs_init(int fstype, char *name)
+{
+ int error;
+
+ lx_autofs_major = ddi_name_to_major(LX_AUTOFS_NAME);
+
+ lx_autofs_fstype = fstype;
+ if ((error = vfs_setfsops(fstype, lx_autofs_vfstops,
+ &lx_autofs_vfsops)) != 0) {
+ cmn_err(CE_WARN, "lx_autofs_init: bad vfs ops template");
+ return (error);
+ }
+
+ if ((error = vn_make_ops(name, lx_autofs_tops_root,
+ &lx_autofs_vn_ops)) != 0) {
+ VERIFY(vfs_freevfsops_by_type(fstype) == 0);
+ lx_autofs_vn_ops = NULL;
+ return (error);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(dip);
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ ASSERT(instance == 0);
+ if (instance != 0)
+ return (DDI_FAILURE);
+
+ /* create our minor node */
+ if (ddi_create_minor_node(dip, LX_AUTOFS_MINORNAME, S_IFCHR, 0,
+ DDI_PSEUDO, 0) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = dip;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **resultp)
+{
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resultp = lx_autofs_dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)0;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * Driver flags
+ */
+static struct cb_ops lx_autofs_cb_ops = {
+ lx_autofs_dev_open, /* open */
+ lx_autofs_dev_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ lx_autofs_dev_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* vb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+/*
+ * Module linkage
+ */
+static mntopt_t lx_autofs_mntopt[] = {
+ { LX_MNTOPT_FD, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_PGRP, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_MINPROTO, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_MAXPROTO, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_INDIRECT, NULL, 0, 0 },
+ { LX_MNTOPT_DIRECT, NULL, 0, 0 },
+ { LX_MNTOPT_OFFSET, NULL, 0, 0 }
+};
+
+static mntopts_t lx_autofs_mntopts = {
+ sizeof (lx_autofs_mntopt) / sizeof (mntopt_t),
+ lx_autofs_mntopt
+};
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ LX_AUTOFS_NAME,
+ lx_autofs_init,
+ VSW_HASPROTO | VSW_VOLATILEDEV | VSW_ZMOUNT,
+ &lx_autofs_mntopts
+};
+
+static struct dev_ops lx_autofs_dev_ops = {
+ DEVO_REV, /* version */
+ 0, /* refcnt */
+ lx_autofs_info, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lx_autofs_attach, /* attach */
+ lx_autofs_detach, /* detach */
+ nodev, /* reset */
+ &lx_autofs_cb_ops, /* driver operations */
+ NULL, /* no bus operations */
+ NULL, /* power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx autofs filesystem", &vfw
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, "lx autofs driver", &lx_autofs_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modlfs,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ if (lx_autofs_vn_ops != NULL) {
+ vn_freevnodeops(lx_autofs_vn_ops);
+ lx_autofs_vn_ops = NULL;
+ }
+
+ /*
+ * In our init routine, if we get an error after calling
+ * vfs_setfsops() we cleanup by calling vfs_freevfsops_by_type().
+ * But we don't need to call vfs_freevfsops_by_type() here
+ * because the fs framework did this for us as part of the
+ * mod_remove() call above.
+ */
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/autofs/lxautofs.conf b/usr/src/uts/common/brand/lx/autofs/lxautofs.conf
new file mode 100644
index 0000000000..36e0119e33
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/autofs/lxautofs.conf
@@ -0,0 +1,14 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+name="lxautofs" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps.h b/usr/src/uts/common/brand/lx/cgroups/cgrps.h
new file mode 100644
index 0000000000..46e2cdd886
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps.h
@@ -0,0 +1,222 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LXCGRPS_H
+#define _LXCGRPS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * cgrps.h: declarations, data structures and macros for lx_cgroup
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+
+/*
+ * cgrpmgr ioctl interface.
+ */
+#define CGRPFS_IOC ('C' << 16 | 'G' << 8)
+#define CGRPFS_GETEVNT (CGRPFS_IOC | 1)
+
+typedef struct cgrpmgr_info {
+ pid_t cgmi_pid;
+ char *cgmi_rel_agent_path;
+ char *cgmi_cgroup_path;
+} cgrpmgr_info_t;
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+typedef struct cgrpmgr_info32 {
+ pid_t cgmi_pid;
+ caddr32_t cgmi_rel_agent_path;
+ caddr32_t cgmi_cgroup_path;
+} cgrpmgr_info32_t;
+
+#define CG_PSNSIZE 256 /* max size of pseudo file name entries */
+#define CG_PSDSIZE 16 /* pretend that a dir entry takes 16 bytes */
+
+/*
+ * The order of these entries must be in sync with the cg_ssde_dir array.
+ */
+typedef enum cgrp_ssid {
+ CG_SSID_GENERIC = 1,
+ CG_SSID_NUM /* last ssid for range checking */
+} cgrp_ssid_t;
+
+typedef enum cgrp_nodetype {
+ CG_CGROUP_DIR = 1, /* cgroup directory entry */
+ CG_NOTIFY, /* notify_on_release file */
+ CG_PROCS, /* cgroup.procs file */
+ CG_REL_AGENT, /* release_agent file */
+ CG_TASKS, /* tasks file */
+} cgrp_nodetype_t;
+
+typedef struct cgrp_subsys_dirent {
+ cgrp_nodetype_t cgrp_ssd_type;
+ char *cgrp_ssd_name;
+} cgrp_subsys_dirent_t;
+
+#define N_DIRENTS(m) (cgrp_num_pseudo_ents((m)->cg_ssid) + 2)
+
+/*
+ * A modern systemd-based Linux system typically has 50-60 cgroups so
+ * we size the hash for 2x that number.
+ */
+#define CGRP_HASH_SZ 128
+#define CGRP_AGENT_LEN (MAXPATHLEN + 1)
+
+/*
+ * cgroups per-mount data structure.
+ *
+ * All but the event related fields are protected by cg_contents.
+ * The evnt_list and counter is protected by cg_events.
+ */
+typedef struct cgrp_mnt {
+ struct vfs *cg_vfsp; /* filesystem's vfs struct */
+ struct cgrp_node *cg_rootnode; /* root cgrp_node */
+ char *cg_mntpath; /* name of cgroup mount point */
+ cgrp_ssid_t cg_ssid; /* subsystem type */
+ dev_t cg_dev; /* unique dev # of mounted `device' */
+ uint_t cg_gen; /* node ID source for files */
+ uint_t cg_grp_gen; /* ID source for cgroups */
+ kmutex_t cg_contents; /* global lock for most fs activity */
+ char cg_agent[CGRP_AGENT_LEN]; /* release_agent path */
+ /* ptr to zone data for containing zone */
+ lx_zone_data_t *cg_lxzdata;
+ struct cgrp_node **cg_grp_hash; /* hash list of cgroups in the fs */
+} cgrp_mnt_t;
+
+/*
+ * cgrp_node is the file system dependent node for cgroups.
+ *
+ * The node is used to represent both directories (a cgroup) and pseudo files
+ * within the directory.
+ *
+ * Members are tagged in the comment to note which type of node they apply to:
+ * A - all
+ * D - dir (i.e. a cgroup)
+ * F - pseudo file
+ */
+
+typedef struct cgrp_node {
+ struct cgrp_node *cgn_back; /* A lnked lst of cgrp_nodes */
+ struct cgrp_node *cgn_forw; /* A lnked lst of cgrp_nodes */
+ struct cgrp_dirent *cgn_dir; /* D dirent list */
+ struct cgrp_node *cgn_parent; /* A dir containing this node */
+ struct cgrp_node *cgn_next; /* D link in per-mount cgroup */
+ /* hash table */
+ uint_t cgn_dirents; /* D number of dirents */
+ cgrp_nodetype_t cgn_type; /* A type for this node */
+ uint_t cgn_notify; /* D notify_on_release value */
+ uint_t cgn_task_cnt; /* D number of threads in grp */
+ struct vnode *cgn_vnode; /* A vnode for this cgrp_node */
+ uint_t cgn_id; /* D ID number for the cgroup */
+ struct vattr cgn_attr; /* A attributes */
+} cgrp_node_t;
+
+/*
+ * File system independent to cgroups conversion macros
+ */
+#define VFSTOCGM(vfsp) ((cgrp_mnt_t *)(vfsp)->vfs_data)
+#define VTOCGM(vp) ((cgrp_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define VTOCGN(vp) ((struct cgrp_node *)(vp)->v_data)
+#define CGNTOV(cn) ((cn)->cgn_vnode)
+#define cgnode_hold(cn) VN_HOLD(CGNTOV(cn))
+#define cgnode_rele(cn) VN_RELE(CGNTOV(cn))
+
+/*
+ * Attributes
+ */
+#define cgn_mask cgn_attr.va_mask
+#define cgn_mode cgn_attr.va_mode
+#define cgn_uid cgn_attr.va_uid
+#define cgn_gid cgn_attr.va_gid
+#define cgn_fsid cgn_attr.va_fsid
+#define cgn_nodeid cgn_attr.va_nodeid
+#define cgn_nlink cgn_attr.va_nlink
+#define cgn_size cgn_attr.va_size
+#define cgn_atime cgn_attr.va_atime
+#define cgn_mtime cgn_attr.va_mtime
+#define cgn_ctime cgn_attr.va_ctime
+#define cgn_rdev cgn_attr.va_rdev
+#define cgn_blksize cgn_attr.va_blksize
+#define cgn_nblocks cgn_attr.va_nblocks
+#define cgn_seq cgn_attr.va_seq
+
+/*
+ * cgroup directories are made up of a linked list of cg_dirent structures
+ * hanging off directory cgrp_nodes. File names are not fixed length,
+ * but are null terminated.
+ */
+typedef struct cgrp_dirent {
+ struct cgrp_node *cgd_cgrp_node; /* cg node for this file */
+ struct cgrp_dirent *cgd_next; /* next directory entry */
+ struct cgrp_dirent *cgd_prev; /* prev directory entry */
+ uint_t cgd_offset; /* "offset" of dir entry */
+ uint_t cgd_hash; /* a hash of cgd_name */
+ struct cgrp_dirent *cgd_link; /* linked via hash table */
+ struct cgrp_node *cgd_parent; /* parent, dir we are in */
+ char *cgd_name; /* null terminated */
+} cgrp_dirent_t;
+
+enum de_op { DE_CREATE, DE_MKDIR, DE_RENAME }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
+
+extern struct vnodeops *cgrp_vnodeops;
+
+int cgrp_dirdelete(cgrp_node_t *, cgrp_node_t *, char *, enum dr_op, cred_t *);
+int cgrp_direnter(cgrp_mnt_t *, cgrp_node_t *, char *, enum de_op,
+ cgrp_node_t *, struct vattr *, cgrp_node_t **, cred_t *);
+void cgrp_dirinit(cgrp_node_t *, cgrp_node_t *, cred_t *);
+int cgrp_dirlookup(cgrp_node_t *, char *, cgrp_node_t **, cred_t *);
+void cgrp_dirtrunc(cgrp_node_t *);
+void cgrp_node_init(cgrp_mnt_t *, cgrp_node_t *, vattr_t *, cred_t *);
+int cgrp_taccess(void *, int, cred_t *);
+ino_t cgrp_inode(cgrp_nodetype_t, unsigned int);
+int cgrp_num_pseudo_ents(cgrp_ssid_t);
+cgrp_node_t *cgrp_cg_hash_lookup(cgrp_mnt_t *, uint_t);
+void cgrp_rel_agent_event(cgrp_mnt_t *, cgrp_node_t *, boolean_t);
+
+#endif /* KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXCGRPS_H */
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c
new file mode 100644
index 0000000000..66b6f60376
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c
@@ -0,0 +1,1014 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "cgrps.h"
+
+static int cgrp_dirmakecgnode(cgrp_node_t *, cgrp_mnt_t *, struct vattr *,
+ enum de_op, cgrp_node_t **, struct cred *);
+static int cgrp_diraddentry(cgrp_node_t *, cgrp_node_t *, char *);
+
+static cgrp_subsys_dirent_t cgrp_generic_dir[] = {
+ { CG_PROCS, "cgroup.procs" },
+ { CG_NOTIFY, "notify_on_release" },
+ { CG_TASKS, "tasks" }
+};
+
+typedef struct cgrp_ssde {
+ cgrp_subsys_dirent_t *cg_ssde_files;
+ int cg_ssde_nfiles;
+} cgrp_ssde_t;
+
+#define CGDIRLISTSZ(l) (sizeof (l) / sizeof ((l)[0]))
+
+/*
+ * Note, these entries must be in the same order as the cgrp_ssid_t entries.
+ */
+static cgrp_ssde_t cg_ssde_dir[] = {
+ /* subsystems start at 1 */
+ {NULL, 0},
+
+ /* CG_SSID_GENERIC */
+ {cgrp_generic_dir, CGDIRLISTSZ(cgrp_generic_dir)},
+};
+
+
+#define CG_HASH_SIZE 8192 /* must be power of 2 */
+#define CG_MUTEX_SIZE 64
+
+static cgrp_dirent_t *cg_hashtable[CG_HASH_SIZE];
+static kmutex_t cg_hashmutex[CG_MUTEX_SIZE];
+
+#define CG_HASH_INDEX(a) ((a) & (CG_HASH_SIZE-1))
+#define CG_MUTEX_INDEX(a) ((a) & (CG_MUTEX_SIZE-1))
+
+#define CG_HASH(cp, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(cp) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ }
+
+#define MODESHIFT 3
+
+typedef enum cgrp_nodehold {
+ NOHOLD,
+ HOLD
+} cgrp_nodehold_t;
+
+void
+cgrp_hash_init(void)
+{
+ int i;
+
+ for (i = 0; i < CG_MUTEX_SIZE; i++)
+ mutex_init(&cg_hashmutex[i], NULL, MUTEX_DEFAULT, NULL);
+}
+
+static void
+cgrp_hash_in(cgrp_dirent_t *c)
+{
+ uint_t hash;
+ cgrp_dirent_t **prevpp;
+ kmutex_t *cg_hmtx;
+
+ CG_HASH(c->cgd_parent, c->cgd_name, hash);
+ c->cgd_hash = hash;
+ prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ c->cgd_link = *prevpp;
+ *prevpp = c;
+ mutex_exit(cg_hmtx);
+}
+
+static void
+cgrp_hash_out(cgrp_dirent_t *c)
+{
+ uint_t hash;
+ cgrp_dirent_t **prevpp;
+ kmutex_t *cg_hmtx;
+
+ hash = c->cgd_hash;
+ prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ while (*prevpp != c)
+ prevpp = &(*prevpp)->cgd_link;
+ *prevpp = c->cgd_link;
+ mutex_exit(cg_hmtx);
+}
+
+static cgrp_dirent_t *
+cgrp_hash_lookup(char *name, cgrp_node_t *parent, cgrp_nodehold_t hold,
+ cgrp_node_t **found)
+{
+ cgrp_dirent_t *l;
+ uint_t hash;
+ kmutex_t *cg_hmtx;
+ cgrp_node_t *cnp;
+
+ CG_HASH(parent, name, hash);
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ l = cg_hashtable[CG_HASH_INDEX(hash)];
+ while (l) {
+ if ((l->cgd_hash == hash) &&
+ (l->cgd_parent == parent) &&
+ (strcmp(l->cgd_name, name) == 0)) {
+ /*
+ * We need to make sure that the cgrp_node that
+ * we put a hold on is the same one that we pass back.
+ * Hence, temporary variable cnp is necessary.
+ */
+ cnp = l->cgd_cgrp_node;
+ if (hold == HOLD) {
+ ASSERT(cnp);
+ cgnode_hold(cnp);
+ }
+ if (found)
+ *found = cnp;
+ mutex_exit(cg_hmtx);
+ return (l);
+ } else {
+ l = l->cgd_link;
+ }
+ }
+ mutex_exit(cg_hmtx);
+ return (NULL);
+}
+
+/*
+ * The following functions maintain the per-mount cgroup hash table.
+ */
+static void
+cgrp_cg_hash_insert(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+ uint_t cgid;
+ int hsh;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ cgid = cn->cgn_id;
+ hsh = cgid % CGRP_HASH_SZ;
+
+ cn->cgn_next = cgm->cg_grp_hash[hsh];
+ cgm->cg_grp_hash[hsh] = cn;
+}
+
+static void
+cgrp_cg_hash_remove(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+ uint_t cgid;
+ int hsh;
+ cgrp_node_t *np = NULL, *curp, *prevp = NULL;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ cgid = cn->cgn_id;
+ hsh = cgid % CGRP_HASH_SZ;
+
+ for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+ curp = curp->cgn_next) {
+ if (curp->cgn_id == cgid) {
+ if (prevp == NULL) {
+ cgm->cg_grp_hash[hsh] = curp->cgn_next;
+ } else {
+ prevp->cgn_next = curp->cgn_next;
+ }
+ np = curp;
+ np->cgn_next = NULL;
+ break;
+ }
+
+ prevp = curp;
+ }
+
+ ASSERT(np != NULL);
+ ASSERT(np->cgn_task_cnt == 0);
+}
+
+/*
+ * Count up the number of threads already running in the zone and initialize the
+ * first cgroup's task counter.
+ *
+ * We have to look at all of the processes to find applicable ones.
+ */
+static void
+cgrp_cg_hash_init(cgrp_node_t *cn)
+{
+ int i;
+ int cnt = 0;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+ /* Scan all of the process entries */
+ mutex_enter(&pidlock);
+ for (i = 1; i < v.v_proc; i++) {
+ proc_t *p;
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_zone->zone_id != zoneid) {
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if (p->p_brand != &lx_brand) {
+ mutex_exit(&p->p_lock);
+ continue;
+ }
+ cnt += p->p_lwpcnt;
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * There should be at least the init process with 1 thread in the zone
+ */
+ ASSERT(cnt > 0);
+ cn->cgn_task_cnt = cnt;
+
+ DTRACE_PROBE2(cgrp__grp__init, void *, cn, int, cnt);
+
+ mutex_exit(&pidlock);
+}
+
+cgrp_node_t *
+cgrp_cg_hash_lookup(cgrp_mnt_t *cgm, uint_t cgid)
+{
+ int hsh = cgid % CGRP_HASH_SZ;
+ cgrp_node_t *curp;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+ curp = curp->cgn_next) {
+ if (curp->cgn_id == cgid) {
+ return (curp);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them to give the inode number for
+ * a cgrp pseudo file node.
+ */
+ino_t
+cgrp_inode(cgrp_nodetype_t type, unsigned int cgrpid)
+{
+ /*
+ * cgroup inode format:
+ * 00000000AABBBBBB
+ *
+ * AA - node type (from subsystem list)
+ * BBBBBB - id of the cgroup
+ */
+
+ return ((ino_t)(type << 24) | (cgrpid & 0xffffff));
+}
+
+/*
+ * Return the number of pseudo file entries in a cgroup directory for the
+ * given subsystem.
+ */
+int
+cgrp_num_pseudo_ents(cgrp_ssid_t ssid)
+{
+ cgrp_ssde_t *ssdp = &cg_ssde_dir[ssid];
+
+ return (ssdp->cg_ssde_nfiles);
+}
+
+int
+cgrp_taccess(void *vcp, int mode, cred_t *cred)
+{
+ cgrp_node_t *cn = vcp;
+ int shift = 0;
+ /*
+ * Check access based on owner, group and public perms in cgrp_node.
+ */
+ if (crgetuid(cred) != cn->cgn_uid) {
+ shift += MODESHIFT;
+ if (groupmember(cn->cgn_gid, cred) == 0)
+ shift += MODESHIFT;
+ }
+
+ return (secpolicy_vnode_access2(cred, CGNTOV(cn), cn->cgn_uid,
+ cn->cgn_mode << shift, mode));
+}
+
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found cgrp_node with its vnode held.
+ */
+int
+cgrp_dirlookup(cgrp_node_t *parent, char *name, cgrp_node_t **foundcp,
+ cred_t *cred)
+{
+ int error;
+
+ ASSERT(MUTEX_HELD(&VTOCGM(parent->cgn_vnode)->cg_contents));
+ *foundcp = NULL;
+ if (parent->cgn_type != CG_CGROUP_DIR)
+ return (ENOTDIR);
+
+ if ((error = cgrp_taccess(parent, VEXEC, cred)))
+ return (error);
+
+ if (*name == '\0') {
+ cgnode_hold(parent);
+ *foundcp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name
+ * We need the lock protecting the cgn_dir list
+ * so that it doesn't change out from underneath us.
+ * cgrp_hash_lookup() will pass back the cgrp_node
+ * with a hold on it.
+ */
+
+ if (cgrp_hash_lookup(name, parent, HOLD, foundcp) != NULL) {
+ ASSERT(*foundcp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Enter a directory entry for 'name' and 'cp' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+cgrp_direnter(
+ cgrp_mnt_t *cgm,
+ cgrp_node_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ cgrp_node_t *cn, /* existing cgrp_node, if rename */
+ struct vattr *va,
+ cgrp_node_t **cnp, /* return cgrp_node, if create/mkdir */
+ cred_t *cred)
+{
+ cgrp_dirent_t *cdp;
+ cgrp_node_t *found = NULL;
+ int error = 0;
+ char *s;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ /*
+ * Don't allow '/' characters in pathname component,
+ */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("cgrp_direnter: NULL name");
+
+ /*
+ * For rename lock the source entry and check the link count
+ * to see if it has been removed while it was unlocked.
+ * Remember that we can only rename within the same directory.
+ */
+ if (op == DE_RENAME) {
+ if (cn->cgn_nlink == 0) {
+ return (ENOENT);
+ }
+
+ if (cn->cgn_nlink == MAXLINK) {
+ return (EMLINK);
+ }
+ cn->cgn_nlink++;
+ gethrestime(&cn->cgn_ctime);
+ }
+
+ /*
+ * This might be a "dangling detached directory".
+ * it could have been removed, but a reference
+ * to it kept in u_cwd. don't bother searching
+ * it, and with any luck the user will get tired
+ * of dealing with us and cd to some absolute
+ * pathway. *sigh*, thus in ufs, too.
+ */
+ if (dir->cgn_nlink == 0) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /*
+ * Search for the entry. In all cases it is an error if it exists.
+ */
+ cdp = cgrp_hash_lookup(name, dir, HOLD, &found);
+
+ if (cdp) {
+ ASSERT(found != NULL);
+ error = EEXIST;
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(found);
+ mutex_enter(&cgm->cg_contents);
+ } else {
+
+ /*
+ * The entry does not exist. Check write permission in
+ * directory to see if entry can be created.
+ */
+ if ((error = cgrp_taccess(dir, VWRITE, cred)) != 0)
+ goto out;
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Make new cgrp_node and directory entry as required.
+ */
+ error = cgrp_dirmakecgnode(dir, cgm, va, op, &cn, cred);
+ if (error)
+ goto out;
+
+ if (op == DE_MKDIR) {
+ /*
+ * inherit notify_on_release value from parent
+ */
+ cn->cgn_notify = dir->cgn_notify;
+ }
+ }
+
+ error = cgrp_diraddentry(dir, cn, name);
+ if (error != 0) {
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Unmake the inode we just made.
+ */
+ if ((cn->cgn_type) == CG_CGROUP_DIR) {
+ ASSERT(cdp == NULL);
+ /*
+ * cleanup allocs made by cgrp_dirinit
+ */
+ cgrp_dirtrunc(cn);
+ }
+ cn->cgn_nlink = 0;
+ gethrestime(&cn->cgn_ctime);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(cn);
+ mutex_enter(&cgm->cg_contents);
+ cn = NULL;
+ }
+ } else if (cnp) {
+ *cnp = cn;
+ } else if (op == DE_CREATE || op == DE_MKDIR) {
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(cn);
+ mutex_enter(&cgm->cg_contents);
+ }
+ }
+
+out:
+ if (error && op == DE_RENAME) {
+ /* Undo bumped link count. */
+ cn->cgn_nlink--;
+ gethrestime(&cn->cgn_ctime);
+ }
+ return (error);
+}
+
+/*
+ * Delete entry cn of name "nm" from parent dir. This is used to both remove
+ * a cgroup directory and to remove the pseudo file nodes within the cgroup
+ * directory (by recursively calling itself). It frees the dir entry space
+ * and decrements link count on cgrp_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+cgrp_dirdelete(cgrp_node_t *dir, cgrp_node_t *cn, char *nm, enum dr_op op,
+ cred_t *cred)
+{
+ cgrp_mnt_t *cgm = VTOCGM(cn->cgn_vnode);
+ cgrp_dirent_t *cndp;
+ int error;
+ size_t namelen;
+ cgrp_node_t *cnnp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ if (nm[0] == '\0')
+ panic("cgrp_dirdelete: empty name for 0x%p", (void *)cn);
+
+ /*
+ * return error when removing . and ..
+ */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = cgrp_taccess(dir, VEXEC|VWRITE, cred)) != 0)
+ return (error);
+
+ if (dir->cgn_dir == NULL)
+ return (ENOENT);
+
+ if (op == DR_RMDIR) {
+ /*
+ * This is the top-level removal of a cgroup dir. Start by
+ * removing the fixed pseudo file entries from the dir. We do
+ * this by recursively calling back into this function with
+ * a different op code. The caller of this function has
+ * already verified that it is safe to remove this directory.
+ */
+ cgrp_dirent_t *cdp;
+
+ ASSERT(cn->cgn_type == CG_CGROUP_DIR);
+
+ cdp = cn->cgn_dir;
+ while (cdp) {
+ cgrp_node_t *pseudo_node;
+ cgrp_dirent_t *nextp;
+
+ if (strcmp(cdp->cgd_name, ".") == 0 ||
+ strcmp(cdp->cgd_name, "..") == 0) {
+ cdp = cdp->cgd_next;
+ continue;
+ }
+
+ pseudo_node = cdp->cgd_cgrp_node;
+ nextp = cdp->cgd_next;
+
+ cgnode_hold(pseudo_node);
+ error = cgrp_dirdelete(cn, pseudo_node,
+ cdp->cgd_name, DR_REMOVE, cred);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(pseudo_node);
+ mutex_enter(&cgm->cg_contents);
+
+ cdp = nextp;
+ }
+
+ cgrp_cg_hash_remove(cgm, cn);
+ }
+
+ cndp = cgrp_hash_lookup(nm, dir, NOHOLD, &cnnp);
+ VERIFY(cndp != NULL);
+ VERIFY(cn == cnnp);
+
+ cgrp_hash_out(cndp);
+
+ /* Take cndp out of the directory list. */
+ ASSERT(cndp->cgd_next != cndp);
+ ASSERT(cndp->cgd_prev != cndp);
+ if (cndp->cgd_prev) {
+ cndp->cgd_prev->cgd_next = cndp->cgd_next;
+ }
+ if (cndp->cgd_next) {
+ cndp->cgd_next->cgd_prev = cndp->cgd_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match cndp,
+ * point it at the previous dirent.
+ */
+ if (dir->cgn_dir->cgd_prev == cndp) {
+ dir->cgn_dir->cgd_prev = cndp->cgd_prev;
+ }
+ ASSERT(cndp->cgd_next != cndp);
+ ASSERT(cndp->cgd_prev != cndp);
+
+ /* cndp points to the correct directory entry */
+ namelen = strlen(cndp->cgd_name) + 1;
+
+ kmem_free(cndp, sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_dirents--;
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+ cn->cgn_ctime = now;
+
+ ASSERT(cn->cgn_nlink > 0);
+ cn->cgn_nlink--;
+ if (op == DR_RMDIR && cn->cgn_type == CG_CGROUP_DIR) {
+ cgrp_dirtrunc(cn);
+ ASSERT(cn->cgn_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * Initialize a cgrp_node and add it to file list under mount point.
+ */
+void
+cgrp_node_init(cgrp_mnt_t *cgm, cgrp_node_t *cn, vattr_t *vap, cred_t *cred)
+{
+ struct vnode *vp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(vap != NULL);
+
+ cn->cgn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ cn->cgn_mask = 0;
+ cn->cgn_attr.va_type = vap->va_type;
+ cn->cgn_nlink = 1;
+ cn->cgn_size = 0;
+
+ if (cred == NULL) {
+ cn->cgn_uid = vap->va_uid;
+ cn->cgn_gid = vap->va_gid;
+ } else {
+ cn->cgn_uid = crgetuid(cred);
+ cn->cgn_gid = crgetgid(cred);
+ }
+
+ cn->cgn_fsid = cgm->cg_dev;
+ cn->cgn_rdev = vap->va_rdev;
+ cn->cgn_blksize = PAGESIZE;
+ cn->cgn_nblocks = 0;
+ gethrestime(&now);
+ cn->cgn_atime = now;
+ cn->cgn_mtime = now;
+ cn->cgn_ctime = now;
+ cn->cgn_seq = 0;
+ cn->cgn_dir = NULL;
+
+ cn->cgn_vnode = vn_alloc(KM_SLEEP);
+ vp = CGNTOV(cn);
+ vn_setops(vp, cgrp_vnodeops);
+ vp->v_vfsp = cgm->cg_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)cn;
+
+ cn->cgn_nodeid = cgm->cg_gen++;
+
+ /*
+ * Add new cgrp_node to end of linked list of cgrp_nodes for this
+ * cgroup fs. Root directory is handled specially in cgrp_mount.
+ */
+ if (cgm->cg_rootnode != (cgrp_node_t *)NULL) {
+ cn->cgn_forw = NULL;
+ cn->cgn_back = cgm->cg_rootnode->cgn_back;
+ cn->cgn_back->cgn_forw = cgm->cg_rootnode->cgn_back = cn;
+ }
+ vn_exists(vp);
+}
+
+void
+cgrp_addnode(cgrp_mnt_t *cgm, cgrp_node_t *dir, char *name,
+ cgrp_nodetype_t type, struct vattr *nattr, cred_t *cr)
+{
+ cgrp_node_t *ncn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ VERIFY0(cgrp_direnter(cgm, dir, name, DE_CREATE, (cgrp_node_t *)NULL,
+ nattr, &ncn, cr));
+
+ /*
+ * Fix the inode and assign the pseudo file type to be correct.
+ */
+ ncn->cgn_nodeid = cgrp_inode(type, dir->cgn_nodeid);
+ ncn->cgn_type = type;
+
+ /*
+ * Since we're creating these entries here and not via the
+ * normal VOP_CREATE code path, we need to do the rele to drop
+ * our hold. This will leave the vnode v_count at 0 when we
+ * come out of cgrp_inactive but we won't reclaim the vnode
+ * there since the cgn_nlink value will still be 1.
+ */
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(ncn);
+ mutex_enter(&cgm->cg_contents);
+}
+
+/*
+ * cgrp_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+cgrp_dirinit(cgrp_node_t *parent, cgrp_node_t *dir, cred_t *cr)
+{
+ cgrp_dirent_t *dot, *dotdot;
+ timestruc_t now;
+ cgrp_mnt_t *cgm = VTOCGM(dir->cgn_vnode);
+ cgrp_ssde_t *ssdp;
+ cgrp_subsys_dirent_t *pseudo_files;
+ struct vattr nattr;
+ int i;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ ASSERT(cgm->cg_ssid > 0 && cgm->cg_ssid < CG_SSID_NUM);
+ ssdp = &cg_ssde_dir[cgm->cg_ssid];
+
+ /*
+ * If this is the top-level cgroup created by the mount then we need to
+ * count up the number of procs and tasks already running in the zone.
+ */
+
+ /*
+ * Set the cgroup ID for this cgrp_node by using a counter on each
+ * mount.
+ */
+ dir->cgn_id = cgm->cg_grp_gen++;
+ cgrp_cg_hash_insert(cgm, dir);
+ /* Initialise the first cgroup if this is top-level group */
+ if (parent == dir)
+ cgrp_cg_hash_init(dir);
+
+ /*
+ * Initialize the entries
+ */
+ dot = kmem_zalloc(sizeof (cgrp_dirent_t) + 2, KM_SLEEP);
+ dot->cgd_cgrp_node = dir;
+ dot->cgd_offset = 0;
+ dot->cgd_name = (char *)dot + sizeof (cgrp_dirent_t);
+ dot->cgd_name[0] = '.';
+ dot->cgd_parent = dir;
+ cgrp_hash_in(dot);
+
+ dotdot = kmem_zalloc(sizeof (cgrp_dirent_t) + 3, KM_SLEEP);
+ dotdot->cgd_cgrp_node = parent;
+ dotdot->cgd_offset = 1;
+ dotdot->cgd_name = (char *)dotdot + sizeof (cgrp_dirent_t);
+ dotdot->cgd_name[0] = '.';
+ dotdot->cgd_name[1] = '.';
+ dotdot->cgd_parent = dir;
+ cgrp_hash_in(dotdot);
+
+ /*
+ * Initialize directory entry list.
+ */
+ dot->cgd_next = dotdot;
+ dot->cgd_prev = dotdot; /* dot's cgd_prev holds roving slot pointer */
+ dotdot->cgd_next = NULL;
+ dotdot->cgd_prev = dot;
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ parent->cgn_nlink++;
+ parent->cgn_ctime = now;
+
+ dir->cgn_dir = dot;
+ dir->cgn_size = 2 * sizeof (cgrp_dirent_t) + 5; /* dot and dotdot */
+ dir->cgn_dirents = 2;
+ dir->cgn_nlink = 2;
+
+ bzero(&nattr, sizeof (struct vattr));
+ nattr.va_mode = (mode_t)(0644);
+ nattr.va_type = VREG;
+ nattr.va_rdev = 0;
+
+ /*
+ * If this is the top-level dir in the file system then it always
+ * has a release_agent pseudo file. Only the top-level dir has this
+ * file.
+ */
+ if (parent == dir) {
+ cgrp_addnode(cgm, dir, "release_agent", CG_REL_AGENT, &nattr,
+ cr);
+ }
+
+ pseudo_files = ssdp->cg_ssde_files;
+ for (i = 0; i < ssdp->cg_ssde_nfiles; i++) {
+ cgrp_addnode(cgm, dir, pseudo_files[i].cgrp_ssd_name,
+ pseudo_files[i].cgrp_ssd_type, &nattr, cr);
+ }
+}
+
+/*
+ * cgrp_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+cgrp_dirtrunc(cgrp_node_t *dir)
+{
+ cgrp_dirent_t *cgdp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&VTOCGM(dir->cgn_vnode)->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ for (cgdp = dir->cgn_dir; cgdp; cgdp = dir->cgn_dir) {
+ size_t namelen;
+ cgrp_node_t *cn;
+
+ ASSERT(cgdp->cgd_next != cgdp);
+ ASSERT(cgdp->cgd_prev != cgdp);
+ ASSERT(cgdp->cgd_cgrp_node);
+
+ dir->cgn_dir = cgdp->cgd_next;
+ namelen = strlen(cgdp->cgd_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this directory entry
+ * removal. We do hold/rele operations to free up these nodes.
+ */
+ cn = cgdp->cgd_cgrp_node;
+ ASSERT(cn->cgn_nlink > 0);
+ cn->cgn_nlink--;
+
+ cgrp_hash_out(cgdp);
+ kmem_free(cgdp, sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ ASSERT(dir->cgn_dir == NULL);
+ ASSERT(dir->cgn_size == 0);
+ ASSERT(dir->cgn_dirents == 0);
+}
+
+static int
+cgrp_diraddentry(cgrp_node_t *dir, cgrp_node_t *cn, char *name)
+{
+ cgrp_dirent_t *cdp, *cpdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent directory wasn't removed from
+ * underneath the caller.
+ */
+ if (dir->cgn_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same filesystem. */
+ if (cn->cgn_vnode->v_vfsp != dir->cgn_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Allocate and initialize directory entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (cgrp_dirent_t);
+ cdp = kmem_zalloc(alloc_size, KM_NOSLEEP | KM_NORMALPRI);
+ if (cdp == NULL)
+ return (ENOSPC);
+
+ cn->cgn_parent = dir;
+
+ dir->cgn_size += alloc_size;
+ dir->cgn_dirents++;
+ cdp->cgd_cgrp_node = cn;
+ cdp->cgd_parent = dir;
+
+ /* The directory entry and its name were allocated sequentially. */
+ cdp->cgd_name = (char *)cdp + sizeof (cgrp_dirent_t);
+ (void) strcpy(cdp->cgd_name, name);
+
+ cgrp_hash_in(cdp);
+
+ /*
+ * Some utilities expect the size of a directory to remain
+ * somewhat static. For example, a routine which removes
+ * subdirectories between calls to readdir(); the size of the
+ * directory changes from underneath it and so the real
+ * directory offset in bytes is invalid. To circumvent
+ * this problem, we initialize a directory entry with an
+ * phony offset, and use this offset to determine end of
+ * file in cgrp_readdir.
+ */
+ cpdp = dir->cgn_dir->cgd_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (cpdp->cgd_next != NULL && (cpdp->cgd_next->cgd_offset -
+ cpdp->cgd_offset) <= 1) {
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+ ASSERT(cpdp->cgd_next->cgd_offset > cpdp->cgd_offset);
+ cpdp = cpdp->cgd_next;
+ }
+ cdp->cgd_offset = cpdp->cgd_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which
+ * is necessarily the largest offset in this directory) is more
+ * than twice the number of dirents, that means the directory is
+ * 50% holes. At this point we reset the slot pointer back to
+ * the beginning of the directory so we start using the holes.
+ * The idea is that if there are N dirents, there must also be
+ * N holes, so we can satisfy the next N creates by walking at
+ * most 2N entries; thus the average cost of a create is constant.
+ * Note that we use the first dirent's cgd_prev as the roving
+ * slot pointer; it's ugly, but it saves a word in every dirent.
+ */
+ if (cpdp->cgd_next == NULL && cpdp->cgd_offset > 2 * dir->cgn_dirents)
+ dir->cgn_dir->cgd_prev = dir->cgn_dir->cgd_next;
+ else
+ dir->cgn_dir->cgd_prev = cdp;
+
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+
+ cdp->cgd_next = cpdp->cgd_next;
+ if (cdp->cgd_next) {
+ cdp->cgd_next->cgd_prev = cdp;
+ }
+ cdp->cgd_prev = cpdp;
+ cpdp->cgd_next = cdp;
+
+ ASSERT(cdp->cgd_next != cdp);
+ ASSERT(cdp->cgd_prev != cdp);
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ return (0);
+}
+
+static int
+cgrp_dirmakecgnode(cgrp_node_t *dir, cgrp_mnt_t *cgm, struct vattr *va,
+ enum de_op op, cgrp_node_t **newnode, struct cred *cred)
+{
+ cgrp_node_t *cn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(va != NULL);
+
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+
+ cn = kmem_zalloc(sizeof (cgrp_node_t), KM_SLEEP);
+ cgrp_node_init(cgm, cn, va, cred);
+
+ cn->cgn_vnode->v_rdev = cn->cgn_rdev = NODEV;
+ cn->cgn_vnode->v_type = va->va_type;
+ cn->cgn_uid = crgetuid(cred);
+ cn->cgn_gid = crgetgid(cred);
+
+ if (va->va_mask & AT_ATIME)
+ cn->cgn_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ cn->cgn_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ cn->cgn_type = CG_CGROUP_DIR;
+ cgrp_dirinit(dir, cn, cred);
+ }
+
+ *newnode = cn;
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c
new file mode 100644
index 0000000000..7805c3f2bd
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c
@@ -0,0 +1,1071 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * The cgroup file system implements a subset of the Linux cgroup functionality
+ * for use by lx-branded zones. On Linux, cgroups are a generic process grouping
+ * mechanism which is used to apply various behaviors to the processes within
+ * the group, although it's primary purpose is for resource management.
+ *
+ * In Linux, the cgroup file system provides two pieces of functionality:
+ * 1) A per-mount set of cgroups arranged in a tree, such that every task in
+ * the system is in one, and only one, of the cgroups in the tree.
+ * 2) A set of subsystems; each subsystem has subsystem-specific state and
+ * behavior and is associated with a cgroup mount. This provides a way to
+ * apply arbitrary functionality (but generally resource management related)
+ * to the processes associated with the nodes in the tree at that mount
+ * point.
+ *
+ * For example, it is common to see cgroup trees (each is its own mount with a
+ * different subsystem controller) for blkio, cpuset, memory, systemd (has no
+ * controller), etc. Within each tree there is a top-level directory with at
+ * least a cgroup.procs, notify_on_release, release_agent, and tasks file.
+ * The cgroup.procs file lists the processes within that group and the tasks
+ * file lists the threads in the group. There could be subdirectories, which
+ * define new cgroups, that then contain a subset of the processes. Each
+ * subdirectory also has, at a minimum, a cgroup.procs, notify_on_release, and
+ * tasks file.
+ *
+ * Since we're using lx to run user-level code within zones, the majority (all?)
+ * of the cgroup resource management functionality simply doesn't apply to us.
+ * The primary need for cgroups is to support the init program 'systemd' as the
+ * consumer. systemd only requires the process grouping hierarchy of cgroups,
+ * although it can also use the resource management features if they are
+ * available. Given this, our cgroup file system only implements the process
+ * hierarchy and does not report that any resource management controllers are
+ * available for separate mounts.
+ *
+ * In addition to the hierarchy, the other important component of cgroups that
+ * is used by systemd is the 'release_agent'. This provides a mechanism to
+ * run a command when a cgroup becomes empty (the last task in the group
+ * leaves, either by exit or move, and there are no more sub-cgroups). The
+ * 'release_agent' file only exists in the top-level cgroup of the mounted
+ * file system and holds the path to a command to run. The 'notify_on_release'
+ * file exists in each cgroup dir. If that file contains a '1' then the agent
+ * is run when that group becomes empty. The agent is passed a path string of
+ * the cgroup, relative to the file system mount point (e.g. a mount on
+ * /sys/fs/cgroups/systemd with a sub-cgroup of /sys/fs/cgroups/systemd/foo/bar
+ * gets the arg /foo/bar).
+ *
+ * Cgroup membership is implemented via hooks into the lx brand code. When
+ * the cgroup file system loads it installs callbacks for:
+ * lx_cgrp_initlwp
+ * lx_cgrp_freelwp
+ * and when it unloads it clears those hooks. The lx brand code calls those
+ * hooks when a lwp starts and when it exits. Internally we use a
+ * simple reference counter (cgn_task_cnt) on the cgroup node to track how many
+ * threads are in the group, so we can tell when a group becomes empty.
+ * To make this quick, a hash table (cg_grp_hash) is maintained on the
+ * cgrp_mnt_t struct to allow quick lookups by cgroup ID. The hash table is
+ * sized so that there should typically only be 0 or 1 cgroups per bucket.
+ * We also keep a reference to the file system in the zone-specific brand data
+ * (lxzd_cgroup) so that the lx brand code can pass in the correct vfs_t
+ * when it runs the hook.
+ *
+ * Once a cgroup is about to become empty, the final process exiting the cgroup
+ * will launch a new user-level process which execs the release agent. The new
+ * process is created as a child of zsched (indicated by the -1 pid argument
+ * to newproc) and is not associated with the exiting process in any way.
+ *
+ * This file system is similar to tmpfs in that directories only exist in
+ * memory. Each subdirectory represents a different cgroup. Within the cgroup
+ * there are pseudo files (see cg_ssde_dir) with well-defined names which
+ * control the configuration and behavior of the cgroup (see cgrp_nodetype_t).
+ * The primary files within every cgroup are named 'cgroup.procs',
+ * 'notify_on_release', and 'tasks' (as well as 'release_agent' in the
+ * top-level cgroup). The cgroup.procs and tasks files are used to control and
+ * list which processes/threads belong to the cgroup. In the general case there
+ * could be additional files in the cgroup, which defined additional behavior
+ * (i.e. subsystem specific pseudo files), although none exist at this time.
+ *
+ * Each cgroup node has a unique ID (cgn_nodeid) within the mount. This ID is
+ * used to correlate with the threads to determine cgroup membership. When
+ * assigning a PID to a cgroup (via write) the code updates the br_cgroupid
+ * member in the brand-specific lx_lwp_data structure to control which cgroup
+ * the thread belongs to. Note that because the br_cgroupid lives in
+ * lx_lwp_data, native processes will not appear in the cgroup hierarchy.
+ *
+ * An overview of the behavior for the various vnode operations is:
+ * - no hardlinks or symlinks
+ * - no file create (the subsystem-specific files are a fixed list of
+ * pseudo-files accessible within the directory)
+ * - no file remove
+ * - no file rename, but a directory (i.e. a cgroup) can be renamed within the
+ * containing directory, but not into a different directory
+ * - can mkdir and rmdir to create/destroy cgroups
+ * - cannot rmdir while it contains tasks or a subdir (i.e. a sub-cgroup)
+ * - open, read/write, close on the subsytem-specific pseudo files is
+ * allowed, as this is the interface to configure and report on the cgroup.
+ * The pseudo file's mode controls write access and cannot be changed.
+ *
+ * The locking in this file system is simple since the file system is not
+ * subjected to heavy I/O activity and all data is in-memory. There is a single
+ * global mutex for each mount (cg_contents). This mutex is held for the life
+ * of most vnode operations. The most active path is probably the LWP start and
+ * exit hooks which increment/decrement the reference counter on the cgroup
+ * node. The lock is important for this case since we don't want concurrent
+ * activity (such as moving the process into another cgroup) while we're trying
+ * to lookup the cgroup from the mount's hash table. We must be careful to
+ * avoid a deadlock while reading or writing since that code can take pidlock
+ * and p_lock, but the cgrp_lwp_fork_helper can also be called while one of
+ * those is held. To prevent deadlock we always take cg_contents after pidlock
+ * and p_lock.
+ *
+ * EXTENDING THE FILE SYSTEM
+ *
+ * When adding support for a new subsystem, be sure to also update the
+ * lxpr_read_cgroups function in lx_procfs so that the subsystem is reported
+ * by proc.
+ *
+ * Although we don't currently support any subsystem controllers, the design
+ * allows for the file system to be extended to add controller emulation
+ * if needed. New controller IDs (i.e. different subsystems) for a mount can
+ * be defined in the cgrp_ssid_t enum (e.g. CG_SSID_CPUSET or CG_SSID_MEMORY)
+ * and new node types for additional pseudo files in the tree can be defined in
+ * the cgrp_nodetype_t enum (e.g. CG_CPUSET_CPUS or CG_MEMORY_USAGE_IN_BYTES).
+ * The cg_ssde_dir array would need a new entry for the new subsystem to
+ * control which nodes are visible in a directory for the new subsystem.
+ *
+ * New emulation would then need to be written to manage the behavior on the
+ * new pseudo file(s) associated with new cgrp_nodetype_t types.
+ *
+ * Within lx procfs the lxpr_read_pid_cgroup() function would need to be
+ * updated so that it reported the various subsystems used by the different
+ * mounts.
+ *
+ * In addition, in order to support more than one cgroup mount we would need a
+ * list of cgroup IDs associated with every thread, instead of just one ID
+ * (br_cgroupid). The thread data would need to become a struct which held
+ * both an ID and an indication as to which mounted cgroup file system instance
+ * the ID was associated with. We would also need a list of cgroup mounts per
+ * zone, instead the current single zone reference.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/vmparam.h>
+#include <sys/corectl.h>
+#include <sys/contract_impl.h>
+#include <sys/pool.h>
+#include <sys/stack.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+/* Module level parameters */
+static int cgrp_fstype;
+static dev_t cgrp_dev;
+
+#define MAX_AGENT_EVENTS 32 /* max num queued events */
+
+#define UMNT_DELAY_TIME drv_usectohz(50000) /* 500th of a second */
+#define UMNT_RETRY_MAX 100 /* 100 times - 2 secs */
+
+/*
+ * cgrp_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t cgrp_mountcount;
+
+/*
+ * cgrp_minfree is the minimum amount of swap space that cgroups leaves for
+ * the rest of the zone. In other words, if the amount of free swap space
+ * in the zone drops below cgrp_minfree, cgroup anon allocations will fail.
+ * This number is only likely to become factor when DRAM and swap have both
+ * been capped low to allow for maximum tenancy.
+ */
+size_t cgrp_minfree = 0;
+
+/*
+ * CGMINFREE -- the value from which cgrp_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for cgroups in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow cgroups to consume
+ * no more than half of this, yielding a CGMINFREE of 64MB.
+ */
+#define CGMINFREE 64 * 1024 * 1024 /* 64 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+/*
+ * cgroup vfs operations.
+ */
+static int cgrp_init(int, char *);
+static int cgrp_mount(struct vfs *, struct vnode *,
+ struct mounta *, struct cred *);
+static int cgrp_unmount(struct vfs *, int, struct cred *);
+static int cgrp_root(struct vfs *, struct vnode **);
+static int cgrp_statvfs(struct vfs *, struct statvfs64 *);
+static void cgrp_freevfs(vfs_t *vfsp);
+
+/* Forward declarations for hooks */
+static void cgrp_lwp_fork_helper(vfs_t *, uint_t, id_t, pid_t);
+static void cgrp_lwp_exit_helper(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_cgroup",
+ cgrp_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand cgroups", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ if (cgrp_mountcount)
+ return (EBUSY);
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ /* Disable hooks used by the lx brand module. */
+ lx_cgrp_initlwp = NULL;
+ lx_cgrp_freelwp = NULL;
+
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(cgrp_fstype);
+ vn_freevnodeops(cgrp_vnodeops);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading cgroup module.
+ */
+static int
+cgrp_init(int fstype, char *name)
+{
+ static const fs_operation_def_t cgrp_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = cgrp_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = cgrp_unmount },
+ VFSNAME_ROOT, { .vfs_root = cgrp_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = cgrp_statvfs },
+ VFSNAME_FREEVFS, { .vfs_freevfs = cgrp_freevfs },
+ NULL, NULL
+ };
+ extern const struct fs_operation_def cgrp_vnodeops_template[];
+ int error;
+ extern void cgrp_hash_init();
+ major_t dev;
+
+ cgrp_hash_init();
+ cgrp_fstype = fstype;
+ ASSERT(cgrp_fstype != 0);
+
+ error = vfs_setfsops(fstype, cgrp_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "cgrp_init: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, cgrp_vnodeops_template, &cgrp_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "cgrp_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * cgrp_minfree doesn't need to be some function of configured
+ * swap space since it really is an absolute limit of swap space
+ * which still allows other processes to execute.
+ */
+ if (cgrp_minfree == 0) {
+ /* Set if not patched */
+ cgrp_minfree = btopr(CGMINFREE);
+ }
+
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "cgrp_init: Can't get unique device number.");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ cgrp_dev = makedevice(dev, 0);
+
+ /* Install the hooks used by the lx brand module. */
+ lx_cgrp_initlwp = cgrp_lwp_fork_helper;
+ lx_cgrp_freelwp = cgrp_lwp_exit_helper;
+
+ return (0);
+}
+
+static int
+cgrp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ cgrp_mnt_t *cgm = NULL;
+ struct cgrp_node *cp;
+ struct pathname dpn;
+ int error;
+ struct vattr rattr;
+ cgrp_ssid_t ssid = CG_SSID_GENERIC;
+ lx_zone_data_t *lxzdata;
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * Since we depend on per-thread lx brand data, only allow mounting
+ * within lx zones.
+ */
+ if (curproc->p_zone->zone_brand != &lx_brand)
+ return (EINVAL);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * Having the resource be anything but "swap" doesn't make sense.
+ */
+ vfs_setresource(vfsp, "swap", 0);
+
+ /* cgroups don't support read-only mounts */
+ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Here is where we could support subsystem-specific controller
+ * mounting. For example, if mounting a cgroup fs with the 'cpuset'
+ * option to specify that particular controller.
+ *
+ * char *argstr;
+ * if (vfs_optionisset(vfsp, "cpuset", &argstr)) {
+ * if (ssid != CG_SSID_GENERIC) {
+ * error = EINVAL;
+ * goto out;
+ * }
+ * ssid = CG_SSID_CPUSET;
+ * }
+ */
+
+ error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+ if (error != 0)
+ goto out;
+
+ /*
+ * We currently only support one mount per zone.
+ */
+ lxzdata = ztolxzd(curproc->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ if (lxzdata->lxzd_cgroup != NULL) {
+ mutex_exit(&lxzdata->lxzd_lock);
+ return (EINVAL);
+ }
+
+ cgm = kmem_zalloc(sizeof (*cgm), KM_SLEEP);
+
+ /* Set but don't bother entering the mutex (not on mount list yet) */
+ mutex_init(&cgm->cg_contents, NULL, MUTEX_DEFAULT, NULL);
+
+ cgm->cg_vfsp = lxzdata->lxzd_cgroup = vfsp;
+ mutex_exit(&lxzdata->lxzd_lock);
+
+ cgm->cg_lxzdata = lxzdata;
+ cgm->cg_ssid = ssid;
+
+ vfsp->vfs_data = (caddr_t)cgm;
+ vfsp->vfs_fstype = cgrp_fstype;
+ vfsp->vfs_dev = cgrp_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, cgrp_dev, cgrp_fstype);
+ cgm->cg_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(cgm->cg_mntpath, dpn.pn_path);
+
+ cgm->cg_grp_hash = kmem_zalloc(sizeof (cgrp_node_t *) * CGRP_HASH_SZ,
+ KM_SLEEP);
+
+ /* allocate and initialize root cgrp_node structure */
+ bzero(&rattr, sizeof (struct vattr));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+ cp = kmem_zalloc(sizeof (struct cgrp_node), KM_SLEEP);
+
+ mutex_enter(&cgm->cg_contents);
+ cgrp_node_init(cgm, cp, &rattr, cr);
+
+ CGNTOV(cp)->v_flag |= VROOT;
+
+ /*
+ * initialize linked list of cgrp_nodes so that the back pointer of
+ * the root cgrp_node always points to the last one on the list
+ * and the forward pointer of the last node is null
+ */
+ cp->cgn_back = cp;
+ cp->cgn_forw = NULL;
+ cp->cgn_nlink = 0;
+ cgm->cg_rootnode = cp;
+
+ cp->cgn_type = CG_CGROUP_DIR;
+ cp->cgn_nodeid = cgrp_inode(CG_CGROUP_DIR, cgm->cg_gen);
+
+ /*
+ * This initial cgrp_node will have an ID of 0. All existing processes
+ * inside the zone will have been started with, or inherited, a
+ * br_cgroupid of 0. The cgrp_cg_hash_init function will initialize the
+ * cgn_task_cnt for cgroup 0 to reflect the number of tasks already in
+ * the group.
+ *
+ * Because we must hold cg_contents in cgrp_lwp_fork_helper and
+ * cgrp_lwp_exit_helper, no process can be creating or exiting another
+ * thread (although that is unlikely anyway since the cgroup filesystem
+ * is normally mounted at the start of zone bootup, before anything
+ * else is started).
+ */
+ cgrp_dirinit(cp, cp, cr);
+
+ mutex_exit(&cgm->cg_contents);
+
+ pn_free(&dpn);
+ error = 0;
+ atomic_inc_32(&cgrp_mountcount);
+
+out:
+ if (error == 0)
+ vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+ return (error);
+}
+
+static int
+cgrp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cgnp, *cancel;
+ struct vnode *vp;
+ int error;
+ uint_t cnt;
+ int retry_cnt = 0;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+
+retry:
+ mutex_enter(&cgm->cg_contents);
+
+ /*
+ * In the normal unmount case, if there were no open files, only the
+ * root node would have a reference count. However, the user-level
+ * agent manager should have the root vnode open and be waiting in
+ * ioctl. We need to wake the manager and it may take some retries
+ * before it closes its file descriptor.
+ *
+ * With cg_contents held, nothing can be added or removed.
+ * There may be some dirty pages. To prevent fsflush from
+ * disrupting the unmount, put a hold on each node while scanning.
+ * If we find a previously referenced node, undo the holds we have
+ * placed and fail EBUSY.
+ */
+ cgnp = cgm->cg_rootnode;
+
+ ASSERT(cgm->cg_lxzdata->lxzd_cgroup != NULL);
+
+ vp = CGNTOV(cgnp);
+ mutex_enter(&vp->v_lock);
+
+ if (flag & MS_FORCE) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ return (EINVAL);
+ }
+
+
+ cnt = vp->v_count;
+ if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ /* Likely because the user-level manager hasn't exited yet */
+ if (retry_cnt++ < UMNT_RETRY_MAX) {
+ delay(UMNT_DELAY_TIME);
+ goto retry;
+ }
+ return (EBUSY);
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * Check for open files. An open file causes everything to unwind.
+ */
+ for (cgnp = cgnp->cgn_forw; cgnp; cgnp = cgnp->cgn_forw) {
+ vp = CGNTOV(cgnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
+ cancel = cgm->cg_rootnode->cgn_forw;
+ while (cancel != cgnp) {
+ vp = CGNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->cgn_forw;
+ }
+ mutex_exit(&cgm->cg_contents);
+ return (EBUSY);
+ } else {
+ /* directly add a VN_HOLD since we have the lock */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
+ }
+ }
+
+ mutex_enter(&cgm->cg_lxzdata->lxzd_lock);
+ cgm->cg_lxzdata->lxzd_cgroup = NULL;
+ mutex_exit(&cgm->cg_lxzdata->lxzd_lock);
+ kmem_free(cgm->cg_grp_hash, sizeof (cgrp_node_t *) * CGRP_HASH_SZ);
+
+ /*
+ * We can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&cgm->cg_contents);
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * cgrp_umount.
+ */
+void
+cgrp_freevfs(vfs_t *vfsp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+ struct vnode *vp;
+
+ /*
+ * Free all kmemalloc'd and anonalloc'd memory associated with
+ * this filesystem. To do this, we go through the file list twice,
+ * once to remove all the directory entries, and then to remove
+ * all the pseudo files.
+ */
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the tmount that says
+ * we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+ /*
+ * Remove all directory entries
+ */
+ for (cn = cgm->cg_rootnode; cn; cn = cn->cgn_forw) {
+ mutex_enter(&cgm->cg_contents);
+ if (cn->cgn_type == CG_CGROUP_DIR)
+ cgrp_dirtrunc(cn);
+ mutex_exit(&cgm->cg_contents);
+ }
+
+ ASSERT(cgm->cg_rootnode);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place.
+ * VN_RELE should make the node disappear, unless somebody
+ * is holding pages against it. Nap and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on
+ * a cgrp_node via its pages or anon slots from blowing it away
+ * (in cgrp_inactive) while we're trying to get to it here. Once
+ * we have a HOLD on it we know it'll stick around.
+ *
+ */
+ mutex_enter(&cgm->cg_contents);
+
+ /* Remove all the files (except the rootnode) backwards. */
+ while ((cn = cgm->cg_rootnode->cgn_back) != cgm->cg_rootnode) {
+ mutex_exit(&cgm->cg_contents);
+ /*
+ * All nodes will be released here. Note we handled the link
+ * count above.
+ */
+ vp = CGNTOV(cn);
+ VN_RELE(vp);
+ mutex_enter(&cgm->cg_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again - we know
+ * they'll give it up soon.
+ */
+ if (cn == cgm->cg_rootnode->cgn_back) {
+ VN_HOLD(vp);
+ mutex_exit(&cgm->cg_contents);
+ delay(hz / 4);
+ mutex_enter(&cgm->cg_contents);
+ }
+ }
+ mutex_exit(&cgm->cg_contents);
+
+ VN_RELE(CGNTOV(cgm->cg_rootnode));
+
+ ASSERT(cgm->cg_mntpath);
+
+ kmem_free(cgm->cg_mntpath, strlen(cgm->cg_mntpath) + 1);
+
+ mutex_destroy(&cgm->cg_contents);
+ kmem_free(cgm, sizeof (cgrp_mnt_t));
+
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&cgrp_mountcount);
+}
+
+/*
+ * return root cgnode for given vnode
+ */
+static int
+cgrp_root(struct vfs *vfsp, struct vnode **vpp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cp = cgm->cg_rootnode;
+ struct vnode *vp;
+
+ ASSERT(cp);
+
+ vp = CGNTOV(cp);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+cgrp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ zp = cgm->cg_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > cgrp_minfree)
+ sbp->f_bfree = blocks - cgrp_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is just what's available
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a zone with a swap cap,
+ * then report the capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * The maximum number of files available is approximately the number
+ * of cgrp_nodes we can allocate from the remaining kernel memory
+ * available to cgroups. This is fairly inaccurate since it doesn't
+ * take into account the names stored in the directory entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (cgrp_node_t) + sizeof (cgrp_dirent_t));
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[cgrp_fstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, cgm->cg_mntpath, sizeof (sbp->f_fstr));
+ /* ensure null termination */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static int
+cgrp_get_dirname(cgrp_node_t *cn, char *buf, int blen)
+{
+ cgrp_node_t *parent;
+ cgrp_dirent_t *dp;
+
+ buf[0] = '\0';
+
+ parent = cn->cgn_parent;
+ if (parent == NULL || parent == cn) {
+ (void) strlcpy(buf, ".", blen);
+ return (0);
+ }
+
+ /*
+ * Search the parent dir list to find this cn's name.
+ */
+ for (dp = parent->cgn_dir; dp != NULL; dp = dp->cgd_next) {
+ if (dp->cgd_cgrp_node->cgn_id == cn->cgn_id) {
+ (void) strlcpy(buf, dp->cgd_name, blen);
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+typedef struct cgrp_rra_arg {
+ char *crraa_agent_path;
+ char *crraa_event_path;
+} cgrp_rra_arg_t;
+
+static void
+cgrp_run_rel_agent(void *a)
+{
+ cgrp_rra_arg_t *rarg = a;
+ proc_t *p = ttoproc(curthread);
+ zone_t *z = p->p_zone;
+ struct core_globals *cg;
+ int res;
+
+ ASSERT(!INGLOBALZONE(curproc));
+
+ /* The following block is derived from start_init_common */
+ ASSERT_STACK_ALIGNED();
+
+ p->p_cstime = p->p_stime = p->p_cutime = p->p_utime = 0;
+ p->p_usrstack = (caddr_t)USRSTACK32;
+ p->p_model = DATAMODEL_ILP32;
+ p->p_stkprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_datprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_stk_ctl = INT32_MAX;
+
+ p->p_as = as_alloc();
+ p->p_as->a_proc = p;
+ p->p_as->a_userlimit = (caddr_t)USERLIMIT32;
+ (void) hat_setup(p->p_as->a_hat, HAT_INIT);
+
+ VERIFY((cg = zone_getspecific(core_zone_key, z)) != NULL);
+
+ corectl_path_hold(cg->core_default_path);
+ corectl_content_hold(cg->core_default_content);
+
+ curproc->p_corefile = cg->core_default_path;
+ curproc->p_content = cg->core_default_content;
+
+ init_mstate(curthread, LMS_SYSTEM);
+ res = exec_init(rarg->crraa_agent_path, rarg->crraa_event_path);
+
+ /* End of code derived from start_init_common */
+
+ kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+ kmem_free(rarg->crraa_agent_path, CGRP_AGENT_LEN);
+ kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+
+ /* The following is derived from zone_start_init - see comments there */
+ if (res != 0 || zone_status_get(global_zone) >= ZONE_IS_SHUTTING_DOWN) {
+ if (proc_exit(CLD_EXITED, res) != 0) {
+ mutex_enter(&p->p_lock);
+ ASSERT(p->p_flag & SEXITLWPS);
+ lwp_exit();
+ }
+ } else {
+ id_t cid = curthread->t_cid;
+
+ mutex_enter(&class_lock);
+ ASSERT(cid < loaded_classes);
+ if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+ z->zone_fixed_hipri) {
+ pcparms_t pcparms;
+
+ pcparms.pc_cid = cid;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+ FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+ FX_DOUPRILIM | FX_DOUPRI;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&curproc->p_lock);
+ (void) parmsset(&pcparms, curthread);
+ mutex_exit(&curproc->p_lock);
+ mutex_exit(&pidlock);
+ } else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+ curthread->t_pri = RTGPPRIO0;
+ }
+ mutex_exit(&class_lock);
+
+ /* cause the process to return to userland. */
+ lwp_rtt();
+ }
+}
+
+/*
+ * Launch the user-level release_agent manager. The event data is the
+ * pathname (relative to the mount point of the file system) of the newly empty
+ * cgroup.
+ *
+ * The cg_contents mutex is held on entry and dropped before returning.
+ */
+void
+cgrp_rel_agent_event(cgrp_mnt_t *cgm, cgrp_node_t *cn, boolean_t is_exit)
+{
+ cgrp_node_t *parent;
+ char nm[MAXNAMELEN];
+ char *argstr, *oldstr, *tmp;
+ id_t cid;
+ proc_t *p = ttoproc(curthread);
+ zone_t *z = p->p_zone;
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ cgrp_rra_arg_t *rarg;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ /* Nothing to do if the agent is not set */
+ if (cgm->cg_agent[0] == '\0') {
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ parent = cn->cgn_parent;
+ /* Cannot remove the top-level cgroup (only via unmount) */
+ if (parent == cn) {
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ argstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ oldstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ *argstr = '\0';
+
+ /*
+ * Iterate up the directory tree to construct the agent argument string.
+ */
+ do {
+ VERIFY0(cgrp_get_dirname(cn, nm, sizeof (nm)));
+ DTRACE_PROBE1(cgrp__dir__name, char *, nm);
+ if (*argstr == '\0') {
+ (void) snprintf(argstr, MAXPATHLEN, "/%s", nm);
+ } else {
+ tmp = oldstr;
+ oldstr = argstr;
+ argstr = tmp;
+ (void) snprintf(argstr, MAXPATHLEN, "/%s%s", nm,
+ oldstr);
+ }
+
+ if (cn->cgn_parent == NULL)
+ break;
+ cn = cn->cgn_parent;
+ parent = cn->cgn_parent;
+
+ /*
+ * The arg path is relative to the mountpoint so we stop when
+ * we get to the top level.
+ */
+ if (parent == NULL || parent == cn)
+ break;
+ } while (parent != cn);
+
+ kmem_free(oldstr, MAXPATHLEN);
+
+ rarg = kmem_alloc(sizeof (cgrp_rra_arg_t), KM_SLEEP);
+ rarg->crraa_agent_path = kmem_alloc(sizeof (cgm->cg_agent), KM_SLEEP);
+ (void) strlcpy(rarg->crraa_agent_path, cgm->cg_agent,
+ sizeof (cgm->cg_agent));
+ rarg->crraa_event_path = argstr;
+
+ DTRACE_PROBE2(cgrp__agent__event, cgrp_rra_arg_t *, rarg,
+ int, plwpd->br_cgroupid);
+
+ /*
+ * When we're exiting, the release agent process cannot belong to our
+ * cgroup. When the release agent is called for a move or rmdir, then
+ * we do not change our cgroupid.
+ */
+ if (is_exit) {
+ plwpd->br_cgroupid = 0;
+ }
+
+ /*
+ * The cg_contents mutex cannot be held while taking the pool lock
+ * or calling newproc.
+ */
+ mutex_exit(&cgm->cg_contents);
+
+ if (z->zone_defaultcid > 0) {
+ cid = z->zone_defaultcid;
+ } else {
+ pool_lock();
+ cid = pool_get_class(z->zone_pool);
+ pool_unlock();
+ }
+ if (cid == -1)
+ cid = defaultcid;
+
+ if (newproc(cgrp_run_rel_agent, (void *)rarg, cid, minclsyspri - 1,
+ NULL, -1) != 0) {
+ /* There's nothing we can do if creating the proc fails. */
+ kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+ kmem_free(rarg->crraa_agent_path, sizeof (cgm->cg_agent));
+ kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+ }
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_fork_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+
+ mutex_enter(&cgm->cg_contents);
+ cn = cgrp_cg_hash_lookup(cgm, cg_id);
+ ASSERT(cn != NULL);
+ cn->cgn_task_cnt++;
+ mutex_exit(&cgm->cg_contents);
+
+ DTRACE_PROBE1(cgrp__lwp__fork, void *, cn);
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_exit_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+
+ mutex_enter(&cgm->cg_contents);
+ cn = cgrp_cg_hash_lookup(cgm, cg_id);
+ ASSERT(cn != NULL);
+ if (cn->cgn_task_cnt == 0) {
+ /* top-level cgroup cnt can be 0 during reboot */
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+ cn->cgn_task_cnt--;
+ DTRACE_PROBE1(cgrp__lwp__exit, void *, cn);
+
+ if (cn->cgn_task_cnt == 0 && cn->cgn_dirents == N_DIRENTS(cgm) &&
+ cn->cgn_notify == 1) {
+ cgrp_rel_agent_event(cgm, cn, B_TRUE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ } else {
+ mutex_exit(&cgm->cg_contents);
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c
new file mode 100644
index 0000000000..27d1715e1b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c
@@ -0,0 +1,1600 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <vm/seg_vn.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/vm.h>
+#include <sys/prsystm.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+typedef enum cgrp_wr_type {
+ CG_WR_PROCS = 1,
+ CG_WR_TASKS
+} cgrp_wr_type_t;
+
+/* ARGSUSED1 */
+static int
+cgrp_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct)
+{
+ /*
+ * swapon to a cgrp file is not supported so access is denied on open
+ * if VISSWAP is set.
+ */
+ if ((*vpp)->v_flag & VISSWAP)
+ return (EINVAL);
+
+ return (0);
+}
+
+/* ARGSUSED1 */
+static int
+cgrp_close(struct vnode *vp, int flag, int count, offset_t offset,
+ struct cred *cred, caller_context_t *ct)
+{
+ cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+ cleanshares(vp, ttoproc(curthread)->p_pid);
+ return (0);
+}
+
+/*
+ * Lookup proc or task based on pid and typ.
+ */
+static proc_t *
+cgrp_p_for_wr(pid_t pid, cgrp_wr_type_t typ)
+{
+ int i;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ /* getting a proc from a pid is easy */
+ if (typ == CG_WR_PROCS)
+ return (prfind(pid));
+
+ ASSERT(typ == CG_WR_TASKS);
+
+ /*
+ * We have to scan all of the process entries to find the proc
+ * containing this task.
+ */
+ mutex_exit(&pidlock);
+ for (i = 1; i < v.v_proc; i++) {
+ proc_t *p;
+ kthread_t *t;
+
+ mutex_enter(&pidlock);
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if ((t = p->p_tlist) == NULL) {
+ /* no threads, skip it */
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Check all threads in this proc.
+ */
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd != NULL && plwpd->br_pid == pid) {
+ mutex_exit(&p->p_lock);
+ return (p);
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ }
+
+ mutex_enter(&pidlock);
+ return (NULL);
+}
+
+/*
+ * Move a thread from one cgroup to another. If the old cgroup is empty
+ * we queue up an agent event. We return true in that case since we've
+ * dropped the locks and the caller needs to reacquire them.
+ */
+static boolean_t
+cgrp_thr_move(cgrp_mnt_t *cgm, lx_lwp_data_t *plwpd, cgrp_node_t *ncn,
+ uint_t cg_id, proc_t *p)
+{
+ cgrp_node_t *ocn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ ocn = cgrp_cg_hash_lookup(cgm, plwpd->br_cgroupid);
+ VERIFY(ocn != NULL);
+
+ ASSERT(ocn->cgn_task_cnt > 0);
+ atomic_dec_32(&ocn->cgn_task_cnt);
+ atomic_inc_32(&ncn->cgn_task_cnt);
+ plwpd->br_cgroupid = cg_id;
+
+ if (ocn->cgn_task_cnt == 0 && ocn->cgn_dirents == N_DIRENTS(cgm) &&
+ ocn->cgn_notify == 1) {
+ /*
+ * We want to drop p_lock before queuing the event since
+ * that might sleep. Dropping p_lock might cause the caller to
+ * have to restart the move process from the beginning.
+ */
+ mutex_exit(&p->p_lock);
+ cgrp_rel_agent_event(cgm, ocn, B_FALSE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Assign either all of the threads, or a single thread, for the specified pid
+ * to the new cgroup. Controlled by the typ argument.
+ */
+static int
+cgrp_proc_set_id(cgrp_mnt_t *cgm, uint_t cg_id, pid_t pid, cgrp_wr_type_t typ)
+{
+ proc_t *p;
+ kthread_t *t;
+ int error;
+ cgrp_node_t *ncn;
+
+ if (pid == 1)
+ pid = curproc->p_zone->zone_proc_initpid;
+
+ /*
+ * Move one or all threads to this cgroup.
+ */
+ if (typ == CG_WR_TASKS) {
+ error = ESRCH;
+ } else {
+ error = 0;
+ }
+
+restart:
+ mutex_enter(&pidlock);
+
+ p = cgrp_p_for_wr(pid, typ);
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+
+ /*
+ * Fail writes for pids for which there is no corresponding process,
+ * system processes, a pid of 0, the pid for our zsched process,
+ * anything the security policy doesn't allow us to look at, and
+ * processes that are not in the zone.
+ */
+ if (p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == curproc->p_zone->zone_zsched->p_pid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_zone->zone_id != curproc->p_zone->zone_id) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+
+ /*
+ * Ignore writes for PID which is not an lx-branded process or with
+ * no threads.
+ */
+
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+ if (p->p_brand != &lx_brand || (t = p->p_tlist) == NULL ||
+ p->p_flag & SEXITING) {
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ mutex_enter(&cgm->cg_contents);
+
+ ncn = cgrp_cg_hash_lookup(cgm, cg_id);
+ VERIFY(ncn != NULL);
+
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd != NULL && plwpd->br_cgroupid != cg_id) {
+ if (typ == CG_WR_PROCS) {
+ if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+ /*
+ * We dropped all of the locks so we
+ * need to start over.
+ */
+ goto restart;
+ }
+
+ } else if (plwpd->br_pid == pid) {
+ /* type is CG_WR_TASKS and we found the task */
+ error = 0;
+ if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+ goto done;
+ } else {
+ break;
+ }
+ }
+ }
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+
+ mutex_exit(&cgm->cg_contents);
+ mutex_exit(&p->p_lock);
+done:
+
+ return (error);
+}
+
+/*
+ * User-level is writing a pid string. We need to get that string and convert
+ * it to a pid. The user-level code has to completely write an entire pid
+ * string at once. The user-level code could write multiple strings (delimited
+ * by newline) although that is frowned upon. However, we must handle this
+ * case too. Thus we consume the input one byte at a time until we get a whole
+ * pid string. We can't consume more than a byte at a time since otherwise we
+ * might be left with a partial pid string.
+ */
+static int
+cgrp_get_pid_str(struct uio *uio, pid_t *pid)
+{
+ char buf[16]; /* big enough for a pid string */
+ int i;
+ int error;
+ char *p = &buf[0];
+ char *ep;
+ long pidnum;
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; uio->uio_resid > 0 && i < sizeof (buf); i++, p++) {
+ error = uiomove(p, 1, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+
+ if (buf[0] == '\0' || i >= sizeof (buf)) /* no input or too long */
+ return (EINVAL);
+
+ error = ddi_strtol(buf, &ep, 10, &pidnum);
+ if (error != 0 || *ep != '\0' || pidnum > maxpid || pidnum < 0)
+ return (EINVAL);
+
+ *pid = (pid_t)pidnum;
+ return (0);
+}
+
+static int
+cgrp_wr_notify(cgrp_node_t *cn, struct uio *uio)
+{
+ int error;
+ uint_t value;
+
+ /*
+ * This is cheesy but since we only take a 0 or 1 value we can
+ * let the pid_str function do the uio string conversion.
+ */
+ error = cgrp_get_pid_str(uio, (pid_t *)&value);
+ if (error != 0)
+ return (error);
+
+ if (value != 0 && value != 1)
+ return (EINVAL);
+
+ /*
+ * The flag is on the containing dir. We don't bother taking the
+ * cg_contents lock since this is a simple assignment.
+ */
+ cn->cgn_parent->cgn_notify = value;
+ return (0);
+}
+
+static int
+cgrp_wr_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+ int error;
+ int len;
+ char *wrp;
+
+ len = uio->uio_offset + uio->uio_resid;
+ if (len > MAXPATHLEN)
+ return (EFBIG);
+
+ mutex_enter(&cgm->cg_contents);
+
+ wrp = &cgm->cg_agent[uio->uio_offset];
+ error = uiomove(wrp, uio->uio_resid, UIO_WRITE, uio);
+ cgm->cg_agent[len] = '\0';
+ if (len > 1 && cgm->cg_agent[len - 1] == '\n')
+ cgm->cg_agent[len - 1] = '\0';
+
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+static int
+cgrp_wr_proc_or_task(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio,
+ cgrp_wr_type_t typ)
+{
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+ int error;
+ pid_t pidnum;
+
+ while (uio->uio_resid > 0) {
+ error = cgrp_get_pid_str(uio, &pidnum);
+ if (error != 0)
+ return (error);
+
+ error = cgrp_proc_set_id(cgm, cg_id, pidnum, typ);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+cgrp_wr(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int error = 0;
+ rlim64_t limit = uio->uio_llimit;
+
+ ASSERT(CGNTOV(cn)->v_type == VREG);
+
+ if (uio->uio_loffset < 0)
+ return (EINVAL);
+
+ if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
+ limit = MAXOFFSET_T;
+
+ if (uio->uio_loffset >= MAXOFF_T)
+ return (EFBIG);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ if (limit > MAXOFF_T)
+ limit = MAXOFF_T;
+
+ switch (cn->cgn_type) {
+ case CG_NOTIFY:
+ error = cgrp_wr_notify(cn, uio);
+ break;
+ case CG_PROCS:
+ error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_PROCS);
+ break;
+ case CG_REL_AGENT:
+ error = cgrp_wr_rel_agent(cgm, uio);
+ break;
+ case CG_TASKS:
+ error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_TASKS);
+ break;
+ default:
+ VERIFY(0);
+ }
+
+ return (error);
+}
+
+/*
+ * pidlock is held on entry but dropped on exit. Because we might have to drop
+ * locks and loop if the process is already P_PR_LOCKed, it is possible that
+ * the process might be gone when we return from this function.
+ */
+static proc_t *
+cgrp_p_lock(proc_t *p)
+{
+ kmutex_t *mp;
+ pid_t pid;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ /* first try the fast path */
+ mutex_enter(&p->p_lock);
+ if (p->p_flag & SEXITING) {
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+
+ if (!(p->p_proc_flag & P_PR_LOCK)) {
+ p->p_proc_flag |= P_PR_LOCK;
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ THREAD_KPRI_REQUEST();
+ return (p);
+ }
+ mutex_exit(&p->p_lock);
+
+ pid = p->p_pid;
+ for (;;) {
+ /*
+ * p_lock is persistent, but p itself is not -- it could
+ * vanish during cv_wait(). Load p->p_lock now so we can
+ * drop it after cv_wait() without referencing p.
+ */
+ mp = &p->p_lock;
+ mutex_enter(mp);
+ mutex_exit(&pidlock);
+
+ if (p->p_flag & SEXITING) {
+ mutex_exit(mp);
+ return (NULL);
+ }
+
+ if (!(p->p_proc_flag & P_PR_LOCK))
+ break;
+
+ cv_wait(&pr_pid_cv[p->p_slot], mp);
+ mutex_exit(mp);
+
+ mutex_enter(&pidlock);
+ p = prfind(pid);
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+ }
+
+ p->p_proc_flag |= P_PR_LOCK;
+ mutex_exit(mp);
+ ASSERT(!MUTEX_HELD(&pidlock));
+ THREAD_KPRI_REQUEST();
+ return (p);
+}
+
+static void
+cgrp_p_unlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ p->p_proc_flag &= ~P_PR_LOCK;
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ mutex_exit(&p->p_lock);
+ THREAD_KPRI_RELEASE();
+}
+
+/*
+ * Read value from the notify_on_release pseudo file on the parent node
+ * (which is the actual cgroup node). We don't bother taking the cg_contents
+ * lock since it's a single instruction so an empty group action/read will
+ * only see one value or the other.
+ */
+/* ARGSUSED */
+static int
+cgrp_rd_notify(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int len;
+ int error = 0;
+ char buf[16];
+ char *rdp;
+ /* the flag is on the containing dir */
+ uint_t value = cn->cgn_parent->cgn_notify;
+
+ len = snprintf(buf, sizeof (buf), "%u\n", value);
+ if (uio->uio_offset > len)
+ return (0);
+
+ len -= uio->uio_offset;
+ rdp = &buf[uio->uio_offset];
+ len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ return (error);
+}
+
+/*
+ * Read value from the release_agent pseudo file.
+ */
+static int
+cgrp_rd_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+ int len;
+ int error = 0;
+ char *rdp;
+
+ mutex_enter(&cgm->cg_contents);
+
+ if (cgm->cg_agent[0] == '\0') {
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ len = strlen(cgm->cg_agent);
+ if (uio->uio_offset > len) {
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ len -= uio->uio_offset;
+ rdp = &cgm->cg_agent[uio->uio_offset];
+ len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+
+ mutex_exit(&cgm->cg_contents);
+
+ return (error);
+}
+
+/*
+ * Read pids from the cgroup.procs pseudo file. We have to look at all of the
+ * processes to find applicable ones, then report pids for any process which
+ * has all of its threads in the same cgroup.
+ */
+static int
+cgrp_rd_procs(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int i;
+ ssize_t offset = 0;
+ ssize_t uresid;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ int error = 0;
+ pid_t initpid = curproc->p_zone->zone_proc_initpid;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+
+ /* Scan all of the process entries */
+ for (i = 1; i < v.v_proc && (uresid = uio->uio_resid) > 0; i++) {
+ proc_t *p;
+ ssize_t len;
+ pid_t pid;
+ char buf[16];
+ char *rdp;
+ kthread_t *t;
+ boolean_t in_cg;
+
+ mutex_enter(&pidlock);
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if ((t = p->p_tlist) == NULL) {
+ /* no threads, skip it */
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Check if all threads are in this cgroup.
+ */
+ in_cg = B_TRUE;
+ mutex_enter(&cgm->cg_contents);
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd == NULL || plwpd->br_cgroupid != cg_id) {
+ in_cg = B_FALSE;
+ break;
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+ mutex_exit(&cgm->cg_contents);
+
+ mutex_exit(&p->p_lock);
+ if (!in_cg) {
+ /*
+ * This proc, or at least one of its threads, is not
+ * in this cgroup.
+ */
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's
+ * init process, otherwise use the value from the proc struct
+ */
+ if (p->p_pid == initpid) {
+ pid = 1;
+ } else {
+ pid = p->p_pid;
+ }
+
+ mutex_exit(&pidlock);
+
+ /*
+ * Generate pid line and write all or part of it if we're
+ * in the right spot within the pseudo file.
+ */
+ len = snprintf(buf, sizeof (buf), "%u\n", pid);
+ if ((offset + len) > uio->uio_offset) {
+ int diff = (int)(uio->uio_offset - offset);
+
+ ASSERT(diff < len);
+ offset += diff;
+ rdp = &buf[diff];
+ len -= diff;
+ if (len > uresid)
+ len = uresid;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ if (error != 0)
+ return (error);
+ }
+ offset += len;
+ }
+
+ return (0);
+}
+
+/*
+ * We are given a locked process we know is valid, report on any of its thresds
+ * that are in the cgroup.
+ */
+static int
+cgrp_rd_proc_tasks(uint_t cg_id, proc_t *p, pid_t initpid, ssize_t *offset,
+ struct uio *uio)
+{
+ int error = 0;
+ uint_t tid;
+ char buf[16];
+ char *rdp;
+ kthread_t *t;
+
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+
+ /*
+ * Report all threads in this cgroup.
+ */
+ t = p->p_tlist;
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd == NULL) {
+ t = t->t_forw;
+ continue;
+ }
+
+ if (plwpd->br_cgroupid == cg_id) {
+ int len;
+
+ /*
+ * Convert taskid to the Linux default of 1 if
+ * we're the zone's init process.
+ */
+ tid = plwpd->br_pid;
+ if (tid == initpid)
+ tid = 1;
+
+ len = snprintf(buf, sizeof (buf), "%u\n", tid);
+ if ((*offset + len) > uio->uio_offset) {
+ int diff;
+
+ diff = (int)(uio->uio_offset - *offset);
+ ASSERT(diff < len);
+ *offset = *offset + diff;
+ rdp = &buf[diff];
+ len -= diff;
+ if (len > uio->uio_resid)
+ len = uio->uio_resid;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ if (error != 0)
+ return (error);
+ }
+ *offset = *offset + len;
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist && uio->uio_resid > 0);
+
+ return (0);
+}
+
+/*
+ * Read pids from the tasks pseudo file. We have to look at all of the
+ * processes to find applicable ones, then report pids for any thread in the
+ * cgroup. We return the emulated lx thread pid here, not the internal thread
+ * ID. Because we're possibly doing IO for each taskid we lock the process
+ * so that the threads don't change while we're working on it (although threads
+ * can change if we fill up the read buffer and come back later for a
+ * subsequent read).
+ */
+static int
+cgrp_rd_tasks(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int i;
+ ssize_t offset = 0;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ int error = 0;
+ pid_t initpid = curproc->p_zone->zone_proc_initpid;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+
+ /* Scan all of the process entries */
+ for (i = 1; i < v.v_proc && uio->uio_resid > 0; i++) {
+ proc_t *p;
+
+ mutex_enter(&pidlock);
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ if (p->p_tlist == NULL) {
+ /* no threads, skip it */
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ p = cgrp_p_lock(p);
+ ASSERT(!MUTEX_HELD(&pidlock));
+ if (p == NULL)
+ continue;
+
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_rd_proc_tasks(cg_id, p, initpid, &offset, uio);
+ mutex_exit(&cgm->cg_contents);
+
+ mutex_enter(&p->p_lock);
+ cgrp_p_unlock(p);
+
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+cgrp_rd(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int error = 0;
+
+ if (uio->uio_loffset >= MAXOFF_T)
+ return (0);
+ if (uio->uio_loffset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+
+ switch (cn->cgn_type) {
+ case CG_NOTIFY:
+ error = cgrp_rd_notify(cgm, cn, uio);
+ break;
+ case CG_PROCS:
+ error = cgrp_rd_procs(cgm, cn, uio);
+ break;
+ case CG_REL_AGENT:
+ error = cgrp_rd_rel_agent(cgm, uio);
+ break;
+ case CG_TASKS:
+ error = cgrp_rd_tasks(cgm, cn, uio);
+ break;
+ default:
+ VERIFY(0);
+ }
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_read(struct vnode *vp, struct uio *uiop, int ioflag, cred_t *cred,
+ struct caller_context *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VTOCGM(vp);
+ int error;
+
+ /*
+ * We don't support reading non-regular files
+ */
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (vp->v_type != VREG)
+ return (EINVAL);
+ error = cgrp_rd(cgm, cn, uiop);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
+ struct caller_context *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VTOCGM(vp);
+ int error;
+
+ /*
+ * We don't support writing to non-regular files
+ */
+ if (vp->v_type != VREG)
+ return (EINVAL);
+
+ if (ioflag & FAPPEND) {
+ /* In append mode start at end of file. */
+ uiop->uio_loffset = cn->cgn_size;
+ }
+
+ error = cgrp_wr(cgm, cn, uiop);
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ vap->va_type = vp->v_type;
+ vap->va_mode = cn->cgn_mode & MODEMASK;
+ vap->va_uid = cn->cgn_uid;
+ vap->va_gid = cn->cgn_gid;
+ vap->va_fsid = cn->cgn_fsid;
+ vap->va_nodeid = (ino64_t)cn->cgn_nodeid;
+ vap->va_nlink = cn->cgn_nlink;
+ vap->va_size = (u_offset_t)cn->cgn_size;
+ vap->va_atime = cn->cgn_atime;
+ vap->va_mtime = cn->cgn_mtime;
+ vap->va_ctime = cn->cgn_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = cn->cgn_rdev;
+ vap->va_seq = cn->cgn_seq;
+
+ vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(vap->va_size)));
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+}
+
+/*ARGSUSED4*/
+static int
+cgrp_setattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ int error = 0;
+ struct vattr *get;
+ long mask;
+
+ /*
+ * Cannot set these attributes
+ */
+ if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR) ||
+ (vap->va_mode & (S_ISUID | S_ISGID)) || (vap->va_mask & AT_SIZE))
+ return (EINVAL);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ get = &cn->cgn_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cred, vp, vap, get, flags, cgrp_taccess,
+ cn);
+
+ if (error)
+ goto out;
+
+ mask = vap->va_mask;
+
+ if (mask & AT_MODE) {
+ get->va_mode &= S_IFMT;
+ get->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ get->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ get->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ get->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ get->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&cn->cgn_ctime);
+
+out:
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_access(struct vnode *vp, int mode, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ int error;
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_taccess(cn, mode, cred);
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+ struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
+ caller_context_t *ct, int *direntflags, pathname_t *realpnp)
+{
+ cgrp_node_t *cn = VTOCGN(dvp);
+ cgrp_mnt_t *cgm;
+ cgrp_node_t *ncn = NULL;
+ int error;
+
+ /* disallow extended attrs */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /*
+ * Null component name is a synonym for directory being searched.
+ */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+ ASSERT(cn);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(cn, nm, &ncn, cred);
+ mutex_exit(&cgm->cg_contents);
+
+ if (error == 0) {
+ ASSERT(ncn);
+ *vpp = CGNTOV(ncn);
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_create(struct vnode *dvp, char *nm, struct vattr *vap,
+ enum vcexcl exclusive, int mode, struct vnode **vpp, struct cred *cred,
+ int flag, caller_context_t *ct, vsecattr_t *vsecp)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_node_t *cn = NULL;
+ cgrp_mnt_t *cgm;
+ int error;
+
+ if (*nm == '\0')
+ return (EPERM);
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &cn, cred);
+ if (error == 0) { /* name found */
+ ASSERT(cn);
+
+ mutex_exit(&cgm->cg_contents);
+ /*
+ * Creating an existing file, allow it except for the following
+ * errors.
+ */
+ if (exclusive == EXCL) {
+ error = EEXIST;
+ } else if ((CGNTOV(cn)->v_type == VDIR) && (mode & VWRITE)) {
+ error = EISDIR;
+ } else {
+ error = cgrp_taccess(cn, mode, cred);
+ }
+ if (error != 0) {
+ cgnode_rele(cn);
+ return (error);
+ }
+ *vpp = CGNTOV(cn);
+ return (0);
+ }
+ mutex_exit(&cgm->cg_contents);
+
+ /*
+ * cgroups doesn't allow creation of additional, non-subsystem specific
+ * files in a dir
+ */
+ return (EPERM);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_remove(struct vnode *dvp, char *nm, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ int error;
+ cgrp_node_t *cn = NULL;
+ cgrp_mnt_t *cgm;
+
+ /*
+ * Removal of subsystem-specific files is not allowed but we need
+ * to return the correct error if they try to remove a non-existent
+ * file.
+ */
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &cn, cred);
+ mutex_exit(&cgm->cg_contents);
+ if (error)
+ return (error);
+
+ ASSERT(cn);
+ cgnode_rele(cn);
+ return (EPERM);
+}
+
+/* ARGSUSED */
+static int
+cgrp_link(struct vnode *dvp, struct vnode *srcvp, char *cnm, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ /* cgroups doesn't support hard links */
+ return (EPERM);
+}
+
+/*
+ * Rename of subsystem-specific files is not allowed but we can rename
+ * directories (i.e. sub-groups). We cannot mv subdirs from one group to
+ * another so the src and dest vnode must be the same.
+ */
+/* ARGSUSED5 */
+static int
+cgrp_rename(
+ struct vnode *odvp, /* source parent vnode */
+ char *onm, /* source name */
+ struct vnode *ndvp, /* destination parent vnode */
+ char *nnm, /* destination name */
+ struct cred *cred,
+ caller_context_t *ct,
+ int flags)
+{
+ cgrp_node_t *fromparent;
+ cgrp_node_t *toparent;
+ cgrp_node_t *fromcn = NULL; /* source cgrp_node */
+ cgrp_mnt_t *cgm = VTOCGM(odvp);
+ int error, err;
+
+ fromparent = VTOCGN(odvp);
+ toparent = VTOCGN(ndvp);
+
+ if (fromparent != toparent)
+ return (EIO);
+
+ /* discourage additional use of toparent */
+ toparent = NULL;
+
+ mutex_enter(&cgm->cg_contents);
+
+ /*
+ * Look up cgrp_node of file we're supposed to rename.
+ */
+ error = cgrp_dirlookup(fromparent, onm, &fromcn, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ if (fromcn->cgn_type != CG_CGROUP_DIR) {
+ error = EPERM;
+ goto done;
+ }
+
+ /*
+ * Make sure we can delete the old (source) entry. This
+ * requires write permission on the containing directory.
+ */
+ if (((error = cgrp_taccess(fromparent, VWRITE, cred)) != 0))
+ goto done;
+
+ /*
+ * Check for renaming to or from '.' or '..' or that
+ * fromcn == fromparent
+ */
+ if ((onm[0] == '.' &&
+ (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+ (nnm[0] == '.' &&
+ (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+ (fromparent == fromcn)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Link source to new target
+ */
+ error = cgrp_direnter(cgm, fromparent, nnm, DE_RENAME,
+ fromcn, (struct vattr *)NULL,
+ (cgrp_node_t **)NULL, cred);
+
+ if (error)
+ goto done;
+
+ /*
+ * Unlink from source.
+ */
+ error = err = cgrp_dirdelete(fromparent, fromcn, onm, DR_RENAME, cred);
+
+ /*
+ * The following handles the case where our source cgrp_node was
+ * removed before we got to it.
+ */
+ if (error == ENOENT)
+ error = 0;
+
+ if (err == 0) {
+ vnevent_rename_src(CGNTOV(fromcn), odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, CGNTOV(fromcn), nnm, ct);
+ }
+
+done:
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(fromcn);
+
+ return (error);
+}
+
+/* ARGSUSED5 */
+static int
+cgrp_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
+ struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_node_t *self = NULL;
+ cgrp_mnt_t *cgm = VTOCGM(dvp);
+ int error;
+
+ /*
+ * Might be dangling directory. Catch it here, because a ENOENT
+ * return from cgrp_dirlookup() is an "ok return".
+ */
+ if (parent->cgn_nlink == 0)
+ return (ENOENT);
+
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &self, cred);
+ if (error == 0) {
+ ASSERT(self != NULL);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(self);
+ return (EEXIST);
+ }
+ if (error != ENOENT) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ error = cgrp_direnter(cgm, parent, nm, DE_MKDIR, (cgrp_node_t *)NULL,
+ va, &self, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ if (self != NULL)
+ cgnode_rele(self);
+ return (error);
+ }
+ mutex_exit(&cgm->cg_contents);
+ *vpp = CGNTOV(self);
+ return (0);
+}
+
+/* ARGSUSED4 */
+static int
+cgrp_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_mnt_t *cgm;
+ cgrp_node_t *self = NULL;
+ struct vnode *vp;
+ int error = 0;
+
+ /*
+ * Return error when removing . and ..
+ */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST); /* Should be ENOTEMPTY */
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ error = cgrp_dirlookup(parent, nm, &self, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ vp = CGNTOV(self);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto done;
+ }
+ if (self->cgn_type != CG_CGROUP_DIR) {
+ error = ENOTDIR;
+ goto done;
+ }
+
+ cgm = (cgrp_mnt_t *)VFSTOCGM(self->cgn_vnode->v_vfsp);
+
+ /*
+ * Check for the existence of any sub-cgroup directories or tasks in
+ * the cgroup.
+ */
+ if (self->cgn_task_cnt > 0 || self->cgn_dirents > N_DIRENTS(cgm)) {
+ error = EEXIST;
+ /*
+ * Update atime because checking cn_dirents is logically
+ * equivalent to reading the directory
+ */
+ gethrestime(&self->cgn_atime);
+ goto done;
+ }
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto done;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ } else {
+ error = cgrp_dirdelete(parent, self, nm, DR_RMDIR, cred);
+ }
+
+ vn_vfsunlock(vp);
+
+ if (parent->cgn_task_cnt == 0 &&
+ parent->cgn_dirents == N_DIRENTS(cgm) && parent->cgn_notify == 1) {
+ cgrp_rel_agent_event(cgm, parent, B_FALSE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ goto dropped;
+ }
+
+done:
+ mutex_exit(&cgm->cg_contents);
+dropped:
+ vnevent_rmdir(CGNTOV(self), dvp, nm, ct);
+ cgnode_rele(self);
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ cgrp_dirent_t *cdp;
+ int error = 0;
+ size_t namelen;
+ struct dirent64 *dp;
+ ulong_t offset;
+ ulong_t total_bytes_wanted;
+ long outcount = 0;
+ long bufsize;
+ int reclen;
+ caddr_t outbuf;
+
+ if (uiop->uio_loffset >= MAXOFF_T) {
+ if (eofp)
+ *eofp = 1;
+ return (0);
+ }
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ if (cn->cgn_dir == NULL) {
+ VERIFY(cn->cgn_nlink == 0);
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ /*
+ * Get space for multiple directory entries
+ */
+ total_bytes_wanted = uiop->uio_iov->iov_len;
+ bufsize = total_bytes_wanted + sizeof (struct dirent64);
+ outbuf = kmem_alloc(bufsize, KM_SLEEP);
+
+ /* LINTED: alignment */
+ dp = (struct dirent64 *)outbuf;
+
+ offset = 0;
+ cdp = cn->cgn_dir;
+ while (cdp) {
+ namelen = strlen(cdp->cgd_name); /* no +1 needed */
+ offset = cdp->cgd_offset;
+ if (offset >= uiop->uio_offset) {
+ reclen = (int)DIRENT64_RECLEN(namelen);
+ if (outcount + reclen > total_bytes_wanted) {
+ if (!outcount) {
+ /* Buffer too small for any entries. */
+ error = EINVAL;
+ }
+ break;
+ }
+ ASSERT(cdp->cgd_cgrp_node != NULL);
+
+ /* use strncpy(9f) to zero out uninitialized bytes */
+
+ (void) strncpy(dp->d_name, cdp->cgd_name,
+ DIRENT64_NAMELEN(reclen));
+ dp->d_reclen = (ushort_t)reclen;
+ dp->d_ino = (ino64_t)cdp->cgd_cgrp_node->cgn_nodeid;
+ dp->d_off = (offset_t)cdp->cgd_offset + 1;
+ dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
+ outcount += reclen;
+ ASSERT(outcount <= bufsize);
+ }
+ cdp = cdp->cgd_next;
+ }
+
+ if (!error)
+ error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+ if (!error) {
+ /*
+ * If we reached the end of the list our offset should now be
+ * just past the end.
+ */
+ if (!cdp) {
+ offset += 1;
+ if (eofp)
+ *eofp = 1;
+ } else if (eofp)
+ *eofp = 0;
+ uiop->uio_offset = offset;
+ }
+ gethrestime(&cn->cgn_atime);
+
+ mutex_exit(&cgm->cg_contents);
+
+ kmem_free(outbuf, bufsize);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_symlink(struct vnode *dvp, char *lnm, struct vattr *cva, char *cnm,
+ struct cred *cred, caller_context_t *ct, int flags)
+{
+ /* cgroups doesn't support symlinks */
+ return (EPERM);
+}
+
+/* ARGSUSED */
+static void
+cgrp_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VFSTOCGM(vp->v_vfsp);
+
+ mutex_enter(&cgm->cg_contents);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's little to do -- just drop our hold.
+ */
+ if (vp->v_count > 1 || cn->cgn_nlink != 0) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ if (cn->cgn_forw == NULL)
+ cgm->cg_rootnode->cgn_back = cn->cgn_back;
+ else
+ cn->cgn_forw->cgn_back = cn->cgn_back;
+ cn->cgn_back->cgn_forw = cn->cgn_forw;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+
+ /* Here's our chance to send invalid event */
+ vn_invalid(CGNTOV(cn));
+
+ vn_free(CGNTOV(cn));
+ kmem_free(cn, sizeof (cgrp_node_t));
+}
+
+/* ARGSUSED */
+static int
+cgrp_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+ caller_context_t *ct)
+{
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+}
+
+/* ARGSUSED */
+static int
+cgrp_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ return (write_lock);
+}
+
+/* ARGSUSED */
+static void
+cgrp_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+}
+
+static int
+cgrp_pathconf(struct vnode *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ switch (cmd) {
+ case _PC_XATTR_EXISTS:
+ if (vp->v_vfsp->vfs_flag & VFS_XATTR) {
+ *valp = 0; /* assume no attributes */
+ error = 0; /* okay to ask */
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case _PC_SATTR_ENABLED:
+ case _PC_SATTR_EXISTS:
+ *valp = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
+ (vp->v_type == VREG || vp->v_type == VDIR);
+ error = 0;
+ break;
+ case _PC_TIMESTAMP_RESOLUTION:
+ /* nanosecond timestamp resolution */
+ *valp = 1L;
+ error = 0;
+ break;
+ default:
+ error = fs_pathconf(vp, cmd, valp, cr, ct);
+ }
+ return (error);
+}
+
+
+struct vnodeops *cgrp_vnodeops;
+
+const fs_operation_def_t cgrp_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = cgrp_open },
+ VOPNAME_CLOSE, { .vop_close = cgrp_close },
+ VOPNAME_READ, { .vop_read = cgrp_read },
+ VOPNAME_WRITE, { .vop_write = cgrp_write },
+ VOPNAME_GETATTR, { .vop_getattr = cgrp_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = cgrp_setattr },
+ VOPNAME_ACCESS, { .vop_access = cgrp_access },
+ VOPNAME_LOOKUP, { .vop_lookup = cgrp_lookup },
+ VOPNAME_CREATE, { .vop_create = cgrp_create },
+ VOPNAME_REMOVE, { .vop_remove = cgrp_remove },
+ VOPNAME_LINK, { .vop_link = cgrp_link },
+ VOPNAME_RENAME, { .vop_rename = cgrp_rename },
+ VOPNAME_MKDIR, { .vop_mkdir = cgrp_mkdir },
+ VOPNAME_RMDIR, { .vop_rmdir = cgrp_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = cgrp_readdir },
+ VOPNAME_SYMLINK, { .vop_symlink = cgrp_symlink },
+ VOPNAME_INACTIVE, { .vop_inactive = cgrp_inactive },
+ VOPNAME_RWLOCK, { .vop_rwlock = cgrp_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = cgrp_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = cgrp_seek },
+ VOPNAME_PATHCONF, { .vop_pathconf = cgrp_pathconf },
+ VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd.h b/usr/src/uts/common/brand/lx/devfs/lxd.h
new file mode 100644
index 0000000000..437b0b6162
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd.h
@@ -0,0 +1,244 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LXD_H
+#define _LXD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxd.h: declarations, data structures and macros for lxd (lxd devfs).
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+#include <sys/lx_types.h>
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+/*
+ * It's unlikely that we need to create more than 50-60 subdirs/symlinks
+ * as front files so we size the file system hash for 2x that number.
+ * The back devfs typically has ~80 nodes so this is also a comfortable size
+ * for the back hash table.
+ */
+#define LXD_HASH_SZ 128
+
+#define LXD_BACK_HASH(v) ((((intptr_t)(v)) >> 10) & ((LXD_HASH_SZ) - 1))
+
+#define LXD_NM_HASH(ldn, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(ldn) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ hash &= (LXD_HASH_SZ - 1); \
+ }
+
+
+enum lxd_node_type { LXDNT_NONE, LXDNT_BACK, LXDNT_FRONT };
+
+typedef struct lxd_dev_attr {
+ list_node_t lxda_link;
+ char lxda_name[MAXPATHLEN];
+ uid_t lxda_uid;
+ gid_t lxda_gid;
+ mode_t lxda_mode;
+} lxd_dev_attr_t;
+
+/*
+ * lxd per-mount data structure.
+ *
+ * All fields are protected by lxd_contents.
+ * File renames on a specific file system are protected lxdm_renamelck.
+ */
+typedef struct lxd_mnt {
+ struct vfs *lxdm_vfsp; /* filesystem's vfs struct */
+ struct lxd_node *lxdm_rootnode; /* root lxd_node */
+ char *lxdm_mntpath; /* name of lxd mount point */
+ dev_t lxdm_dev; /* unique dev # of mounted `device' */
+ kmutex_t lxdm_contents; /* per-mount lock */
+ kmutex_t lxdm_renamelck; /* rename lock for this mount */
+ kmutex_t lxdm_attrlck; /* per-mount attr. file lock */
+ list_t lxdm_devattrs; /* list of device attr. settings */
+ uint_t lxdm_gen; /* node ID source for files */
+
+ /* protects buckets in both "dir ent" and "back" hash tables */
+ kmutex_t lxdm_hash_mutex[LXD_HASH_SZ];
+
+ /* per-mount data for "back" vnodes in the fs */
+ uint_t lxdm_back_refcnt; /* # outstanding "back" vnodes */
+ struct lxd_node *lxdm_back_htable[LXD_HASH_SZ];
+
+ /*
+ * Per-mount directory data for "front" nodes in the fs.
+ * Each front node has a directory entry but directory entries can live
+ * on either front or back nodes.
+ */
+ uint_t lxdm_dent_refcnt; /* # outstanding dir ents */
+ struct lxd_dirent *lxdm_dent_htable[LXD_HASH_SZ];
+} lxd_mnt_t;
+
+/*
+ * lxd_node is the file system dependent node for lxd.
+ *
+ * The node is used to represent both front and back files. For front files
+ * the node can represent either a directory or symlink.
+ */
+typedef struct lxd_node {
+ enum lxd_node_type lxdn_type;
+
+ /* Data for "front" nodes */
+ struct lxd_node *lxdn_prev; /* lnked lst of lxd nodes */
+ struct lxd_node *lxdn_next; /* lnked lst of lxd nodes */
+ struct lxd_node *lxdn_parent; /* dir containing this node */
+ krwlock_t lxdn_rwlock; /* serialize mods/dir updates */
+ kmutex_t lxdn_tlock; /* time, flag, and nlink lock */
+
+ /* these could be in a union ala tmpfs but not really necessary */
+ uint_t lxdn_dirents; /* number of dirents */
+ struct lxd_dirent *lxdn_dir; /* dirent list */
+ char *lxdn_symlink; /* pointer to symlink */
+ struct vattr lxdn_attr; /* attributes */
+
+ /* Hash table link */
+ struct lxd_node *lxdn_hnxt; /* link in per-mount entry */
+ /* hash table */
+ vnode_t *lxdn_vnode; /* vnode for this lxd_node */
+
+ vnode_t *lxdn_real_vp; /* back file - real vnode */
+} lxd_node_t;
+
+/*
+ * Attributes
+ */
+#define lxdn_mask lxdn_attr.va_mask
+#define lxdn_mode lxdn_attr.va_mode
+#define lxdn_uid lxdn_attr.va_uid
+#define lxdn_gid lxdn_attr.va_gid
+#define lxdn_fsid lxdn_attr.va_fsid
+#define lxdn_nodeid lxdn_attr.va_nodeid
+#define lxdn_nlink lxdn_attr.va_nlink
+#define lxdn_size lxdn_attr.va_size
+#define lxdn_atime lxdn_attr.va_atime
+#define lxdn_mtime lxdn_attr.va_mtime
+#define lxdn_ctime lxdn_attr.va_ctime
+#define lxdn_rdev lxdn_attr.va_rdev
+#define lxdn_blksize lxdn_attr.va_blksize
+#define lxdn_nblocks lxdn_attr.va_nblocks
+#define lxdn_seq lxdn_attr.va_seq
+
+/*
+ * lx devfs conversion macros
+ */
+#define VFSTOLXDM(vfsp) ((lxd_mnt_t *)(vfsp)->vfs_data)
+#define VTOLXDM(vp) ((lxd_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define VTOLDN(vp) ((lxd_node_t *)(vp)->v_data)
+#define LDNTOV(ln) ((ln)->lxdn_vnode)
+#define ldnode_hold(ln) VN_HOLD(LDNTOV(ln))
+#define ldnode_rele(ln) VN_RELE(LDNTOV(ln))
+
+#define REALVP(vp) (VTOLDN(vp)->lxdn_real_vp)
+
+/*
+ * front directories are made up of a linked list of lxd_dirent structures
+ * hanging off directory lxdn_nodes. File names are not fixed length, but are
+ * null terminated.
+ */
+typedef struct lxd_dirent {
+ lxd_node_t *lddir_node; /* lxd node for this file */
+ struct lxd_dirent *lddir_next; /* next directory entry */
+ struct lxd_dirent *lddir_prev; /* prev directory entry */
+ uint_t lddir_offset; /* "offset" of dir entry */
+ uint_t lddir_hash; /* a hash of lddir_name */
+ struct lxd_dirent *lddir_link; /* linked via hash table */
+ lxd_node_t *lddir_parent; /* parent, dir we are in */
+ char *lddir_name; /* null terminated */
+} lxd_dirent_t;
+
+enum de_op { DE_CREATE, DE_MKDIR, DE_RENAME }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
+
+typedef struct lxd_minor_translator {
+ char *lxd_mt_path; /* illumos minor node path */
+ minor_t lxd_mt_minor; /* illumos minor node number */
+ int lxd_mt_lx_major; /* linux major node number */
+ int lxd_mt_lx_minor; /* linux minor node number */
+} lxd_minor_translator_t;
+
+enum lxd_xl_tp { DTT_INVALID, DTT_LIST, DTT_CUSTOM };
+
+#define xl_list lxd_xl_minor.lxd_xl_list
+#define xl_custom lxd_xl_minor.lxd_xl_custom
+
+typedef struct lxd_devt_translator {
+ char *lxd_xl_driver; /* driver name */
+ major_t lxd_xl_major; /* driver number */
+
+ enum lxd_xl_tp lxd_xl_type; /* dictates how we intrep. xl_minor */
+ union {
+ uintptr_t lxd_xl_foo; /* required to compile */
+ lxd_minor_translator_t *lxd_xl_list;
+ void (*lxd_xl_custom)(dev_t, dev_t *);
+ } lxd_xl_minor;
+} lxd_devt_translator_t;
+
+extern struct vnodeops *lxd_vnodeops;
+extern lxd_devt_translator_t lxd_devt_translators[];
+
+vnode_t *lxd_make_back_node(vnode_t *, lxd_mnt_t *);
+void lxd_free_back_node(lxd_node_t *);
+int lxd_dirdelete(lxd_node_t *, lxd_node_t *, char *, enum dr_op, cred_t *);
+int lxd_direnter(lxd_mnt_t *, lxd_node_t *, char *, enum de_op, lxd_node_t *,
+ lxd_node_t *, struct vattr *, lxd_node_t **, cred_t *);
+void lxd_dirinit(lxd_node_t *, lxd_node_t *);
+int lxd_dirlookup(lxd_node_t *, char *, lxd_node_t **, cred_t *);
+void lxd_dirtrunc(lxd_node_t *);
+void lxd_node_init(lxd_mnt_t *, lxd_node_t *, vnode_t *, vattr_t *, cred_t *);
+int lxd_naccess(void *, int, cred_t *);
+
+void lxd_save_attrs(lxd_mnt_t *, vnode_t *);
+void lxd_apply_db(lxd_mnt_t *);
+
+#endif /* KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXD_H */
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c b/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c
new file mode 100644
index 0000000000..02d396a36d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c
@@ -0,0 +1,368 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+
+#include "lxd.h"
+
+#define LX_ATTR_FILE "/etc/.lxd_dev_attr"
+
+#define RD_BUFSIZE MAXPATHLEN
+#define ENTRY_BUFSIZE (MAXPATHLEN + 32)
+
+static int
+lxd_db_open(int fmode, vnode_t **vpp)
+{
+ return (vn_open(LX_ATTR_FILE, UIO_SYSSPACE, fmode,
+ (int)(0644 & MODEMASK), vpp, CRCREAT, PTOU(curproc)->u_cmask));
+}
+
+static int
+lxd_wr_entry(vnode_t *wvn, off_t offset, char *entry)
+{
+ int len, err;
+ struct uio auio;
+ struct iovec aiov;
+
+ len = strlen(entry);
+ aiov.iov_base = entry;
+ aiov.iov_len = len;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = len;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = FWRITE;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ (void) VOP_RWLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+ err = VOP_WRITE(wvn, &auio, FAPPEND, CRED(), NULL);
+ VOP_RWUNLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+
+ if (err != 0)
+ return (0);
+ return (len);
+}
+
+/*
+ * Given an entry, apply a uid, gid and mode change to the given device. There
+ * is no strtok in the kernel but it's easy to tokenize the entry ourselves.
+ *
+ * entries have the form (newline removed by caller):
+ * path uid gid mode\0
+ */
+static int
+lxd_apply_entry(char *entry, char **dpath, uid_t *uidp, gid_t *gidp,
+ mode_t *modep)
+{
+ char *dp, *up, *gp, *mp, *ep;
+ long uid, gid, mode;
+ int error, res = 0;
+ vnode_t *vp;
+ vattr_t va;
+
+ dp = entry;
+
+ /* find and delimit the first field (device name) */
+ for (up = dp; *up != ' ' && *up != '\0'; up++)
+ ;
+ if (*up != ' ')
+ return (-1);
+ *up++ = '\0';
+
+ /* find and delimit the second field (uid) */
+ for (gp = up; *gp != ' ' && *gp != '\0'; gp++)
+ ;
+ if (*gp != ' ')
+ return (-1);
+ *gp++ = '\0';
+
+ /* find and delimit the third field (gid) */
+ for (mp = gp; *mp != ' ' && *mp != '\0'; mp++)
+ ;
+ if (*mp != ' ')
+ return (-1);
+ *mp++ = '\0';
+
+ /* validate the fourth field (mode) */
+ ep = mp + strlen(mp);
+ if (*ep != '\0')
+ return (-1);
+
+ if (*dp != '/')
+ return (-1);
+
+ error = ddi_strtol(up, &ep, 10, &uid);
+ if (error != 0 || *ep != '\0' || uid > MAXUID || uid < 0)
+ return (-1);
+
+ error = ddi_strtol(gp, &ep, 10, &gid);
+ if (error != 0 || *ep != '\0' || gid > MAXUID || gid < 0)
+ return (-1);
+
+ /* note that the mode is octal */
+ error = ddi_strtol(mp, &ep, 8, &mode);
+ if (error != 0 || *ep != '\0' || mode > 0777 || mode < 0)
+ return (-1);
+
+ if (lookupname(dp, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp) != 0) {
+ /*
+ * It's likely the device is no longer visible to the zone.
+ * No matter the reason, we indicate failure.
+ */
+ return (-1);
+ }
+
+ va.va_mask = AT_UID | AT_GID | AT_MODE;
+ va.va_uid = (uid_t)uid;
+ va.va_gid = (gid_t)gid;
+ va.va_mode = (mode_t)mode;
+
+ if (VOP_SETATTR(vp, &va, 0, CRED(), NULL) != 0)
+ res = -1;
+
+ VN_RELE(vp);
+
+ *dpath = dp;
+ *uidp = (uid_t)uid;
+ *gidp = (gid_t)gid;
+ *modep = (mode_t)mode;
+ return (res);
+}
+
+/*
+ * Return true if this is a pre-existing record.
+ */
+static boolean_t
+lxd_save_devattr(lxd_mnt_t *lxdm, char *dpath, uid_t uid, gid_t gid,
+ mode_t mode)
+{
+ lxd_dev_attr_t *da;
+
+ da = list_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ if (strcmp(dpath, da->lxda_name) == 0) {
+ da->lxda_uid = uid;
+ da->lxda_gid = gid;
+ da->lxda_mode = mode;
+ return (B_TRUE);
+ }
+ da = list_next(&lxdm->lxdm_devattrs, da);
+ }
+
+ da = kmem_zalloc(sizeof (lxd_dev_attr_t), KM_SLEEP);
+ (void) strlcpy(da->lxda_name, dpath, sizeof (da->lxda_name));
+ da->lxda_uid = uid;
+ da->lxda_gid = gid;
+ da->lxda_mode = mode;
+
+ list_insert_tail(&lxdm->lxdm_devattrs, da);
+ return (B_FALSE);
+}
+
+static void
+lxd_save_db(lxd_mnt_t *lxdm)
+{
+ lxd_dev_attr_t *da;
+ char *entry;
+ vnode_t *wvn;
+ off_t woff = 0;
+
+ if (list_is_empty(&lxdm->lxdm_devattrs)) {
+ /* The attribute file is no longer needed. */
+ (void) vn_remove(LX_ATTR_FILE, UIO_SYSSPACE, RMFILE);
+ return;
+ }
+
+ if (lxd_db_open(FWRITE | FCREAT | FTRUNC, &wvn) != 0)
+ return;
+
+ entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+ woff = lxd_wr_entry(wvn, woff, "# DO NOT EDIT: this file is "
+ "automatically maintained for lx container devices\n");
+
+ da = list_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ (void) snprintf(entry, ENTRY_BUFSIZE, "%s %d %d %o\n",
+ da->lxda_name, da->lxda_uid, da->lxda_gid,
+ da->lxda_mode & 0777);
+ woff += lxd_wr_entry(wvn, woff, entry);
+ da = list_next(&lxdm->lxdm_devattrs, da);
+ }
+
+ (void) VOP_CLOSE(wvn, FWRITE, 1, woff, CRED(), NULL);
+
+ kmem_free(entry, ENTRY_BUFSIZE);
+}
+
+/*
+ * This function records the uid, gid and mode information for an lx devfs
+ * block device node after a chown/chmod setattr operation so that these
+ * changes can be persistent across reboots. Since the actual setattr has
+ * already suceeded, the tracking of these changes is done on a "best effort"
+ * basis. That is, if we fail to record the change for some reason, the setattr
+ * will still return success. The vp passed in is the "real vp" for the back
+ * device node.
+ */
+void
+lxd_save_attrs(lxd_mnt_t *lxdm, vnode_t *vp)
+{
+ vattr_t va;
+ char devpath[MAXPATHLEN];
+
+ /* the path returned is relative to the zone's root */
+ if (vnodetopath(curproc->p_zone->zone_rootvp, vp, devpath,
+ sizeof (devpath), CRED()) != 0)
+ return;
+
+ va.va_mask = AT_MODE | AT_UID | AT_GID;
+
+ /*
+ * We just set attrs, so the getattr shouldn't fail. If the device
+ * is not a block device we don't persist the change.
+ */
+ if (VOP_GETATTR(vp, &va, 0, CRED(), NULL) != 0 ||
+ ((va.va_mode & S_IFBLK) != S_IFBLK))
+ return;
+
+ /*
+ * We serialize all updates to the attribute DB file. In practice this
+ * should not be a problem since there is rarely concurrent device
+ * file mode changes.
+ */
+ mutex_enter(&lxdm->lxdm_attrlck);
+
+ (void) lxd_save_devattr(lxdm, devpath, va.va_uid, va.va_gid,
+ va.va_mode & 0777);
+ lxd_save_db(lxdm);
+
+ mutex_exit(&lxdm->lxdm_attrlck);
+}
+
+/*
+ * Re-apply the persistent attribute settings to the devices when this lx
+ * devfs is mounted. As with lxd_save_attrs, this is done on a best effort and
+ * we won't prevent the mount if there is a problem. No locking is needed
+ * while reading the DB file since this action is performed during the
+ * mount of the devfs.
+ */
+void
+lxd_apply_db(lxd_mnt_t *lxdm)
+{
+ vnode_t *rvn;
+ char *buf, *entry, *bp, *ep;
+ struct uio auio;
+ struct iovec aiov;
+ size_t cnt, len, ecnt, roff;
+ char *devpath;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ boolean_t needs_update = B_FALSE;
+
+ if (lxd_db_open(FREAD, &rvn) != 0)
+ return;
+
+ buf = kmem_alloc(RD_BUFSIZE, KM_SLEEP);
+ entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+ roff = 0;
+ ep = entry;
+ ecnt = 0;
+ (void) VOP_RWLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+loop:
+ aiov.iov_base = buf;
+ aiov.iov_len = RD_BUFSIZE;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = roff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = RD_BUFSIZE;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ (void) VOP_READ(rvn, &auio, 0, CRED(), NULL);
+
+ len = RD_BUFSIZE - auio.uio_resid;
+ roff += len;
+
+ if (len > 0) {
+ for (bp = buf, cnt = 0; cnt < len; bp++, cnt++) {
+
+ /*
+ * We have an improperly formed entry in the file (too
+ * long). In an attempt to recover we reset the entry
+ * pointer so we can read the rest of the line and try
+ * to absorb the bad line. The code in lxd_apply_entry
+ * will handle any malformed or inapplicable entries.
+ */
+ if (ecnt >= (ENTRY_BUFSIZE - 1)) {
+ ep = entry;
+ ecnt = 0;
+ needs_update = B_TRUE;
+ }
+
+ if (*bp == '\n') {
+ *ep = '\0';
+
+ /* skip comments */
+ if (entry[0] != '#') {
+ if (lxd_apply_entry(entry, &devpath,
+ &uid, &gid, &mode) != 0 ||
+ lxd_save_devattr(lxdm, devpath,
+ uid, gid, mode)) {
+ /*
+ * An invalid entry, a
+ * non-existent device node or
+ * a duplicate entry.
+ */
+ needs_update = B_TRUE;
+ }
+ }
+ ep = entry;
+ ecnt = 0;
+ } else {
+ *ep++ = *bp;
+ ecnt++;
+ }
+ }
+ goto loop;
+ }
+ VOP_RWUNLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+
+ kmem_free(buf, RD_BUFSIZE);
+ kmem_free(entry, ENTRY_BUFSIZE);
+
+ (void) VOP_CLOSE(rvn, FREAD, 1, 0, CRED(), NULL);
+
+ if (needs_update)
+ lxd_save_db(lxdm);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_node.c b/usr/src/uts/common/brand/lx/devfs/lxd_node.c
new file mode 100644
index 0000000000..0d056ab167
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_node.c
@@ -0,0 +1,1003 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "lxd.h"
+
+#define LXD_HASH_SIZE 8192 /* must be power of 2 */
+#define LXD_MUTEX_SIZE 64
+
+
+#define MODESHIFT 3
+
+typedef enum lxd_nodehold {
+ NOHOLD,
+ HOLD
+} lxd_nodehold_t;
+
+/*
+ * The following functions maintain the per-mount "front" files.
+ */
+static void
+lxd_save_dirent(lxd_dirent_t *de)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+ uint_t hash;
+ kmutex_t *hmtx;
+
+ LXD_NM_HASH(de->lddir_parent, de->lddir_name, hash);
+ de->lddir_hash = hash;
+
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ ASSERT(de->lddir_link == NULL);
+ de->lddir_link = lxdm->lxdm_dent_htable[hash];
+ lxdm->lxdm_dent_htable[hash] = de;
+ mutex_exit(hmtx);
+
+ atomic_inc_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static void
+lxd_rm_dirent(lxd_dirent_t *de)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+ uint_t hash;
+ lxd_dirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ hash = de->lddir_hash;
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ prevpp = &lxdm->lxdm_dent_htable[hash];
+ while (*prevpp != de)
+ prevpp = &(*prevpp)->lddir_link;
+ *prevpp = de->lddir_link;
+ de->lddir_link = NULL;
+ mutex_exit(hmtx);
+
+ ASSERT(lxdm->lxdm_dent_refcnt > 0);
+ atomic_dec_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static lxd_dirent_t *
+lxd_find_dirent(char *name, lxd_node_t *parent, lxd_nodehold_t do_hold,
+ lxd_node_t **found)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(parent));
+ lxd_dirent_t *de;
+ uint_t hash;
+ kmutex_t *hmtx;
+
+ LXD_NM_HASH(parent, name, hash);
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ de = lxdm->lxdm_dent_htable[hash];
+ while (de) {
+ if (de->lddir_hash == hash && de->lddir_parent == parent &&
+ strcmp(de->lddir_name, name) == 0) {
+ lxd_node_t *ldn = de->lddir_node;
+
+ if (do_hold == HOLD) {
+ ASSERT(ldn != NULL);
+ ldnode_hold(ldn);
+ }
+ if (found != NULL)
+ *found = ldn;
+ mutex_exit(hmtx);
+ return (de);
+ }
+
+ de = de->lddir_link;
+ }
+ mutex_exit(hmtx);
+ return (NULL);
+}
+
+int
+lxd_naccess(void *vcp, int mode, cred_t *cr)
+{
+ lxd_node_t *ldn = vcp;
+ int shift = 0;
+ /*
+ * Check access based on owner, group and public perms in lxd_node.
+ */
+ if (crgetuid(cr) != ldn->lxdn_uid) {
+ shift += MODESHIFT;
+ if (groupmember(ldn->lxdn_gid, cr) == 0)
+ shift += MODESHIFT;
+ }
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (secpolicy_vnode_access2(cr, LDNTOV(ldn),
+ ldn->lxdn_uid, ldn->lxdn_mode << shift, mode));
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ return (VOP_ACCESS(ldn->lxdn_real_vp, mode, 0, cr, NULL));
+}
+
+static lxd_node_t *
+lxd_find_back(struct vnode *vp, uint_t hash, lxd_mnt_t *lxdm)
+{
+ lxd_node_t *l;
+
+ ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+ for (l = lxdm->lxdm_back_htable[hash]; l != NULL; l = l->lxdn_hnxt) {
+ if (l->lxdn_real_vp == vp) {
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+
+ VN_HOLD(LDNTOV(l));
+ return (l);
+ }
+ }
+ return (NULL);
+}
+
+static void
+lxd_save_back(lxd_node_t *l, uint_t hash, lxd_mnt_t *lxdm)
+{
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+ ASSERT(l->lxdn_real_vp != NULL);
+ ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+ atomic_inc_32(&lxdm->lxdm_back_refcnt);
+
+ l->lxdn_hnxt = lxdm->lxdm_back_htable[hash];
+ lxdm->lxdm_back_htable[hash] = l;
+}
+
+
+struct vnode *
+lxd_make_back_node(struct vnode *vp, lxd_mnt_t *lxdm)
+{
+ uint_t hash;
+ kmutex_t *hmtx;
+ lxd_node_t *l;
+
+ hash = LXD_BACK_HASH(vp); /* Note: hashing with realvp */
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+ mutex_enter(hmtx);
+
+ l = lxd_find_back(vp, hash, lxdm);
+ if (l == NULL) {
+ vnode_t *nvp;
+
+ l = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+ nvp = vn_alloc(KM_SLEEP);
+
+ rw_init(&l->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&l->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+
+ l->lxdn_vnode = nvp;
+ l->lxdn_type = LXDNT_BACK;
+ l->lxdn_real_vp = vp;
+
+ VN_SET_VFS_TYPE_DEV(nvp, lxdm->lxdm_vfsp, vp->v_type,
+ vp->v_rdev);
+ nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
+ vn_setops(nvp, lxd_vnodeops);
+ nvp->v_data = (caddr_t)l;
+
+ lxd_save_back(l, hash, lxdm);
+ vn_exists(vp);
+ } else {
+ VN_RELE(vp);
+ }
+
+ mutex_exit(hmtx);
+ return (LDNTOV(l));
+}
+
+void
+lxd_free_back_node(lxd_node_t *lp)
+{
+ uint_t hash;
+ kmutex_t *hmtx;
+ lxd_node_t *l;
+ lxd_node_t *lprev = NULL;
+ vnode_t *vp = LDNTOV(lp);
+ vnode_t *realvp = REALVP(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+ /* in lxd_make_back_node we call lxd_find_back with the realvp */
+ hash = LXD_BACK_HASH(realvp);
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+ mutex_enter(hmtx);
+
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--; /* release our hold from vn_rele */
+ mutex_exit(&vp->v_lock);
+ mutex_exit(hmtx);
+ return;
+ }
+ mutex_exit(&vp->v_lock);
+
+ for (l = lxdm->lxdm_back_htable[hash]; l != NULL;
+ lprev = l, l = l->lxdn_hnxt) {
+
+ if (l != lp)
+ continue;
+
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+ ASSERT(lxdm->lxdm_back_refcnt > 0);
+
+ atomic_dec_32(&lxdm->lxdm_back_refcnt);
+ vn_invalid(vp);
+
+ if (lprev == NULL) {
+ lxdm->lxdm_back_htable[hash] = l->lxdn_hnxt;
+ } else {
+ lprev->lxdn_hnxt = l->lxdn_hnxt;
+ }
+
+ mutex_exit(hmtx);
+ rw_destroy(&l->lxdn_rwlock);
+ mutex_destroy(&l->lxdn_tlock);
+ kmem_free(l, sizeof (lxd_node_t));
+ vn_free(vp);
+ VN_RELE(realvp);
+ return;
+ }
+
+ panic("lxd_free_back_node");
+ /*NOTREACHED*/
+}
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found lxd_node with its vnode held.
+ */
+int
+lxd_dirlookup(lxd_node_t *parent, char *name, lxd_node_t **foundnp, cred_t *cr)
+{
+ int error;
+
+ *foundnp = NULL;
+ if (parent->lxdn_vnode->v_type != VDIR)
+ return (ENOTDIR);
+
+ if ((error = lxd_naccess(parent, VEXEC, cr)))
+ return (error);
+
+ if (*name == '\0') {
+ ldnode_hold(parent);
+ *foundnp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name
+ * We need the lock protecting the lxdn_dir list
+ * so that it doesn't change out from underneath us.
+ * lxd_find_dirent() will pass back the lxd_node
+ * with a hold on it.
+ */
+
+ if (lxd_find_dirent(name, parent, HOLD, foundnp) != NULL) {
+ ASSERT(*foundnp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Check if the source directory is in the path of the target directory.
+ * The target directory is locked by the caller.
+ */
+static int
+lxd_dircheckpath(lxd_node_t *fromnode, lxd_node_t *toparent)
+{
+ int error = 0;
+ lxd_node_t *dir, *dotdot;
+
+ ASSERT(RW_WRITE_HELD(&toparent->lxdn_rwlock));
+ ASSERT(toparent->lxdn_vnode->v_type == VDIR);
+
+ dotdot = toparent->lxdn_parent;
+ if (dotdot == NULL)
+ return (ENOENT);
+ ldnode_hold(dotdot);
+
+ if (dotdot == toparent) {
+ /* root of fs. search trivially satisfied. */
+ ldnode_rele(dotdot);
+ return (0);
+ }
+
+ for (;;) {
+ /*
+ * Return error for cases like "mv c c/d",
+ * "mv c c/d/e" and so on.
+ */
+ if (dotdot == fromnode) {
+ ldnode_rele(dotdot);
+ error = EINVAL;
+ break;
+ }
+
+ dir = dotdot;
+ dotdot = dir->lxdn_parent;
+ if (dotdot == NULL) {
+ ldnode_rele(dir);
+ error = ENOENT;
+ break;
+ }
+ ldnode_hold(dotdot);
+
+ /*
+ * We're okay if we traverse the directory tree up to
+ * the root directory and don't run into the
+ * parent directory.
+ */
+ if (dir == dotdot) {
+ ldnode_rele(dir);
+ ldnode_rele(dotdot);
+ break;
+ }
+ ldnode_rele(dir);
+ }
+
+ return (error);
+}
+
+static int
+lxd_dir_make_node(lxd_node_t *dir, lxd_mnt_t *lxdm, struct vattr *va,
+ enum de_op op, lxd_node_t **newnode, struct cred *cred)
+{
+ lxd_node_t *ldn;
+
+ ASSERT(va != NULL);
+
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+
+ ldn = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+
+ ldn->lxdn_type = LXDNT_FRONT;
+ lxd_node_init(lxdm, ldn, NULL, va, cred);
+
+ ldn->lxdn_vnode->v_rdev = ldn->lxdn_rdev = NODEV;
+ ldn->lxdn_vnode->v_type = va->va_type;
+ ldn->lxdn_uid = crgetuid(cred);
+ ldn->lxdn_gid = crgetgid(cred);
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ if (va->va_mask & AT_ATIME)
+ ldn->lxdn_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ ldn->lxdn_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ lxd_dirinit(dir, ldn);
+ }
+
+ *newnode = ldn;
+ return (0);
+}
+
+static int
+lxd_diraddentry(lxd_node_t *dir, lxd_node_t *ldn, char *name)
+{
+ lxd_dirent_t *dp, *pdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent directory wasn't removed from
+ * underneath the caller.
+ */
+ if (dir->lxdn_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same filesystem. */
+ if (ldn->lxdn_vnode->v_vfsp != dir->lxdn_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Allocate and initialize directory entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (lxd_dirent_t);
+ dp = kmem_zalloc(alloc_size, KM_NOSLEEP | KM_NORMALPRI);
+ if (dp == NULL)
+ return (ENOSPC);
+
+ ldn->lxdn_parent = dir;
+
+ dir->lxdn_size += alloc_size;
+ dir->lxdn_dirents++;
+ dp->lddir_node = ldn;
+ dp->lddir_parent = dir;
+
+ /* The directory entry and its name were allocated sequentially. */
+ dp->lddir_name = (char *)dp + sizeof (lxd_dirent_t);
+ (void) strcpy(dp->lddir_name, name);
+
+ lxd_save_dirent(dp);
+
+ /*
+ * Some utilities expect the size of a directory to remain
+ * somewhat static. For example, a routine which removes
+ * subdirectories between calls to readdir(); the size of the
+ * directory changes from underneath it and so the real
+ * directory offset in bytes is invalid. To circumvent
+ * this problem, we initialize a directory entry with an
+ * phony offset, and use this offset to determine end of
+ * file in lxd_readdir.
+ */
+ pdp = dir->lxdn_dir->lddir_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (pdp->lddir_next != NULL &&
+ (pdp->lddir_next->lddir_offset - pdp->lddir_offset) <= 1) {
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+ ASSERT(pdp->lddir_next->lddir_offset > pdp->lddir_offset);
+ pdp = pdp->lddir_next;
+ }
+ dp->lddir_offset = pdp->lddir_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which
+ * is necessarily the largest offset in this directory) is more
+ * than twice the number of dirents, that means the directory is
+ * 50% holes. At this point we reset the slot pointer back to
+ * the beginning of the directory so we start using the holes.
+ * The idea is that if there are N dirents, there must also be
+ * N holes, so we can satisfy the next N creates by walking at
+ * most 2N entries; thus the average cost of a create is constant.
+ * Note that we use the first dirent's lddir_prev as the roving
+ * slot pointer; it's ugly, but it saves a word in every dirent.
+ */
+ if (pdp->lddir_next == NULL &&
+ pdp->lddir_offset > 2 * dir->lxdn_dirents)
+ dir->lxdn_dir->lddir_prev = dir->lxdn_dir->lddir_next;
+ else
+ dir->lxdn_dir->lddir_prev = dp;
+
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+
+ dp->lddir_next = pdp->lddir_next;
+ if (dp->lddir_next) {
+ dp->lddir_next->lddir_prev = dp;
+ }
+ dp->lddir_prev = pdp;
+ pdp->lddir_next = dp;
+
+ ASSERT(dp->lddir_next != dp);
+ ASSERT(dp->lddir_prev != dp);
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ return (0);
+}
+
+/*
+ * Enter a directory entry for 'name' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+lxd_direnter(
+ lxd_mnt_t *lxdm,
+ lxd_node_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ lxd_node_t *fromparent, /* original directory if rename */
+ lxd_node_t *ldn, /* existing lxd_node, if rename */
+ struct vattr *va,
+ lxd_node_t **rnp, /* return lxd_node, if create/mkdir */
+ cred_t *cr)
+{
+ lxd_dirent_t *dirp;
+ lxd_node_t *found = NULL;
+ int error = 0;
+ char *s;
+
+ /* lxdn_rwlock is held to serialize direnter and dirdeletes */
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ /*
+ * Don't allow '/' characters in pathname component,
+ */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("lxd_direnter: NULL name");
+
+ /*
+ * For rename lock the source entry and check the link count
+ * to see if it has been removed while it was unlocked.
+ */
+ if (op == DE_RENAME) {
+ mutex_enter(&ldn->lxdn_tlock);
+ if (ldn->lxdn_nlink == 0) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (ENOENT);
+ }
+
+ if (ldn->lxdn_nlink == MAXLINK) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (EMLINK);
+ }
+ ldn->lxdn_nlink++;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ }
+
+ /*
+ * This might be a "dangling detached directory" (it could have been
+ * removed, but a reference to it kept in u_cwd). Don't bother
+ * searching it, and with any luck the user will get tired of dealing
+ * with us and cd to some absolute pathway (thus in ufs, too).
+ */
+ if (dir->lxdn_nlink == 0) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /*
+ * If this is a rename of a directory and the parent is different
+ * (".." must be changed), then the source directory must not be in the
+ * directory hierarchy above the target, as this would orphan
+ * everything below the source directory.
+ */
+ if (op == DE_RENAME) {
+ if (ldn == dir) {
+ error = EINVAL;
+ goto out;
+ }
+ if ((ldn->lxdn_vnode->v_type) == VDIR) {
+ if ((fromparent != dir) &&
+ (error = lxd_dircheckpath(ldn, dir)) != 0) {
+ goto out;
+ }
+ }
+ }
+
+ /* Search for an existing entry. */
+ dirp = lxd_find_dirent(name, dir, HOLD, &found);
+ if (dirp != NULL) {
+ ASSERT(found != NULL);
+ switch (op) {
+ case DE_CREATE:
+ case DE_MKDIR:
+ if (rnp != NULL) {
+ *rnp = found;
+ error = EEXIST;
+ } else {
+ ldnode_rele(found);
+ }
+ break;
+
+ case DE_RENAME:
+ /*
+ * Note that we only hit this path when we're renaming
+ * a symlink from one directory to another and there is
+ * a pre-existing symlink as the target. lxd_rename
+ * will unlink the src from the original directory but
+ * here we need to unlink the dest that we collided
+ * with, then create the new directory entry as we do
+ * below when there is no pre-existing symlink.
+ */
+ if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+ goto out;
+
+ ASSERT(found->lxdn_vnode->v_type == VLNK);
+ /* dir rw lock is already held and asserted above */
+ rw_enter(&found->lxdn_rwlock, RW_WRITER);
+ error = lxd_dirdelete(dir, found, name, DR_RENAME, cr);
+ rw_exit(&found->lxdn_rwlock);
+ ldnode_rele(found);
+ if (error != 0)
+ goto out;
+
+ error = lxd_diraddentry(dir, ldn, name);
+ if (error == 0 && rnp != NULL)
+ *rnp = ldn;
+ break;
+ }
+ } else {
+
+ /*
+ * The directory entry does not exist, but the node might if
+ * this is a rename. Check write permission in directory to
+ * see if entry can be created.
+ */
+ if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+ goto out;
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Make new lxd_node and directory entry as required.
+ */
+ error = lxd_dir_make_node(dir, lxdm, va, op, &ldn, cr);
+ if (error)
+ goto out;
+ }
+
+ error = lxd_diraddentry(dir, ldn, name);
+ if (error != 0) {
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Unmake the inode we just made.
+ */
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ if ((ldn->lxdn_vnode->v_type) == VDIR) {
+ ASSERT(dirp == NULL);
+ /*
+ * cleanup allocs made by lxd_dirinit
+ */
+ lxd_dirtrunc(ldn);
+ }
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink = 0;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ rw_exit(&ldn->lxdn_rwlock);
+ ldnode_rele(ldn);
+ ldn = NULL;
+ }
+ } else if (rnp != NULL) {
+ *rnp = ldn;
+ } else if (op == DE_CREATE || op == DE_MKDIR) {
+ ldnode_rele(ldn);
+ }
+ }
+
+out:
+ if (error && op == DE_RENAME) {
+ /* Undo bumped link count. */
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ }
+ return (error);
+}
+
+/*
+ * Delete entry ldn of name "nm" from parent dir. This is used to both remove
+ * a directory and to remove file nodes within the directory (by recursively
+ * calling itself). It frees the dir entry space and decrements link count on
+ * lxd_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+lxd_dirdelete(lxd_node_t *dir, lxd_node_t *ldn, char *nm, enum dr_op op,
+ cred_t *cred)
+{
+ lxd_dirent_t *dirp;
+ int error;
+ size_t namelen;
+ lxd_node_t *fndnp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(RW_WRITE_HELD(&ldn->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ if (nm[0] == '\0')
+ panic("lxd_dirdelete: empty name for 0x%p", (void *)ldn);
+
+ /*
+ * return error when removing . and ..
+ */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = lxd_naccess(dir, VEXEC|VWRITE, cred)) != 0)
+ return (error);
+
+ if (dir->lxdn_dir == NULL)
+ return (ENOENT);
+
+ if (op == DR_RMDIR) {
+ /*
+ * This is the top-level removal of a directory. Start by
+ * removing any file entries from the dir. We do this by
+ * recursively calling back into this function with a different
+ * op code. The caller of this function has already verified
+ * that it is safe to remove this directory.
+ */
+ lxd_dirent_t *dirp;
+
+ ASSERT(ldn->lxdn_vnode->v_type == VDIR);
+
+ dirp = ldn->lxdn_dir;
+ while (dirp) {
+ lxd_node_t *dn;
+ lxd_dirent_t *nextp;
+
+ if (strcmp(dirp->lddir_name, ".") == 0 ||
+ strcmp(dirp->lddir_name, "..") == 0) {
+ dirp = dirp->lddir_next;
+ continue;
+ }
+
+ dn = dirp->lddir_node;
+ nextp = dirp->lddir_next;
+
+ ldnode_hold(dn);
+ error = lxd_dirdelete(ldn, dn, dirp->lddir_name,
+ DR_REMOVE, cred);
+ ldnode_rele(dn);
+
+ dirp = nextp;
+ }
+ }
+
+ dirp = lxd_find_dirent(nm, dir, NOHOLD, &fndnp);
+ VERIFY(dirp != NULL);
+ VERIFY(ldn == fndnp);
+
+ lxd_rm_dirent(dirp);
+
+ /* Take dirp out of the directory list. */
+ ASSERT(dirp->lddir_next != dirp);
+ ASSERT(dirp->lddir_prev != dirp);
+ if (dirp->lddir_prev) {
+ dirp->lddir_prev->lddir_next = dirp->lddir_next;
+ }
+ if (dirp->lddir_next) {
+ dirp->lddir_next->lddir_prev = dirp->lddir_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match dirp,
+ * point it at the previous dirent.
+ */
+ if (dir->lxdn_dir->lddir_prev == dirp) {
+ dir->lxdn_dir->lddir_prev = dirp->lddir_prev;
+ }
+ ASSERT(dirp->lddir_next != dirp);
+ ASSERT(dirp->lddir_prev != dirp);
+
+ /* dirp points to the correct directory entry */
+ namelen = strlen(dirp->lddir_name) + 1;
+
+ kmem_free(dirp, sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_dirents--;
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+ ldn->lxdn_ctime = now;
+
+ ASSERT(ldn->lxdn_nlink > 0);
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ mutex_exit(&ldn->lxdn_tlock);
+ if (op == DR_RMDIR && ldn->lxdn_vnode->v_type == VDIR) {
+ lxd_dirtrunc(ldn);
+ ASSERT(ldn->lxdn_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * Initialize a lxd_node and add it to file list under mount point.
+ */
+void
+lxd_node_init(lxd_mnt_t *lxdm, lxd_node_t *ldn, vnode_t *realvp, vattr_t *vap,
+ cred_t *cred)
+{
+ struct vnode *vp;
+ timestruc_t now;
+
+ ASSERT(vap != NULL);
+
+ rw_init(&ldn->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&ldn->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+ ldn->lxdn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ ldn->lxdn_mask = 0;
+ ldn->lxdn_attr.va_type = vap->va_type;
+ ldn->lxdn_nlink = 1;
+ ldn->lxdn_size = 0;
+
+ if (cred == NULL) {
+ ldn->lxdn_uid = vap->va_uid;
+ ldn->lxdn_gid = vap->va_gid;
+ } else {
+ ldn->lxdn_uid = crgetuid(cred);
+ ldn->lxdn_gid = crgetgid(cred);
+ }
+
+ ldn->lxdn_fsid = lxdm->lxdm_dev;
+ ldn->lxdn_rdev = vap->va_rdev;
+ ldn->lxdn_blksize = PAGESIZE;
+ ldn->lxdn_nblocks = 0;
+ gethrestime(&now);
+ ldn->lxdn_atime = now;
+ ldn->lxdn_mtime = now;
+ ldn->lxdn_ctime = now;
+ ldn->lxdn_seq = 0;
+ ldn->lxdn_dir = NULL;
+
+ ldn->lxdn_real_vp = realvp;
+
+ ldn->lxdn_vnode = vn_alloc(KM_SLEEP);
+ vp = LDNTOV(ldn);
+ vn_setops(vp, lxd_vnodeops);
+ vp->v_vfsp = lxdm->lxdm_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)ldn;
+
+ mutex_enter(&lxdm->lxdm_contents);
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ /*
+ * Add new lxd_node to end of linked list of lxd_nodes for this
+ * lxdevfs. Root directory is handled specially in lxd_mount.
+ */
+ if (lxdm->lxdm_rootnode != (lxd_node_t *)NULL) {
+ ldn->lxdn_next = NULL;
+ ldn->lxdn_prev = lxdm->lxdm_rootnode->lxdn_prev;
+ ldn->lxdn_prev->lxdn_next = lxdm->lxdm_rootnode->lxdn_prev =
+ ldn;
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+ vn_exists(vp);
+}
+
+/*
+ * lxd_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+lxd_dirinit(lxd_node_t *parent, lxd_node_t *dir)
+{
+ lxd_dirent_t *dot, *dotdot;
+ timestruc_t now;
+ lxd_mnt_t *lxdm = VTOLXDM(dir->lxdn_vnode);
+ struct vattr nattr;
+
+ ASSERT(RW_WRITE_HELD(&parent->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ dir->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ /*
+ * Initialize the entries
+ */
+ dot = kmem_zalloc(sizeof (lxd_dirent_t) + 2, KM_SLEEP);
+ dot->lddir_node = dir;
+ dot->lddir_offset = 0;
+ dot->lddir_name = (char *)dot + sizeof (lxd_dirent_t);
+ dot->lddir_name[0] = '.';
+ dot->lddir_parent = dir;
+ lxd_save_dirent(dot);
+
+ dotdot = kmem_zalloc(sizeof (lxd_dirent_t) + 3, KM_SLEEP);
+ dotdot->lddir_node = parent;
+ dotdot->lddir_offset = 1;
+ dotdot->lddir_name = (char *)dotdot + sizeof (lxd_dirent_t);
+ dotdot->lddir_name[0] = '.';
+ dotdot->lddir_name[1] = '.';
+ dotdot->lddir_parent = dir;
+ lxd_save_dirent(dotdot);
+
+ /*
+ * Initialize directory entry list.
+ */
+ dot->lddir_next = dotdot;
+ dot->lddir_prev = dotdot; /* dot's lddir_prev holds roving slot ptr */
+ dotdot->lddir_next = NULL;
+ dotdot->lddir_prev = dot;
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ parent->lxdn_nlink++;
+ parent->lxdn_ctime = now;
+
+ dir->lxdn_dir = dot;
+ dir->lxdn_size = 2 * sizeof (lxd_dirent_t) + 5; /* dot and dotdot */
+ dir->lxdn_dirents = 2;
+ dir->lxdn_nlink = 2;
+ dir->lxdn_parent = parent;
+
+ bzero(&nattr, sizeof (struct vattr));
+ nattr.va_mode = (mode_t)(0644);
+ nattr.va_type = VREG;
+ nattr.va_rdev = 0;
+}
+
+/*
+ * lxd_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+lxd_dirtrunc(lxd_node_t *dir)
+{
+ lxd_dirent_t *ldp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ for (ldp = dir->lxdn_dir; ldp; ldp = dir->lxdn_dir) {
+ size_t namelen;
+ lxd_node_t *ldn;
+
+ ASSERT(ldp->lddir_next != ldp);
+ ASSERT(ldp->lddir_prev != ldp);
+ ASSERT(ldp->lddir_node);
+
+ dir->lxdn_dir = ldp->lddir_next;
+ namelen = strlen(ldp->lddir_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this directory entry
+ * removal. We do hold/rele operations to free up these nodes.
+ */
+ ldn = ldp->lddir_node;
+
+ ASSERT(ldn->lxdn_nlink > 0);
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ mutex_exit(&ldn->lxdn_tlock);
+
+ lxd_rm_dirent(ldp);
+ kmem_free(ldp, sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ ASSERT(dir->lxdn_dir == NULL);
+ ASSERT(dir->lxdn_size == 0);
+ ASSERT(dir->lxdn_dirents == 0);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c b/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c
new file mode 100644
index 0000000000..b2e2b9b9e3
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c
@@ -0,0 +1,860 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * The lx devfs (lxd) file system is used within lx branded zones to provide
+ * the Linux view of /dev.
+ *
+ * In the past, the Linux /dev was simply a lofs mount pointing at /native/dev.
+ * lxd now provides the Linux /dev.
+ *
+ * The lxd file system is a hybrid of lofs and tmpfs. It supports a "back" file
+ * system which is the special device and corresponds to the special device in
+ * a lofs mount. As with lofs, all files in the special device are accessible
+ * through the lxd mount. Because the zone's devfs is not directly modifiable
+ * within the zone (also mknod(2) is not generally allowed within a zone) it is
+ * impossible to create files in devfs. For lx, in some cases it's useful to be
+ * able to make new symlinks or new directories under /dev. lxd implements
+ * these operations by creating "files" in memory in the same way as tmpfs
+ * does. Within lxd these are referred to as "front" files. For operations such
+ * as lookup or readdir, lxd provides a merged view of both the front and back
+ * files. lxd does not support regular front files or simple I/O (read/write)
+ * to front files, since there is no need for that. For back files, all
+ * operations are simply passed through to the real vnode, as is done with
+ * lofs. Front files are not allowed to mask back files.
+ *
+ * The Linux /dev is now a lxd mount with the special file (i.e. the back
+ * file system) as /native/dev.
+ *
+ * In addition, lx has a need for some illumos/Linux translation for the
+ * various *stat(2) system calls when used on a device. This translation can
+ * be centralized within lxd's getattr vnode entry point.
+ *
+ * Because the front file system only exists in memory and the back file
+ * system is the zone's devfs, which is not persistent across reboots, we
+ * track any device uid/gid/mode changes in a per-zone /etc/.lxd_dev_attr
+ * file and re-apply those changes when the lx devfs file system is mounted.
+ * Currently only changes to block device nodes are persistent.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ptm.h>
+#include <sys/lx_impl.h>
+
+#include "lxd.h"
+
+/* Module level parameters */
+static int lxd_fstype;
+static dev_t lxd_dev;
+
+/*
+ * lxd_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t lxd_mountcount;
+
+/*
+ * lxd_minfree is the minimum amount of swap space that lx devfs leaves for
+ * the rest of the zone.
+ */
+size_t lxd_minfree = 0;
+
+/*
+ * LXDMINFREE -- the value from which lxd_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for lxd in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow lxd to consume
+ * no more than ~10% of this, yielding a LXDMINFREE of 12MB.
+ */
+#define LXDMINFREE 12 * 1024 * 1024 /* 12 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+extern int lxd_symlink(vnode_t *, char *, struct vattr *, char *, cred_t *,
+ caller_context_t *, int);
+extern int stat64(char *, struct stat64 *);
+
+/*
+ * lxd vfs operations.
+ */
+static int lxd_init(int, char *);
+static int lxd_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int lxd_unmount(vfs_t *, int, cred_t *);
+static int lxd_root(vfs_t *, vnode_t **);
+static int lxd_statvfs(vfs_t *, statvfs64_t *);
+static void lxd_freevfs(vfs_t *vfsp);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_devfs",
+ lxd_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand devfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+/*
+ * Definitions and translators for devt's.
+ */
+static void lxd_pts_devt_translator(dev_t, dev_t *);
+static void lxd_ptm_devt_translator(dev_t, dev_t *);
+
+static kmutex_t lxd_xlate_lock;
+static boolean_t lxd_xlate_initialized = B_FALSE;
+
+static lxd_minor_translator_t lxd_mtranslator_mm[] = {
+ { "/dev/null", 0, 1, 3 },
+ { "/dev/zero", 0, 1, 5 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_random[] = {
+ { "/dev/random", 0, 1, 8 },
+ { "/dev/urandom", 0, 1, 9 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_sy[] = {
+ { "/dev/tty", 0, LX_TTY_MAJOR, 0 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_zcons[] = {
+ { "/dev/console", 0, LX_TTY_MAJOR, 1 },
+ { NULL, 0, 0, 0 }
+};
+lxd_devt_translator_t lxd_devt_translators[] = {
+ { "mm", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_mm },
+ { "random", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_random },
+ { "sy", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_sy },
+ { "zcons", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_zcons },
+ { LX_PTM_DRV, 0, DTT_CUSTOM, (uintptr_t)lxd_ptm_devt_translator },
+ { "pts", 0, DTT_CUSTOM, (uintptr_t)lxd_pts_devt_translator },
+ { NULL, 0, DTT_INVALID, NULL }
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ if (lxd_mountcount > 0)
+ return (EBUSY);
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(lxd_fstype);
+ vn_freevnodeops(lxd_vnodeops);
+ mutex_destroy(&lxd_xlate_lock);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading lxd module.
+ */
+static int
+lxd_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxd_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxd_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxd_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxd_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxd_statvfs },
+ VFSNAME_FREEVFS, { .vfs_freevfs = lxd_freevfs },
+ NULL, NULL
+ };
+ extern const struct fs_operation_def lxd_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ lxd_fstype = fstype;
+ ASSERT(lxd_fstype != 0);
+
+ error = vfs_setfsops(fstype, lxd_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxd_init: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, lxd_vnodeops_template, &lxd_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxd_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * lxd_minfree doesn't need to be some function of configured
+ * swap space since it really is an absolute limit of swap space
+ * which still allows other processes to execute.
+ */
+ if (lxd_minfree == 0) {
+ /* Set if not patched */
+ lxd_minfree = btopr(LXDMINFREE);
+ }
+
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxd_init: Can't get unique device number.");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxd_dev = makedevice(dev, 0);
+
+ mutex_init(&lxd_xlate_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ return (0);
+}
+
+/*
+ * Initialize device translator mapping table.
+ *
+ * Note that we cannot do this in lxd_init since that can lead to a recursive
+ * rw_enter while we're doing lookupnameat (via sdev_lookup/prof_make_maps/
+ * devi_attach_node/modload). Thus we do it in the mount path and keep track
+ * so that we only initialize the table once.
+ */
+static void
+lxd_xlate_init()
+{
+ int i;
+
+ mutex_enter(&lxd_xlate_lock);
+ if (lxd_xlate_initialized) {
+ mutex_exit(&lxd_xlate_lock);
+ return;
+ }
+
+ for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+ lxd_minor_translator_t *mt;
+ int j;
+
+ lxd_devt_translators[i].lxd_xl_major =
+ mod_name_to_major(lxd_devt_translators[i].lxd_xl_driver);
+
+ /* if this translator doesn't use a list mapping we're done. */
+ if (lxd_devt_translators[i].lxd_xl_type != DTT_LIST)
+ continue;
+
+ /* for each device listed, lookup the minor node number */
+ mt = lxd_devt_translators[i].xl_list;
+ for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+ vnode_t *vp;
+ struct vattr va;
+ char *tpath;
+ char tnm[MAXPATHLEN];
+
+ /*
+ * The attach might be triggered in either the global
+ * zone or in a non-global zone, so we may need to
+ * adjust the path if we're in a NGZ.
+ */
+ if (curproc->p_zone->zone_id == GLOBAL_ZONEUNIQID) {
+ tpath = mt[j].lxd_mt_path;
+ } else {
+ (void) snprintf(tnm, sizeof (tnm), "/native%s",
+ mt[j].lxd_mt_path);
+ tpath = tnm;
+ }
+
+ if (lookupnameat(tpath, UIO_SYSSPACE, FOLLOW, NULL,
+ &vp, NULL) != 0) {
+ mt[j].lxd_mt_minor = UINT_MAX;
+ continue;
+ }
+
+ va.va_mask = AT_RDEV;
+ if (VOP_GETATTR(vp, &va, 0, kcred, NULL) != 0) {
+ va.va_rdev = NODEV;
+ } else {
+ ASSERT(getmajor(va.va_rdev) ==
+ lxd_devt_translators[i].lxd_xl_major);
+ ASSERT(mt[j].lxd_mt_lx_minor < LX_MAXMIN);
+ }
+
+ mt[j].lxd_mt_minor = getminor(va.va_rdev);
+
+ VN_RELE(vp);
+ }
+ }
+
+ lxd_xlate_initialized = B_TRUE;
+ mutex_exit(&lxd_xlate_lock);
+}
+
+static int
+lxd_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ lxd_mnt_t *lxdm = NULL;
+ struct lxd_node *ldn;
+ struct pathname dpn;
+ int error;
+ int i;
+ int nodev;
+ struct vattr rattr;
+ vnode_t *realrootvp;
+ vnode_t *tvp;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+ vattr_t vattr;
+
+ nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL);
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ lxd_xlate_init();
+
+ /*
+ * This is the same behavior as with lofs.
+ * Loopback devices which get "nodevices" added can be done without
+ * "nodevices" set because we cannot import devices into a zone
+ * with loopback. Note that we have all zone privileges when
+ * this happens; if not, we'd have gotten "nosuid".
+ */
+ if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
+ vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY);
+
+ /*
+ * Only allow mounting within lx zones.
+ */
+ if (curproc->p_zone->zone_brand != &lx_brand)
+ return (EINVAL);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /* lxd doesn't support read-only mounts */
+ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Find real root
+ */
+ if ((error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ?
+ UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp))) {
+ pn_free(&dpn);
+ return (error);
+ }
+
+ if ((error = VOP_ACCESS(realrootvp, 0, 0, cr, NULL)) != 0) {
+ pn_free(&dpn);
+ VN_RELE(realrootvp);
+ return (error);
+ }
+
+ /* If realroot is not a devfs, error out */
+ if (strcmp(realrootvp->v_op->vnop_name, "dev") != 0) {
+ pn_free(&dpn);
+ VN_RELE(realrootvp);
+ return (EINVAL);
+ }
+
+ lxdm = kmem_zalloc(sizeof (*lxdm), KM_SLEEP);
+
+ /* init but don't bother entering the mutex (not on mount list yet) */
+ mutex_init(&lxdm->lxdm_contents, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxdm->lxdm_renamelck, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxdm->lxdm_attrlck, NULL, MUTEX_DEFAULT, NULL);
+
+ list_create(&lxdm->lxdm_devattrs, sizeof (lxd_dev_attr_t),
+ offsetof(lxd_dev_attr_t, lxda_link));
+
+ /* Initialize the hash table mutexes */
+ for (i = 0; i < LXD_HASH_SZ; i++) {
+ mutex_init(&lxdm->lxdm_hash_mutex[i], NULL, MUTEX_DEFAULT,
+ NULL);
+ }
+
+ lxdm->lxdm_vfsp = vfsp;
+ lxdm->lxdm_gen = 1; /* start inode counter at 1 */
+
+ vfsp->vfs_data = (caddr_t)lxdm;
+ vfsp->vfs_fstype = lxd_fstype;
+ vfsp->vfs_dev = lxd_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, lxd_dev, lxd_fstype);
+ lxdm->lxdm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(lxdm->lxdm_mntpath, dpn.pn_path);
+
+ /* allocate and initialize root lxd_node structure */
+ bzero(&rattr, sizeof (struct vattr));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+
+ tvp = lxd_make_back_node(realrootvp, lxdm);
+ ldn = VTOLDN(tvp);
+
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ LDNTOV(ldn)->v_flag |= VROOT;
+
+ /*
+ * initialize linked list of lxd_nodes so that the back pointer of
+ * the root lxd_node always points to the last one on the list
+ * and the forward pointer of the last node is null
+ */
+ ldn->lxdn_prev = ldn;
+ ldn->lxdn_next = NULL;
+ ldn->lxdn_nlink = 0;
+ lxdm->lxdm_rootnode = ldn;
+
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+ lxd_dirinit(ldn, ldn);
+
+ rw_exit(&ldn->lxdn_rwlock);
+
+ pn_free(&dpn);
+ error = 0;
+ atomic_inc_32(&lxd_mountcount);
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vattr.va_mask = AT_TYPE | AT_MODE;
+ vattr.va_type = VLNK;
+ vattr.va_mode = 0777;
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZVOL) {
+ char lnknm[MAXPATHLEN];
+
+ /* Create a symlink for the actual zvol. */
+ (void) snprintf(lnknm, sizeof (lnknm),
+ "./zvol/dsk/%s", vd->lxvd_real_name);
+ (void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+ lnknm, cr, NULL, 0);
+ } else if (vd->lxvd_type == LXVD_ZFS_DS) {
+ /*
+ * Create a symlink for the root "disk" using /dev/zfs
+ * as the target device.
+ */
+ (void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+ "./zfs", cr, NULL, 0);
+ }
+
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ /* Apply any persistent attribute changes. */
+ lxd_apply_db(lxdm);
+
+out:
+ if (error == 0)
+ vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+ return (error);
+}
+
+static int
+lxd_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn, *cancel;
+ struct vnode *vp;
+ int error;
+ uint_t cnt;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+
+ mutex_enter(&lxdm->lxdm_contents);
+
+ /*
+ * In the normal unmount case only the root node would have a reference
+ * count.
+ *
+ * With lxdm_contents held, nothing can be added or removed.
+ * If we find a previously referenced node, undo the holds we have
+ * placed and fail EBUSY.
+ */
+ ldn = lxdm->lxdm_rootnode;
+
+ vp = LDNTOV(ldn);
+ mutex_enter(&vp->v_lock);
+
+ if (flag & MS_FORCE) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EINVAL);
+ }
+
+ cnt = vp->v_count;
+ if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EBUSY);
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * Check for open files. An open file causes everything to unwind.
+ */
+ for (ldn = ldn->lxdn_next; ldn; ldn = ldn->lxdn_next) {
+ vp = LDNTOV(ldn);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
+ cancel = lxdm->lxdm_rootnode->lxdn_next;
+ while (cancel != ldn) {
+ vp = LDNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->lxdn_next;
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EBUSY);
+ } else {
+ /*
+ * It may seem incorrect for us to have a vnode with
+ * a count of 0, but this is modeled on tmpfs and works
+ * the same way. See lxd_front_inactive. There we allow
+ * the v_count to go to 0 but rely on the link count to
+ * keep the vnode alive. Since we now want to cleanup
+ * these vnodes we manually add a VN_HOLD so that the
+ * VN_RELEs that occur in the lxd_freevfs() cleanup
+ * will take us down the lxd_inactive code path. We
+ * can directly add a VN_HOLD since we have the lock.
+ */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
+ }
+ }
+
+ /*
+ * We can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&lxdm->lxdm_contents);
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * lxd_unmount.
+ */
+void
+lxd_freevfs(vfs_t *vfsp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn;
+ struct vnode *vp;
+ lxd_dev_attr_t *da;
+
+ /*
+ * Free all kmemalloc'd and anonalloc'd memory associated with
+ * this filesystem. To do this, we go through the file list twice,
+ * once to remove all the directory entries, and then to remove
+ * all the pseudo files.
+ */
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the lxd_mnt_t that
+ * says we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+ /*
+ * Remove all directory entries (this doesn't remove top-level dirs).
+ */
+ for (ldn = lxdm->lxdm_rootnode; ldn; ldn = ldn->lxdn_next) {
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ if (ldn->lxdn_vnode->v_type == VDIR)
+ lxd_dirtrunc(ldn);
+ rw_exit(&ldn->lxdn_rwlock);
+ }
+
+ ASSERT(lxdm->lxdm_rootnode != NULL);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place.
+ * VN_RELE should make the node disappear, unless somebody
+ * is holding pages against it. Nap and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on a
+ * lxd_node from blowing it away (in lxd_inactive) while we're trying
+ * to get to it here. Once we have a HOLD on it we know it'll stick
+ * around.
+ */
+ mutex_enter(&lxdm->lxdm_contents);
+
+ /*
+ * Remove all the files (except the rootnode) backwards.
+ */
+ while ((ldn = lxdm->lxdm_rootnode->lxdn_prev) != lxdm->lxdm_rootnode) {
+ mutex_exit(&lxdm->lxdm_contents);
+ /*
+ * All nodes will be released here. Note we handled the link
+ * count above.
+ */
+ vp = LDNTOV(ldn);
+ ASSERT(vp->v_type == VLNK || vp->v_type == VDIR ||
+ vp->v_type == VSOCK);
+ VN_RELE(vp);
+ mutex_enter(&lxdm->lxdm_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again - we know
+ * they'll give it up soon.
+ */
+ if (ldn == lxdm->lxdm_rootnode->lxdn_prev) {
+ VN_HOLD(vp);
+ mutex_exit(&lxdm->lxdm_contents);
+ delay(hz / 4);
+ mutex_enter(&lxdm->lxdm_contents);
+ }
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+
+ ASSERT(lxdm->lxdm_back_refcnt == 1);
+ ASSERT(lxdm->lxdm_dent_refcnt == 0);
+
+ VN_RELE(LDNTOV(lxdm->lxdm_rootnode));
+
+ ASSERT(lxdm->lxdm_mntpath != NULL);
+ kmem_free(lxdm->lxdm_mntpath, strlen(lxdm->lxdm_mntpath) + 1);
+
+ da = list_remove_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ kmem_free(da, sizeof (lxd_dev_attr_t));
+ da = list_remove_head(&lxdm->lxdm_devattrs);
+ }
+ list_destroy(&lxdm->lxdm_devattrs);
+
+ mutex_destroy(&lxdm->lxdm_contents);
+ mutex_destroy(&lxdm->lxdm_renamelck);
+ mutex_destroy(&lxdm->lxdm_attrlck);
+ kmem_free(lxdm, sizeof (lxd_mnt_t));
+
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&lxd_mountcount);
+}
+
+/*
+ * return root lxdnode for given vnode
+ */
+static int
+lxd_root(struct vfs *vfsp, struct vnode **vpp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn = lxdm->lxdm_rootnode;
+ struct vnode *vp;
+
+ ASSERT(ldn != NULL);
+
+ vp = LDNTOV(ldn);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxd_statvfs(struct vfs *vfsp, statvfs64_t *sbp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ zp = lxdm->lxdm_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > lxd_minfree)
+ sbp->f_bfree = blocks - lxd_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is just what's available
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a zone with a swap cap,
+ * then report the capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * The maximum number of files available is approximately the number
+ * of lxd_nodes we can allocate from the remaining kernel memory
+ * available to lxdevfs in this zone. This is fairly inaccurate since
+ * it doesn't take into account the names stored in the directory
+ * entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (lxd_node_t) + sizeof (lxd_dirent_t));
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[lxd_fstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, lxdm->lxdm_mntpath, sizeof (sbp->f_fstr));
+ /* ensure null termination */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static void
+lxd_pts_devt_translator(dev_t dev, dev_t *jdev)
+{
+ minor_t min = getminor(dev);
+ int lx_maj, lx_min;
+
+ /*
+ * Linux uses a range of major numbers for pts devices to address the
+ * relatively small minor number space (20 bits).
+ */
+
+ lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+ lx_min = min % LX_MAXMIN;
+ if (lx_maj > LX_PTS_MAJOR_MAX) {
+ /*
+ * The major is outside the acceptable range but there's little
+ * we can presently do about it short of overhauling the
+ * translation logic.
+ */
+ lx_unsupported("pts major out of translation range");
+ }
+
+ *jdev = LX_MAKEDEVICE(lx_maj, lx_min);
+}
+
+/* ARGSUSED */
+static void
+lxd_ptm_devt_translator(dev_t dev, dev_t *jdev)
+{
+ *jdev = LX_MAKEDEVICE(LX_PTM_MAJOR, LX_PTM_MINOR);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c b/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c
new file mode 100644
index 0000000000..af0dbb6550
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c
@@ -0,0 +1,1519 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <sys/lx_brand.h>
+#include <sys/brand.h>
+
+#include "lxd.h"
+
+static int
+lxd_open(vnode_t **vpp, int flag, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(*vpp);
+ vnode_t *vp = *vpp;
+ vnode_t *rvp;
+ vnode_t *oldvp;
+ int error;
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ oldvp = vp;
+ vp = rvp = REALVP(vp);
+ /*
+ * Need to hold new reference to vp since VOP_OPEN() may
+ * decide to release it.
+ */
+ VN_HOLD(vp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+
+ if (!error && rvp != vp) {
+ /*
+ * the FS which we called should have released the
+ * new reference on vp
+ */
+ *vpp = lxd_make_back_node(rvp, VFSTOLXDM(oldvp->v_vfsp));
+
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL)
+ error = ENOSYS;
+ else
+ *vpp = svp;
+ }
+ VN_RELE(oldvp);
+ } else {
+ ASSERT(rvp->v_count > 1);
+ VN_RELE(rvp);
+ }
+
+ return (error);
+}
+
+static int
+lxd_close(vnode_t *vp, int flag, int count, offset_t offset, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_CLOSE(vp, flag, count, offset, cr, ct));
+}
+
+static int
+lxd_read(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_READ(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_write(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_WRITE(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, struct cred *cr,
+ int *rvalp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp, ct));
+}
+
+static int
+lxd_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SETFL(vp, oflags, nflags, cr, ct));
+}
+
+/*
+ * Translate SunOS devt to Linux devt.
+ */
+static void
+lxd_s2l_devt(dev_t dev, dev_t *rdev)
+{
+ lxd_minor_translator_t *mt;
+ int i, j;
+ major_t maj = getmajor(dev);
+ minor_t min = getminor(dev);
+
+ /* look for a devt translator for this major number */
+ for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+ if (lxd_devt_translators[i].lxd_xl_major == maj)
+ break;
+ }
+
+ if (lxd_devt_translators[i].lxd_xl_driver != NULL) {
+ /* try to translate the illumos devt to a linux devt */
+ switch (lxd_devt_translators[i].lxd_xl_type) {
+ case DTT_INVALID:
+ ASSERT(0);
+ break;
+
+ case DTT_LIST:
+ mt = lxd_devt_translators[i].xl_list;
+ for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+ if (mt[j].lxd_mt_minor == min) {
+ ASSERT(mt[j].lxd_mt_minor < LX_MAXMIN);
+
+ /* found a translation */
+ *rdev = LX_MAKEDEVICE(
+ mt[j].lxd_mt_lx_major,
+ mt[j].lxd_mt_lx_minor);
+ return;
+ }
+ }
+ break;
+
+ case DTT_CUSTOM:
+ lxd_devt_translators[i].xl_custom(dev, rdev);
+ return;
+ }
+ }
+
+ /* we don't have a translator for this device */
+ *rdev = LX_MAKEDEVICE(maj, min);
+}
+
+static int
+lxd_getattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ int error;
+ vnode_t *rvp;
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ mutex_enter(&ldn->lxdn_tlock);
+
+ vap->va_type = vp->v_type;
+ vap->va_mode = ldn->lxdn_mode & MODEMASK;
+ vap->va_uid = ldn->lxdn_uid;
+ vap->va_gid = ldn->lxdn_gid;
+ vap->va_fsid = ldn->lxdn_fsid;
+ vap->va_nodeid = (ino64_t)ldn->lxdn_nodeid;
+ vap->va_nlink = ldn->lxdn_nlink;
+ vap->va_size = (u_offset_t)ldn->lxdn_size;
+ vap->va_atime = ldn->lxdn_atime;
+ vap->va_mtime = ldn->lxdn_mtime;
+ vap->va_ctime = ldn->lxdn_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = 0; /* no devs in front */
+ vap->va_seq = ldn->lxdn_seq;
+
+ vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(
+ vap->va_size)));
+ mutex_exit(&ldn->lxdn_tlock);
+ return (0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ rvp = REALVP(vp);
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)))
+ return (error);
+
+ /* Skip devt translation for native programs */
+ if (curproc->p_brand != &lx_brand) {
+ return (0);
+ } else {
+ /*
+ * We also skip translation when called from the user-land
+ * emulation code.
+ */
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ if (lwpd == NULL || lwpd->br_stack_mode != LX_STACK_MODE_BRAND)
+ return (0);
+ }
+
+ if (rvp->v_type == VCHR) {
+ dev_t ldev;
+
+ lxd_s2l_devt(vap->va_rdev, &ldev);
+ DTRACE_PROBE3(lxd__devxl, void *, rvp, void *, vap, int, ldev);
+ vap->va_rdev = ldev;
+ }
+
+ return (0);
+}
+
+static int
+lxd_setattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+ int res;
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error = 0;
+ struct vattr *set;
+ long mask = vap->va_mask;
+
+ /* Cannot set these attributes */
+ if ((mask & AT_NOSET) || (mask & AT_XVATTR) ||
+ (mask & AT_MODE && vap->va_mode & (S_ISUID | S_ISGID)) ||
+ (mask & AT_SIZE))
+ return (EINVAL);
+
+ mutex_enter(&ldn->lxdn_tlock);
+
+ set = &ldn->lxdn_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cr, vp, vap, set, flags,
+ lxd_naccess, ldn);
+ if (error) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ if (mask & AT_MODE) {
+ set->va_mode &= S_IFMT;
+ set->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ set->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ set->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ set->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ set->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&ldn->lxdn_ctime);
+
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ res = VOP_SETATTR(vp, vap, flags, cr, ct);
+ if (res == 0 && (vap->va_mask & (AT_MODE | AT_UID | AT_GID))) {
+ lxd_save_attrs(lxdm, vp);
+ }
+ return (res);
+}
+
+static int
+lxd_access(vnode_t *vp, int mode, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error;
+
+ mutex_enter(&ldn->lxdn_tlock);
+ error = lxd_naccess(ldn, mode, cr);
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ if (mode & VWRITE) {
+ if (vp->v_type == VREG && vn_is_readonly(vp))
+ return (EROFS);
+ }
+ vp = REALVP(vp);
+ return (VOP_ACCESS(vp, mode, flags, cr, ct));
+}
+
+static int
+lxd_fsync(vnode_t *vp, int syncflag, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FSYNC(vp, syncflag, cr, ct));
+}
+
+/* ARGSUSED */
+static void
+lxd_front_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ mutex_enter(&ldn->lxdn_tlock);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's little to do -- just drop our hold.
+ */
+ if (vp->v_count > 1 || ldn->lxdn_nlink != 0) {
+ vp->v_count--;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&ldn->lxdn_tlock);
+ rw_exit(&ldn->lxdn_rwlock);
+ return;
+ }
+
+ /*
+ * We have the last hold *and* the link count is zero, so this node is
+ * dead from the filesystem's viewpoint.
+ */
+ if (ldn->lxdn_size != 0) {
+ if (ldn->lxdn_vnode->v_type == VLNK)
+ kmem_free(ldn->lxdn_symlink, ldn->lxdn_size + 1);
+ }
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&ldn->lxdn_tlock);
+
+ vn_invalid(LDNTOV(ldn));
+
+ mutex_enter(&lxdm->lxdm_contents);
+ if (ldn->lxdn_next == NULL)
+ lxdm->lxdm_rootnode->lxdn_prev = ldn->lxdn_prev;
+ else
+ ldn->lxdn_next->lxdn_prev = ldn->lxdn_prev;
+ ldn->lxdn_prev->lxdn_next = ldn->lxdn_next;
+
+ mutex_exit(&lxdm->lxdm_contents);
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_destroy(&ldn->lxdn_rwlock);
+ mutex_destroy(&ldn->lxdn_tlock);
+
+ vn_free(LDNTOV(ldn));
+ kmem_free(ldn, sizeof (lxd_node_t));
+}
+
+/*ARGSUSED*/
+static void
+lxd_inactive(vnode_t *vp, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ lxd_front_inactive(vp, cr, ct);
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ lxd_free_back_node(ldn);
+}
+
+/* ARGSUSED */
+static int
+lxd_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FID(vp, fidp, ct));
+}
+
+/*
+ * For a front node lookup in the dirent hash table and return a shadow vnode
+ * (lxd_node_t type) of type LXDNT_FRONT.
+ *
+ * For a back node, lookup nm name and return a shadow vnode (lxd_node_t type)
+ * of the real vnode found.
+ */
+static int
+lxd_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, struct cred *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ vnode_t *vp = NULL;
+ int error;
+ vnode_t *realdvp;
+ lxd_mnt_t *lxdm = VTOLXDM(dvp);
+ int doingdotdot = 0;
+ lxd_node_t *ldn = VTOLDN(dvp);
+ lxd_node_t *nldn = NULL;
+
+ /*
+ * First check for front file which could be instantiated on either a
+ * front or back node (e.g. the top-level moint point directory node is
+ * a back node which can have front files created in it).
+ */
+
+ /* disallow extended attrs */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /* Null component name is a synonym for dir being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ error = lxd_dirlookup(ldn, nm, &nldn, cr);
+ rw_exit(&ldn->lxdn_rwlock);
+
+ if (error == 0) {
+ /* found */
+ ASSERT(nldn != NULL);
+ *vpp = LDNTOV(nldn);
+ return (0);
+ }
+
+ /* At this point, if dir node is a front node, error */
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOENT);
+ }
+
+ realdvp = REALVP(dvp);
+
+ if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
+ doingdotdot++;
+ /*
+ * Handle ".." out of mounted filesystem
+ */
+ while ((realdvp->v_flag & VROOT) && realdvp != rootdir) {
+ realdvp = realdvp->v_vfsp->vfs_vnodecovered;
+ ASSERT(realdvp != NULL);
+ }
+ }
+
+ *vpp = NULL; /* default(error) case */
+
+ /*
+ * Do the normal lookup
+ */
+ if ((error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr,
+ ct, direntflags, realpnp)) != 0) {
+ vp = NULL;
+ goto out;
+ }
+
+ /*
+ * We do this check here to avoid returning a stale file handle to the
+ * caller.
+ */
+ if (nm[0] == '.' && nm[1] == '\0') {
+ ASSERT(vp == realdvp);
+ VN_HOLD(dvp);
+ VN_RELE(vp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ if (doingdotdot) {
+ *vpp = lxd_make_back_node(vp, lxdm);
+ return (0);
+ }
+
+ /*
+ * If this vnode is mounted on, then we
+ * traverse to the vnode which is the root of
+ * the mounted file system.
+ */
+ if ((error = traverse(&vp)) != 0)
+ goto out;
+
+ /*
+ * Make a lxd node for the real vnode.
+ */
+ *vpp = lxd_make_back_node(vp, lxdm);
+ if (vp->v_type != VDIR) {
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL) {
+ VN_RELE(vp);
+ error = ENOSYS;
+ } else {
+ *vpp = svp;
+ }
+ }
+ return (error);
+ }
+
+out:
+ if (error != 0 && vp != NULL)
+ VN_RELE(vp);
+
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+lxd_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
+ int mode, vnode_t **vpp, struct cred *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ int error;
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_node_t *lnp = NULL;
+
+ rw_enter(&parent->lxdn_rwlock, RW_READER);
+ error = lxd_dirlookup(parent, nm, &lnp, cr);
+ rw_exit(&parent->lxdn_rwlock);
+ /*
+ * If this vnode already exists in lx devfs, we should pass the create
+ * operation through to the underlying resource it represents. For
+ * existing back nodes, the VOP_CREATE is done directly against the
+ * returned lxd node with an empty name (to avoid a redunant lookup).
+ * For existing front nodes, an appropriate error must be chosen since
+ * they cannot represent regular files
+ */
+ if (error == 0) {
+ if (lnp->lxdn_type == LXDNT_BACK) {
+ error = VOP_CREATE(lnp->lxdn_real_vp, "\0", va,
+ exclusive, mode, vpp, cr, flag, ct, vsecp);
+ } else {
+ if (exclusive == EXCL) {
+ error = EEXIST;
+ } else if (LDNTOV(lnp)->v_type == VDIR &&
+ (mode & S_IWRITE)) {
+ error = EISDIR;
+ } else {
+ error = ENOTSUP;
+ }
+ }
+ if (error != 0) {
+ ldnode_rele(lnp);
+ }
+ return (error);
+ }
+
+ /*
+ * We cannot create files in the back devfs but we want to allow for
+ * O_CREAT on existing files. Pass this through and let the back file
+ * system allow or deny it.
+ */
+ if (parent->lxdn_type == LXDNT_BACK) {
+ vnode_t *vp = NULL;
+
+ if (*nm == '\0') {
+ ASSERT(vpp && dvp == *vpp);
+ vp = REALVP(*vpp);
+ }
+ if ((error = VOP_CREATE(REALVP(dvp), nm, va, exclusive, mode,
+ &vp, cr, flag, ct, vsecp)) == 0) {
+ *vpp = lxd_make_back_node(vp, VFSTOLXDM(dvp->v_vfsp));
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev,
+ (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL) {
+ return (ENOSYS);
+ }
+ *vpp = svp;
+ }
+ return (0);
+ }
+ /*
+ * If we were unable to perform the VOP_CREATE for any reason
+ * other than sdev being read-only, we should bail.
+ */
+ if (error != ENOTSUP && error != EROFS) {
+ return (error);
+ }
+ }
+
+ /*
+ * While we don't allow create data-containing files under LX devfs, we
+ * must allow VSOCK front nodes to be created so that paths such as
+ * /dev/log can be used as AF_UNIX sockets.
+ */
+ if (va->va_type == VSOCK) {
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+ lnp = NULL;
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+ va, &lnp, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error == 0) {
+ *vpp = LDNTOV(lnp);
+ } else if (lnp != NULL) {
+ /*
+ * It's possible that a racing process created an entry
+ * at this name since we last performed the lookup.
+ */
+ ldnode_rele(lnp);
+ }
+ } else {
+ error = ENOTSUP;
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_remove(vnode_t *dvp, char *nm, struct cred *cr, caller_context_t *ct,
+ int flags)
+{
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_node_t *ldn = NULL;
+ int error;
+
+ /* can only remove existing front nodes */
+ error = lxd_dirlookup(parent, nm, &ldn, cr);
+ if (error) {
+ return (error);
+ }
+
+ ASSERT(ldn != NULL);
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ error = lxd_dirdelete(parent, ldn, nm, DR_REMOVE, cr);
+
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_exit(&parent->lxdn_rwlock);
+
+ ldnode_rele(ldn);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static int
+lxd_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ lxd_node_t *oldparent = VTOLDN(odvp);
+ lxd_node_t *newparent;
+ lxd_mnt_t *lxdm = VTOLXDM(oldparent->lxdn_vnode);
+ lxd_node_t *fromnode = NULL;
+ int error;
+ int samedir = 0;
+
+ if (!vn_matchops(ndvp, lxd_vnodeops)) {
+ /* cannot rename out of this file system */
+ return (EACCES);
+ }
+
+ mutex_enter(&lxdm->lxdm_renamelck);
+
+ newparent = VTOLDN(ndvp);
+
+ /*
+ * We can only rename front nodes.
+ */
+ error = lxd_dirlookup(oldparent, onm, &fromnode, cr);
+ if (error != 0) {
+ /* not found in front */
+ mutex_exit(&lxdm->lxdm_renamelck);
+ return (error);
+ }
+
+ /*
+ * Make sure we can delete the old (source) entry. This
+ * requires write permission on the containing directory. If
+ * that directory is "sticky" it requires further checks.
+ */
+ if ((error = lxd_naccess(oldparent, VWRITE, cr)) != 0)
+ goto done;
+
+ /*
+ * Check for renaming to or from '.' or '..' or that
+ * fromnode == oldparent
+ */
+ if ((onm[0] == '.' &&
+ (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+ (nnm[0] == '.' &&
+ (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+ (oldparent == fromnode)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ samedir = (oldparent == newparent);
+
+ /*
+ * Make sure we can search and rename into the destination directory.
+ */
+ if (!samedir) {
+ if ((error = lxd_naccess(newparent, VEXEC|VWRITE, cr)) != 0)
+ goto done;
+ }
+
+ /*
+ * Link source to new target
+ */
+ rw_enter(&newparent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, newparent, nnm, DE_RENAME,
+ oldparent, fromnode, (struct vattr *)NULL, (lxd_node_t **)NULL,
+ cr);
+ rw_exit(&newparent->lxdn_rwlock);
+
+ if (error)
+ goto done;
+
+ /*
+ * Unlink from source.
+ */
+ rw_enter(&oldparent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&fromnode->lxdn_rwlock, RW_WRITER);
+
+ error = lxd_dirdelete(oldparent, fromnode, onm, DR_RENAME, cr);
+
+ /*
+ * The following handles the case where our source node was
+ * removed before we got to it.
+ */
+ if (error == ENOENT)
+ error = 0;
+
+ rw_exit(&fromnode->lxdn_rwlock);
+ rw_exit(&oldparent->lxdn_rwlock);
+
+done:
+ ldnode_rele(fromnode);
+ mutex_exit(&lxdm->lxdm_renamelck);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
+ struct cred *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ int error;
+ vnode_t *tvp;
+ lxd_node_t *ndir = NULL;
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+ /* check for existence in both front and back */
+ if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ /* The entry already exists */
+ VN_RELE(tvp);
+ return (EEXIST);
+ }
+
+ /* make front directory */
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_MKDIR, NULL, NULL,
+ va, &ndir, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error != 0) {
+ if (ndir != NULL)
+ ldnode_rele(ndir);
+ } else {
+ *vpp = LDNTOV(ndir);
+ }
+
+ return (error);
+}
+
+static int
+lxd_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ *vpp = vp;
+ return (0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ while (vn_matchops(vp, lxd_vnodeops))
+ vp = REALVP(vp);
+
+ if (VOP_REALVP(vp, vpp, ct) != 0)
+ *vpp = vp;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lxd_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ int error;
+ lxd_node_t *ldn;
+ struct vnode *vp;
+ lxd_node_t *parent = VTOLDN(dvp);
+
+ /*
+ * Return error if trying to remove . or ..
+ */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST);
+
+ error = lxd_dirlookup(VTOLDN(dvp), nm, &ldn, cr);
+ if (error != 0) {
+ /* not found in front */
+ return (error);
+ }
+
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ vp = LDNTOV(ldn);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto err;
+ }
+
+ if (ldn->lxdn_vnode->v_type != VDIR) {
+ error = ENOTDIR;
+ goto err;
+ }
+
+ mutex_enter(&ldn->lxdn_tlock);
+ if (ldn->lxdn_nlink > 2) {
+ mutex_exit(&ldn->lxdn_tlock);
+ error = EEXIST;
+ goto err;
+ }
+ mutex_exit(&ldn->lxdn_tlock);
+
+ /* Check for an empty directory */
+ if (ldn->lxdn_dirents > 2) {
+ error = EEXIST;
+ gethrestime(&ldn->lxdn_atime);
+ goto err;
+ }
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto err;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ vn_vfsunlock(vp);
+ goto err;
+ }
+
+ error = lxd_dirdelete(parent, ldn, nm, DR_RMDIR, cr);
+ vn_vfsunlock(vp);
+
+err:
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_exit(&parent->lxdn_rwlock);
+ ldnode_rele(ldn);
+
+ return (error);
+}
+
+/* Not static so it can be used during mount. */
+/* ARGSUSED */
+int
+lxd_symlink(vnode_t *dvp, char *nm, struct vattr *tva, char *tnm,
+ struct cred *cr, caller_context_t *ct, int flags)
+{
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+ lxd_node_t *self = NULL;
+ vnode_t *tvp;
+ char *cp = NULL;
+ int error;
+ size_t len;
+
+ /* this will check for existence in both front and back */
+ if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ /* The entry already exists */
+ VN_RELE(tvp);
+ return (EEXIST);
+ }
+
+ /* make symlink in the front */
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+ tva, &self, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error) {
+ if (self != NULL)
+ ldnode_rele(self);
+ return (error);
+ }
+
+ len = strlen(tnm) + 1;
+ cp = kmem_alloc(len, KM_NOSLEEP | KM_NORMALPRI);
+ if (cp == NULL) {
+ ldnode_rele(self);
+ return (ENOSPC);
+ }
+ (void) strcpy(cp, tnm);
+
+ self->lxdn_symlink = cp;
+ self->lxdn_size = len - 1;
+ ldnode_rele(self);
+
+ return (error);
+}
+
+static int
+lxd_readlink(vnode_t *vp, struct uio *uiop, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error;
+
+ if (vp->v_type != VLNK)
+ return (EINVAL);
+
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ error = uiomove(ldn->lxdn_symlink, ldn->lxdn_size, UIO_READ,
+ uiop);
+ gethrestime(&ldn->lxdn_atime);
+ rw_exit(&ldn->lxdn_rwlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_READLINK(vp, uiop, cr, ct));
+}
+
+static int
+lx_merge_front(vnode_t *vp, struct uio *uiop, off_t req_off, int *eofp)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ struct dirent *sd;
+ lxd_dirent_t *ldp;
+ enum lxd_node_type type = ldn->lxdn_type;
+ ssize_t uresid;
+ off_t front_off;
+ int error = 0;
+ int sdlen;
+
+ /* skip the front entries if the back read was incomplete */
+ if (*eofp == 0)
+ return (0);
+
+ /*
+ * If this was a back node then reading that node has completed and we
+ * may have a partially full uio struct. eof should be set to true.
+ * Leave it set since we're likely to hit eof for the front nodes (if
+ * any).
+ */
+
+ front_off = uiop->uio_offset + 1;
+ sdlen = sizeof (struct dirent) + MAXPATHLEN;
+ /* zalloc to ensure we don't have anything in the d_name buffer */
+ sd = (struct dirent *)kmem_zalloc(sdlen, KM_SLEEP);
+ ldp = ldn->lxdn_dir;
+ while (ldp != NULL && (uresid = uiop->uio_resid) > 0) {
+ int namelen;
+ int reclen;
+
+ /*
+ * Skip dot and dotdot for back nodes since we have them
+ * already.
+ */
+ if (type == LXDNT_BACK &&
+ (strcmp(ldp->lddir_name, ".") == 0 ||
+ strcmp(ldp->lddir_name, "..") == 0)) {
+ ldp = ldp->lddir_next;
+ continue;
+ }
+
+ /*
+ * Might have previously had a partial readdir of the front
+ * nodes, and now we're back for more, or we may just be
+ * be doing a follow-up readdir after we've previously
+ * returned all front and back nodes.
+ */
+ if (front_off > req_off) {
+ namelen = strlen(ldp->lddir_name); /* no +1 needed */
+ reclen = (int)DIRENT64_RECLEN(namelen);
+
+ /*
+ * If the size of the data to transfer is greater
+ * than that requested, then we can't do it this
+ * transfer.
+ */
+ if (reclen > uresid) {
+ *eofp = 0;
+ /* Buffer too small for any entries. */
+ if (front_off == 0)
+ error = EINVAL;
+ break;
+ }
+
+ (void) strncpy(sd->d_name, ldp->lddir_name,
+ DIRENT64_NAMELEN(reclen));
+ sd->d_reclen = (ushort_t)reclen;
+ sd->d_ino = (ino_t)ldp->lddir_node->lxdn_nodeid;
+ sd->d_off = front_off;
+
+ /* uiomove will adjust iov_base properly */
+ if ((error = uiomove((caddr_t)sd, reclen, UIO_READ,
+ uiop)) != 0) {
+ *eofp = 0;
+ break;
+ }
+ }
+
+ /*
+ * uiomove() above updates both uio_resid and uio_offset by the
+ * same amount but we want uio_offset to change in increments
+ * of 1, which is different from the number of bytes being
+ * returned to the caller, so we set uio_offset explicitly,
+ * ignoring what uiomove() did.
+ */
+ uiop->uio_offset = front_off;
+ front_off++;
+
+ ldp = ldp->lddir_next;
+ }
+
+ kmem_free(sd, sdlen);
+ return (error);
+}
+
+static int
+lxd_readdir(vnode_t *vp, struct uio *uiop, struct cred *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ vnode_t *rvp;
+ int res;
+ off_t req_off;
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ req_off = uiop->uio_offset;
+
+ /* First read the back node (if it is one) */
+ if (ldn->lxdn_type == LXDNT_BACK) {
+ rvp = REALVP(vp);
+ res = VOP_READDIR(rvp, uiop, cr, eofp, ct, flags);
+ if (res != 0)
+ return (res);
+ } else {
+ /* setup for merge_front */
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ /* caller should have already called lxd_rwlock */
+ ASSERT(RW_READ_HELD(&ldn->lxdn_rwlock));
+
+ *eofp = 1;
+ /*
+ * The merge code starts the offset calculation from uio_offset,
+ * which is normally already set to the high value by the back
+ * code, but in this case we need to count up from 0.
+ */
+ uiop->uio_offset = 0;
+ }
+
+ /*
+ * Our back nodes can also have front entries hanging on them so we
+ * need to merge those in. Or, we may simply have a front node (i.e. a
+ * front subdir).
+ */
+ res = lx_merge_front(vp, uiop, req_off, eofp);
+ return (res);
+}
+
+static int
+lxd_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ if (write_lock) {
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ } else {
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ }
+ return (write_lock);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_RWLOCK(vp, write_lock, ct));
+}
+
+static void
+lxd_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ rw_exit(&ldn->lxdn_rwlock);
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ VOP_RWUNLOCK(vp, write_lock, ct);
+}
+
+static int
+lxd_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SEEK(vp, ooff, noffp, ct));
+}
+
+static int
+lxd_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ while (vn_matchops(vp1, lxd_vnodeops) &&
+ VTOLDN(vp1)->lxdn_type == LXDNT_BACK) {
+ vp1 = REALVP(vp1);
+ }
+ while (vn_matchops(vp2, lxd_vnodeops) &&
+ VTOLDN(vp2)->lxdn_type == LXDNT_BACK) {
+ vp2 = REALVP(vp2);
+ }
+
+ if (vn_matchops(vp1, lxd_vnodeops) || vn_matchops(vp2, lxd_vnodeops))
+ return (vp1 == vp2);
+
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+static int
+lxd_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+ struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+}
+
+static int
+lxd_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+ struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SPACE(vp, cmd, bfp, flag, offset, cr, ct));
+}
+
+static int
+lxd_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *prot,
+ struct page *parr[], size_t psz, struct seg *seg, caddr_t addr,
+ enum seg_rw rw, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr,
+ ct));
+}
+
+static int
+lxd_putpage(vnode_t *vp, offset_t off, size_t len, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PUTPAGE(vp, off, len, flags, cr, ct));
+}
+
+static int
+lxd_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len,
+ uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr, ct));
+}
+
+static int
+lxd_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+ uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+ ct));
+}
+
+static int
+lxd_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+ uint_t prot, uint_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+ ct));
+}
+
+static int
+lxd_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_POLL(vp, events, anyyet, reventsp, phpp, ct));
+}
+
+static int
+lxd_dump(vnode_t *vp, caddr_t addr, offset_t bn, offset_t count,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_DUMP(vp, addr, bn, count, ct));
+}
+
+static int
+lxd_pathconf(vnode_t *vp, int cmd, ulong_t *valp, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PATHCONF(vp, cmd, valp, cr, ct));
+}
+
+static int
+lxd_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, size_t io_len,
+ int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr, ct));
+}
+
+static void
+lxd_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ if (vp != NULL && !VN_ISKAS(vp))
+ VOP_DISPOSE(vp, pp, fl, dn, cr, ct);
+}
+
+static int
+lxd_setsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOSYS);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ if (vn_is_readonly(vp))
+ return (EROFS);
+
+ vp = REALVP(vp);
+ return (VOP_SETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_getsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOSYS);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_GETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SHRLOCK(vp, cmd, shr, flag, cr, ct));
+}
+
+/*
+ * Loopback vnode operations vector.
+ */
+
+struct vnodeops *lxd_vnodeops;
+
+const fs_operation_def_t lxd_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxd_open },
+ VOPNAME_CLOSE, { .vop_close = lxd_close },
+ VOPNAME_READ, { .vop_read = lxd_read },
+ VOPNAME_WRITE, { .vop_write = lxd_write },
+ VOPNAME_IOCTL, { .vop_ioctl = lxd_ioctl },
+ VOPNAME_SETFL, { .vop_setfl = lxd_setfl },
+ VOPNAME_GETATTR, { .vop_getattr = lxd_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = lxd_setattr },
+ VOPNAME_ACCESS, { .vop_access = lxd_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxd_lookup },
+ VOPNAME_CREATE, { .vop_create = lxd_create },
+ VOPNAME_REMOVE, { .vop_remove = lxd_remove },
+ VOPNAME_LINK, { .vop_link = lxd_link },
+ VOPNAME_RENAME, { .vop_rename = lxd_rename },
+ VOPNAME_MKDIR, { .vop_mkdir = lxd_mkdir },
+ VOPNAME_RMDIR, { .vop_rmdir = lxd_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = lxd_readdir },
+ VOPNAME_SYMLINK, { .vop_symlink = lxd_symlink },
+ VOPNAME_READLINK, { .vop_readlink = lxd_readlink },
+ VOPNAME_FSYNC, { .vop_fsync = lxd_fsync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxd_inactive },
+ VOPNAME_FID, { .vop_fid = lxd_fid },
+ VOPNAME_RWLOCK, { .vop_rwlock = lxd_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = lxd_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = lxd_seek },
+ VOPNAME_CMP, { .vop_cmp = lxd_cmp },
+ VOPNAME_FRLOCK, { .vop_frlock = lxd_frlock },
+ VOPNAME_SPACE, { .vop_space = lxd_space },
+ VOPNAME_REALVP, { .vop_realvp = lxd_realvp },
+ VOPNAME_GETPAGE, { .vop_getpage = lxd_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = lxd_putpage },
+ VOPNAME_MAP, { .vop_map = lxd_map },
+ VOPNAME_ADDMAP, { .vop_addmap = lxd_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = lxd_delmap },
+ VOPNAME_POLL, { .vop_poll = lxd_poll },
+ VOPNAME_DUMP, { .vop_dump = lxd_dump },
+ VOPNAME_DUMPCTL, { .error = fs_error },
+ VOPNAME_PATHCONF, { .vop_pathconf = lxd_pathconf },
+ VOPNAME_PAGEIO, { .vop_pageio = lxd_pageio },
+ VOPNAME_DISPOSE, { .vop_dispose = lxd_dispose },
+ VOPNAME_SETSECATTR, { .vop_setsecattr = lxd_setsecattr },
+ VOPNAME_GETSECATTR, { .vop_getsecattr = lxd_getsecattr },
+ VOPNAME_SHRLOCK, { .vop_shrlock = lxd_shrlock },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c
new file mode 100644
index 0000000000..de5a16c414
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c
@@ -0,0 +1,499 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/frame.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_impl.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We store the syscall number in the low 16 bits (which limits us to 64k
+ * syscalls). The next bit indicates entry/return probe and the next bit
+ * indicates 64bit/32bit syscall.
+ */
+#define SCALL_MASK 0xffff
+#define ENTRY_FLAG 0x10000
+#define SYSC_64_BIT 0x100000
+
+#define LX_SYSTRACE_IS64BIT(x) ((int)(x) & SYSC_64_BIT)
+#define LX_SYSTRACE_ISENTRY(x) ((int)(x) & ENTRY_FLAG)
+#define LX_SYSTRACE_SYSNUM(x) ((int)(x) & SCALL_MASK)
+
+#define LX_SYSTRACE32_ENTRY(id) (ENTRY_FLAG | (id))
+#define LX_SYSTRACE32_RETURN(id) (id)
+
+#define LX_SYSTRACE64_ENTRY(id) (SYSC_64_BIT | ENTRY_FLAG | (id))
+#define LX_SYSTRACE64_RETURN(id) (SYSC_64_BIT | id)
+
+#define LX_SYSTRACE_ENTRY_AFRAMES 2
+#define LX_SYSTRACE_RETURN_AFRAMES 4
+
+typedef struct lx_systrace_sysent {
+ const char *lss_name;
+ dtrace_id_t lss_entry;
+ dtrace_id_t lss_return;
+} lx_systrace_sysent_t;
+
+static dev_info_t *lx_systrace_devi;
+static dtrace_provider_id_t lx_systrace_id;
+static kmutex_t lx_systrace_lock;
+static uint_t lx_systrace_nenabled;
+
+static int lx_systrace_nsysent32;
+static lx_systrace_sysent_t *lx_systrace_sysent32;
+
+#if defined(_LP64)
+static int lx_systrace_nsysent64;
+static lx_systrace_sysent_t *lx_systrace_sysent64;
+#endif
+
+/*ARGSUSED*/
+static void
+lx_systrace_entry(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+ ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+ dtrace_id_t id;
+
+#if defined(_LP64)
+ if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+ if (sysnum >= lx_systrace_nsysent64)
+ return;
+ id = lx_systrace_sysent64[sysnum].lss_entry;
+ } else
+#endif
+ {
+ if (sysnum >= lx_systrace_nsysent32)
+ return;
+ id = lx_systrace_sysent32[sysnum].lss_entry;
+ }
+
+ if (id == DTRACE_IDNONE)
+ return;
+ dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_return(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+ ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+ dtrace_id_t id;
+
+#if defined(_LP64)
+ if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+ if (sysnum >= lx_systrace_nsysent64)
+ return;
+ id = lx_systrace_sysent64[sysnum].lss_return;
+ } else
+#endif
+ {
+ if (sysnum >= lx_systrace_nsysent32)
+ return;
+ id = lx_systrace_sysent32[sysnum].lss_return;
+ }
+
+ if (id == DTRACE_IDNONE)
+ return;
+ dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ int i;
+
+ if (desc != NULL)
+ return;
+
+ for (i = 0; i < lx_systrace_nsysent32; i++) {
+ if (dtrace_probe_lookup(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "entry",
+ LX_SYSTRACE_ENTRY_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE32_ENTRY(i)));
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "return",
+ LX_SYSTRACE_RETURN_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE32_RETURN(i)));
+
+ lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+ }
+
+#if defined(_LP64)
+ for (i = 0; i < lx_systrace_nsysent64; i++) {
+ if (dtrace_probe_lookup(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "entry",
+ LX_SYSTRACE_ENTRY_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE64_ENTRY(i)));
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "return",
+ LX_SYSTRACE_RETURN_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE64_RETURN(i)));
+
+ lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+ }
+#endif
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ mutex_enter(&lx_systrace_lock);
+ if (lx_systrace_nenabled++ == 0)
+ lx_brand_systrace_enable();
+ mutex_exit(&lx_systrace_lock);
+
+#if defined(_LP64)
+ if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+ ASSERT(sysnum < lx_systrace_nsysent64);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent64[sysnum].lss_entry = id;
+ } else {
+ lx_systrace_sysent64[sysnum].lss_return = id;
+ }
+ } else
+#endif
+ {
+ ASSERT(sysnum < lx_systrace_nsysent32);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent32[sysnum].lss_entry = id;
+ } else {
+ lx_systrace_sysent32[sysnum].lss_return = id;
+ }
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+#if defined(_LP64)
+ if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+ ASSERT(sysnum < lx_systrace_nsysent64);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent64[sysnum].lss_entry = DTRACE_IDNONE;
+ } else {
+ lx_systrace_sysent64[sysnum].lss_return = DTRACE_IDNONE;
+ }
+ } else
+#endif
+ {
+ ASSERT(sysnum < lx_systrace_nsysent32);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent32[sysnum].lss_entry = DTRACE_IDNONE;
+ } else {
+ lx_systrace_sysent32[sysnum].lss_return = DTRACE_IDNONE;
+ }
+ }
+
+ mutex_enter(&lx_systrace_lock);
+ if (--lx_systrace_nenabled == 0)
+ lx_brand_systrace_disable();
+ mutex_exit(&lx_systrace_lock);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+/*ARGSUSED*/
+static uint64_t
+lx_systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
+ int aframes)
+{
+ struct frame *fp = (struct frame *)dtrace_getfp();
+ uintptr_t *stack;
+ uint64_t val = 0;
+ int i;
+
+ if (argno >= 6)
+ return (0);
+
+ /*
+ * Walk the four frames down the stack to the entry or return callback.
+ * Our callback calls dtrace_probe() which calls dtrace_dif_variable()
+ * which invokes this function to get the extended arguments. We get
+ * the frame pointer in via call to dtrace_getfp() above which makes for
+ * four frames.
+ */
+ for (i = 0; i < 4; i++) {
+ fp = (struct frame *)fp->fr_savfp;
+ }
+
+ stack = (uintptr_t *)&fp[1];
+
+ /*
+ * Skip the first argument to the callback -- the system call number.
+ */
+ argno++;
+
+#ifdef __amd64
+ /*
+ * On amd64, the first 6 arguments are passed in registers while
+ * subsequent arguments are on the stack.
+ */
+ argno -= 6;
+#endif
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ val = stack[argno];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return (val);
+}
+
+
+static const dtrace_pattr_t lx_systrace_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pops_t lx_systrace_pops = {
+ lx_systrace_provide,
+ NULL,
+ lx_systrace_enable,
+ lx_systrace_disable,
+ NULL,
+ NULL,
+ NULL,
+ lx_systrace_getarg,
+ NULL,
+ lx_systrace_destroy
+};
+
+static int
+lx_systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ int i;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "lx_systrace", S_IFCHR,
+ 0, DDI_PSEUDO, NULL) == DDI_FAILURE ||
+ dtrace_register("lx-syscall", &lx_systrace_attr,
+ DTRACE_PRIV_USER, 0, &lx_systrace_pops, NULL,
+ &lx_systrace_id) != 0) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ lx_systrace_devi = devi;
+
+ /*
+ * Initialize the 32-bit table.
+ */
+ VERIFY(lx_nsysent32 > 0);
+ lx_systrace_nsysent32 = lx_nsysent32;
+ lx_systrace_sysent32 = kmem_zalloc(lx_systrace_nsysent32 *
+ sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+ for (i = 0; i < lx_systrace_nsysent32; i++) {
+ lx_systrace_sysent32[i].lss_name = lx_sysent32[i].sy_name;
+ lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+ }
+
+#if defined(_LP64)
+ /*
+ * Initialize the 64-bit table.
+ */
+ VERIFY(lx_nsysent64 > 0);
+ lx_systrace_nsysent64 = lx_nsysent64;
+ lx_systrace_sysent64 = kmem_zalloc(lx_systrace_nsysent64 *
+ sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+ for (i = 0; i < lx_systrace_nsysent64; i++) {
+ lx_systrace_sysent64[i].lss_name = lx_sysent64[i].sy_name;
+ lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+ }
+#endif
+
+ /*
+ * Install probe triggers.
+ */
+ lx_systrace_entry_ptr = lx_systrace_entry;
+ lx_systrace_return_ptr = lx_systrace_return;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister(lx_systrace_id) != 0)
+ return (DDI_FAILURE);
+
+ /*
+ * Free tables.
+ */
+ kmem_free(lx_systrace_sysent32, lx_systrace_nsysent32 *
+ sizeof (lx_systrace_sysent_t));
+ lx_systrace_sysent32 = NULL;
+ lx_systrace_nsysent32 = 0;
+
+#if defined(_LP64)
+ kmem_free(lx_systrace_sysent64, lx_systrace_nsysent64 *
+ sizeof (lx_systrace_sysent_t));
+ lx_systrace_sysent64 = NULL;
+ lx_systrace_nsysent64 = 0;
+#endif
+
+ /*
+ * Reset probe triggers.
+ */
+ lx_systrace_entry_ptr = NULL;
+ lx_systrace_return_ptr = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+static struct cb_ops lx_systrace_cb_ops = {
+ lx_systrace_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops lx_systrace_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ ddi_getinfo_1to1, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lx_systrace_attach, /* attach */
+ lx_systrace_detach, /* detach */
+ nodev, /* reset */
+ &lx_systrace_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "Linux Brand System Call Tracing", /* name of module */
+ &lx_systrace_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf
new file mode 100644
index 0000000000..e4499c8a5b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="lx_systrace" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/io/lx_netlink.c b/usr/src/uts/common/brand/lx/io/lx_netlink.c
new file mode 100644
index 0000000000..d25a5d1296
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_netlink.c
@@ -0,0 +1,1686 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Compatibility for the Linux netlink(7) kernel/user transport, as well as
+ * for in-kernel netlink(7) providers like rtnetlink(7). See RFC 3549 for
+ * details of the protocol, and the Linux man pages for details of the Linux
+ * implementation that we're mimicking.
+ */
+
+#include <sys/strsubr.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/strsun.h>
+#include <sys/tihdr.h>
+#include <sys/sockio.h>
+#include <sys/brand.h>
+#include <sys/debug.h>
+#include <sys/ucred.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <inet/ip_impl.h>
+#include <inet/ip_ire.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/policy.h>
+
+/*
+ * Flags in netlink header
+ */
+#define LX_NETLINK_NLM_F_REQUEST 1
+#define LX_NETLINK_NLM_F_MULTI 2
+#define LX_NETLINK_NLM_F_ACK 4
+#define LX_NETLINK_NLM_F_ECHO 8
+#define LX_NETLINK_NLM_F_DUMP_INTR 16
+#define LX_NETLINK_NLM_F_ROOT 0x100
+#define LX_NETLINK_NLM_F_MATCH 0x200
+#define LX_NETLINK_NLM_F_ATOMIC 0x400
+
+/*
+ * Generic message type constants
+ */
+#define LX_NETLINK_NLMSG_NONE 0
+#define LX_NETLINK_NLMSG_NOOP 1
+#define LX_NETLINK_NLMSG_ERROR 2
+#define LX_NETLINK_NLMSG_DONE 3
+#define LX_NETLINK_NLMSG_OVERRUN 4
+
+/*
+ * Protocol constants.
+ */
+#define LX_NETLINK_ROUTE 0
+#define LX_NETLINK_UNUSED 1
+#define LX_NETLINK_USERSOCK 2
+#define LX_NETLINK_FIREWALL 3
+#define LX_NETLINK_SOCK_DIAG 4
+#define LX_NETLINK_NFLOG 5
+#define LX_NETLINK_XFRM 6
+#define LX_NETLINK_SELINUX 7
+#define LX_NETLINK_ISCSI 8
+#define LX_NETLINK_AUDIT 9
+#define LX_NETLINK_FIB_LOOKUP 10
+#define LX_NETLINK_CONNECTOR 11
+#define LX_NETLINK_NETFILTER 12
+#define LX_NETLINK_IP6_FW 13
+#define LX_NETLINK_DNRTMSG 14
+#define LX_NETLINK_KOBJECT_UEVENT 15
+#define LX_NETLINK_GENERIC 16
+#define LX_NETLINK_SCSITRANSPORT 18
+#define LX_NETLINK_ECRYPTFS 19
+#define LX_NETLINK_RDMA 20
+#define LX_NETLINK_CRYPTO 21
+
+/*
+ * rtnetlink(7) attribute-related constants
+ */
+#define LX_NETLINK_NLA_ALIGNTO 4
+
+#define LX_NETLINK_RTM_NEWLINK 16
+#define LX_NETLINK_RTM_DELLINK 17
+#define LX_NETLINK_RTM_GETLINK 18
+#define LX_NETLINK_RTM_SETLINK 19
+#define LX_NETLINK_RTM_NEWADDR 20
+#define LX_NETLINK_RTM_DELADDR 21
+#define LX_NETLINK_RTM_GETADDR 22
+#define LX_NETLINK_RTM_NEWROUTE 24
+#define LX_NETLINK_RTM_DELROUTE 25
+#define LX_NETLINK_RTM_GETROUTE 26
+#define LX_NETLINK_RTM_NEWNEIGH 28
+#define LX_NETLINK_RTM_DELNEIGH 29
+#define LX_NETLINK_RTM_GETNEIGH 30
+#define LX_NETLINK_RTM_NEWRULE 32
+#define LX_NETLINK_RTM_DELRULE 33
+#define LX_NETLINK_RTM_GETRULE 34
+#define LX_NETLINK_RTM_NEWQDISC 36
+#define LX_NETLINK_RTM_DELQDISC 37
+#define LX_NETLINK_RTM_GETQDISC 38
+#define LX_NETLINK_RTM_NEWTCLASS 40
+#define LX_NETLINK_RTM_DELTCLASS 41
+#define LX_NETLINK_RTM_GETTCLASS 42
+#define LX_NETLINK_RTM_NEWTFILTER 44
+#define LX_NETLINK_RTM_DELTFILTER 45
+#define LX_NETLINK_RTM_GETTFILTER 46
+#define LX_NETLINK_RTM_NEWACTION 48
+#define LX_NETLINK_RTM_DELACTION 49
+#define LX_NETLINK_RTM_GETACTION 50
+#define LX_NETLINK_RTM_NEWPREFIX 52
+#define LX_NETLINK_RTM_GETMULTICAST 58
+#define LX_NETLINK_RTM_GETANYCAST 62
+#define LX_NETLINK_RTM_NEWNEIGHTBL 64
+#define LX_NETLINK_RTM_GETNEIGHTBL 66
+#define LX_NETLINK_RTM_SETNEIGHTBL 67
+#define LX_NETLINK_RTM_NEWNDUSEROPT 68
+#define LX_NETLINK_RTM_NEWADDRLABEL 72
+#define LX_NETLINK_RTM_DELADDRLABEL 73
+#define LX_NETLINK_RTM_GETADDRLABEL 74
+#define LX_NETLINK_RTM_GETDCB 78
+#define LX_NETLINK_RTM_SETDCB 79
+#define LX_NETLINK_RTM_NEWNETCONF 80
+#define LX_NETLINK_RTM_GETNETCONF 82
+#define LX_NETLINK_RTM_NEWMDB 84
+#define LX_NETLINK_RTM_DELMDB 85
+#define LX_NETLINK_RTM_GETMDB 86
+#define LX_NETLINK_RTM_MAX 87
+
+/*
+ * rtnetlink(7) attribute constants
+ */
+#define LX_NETLINK_RTA_UNSPEC 0
+#define LX_NETLINK_RTA_DST 1
+#define LX_NETLINK_RTA_SRC 2
+#define LX_NETLINK_RTA_IIF 3
+#define LX_NETLINK_RTA_OIF 4
+#define LX_NETLINK_RTA_GATEWAY 5
+#define LX_NETLINK_RTA_PRIORITY 6
+#define LX_NETLINK_RTA_PREFSRC 7
+#define LX_NETLINK_RTA_METRICS 8
+#define LX_NETLINK_RTA_MULTIPATH 9
+#define LX_NETLINK_RTA_PROTOINFO 10
+#define LX_NETLINK_RTA_FLOW 11
+#define LX_NETLINK_RTA_CACHEINFO 12
+#define LX_NETLINK_RTA_SESSION 13
+#define LX_NETLINK_RTA_MP_ALGO 14
+#define LX_NETLINK_RTA_TABLE 15
+#define LX_NETLINK_RTA_MARK 16
+#define LX_NETLINK_RTA_MFC_STATS 17
+#define LX_NETLINK_MAX_RTA LX_NETLINK_RTA_MFC_STATS
+
+/*
+ * rtnetlink(7) NEWLINK/DELLINK/GETLINK constants
+ */
+#define LX_NETLINK_IFLA_UNSPEC 0
+#define LX_NETLINK_IFLA_ADDRESS 1
+#define LX_NETLINK_IFLA_BROADCAST 2
+#define LX_NETLINK_IFLA_IFNAME 3
+#define LX_NETLINK_IFLA_MTU 4
+#define LX_NETLINK_IFLA_LINK 5
+#define LX_NETLINK_IFLA_QDISC 6
+#define LX_NETLINK_IFLA_STATS 7
+#define LX_NETLINK_IFLA_COST 8
+#define LX_NETLINK_IFLA_PRIORITY 9
+#define LX_NETLINK_IFLA_MASTER 10
+#define LX_NETLINK_IFLA_WIRELESS 11
+#define LX_NETLINK_IFLA_PROTINFO 12
+#define LX_NETLINK_IFLA_TXQLEN 13
+#define LX_NETLINK_IFLA_MAP 14
+#define LX_NETLINK_IFLA_WEIGHT 15
+#define LX_NETLINK_IFLA_OPERSTATE 16
+#define LX_NETLINK_IFLA_LINKMODE 17
+#define LX_NETLINK_IFLA_LINKINFO 18
+#define LX_NETLINK_IFLA_NET_NS_PID 19
+#define LX_NETLINK_IFLA_IFALIAS 20
+#define LX_NETLINK_IFLA_NUM_VF 21
+#define LX_NETLINK_IFLA_VFINFO_LIST 22
+#define LX_NETLINK_IFLA_STATS64 23
+#define LX_NETLINK_IFLA_VF_PORTS 24
+#define LX_NETLINK_IFLA_PORT_SELF 25
+#define LX_NETLINK_IFLA_AF_SPEC 26
+#define LX_NETLINK_IFLA_GROUP 27
+#define LX_NETLINK_IFLA_NET_NS_FD 28
+#define LX_NETLINK_IFLA_EXT_MASK 29
+#define LX_NETLINK_IFLA_PROMISCUITY 30
+#define LX_NETLINK_IFLA_NUM_TX_QUEUES 31
+#define LX_NETLINK_IFLA_NUM_RX_QUEUES 32
+#define LX_NETLINK_IFLA_CARRIER 33
+#define LX_NETLINK_IFLA_PHYS_PORT_ID 34
+#define LX_NETLINK_IFLA_CARRIER_CHANGES 35
+#define LX_NETLINK_IFLA_MAX 36
+
+/*
+ * rtnetlink(7) NEWADDR/DELADDR/GETADDR constants
+ */
+#define LX_NETLINK_IFA_UNSPEC 0
+#define LX_NETLINK_IFA_ADDRESS 1
+#define LX_NETLINK_IFA_LOCAL 2
+#define LX_NETLINK_IFA_LABEL 3
+#define LX_NETLINK_IFA_BROADCAST 4
+#define LX_NETLINK_IFA_ANYCAST 5
+#define LX_NETLINK_IFA_CACHEINFO 6
+#define LX_NETLINK_IFA_MULTICAST 7
+#define LX_NETLINK_IFA_FLAGS 8
+#define LX_NETLINK_IFA_MAX 9
+
+#define LX_NETLINK_IFA_F_SECONDARY 0x01
+#define LX_NETLINK_IFA_F_TEMPORARY LX_NETLINK_IFA_F_SECONDARY
+#define LX_NETLINK_IFA_F_NODAD 0x02
+#define LX_NETLINK_IFA_F_OPTIMISTIC 0x04
+#define LX_NETLINK_IFA_F_DADFAILED 0x08
+#define LX_NETLINK_IFA_F_HOMEADDRESS 0x10
+#define LX_NETLINK_IFA_F_DEPRECATED 0x20
+#define LX_NETLINK_IFA_F_TENTATIVE 0x40
+#define LX_NETLINK_IFA_F_PERMANENT 0x80
+#define LX_NETLINK_IFA_F_MANAGETEMPADDR 0x100
+#define LX_NETLINK_IFA_F_NOPREFIXROUTE 0x200
+
+/*
+ * Linux interface flags.
+ */
+#define LX_IFF_UP (1<<0)
+#define LX_IFF_BROADCAST (1<<1)
+#define LX_IFF_DEBUG (1<<2)
+#define LX_IFF_LOOPBACK (1<<3)
+#define LX_IFF_POINTOPOINT (1<<4)
+#define LX_IFF_NOTRAILERS (1<<5)
+#define LX_IFF_RUNNING (1<<6)
+#define LX_IFF_NOARP (1<<7)
+#define LX_IFF_PROMISC (1<<8)
+#define LX_IFF_ALLMULTI (1<<9)
+#define LX_IFF_MASTER (1<<10)
+#define LX_IFF_SLAVE (1<<11)
+#define LX_IFF_MULTICAST (1<<12)
+#define LX_IFF_PORTSEL (1<<13)
+#define LX_IFF_AUTOMEDIA (1<<14)
+#define LX_IFF_DYNAMIC (1<<15)
+#define LX_IFF_LOWER_UP (1<<16)
+#define LX_IFF_DORMANT (1<<17)
+#define LX_IFF_ECHO (1<<18)
+
+/* rtm_table */
+#define LX_ROUTE_TABLE_MAIN 254
+
+/* rtm_type */
+#define LX_RTN_UNSPEC 0
+#define LX_RTN_UNICAST 1
+#define LX_RTN_LOCAL 2
+#define LX_RTN_BROADCAST 3
+#define LX_RTN_ANYCAST 4
+#define LX_RTN_MULTICAST 5
+#define LX_RTN_BLACKHOLE 6
+#define LX_RTN_UNREACHABLE 7
+#define LX_RTN_PROHIBIT 8
+#define LX_RTN_THROW 9
+#define LX_RTN_NAT 10
+#define LX_RTN_XRESOLVE 11
+
+/* rtm_protocol */
+#define LX_RTPROT_UNSPEC 0
+#define LX_RTPROT_REDIRECT 1 /* From ICMP redir */
+#define LX_RTPROT_KERNEL 2 /* From kernel */
+#define LX_RTPROT_BOOT 3 /* From boot */
+#define LX_RTPROT_STATIC 4 /* From administrator */
+#define LX_RTPROT_NULL 0xff /* Uninitialized */
+
+/* rtm_scope */
+#define LX_RTSCOPE_UNIVERSE 0
+#define LX_RTSCOPE_SITE 200
+#define LX_RTSCOPE_LINK 253
+#define LX_RTSCOPE_HOST 254
+#define LX_RTSCOPE_NOWHERE 255
+
+
+/*
+ * Netlink sockopts
+ */
+#define SOL_LX_NETLINK 270
+
+#define LX_NETLINK_SO_ADD_MEMBERSHIP 1
+#define LX_NETLINK_SO_DROP_MEMBERSHIP 2
+#define LX_NETLINK_SO_PKTINFO 3
+#define LX_NETLINK_SO_BROADCAST_ERROR 4
+#define LX_NETLINK_SO_NO_ENOBUFS 5
+#define LX_NETLINK_SO_RX_RING 6
+#define LX_NETLINK_SO_TX_RING 7
+
+/* Internal socket flags */
+#define LXNLF_RECVUCRED 0x1
+
+/* nlmsg structure macros */
+#define LXNLMSG_ALIGNTO 4
+#define LXNLMSG_ALIGN(len) \
+ (((len) + LXNLMSG_ALIGNTO - 1) & ~(LXNLMSG_ALIGNTO - 1))
+#define LXNLMSG_HDRLEN \
+ ((int)LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t)))
+#define LXNLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define LXNLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define LXNLMSG_DATA(nlh) ((void*)(((char *)nlh) + NLMSG_LENGTH(0)))
+#define LXNLMSG_PAYLOAD(nlh, len) \
+ ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define LXATTR_PAYLOAD(lxa) \
+ ((void*)((caddr_t)(lxa) + sizeof (lx_netlink_attr_t)))
+#define LXATTR_HDRLEN LXNLMSG_ALIGN(sizeof (lx_netlink_attr_t))
+#define LXATTR_LEN(len) (LXATTR_HDRLEN + LXNLMSG_ALIGN(len))
+
+typedef struct lx_netlink_hdr {
+ uint32_t lxnh_len; /* length of message */
+ uint16_t lxnh_type; /* type of message */
+ uint16_t lxnh_flags; /* flags */
+ uint32_t lxnh_seq; /* sequence number */
+ uint32_t lxnh_pid; /* sending pid */
+} lx_netlink_hdr_t;
+
+typedef struct lx_netlink_err {
+ lx_netlink_hdr_t lxne_hdr; /* header */
+ int32_t lxne_errno; /* errno */
+ lx_netlink_hdr_t lxne_failed; /* header of err */
+} lx_netlink_err_t;
+
+typedef struct lx_netlink_attr {
+ uint16_t lxna_len; /* length of attribute */
+ uint16_t lxna_type; /* type of attribute */
+} lx_netlink_attr_t;
+
+typedef struct lx_netlink_ifinfomsg {
+ uint8_t lxnl_ifi_family; /* family: AF_UNSPEC */
+ uint8_t lxnl_ifi__pad;
+ uint16_t lxnl_ifi_type; /* device type */
+ uint32_t lxnl_ifi_index; /* interface index */
+ uint32_t lxnl_ifi_flags; /* device flags */
+ uint32_t lxnl_ifi_change; /* unused; must be -1 */
+} lx_netlink_ifinfomsg_t;
+
+typedef struct lx_netlink_ifaddrmsg {
+ uint8_t lxnl_ifa_family; /* address type */
+ uint8_t lxnl_ifa_prefixlen; /* prefix length of address */
+ uint8_t lxnl_ifa_flags; /* address flags */
+ uint8_t lxnl_ifa_scope; /* address scope */
+ uint8_t lxnl_ifa_index; /* interface index */
+} lx_netlink_ifaddrmsg_t;
+
+typedef struct lx_netlink_rtmsg {
+ uint8_t rtm_family; /* route AF */
+ uint8_t rtm_dst_len; /* destination addr length */
+ uint8_t rtm_src_len; /* source addr length */
+ uint8_t rtm_tos; /* TOS filter */
+ uint8_t rtm_table; /* routing table ID */
+ uint8_t rtm_protocol; /* routing protocol */
+ uint8_t rtm_scope;
+ uint8_t rtm_type;
+ uint32_t rtm_flags;
+} lx_netlink_rtmsg_t;
+
+typedef struct lx_netlink_sockaddr {
+ sa_family_t lxnl_family; /* AF_LX_NETLINK */
+ uint16_t lxnl_pad; /* padding */
+ uint32_t lxnl_port; /* port id */
+ uint32_t lxnl_groups; /* multicast groups mask */
+} lx_netlink_sockaddr_t;
+
+typedef struct lx_netlink_sock {
+ struct lx_netlink_sock *lxns_next; /* list of lx_netlink sockets */
+ sock_upcalls_t *lxns_upcalls; /* pointer to socket upcalls */
+ sock_upper_handle_t lxns_uphandle; /* socket upcall handle */
+ ldi_handle_t lxns_iphandle; /* handle to /dev/ip */
+ ldi_handle_t lxns_ip6handle; /* handle to /dev/ip6 */
+ ldi_handle_t lxns_current; /* current ip handle */
+ int lxns_proto; /* protocol */
+ uint32_t lxns_port; /* port identifier */
+ uint32_t lxns_groups; /* group subscriptions */
+ uint32_t lxns_bufsize; /* buffer size */
+ uint32_t lxns_flags; /* socket flags */
+} lx_netlink_sock_t;
+
+typedef struct lx_netlink_reply {
+ lx_netlink_hdr_t lxnr_hdr; /* header that we're reply to */
+ lx_netlink_sock_t *lxnr_sock; /* socket */
+ uint32_t lxnr_seq; /* sequence number */
+ uint16_t lxnr_type; /* type of reply */
+ mblk_t *lxnr_mp; /* current mblk */
+ mblk_t *lxnr_err; /* error mblk */
+ mblk_t *lxnr_mp1; /* T_UNITDATA_IND mblk */
+ int lxnr_errno; /* errno, if any */
+} lx_netlink_reply_t;
+
+static lx_netlink_sock_t *lx_netlink_head; /* head of lx_netlink sockets */
+static kmutex_t lx_netlink_lock; /* lock to protect state */
+static ldi_ident_t lx_netlink_ldi; /* LDI handle */
+static int lx_netlink_bufsize = 4096; /* default buffer size */
+static int lx_netlink_flowctrld; /* # of times flow controlled */
+
+/*ARGSUSED*/
+static void
+lx_netlink_activate(sock_lower_handle_t handle,
+ sock_upper_handle_t sock_handle, sock_upcalls_t *sock_upcalls,
+ int flags, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ struct sock_proto_props sopp;
+
+ sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT |
+ SOCKOPT_RCVLOWAT | SOCKOPT_MAXADDRLEN | SOCKOPT_MAXPSZ |
+ SOCKOPT_MAXBLK | SOCKOPT_MINPSZ;
+ sopp.sopp_wroff = 0;
+ sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
+ sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
+ sopp.sopp_maxaddrlen = sizeof (struct sockaddr_dl);
+ sopp.sopp_maxpsz = INFPSZ;
+ sopp.sopp_maxblk = INFPSZ;
+ sopp.sopp_minpsz = 0;
+
+ lxsock->lxns_upcalls = sock_upcalls;
+ lxsock->lxns_uphandle = sock_handle;
+
+ sock_upcalls->su_set_proto_props(sock_handle, &sopp);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_setsockopt(sock_lower_handle_t handle, int level,
+ int option_name, const void *optval, socklen_t optlen, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+
+ if (level == SOL_SOCKET && option_name == SO_RECVUCRED) {
+ int *ival;
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ ival = (int *)optval;
+ if (*ival == 0) {
+ lxsock->lxns_flags &= ~LXNLF_RECVUCRED;
+ } else {
+ lxsock->lxns_flags |= LXNLF_RECVUCRED;
+ }
+ return (0);
+ } else if (level == SOL_SOCKET) {
+ /* Punt on the other SOL_SOCKET options */
+ return (0);
+ } else if (level != SOL_LX_NETLINK) {
+ return (EOPNOTSUPP);
+ }
+
+ switch (option_name) {
+ case LX_NETLINK_SO_ADD_MEMBERSHIP:
+ case LX_NETLINK_SO_DROP_MEMBERSHIP:
+ case LX_NETLINK_SO_PKTINFO:
+ case LX_NETLINK_SO_BROADCAST_ERROR:
+ case LX_NETLINK_SO_NO_ENOBUFS:
+ case LX_NETLINK_SO_RX_RING:
+ case LX_NETLINK_SO_TX_RING:
+ /* Blatant lie */
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_bind(sock_lower_handle_t handle, struct sockaddr *name,
+ socklen_t namelen, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)name;
+
+ if (namelen != sizeof (lx_netlink_sockaddr_t) ||
+ lxsa->lxnl_family != AF_LX_NETLINK) {
+ return (EINVAL);
+ }
+
+
+ if (lxsa->lxnl_groups != 0) {
+ /*
+ * On linux, CAP_NET_ADMIN is needed to bind to netlink groups.
+ * This roughly maps to PRIV_SYS_IP_CONFIG.
+ */
+ if (secpolicy_ip_config(cr, B_FALSE) != 0) {
+ return (EACCES);
+ }
+
+ /* Lie about group subscription for now */
+ lxsock->lxns_groups = lxsa->lxnl_groups;
+ }
+
+ /*
+ * Linux netlink uses nl_port to identify distinct netlink sockets.
+ * Binding to an address of nl_port=0 triggers the kernel to
+ * automatically assign a free nl_port identifier. Originally,
+ * consumers of lx_netlink were required to bind with that automatic
+ * address. We now support non-zero values for nl_port although strict
+ * checking to identify conflicts is not performed. Use of the
+ * id_space facility could be a convenient solution, if a need arose.
+ */
+ if (lxsa->lxnl_port == 0) {
+ /*
+ * Because we are not doing conflict detection, there is no
+ * need to expend effort selecting a unique port for automatic
+ * addressing during bind.
+ */
+ lxsock->lxns_port = curproc->p_pid;
+ } else {
+ lxsock->lxns_port = lxsa->lxnl_port;
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getsockname(sock_lower_handle_t handle, struct sockaddr *sa,
+ socklen_t *len, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)sa;
+
+ if (*len < sizeof (lx_netlink_sockaddr_t))
+ return (EINVAL);
+
+ lxsa->lxnl_family = AF_LX_NETLINK;
+ lxsa->lxnl_pad = 0;
+ lxsa->lxnl_port = lxsock->lxns_port;
+ lxsa->lxnl_groups = lxsock->lxns_groups;
+
+ *len = sizeof (lx_netlink_sockaddr_t);
+
+ return (0);
+}
+
+static mblk_t *
+lx_netlink_alloc_mp1(lx_netlink_sock_t *lxsock)
+{
+ mblk_t *mp;
+ size_t size;
+ struct T_unitdata_ind *tunit;
+ lx_netlink_sockaddr_t *lxsa;
+ boolean_t send_ucred;
+
+ /*
+ * Certain netlink clients (such as systemd) will set SO_RECVUCRED
+ * (via the Linux SCM_CREDENTIALS) on the expectation that all replies
+ * will contain credentials passed via cmsg. They require this to
+ * authenticate those messages as having originated in the kernel by
+ * checking uc_pid == 0.
+ */
+ VERIFY(lxsock != NULL);
+ send_ucred = ((lxsock->lxns_flags & LXNLF_RECVUCRED) != 0);
+
+ /*
+ * Message structure:
+ * +----------------------------+
+ * | struct T_unit_data_ind |
+ * +----------------------------+
+ * | lx_netlink_sockaddr_t |
+ * +----------------------------+ -+
+ * | struct cmsghdr (SCM_UCRED) | |
+ * +----------------------------+ +-(optional)
+ * | struct ucred_s (cmsg data) | |
+ * +----------------------------+ -+
+ */
+ size = sizeof (*tunit) + sizeof (*lxsa);
+ if (send_ucred) {
+ size += sizeof (struct cmsghdr) +
+ ROUNDUP_cmsglen(sizeof (struct ucred_s));
+ }
+ mp = allocb(size, 0);
+ if (mp == NULL) {
+ return (NULL);
+ }
+
+ tunit = (struct T_unitdata_ind *)mp->b_rptr;
+ lxsa = (lx_netlink_sockaddr_t *)((caddr_t)tunit + sizeof (*tunit));
+ mp->b_wptr += size;
+
+ mp->b_datap->db_type = M_PROTO;
+ tunit->PRIM_type = T_UNITDATA_IND;
+ tunit->SRC_length = sizeof (*lxsa);
+ tunit->SRC_offset = sizeof (*tunit);
+
+ lxsa->lxnl_family = AF_LX_NETLINK;
+ lxsa->lxnl_port = 0;
+ lxsa->lxnl_groups = 0;
+ lxsa->lxnl_pad = 0;
+
+ if (send_ucred) {
+ struct cmsghdr *cmsg;
+ struct ucred_s *ucred;
+
+ cmsg = (struct cmsghdr *)((caddr_t)lxsa + sizeof (*lxsa));
+ ucred = (struct ucred_s *)CMSG_CONTENT(cmsg);
+ cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*ucred);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_UCRED;
+ bzero(ucred, sizeof (*ucred));
+ ucred->uc_size = sizeof (*ucred);
+ ucred->uc_zoneid = getzoneid();
+
+ tunit->OPT_length = sizeof (*cmsg) +
+ ROUNDUP_cmsglen(sizeof (*ucred));
+ tunit->OPT_offset = tunit->SRC_offset + tunit->SRC_length;
+ } else {
+ tunit->OPT_length = 0;
+ tunit->OPT_offset = 0;
+ }
+
+ return (mp);
+}
+
+static lx_netlink_reply_t *
+lx_netlink_reply(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, uint16_t type)
+{
+ lx_netlink_reply_t *reply;
+ mblk_t *err, *mp1;
+
+ /*
+ * We always allocate an error block to assure that even if subsequent
+ * allocations fail, we can return an error.
+ */
+ if ((err = allocb(sizeof (lx_netlink_err_t), 0)) == NULL)
+ return (NULL);
+
+ if ((mp1 = lx_netlink_alloc_mp1(lxsock)) == NULL) {
+ freeb(err);
+ return (NULL);
+ }
+
+ reply = kmem_zalloc(sizeof (lx_netlink_reply_t), KM_SLEEP);
+ reply->lxnr_err = err;
+ reply->lxnr_sock = lxsock;
+ reply->lxnr_hdr = *hdr;
+ reply->lxnr_type = type;
+ reply->lxnr_mp1 = mp1;
+
+ return (reply);
+}
+
+static void
+lx_netlink_reply_add(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ uint32_t aligned;
+ mblk_t *mp = reply->lxnr_mp;
+
+ if (reply->lxnr_errno)
+ return;
+
+ aligned = LXNLMSG_ALIGN(size);
+ hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+
+ if (hdr->lxnh_len + aligned > lxsock->lxns_bufsize) {
+ reply->lxnr_errno = E2BIG;
+ return;
+ }
+
+ bcopy(payload, mp->b_wptr, size);
+ hdr->lxnh_len += aligned;
+ mp->b_wptr += aligned;
+}
+
+static void
+lx_netlink_reply_msg(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ mblk_t *mp;
+
+ if (reply->lxnr_errno)
+ return;
+
+ VERIFY(reply->lxnr_mp == NULL);
+
+ if ((reply->lxnr_mp = mp = allocb(lxsock->lxns_bufsize, 0)) == NULL) {
+ reply->lxnr_errno = ENOMEM;
+ return;
+ }
+
+ bzero(mp->b_rptr, lxsock->lxns_bufsize);
+ hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ hdr->lxnh_flags = LX_NETLINK_NLM_F_MULTI;
+ hdr->lxnh_len = LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_pid = lxsock->lxns_port;
+
+ mp->b_wptr += LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+
+ if (payload == NULL) {
+ /*
+ * A NULL payload denotes a "done" message.
+ */
+ hdr->lxnh_type = LX_NETLINK_NLMSG_DONE;
+ } else {
+ hdr->lxnh_type = reply->lxnr_type;
+ lx_netlink_reply_add(reply, payload, size);
+ }
+}
+
+static void
+lx_netlink_reply_attr(lx_netlink_reply_t *reply, uint16_t type,
+ void *payload, uint32_t size)
+{
+ lx_netlink_attr_t attr;
+
+ attr.lxna_len = size + sizeof (lx_netlink_attr_t);
+ attr.lxna_type = type;
+
+ lx_netlink_reply_add(reply, &attr, sizeof (attr));
+ lx_netlink_reply_add(reply, payload, size);
+}
+
+static void
+lx_netlink_reply_attr_string(lx_netlink_reply_t *reply,
+ uint16_t type, const char *str)
+{
+ lx_netlink_reply_attr(reply, type, (void *)str, strlen(str) + 1);
+}
+
+static void
+lx_netlink_reply_attr_int32(lx_netlink_reply_t *reply,
+ uint16_t type, int32_t val)
+{
+ int32_t v = val;
+
+ lx_netlink_reply_attr(reply, type, &v, sizeof (int32_t));
+}
+
+static int
+lx_netlink_reply_ioctl(lx_netlink_reply_t *reply, int cmd, void *arg)
+{
+ int rval;
+
+ if (reply->lxnr_errno != 0)
+ return (reply->lxnr_errno);
+
+ if ((rval = ldi_ioctl(reply->lxnr_sock->lxns_current,
+ cmd, (intptr_t)arg, FKIOCTL, kcred, NULL)) != 0) {
+ reply->lxnr_errno = rval;
+ }
+
+ return (rval);
+}
+
+static void
+lx_netlink_reply_sendup(lx_netlink_reply_t *reply, mblk_t *mp, mblk_t *mp1)
+{
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ int error;
+
+ /*
+ * To prevent the stream head from coalescing messages and to indicate
+ * their origin, we send them as T_UNITDATA_IND messages, not as raw
+ * M_DATA.
+ */
+ mp1->b_cont = mp;
+
+ lxsock->lxns_upcalls->su_recv(lxsock->lxns_uphandle, mp1,
+ msgdsize(mp1), 0, &error, NULL);
+
+ if (error != 0)
+ lx_netlink_flowctrld++;
+}
+
+static void
+lx_netlink_reply_send(lx_netlink_reply_t *reply)
+{
+ mblk_t *mp1;
+
+ if (reply->lxnr_errno)
+ return;
+
+ if ((mp1 = lx_netlink_alloc_mp1(reply->lxnr_sock)) == NULL) {
+ reply->lxnr_errno = ENOMEM;
+ return;
+ }
+
+ lx_netlink_reply_sendup(reply, reply->lxnr_mp, mp1);
+ reply->lxnr_mp = NULL;
+}
+
+static void
+lx_netlink_reply_done(lx_netlink_reply_t *reply)
+{
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ mblk_t *mp;
+
+ /*
+ * Denote that we're done via a message with a NULL payload.
+ */
+ lx_netlink_reply_msg(reply, NULL, 0);
+
+ if (reply->lxnr_errno) {
+ /*
+ * If anything failed, we'll send up an error message.
+ */
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_err_t *err;
+
+ if (reply->lxnr_mp != NULL) {
+ freeb(reply->lxnr_mp);
+ reply->lxnr_mp = NULL;
+ }
+
+ mp = reply->lxnr_err;
+ VERIFY(mp != NULL);
+ reply->lxnr_err = NULL;
+ err = (lx_netlink_err_t *)mp->b_rptr;
+ hdr = &err->lxne_hdr;
+ mp->b_wptr += sizeof (lx_netlink_err_t);
+
+ err->lxne_failed = reply->lxnr_hdr;
+ err->lxne_errno = reply->lxnr_errno;
+ hdr->lxnh_type = LX_NETLINK_NLMSG_ERROR;
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_len = sizeof (lx_netlink_err_t);
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_pid = lxsock->lxns_port;
+ } else {
+ mp = reply->lxnr_mp;
+ VERIFY(mp != NULL);
+ reply->lxnr_mp = NULL;
+ }
+
+ lx_netlink_reply_sendup(reply, mp, reply->lxnr_mp1);
+
+ if (reply->lxnr_mp != NULL)
+ freeb(reply->lxnr_mp);
+
+ if (reply->lxnr_err != NULL)
+ freeb(reply->lxnr_err);
+
+ kmem_free(reply, sizeof (lx_netlink_reply_t));
+}
+
+static int
+lx_netlink_reply_error(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, int errno)
+{
+ /*
+ * The type of the message doesn't matter, as we're going to explicitly
+ * set lxnr_errno and therefore send only an error message.
+ */
+ lx_netlink_reply_t *reply = lx_netlink_reply(lxsock, hdr, 0);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ reply->lxnr_errno = errno;
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+static int
+lx_netlink_parse_msg_attrs(mblk_t *mp, void **msgp, unsigned int msg_size,
+ lx_netlink_attr_t **attrp, unsigned int *attr_max)
+{
+ lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ lx_netlink_attr_t *lxa;
+ unsigned char *buf = mp->b_rptr + LXNLMSG_HDRLEN;
+ unsigned int i;
+ uint32_t buf_left = MBLKL(mp) - LXNLMSG_HDRLEN;
+ uint32_t msg_left = hdr->lxnh_len;
+
+ msg_size = LXNLMSG_ALIGN(msg_size);
+ if (msg_size > buf_left || msg_size > msg_left) {
+ return (-1);
+ }
+
+ *msgp = (void *)buf;
+ buf += msg_size;
+ buf_left -= msg_size;
+ msg_left -= msg_size;
+
+ /* Do not bother with attr parsing if not requested */
+ if (attrp == NULL || *attr_max == 0) {
+ return (0);
+ }
+
+ for (i = 0; i < *attr_max; i++) {
+ if (buf_left < LXATTR_HDRLEN || msg_left < LXATTR_HDRLEN) {
+ break;
+ }
+
+ lxa = (lx_netlink_attr_t *)buf;
+ if (lxa->lxna_len > buf_left || lxa->lxna_len > msg_left) {
+ return (-1);
+ }
+
+ attrp[i] = lxa;
+ buf += lxa->lxna_len;
+ buf_left -= lxa->lxna_len;
+ msg_left -= lxa->lxna_len;
+ }
+ *attr_max = i;
+
+ return (0);
+}
+
+/*
+ * Takes an IPv4 address (in network byte order) and returns the address scope.
+ */
+static uint8_t
+lx_ipv4_rtscope(in_addr_t nbo_addr)
+{
+ in_addr_t addr = ntohl(nbo_addr);
+ if ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
+ return (LX_RTSCOPE_HOST);
+ } else if ((addr & IN_AUTOCONF_MASK) == IN_AUTOCONF_NET) {
+ return (LX_RTSCOPE_LINK);
+ } else if ((addr & IN_PRIVATE8_MASK) == IN_PRIVATE8_NET ||
+ (addr & IN_PRIVATE12_MASK) == IN_PRIVATE12_NET ||
+ (addr & IN_PRIVATE16_MASK) == IN_PRIVATE16_NET) {
+ return (LX_RTSCOPE_SITE);
+ } else {
+ return (LX_RTSCOPE_UNIVERSE);
+ }
+}
+
+/*
+ * Takes an IPv6 address and returns the address scope.
+ */
+static uint8_t
+lx_ipv6_rtscope(const in6_addr_t *addr)
+{
+ if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+ return (LX_RTSCOPE_HOST);
+ } else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+ return (LX_RTSCOPE_LINK);
+ } else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+ return (LX_RTSCOPE_SITE);
+ } else {
+ return (LX_RTSCOPE_UNIVERSE);
+ }
+}
+
+static void
+lx_netlink_getlink_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+ lx_netlink_ifinfomsg_t ifi;
+ int i;
+ char if_name[IFNAMSIZ];
+ struct sockaddr_dl *sdl;
+ struct sockaddr hwaddr;
+ int hwaddr_size;
+ boolean_t is_loopback;
+
+ struct {
+ int native;
+ int lx;
+ } flags[] = {
+ { IFF_UP, LX_IFF_UP },
+ { IFF_BROADCAST, LX_IFF_BROADCAST },
+ { IFF_DEBUG, LX_IFF_DEBUG },
+ { IFF_LOOPBACK, LX_IFF_LOOPBACK },
+ { IFF_POINTOPOINT, LX_IFF_POINTOPOINT },
+ { IFF_NOTRAILERS, LX_IFF_NOTRAILERS },
+ { IFF_RUNNING, LX_IFF_RUNNING },
+ { IFF_NOARP, LX_IFF_NOARP },
+ { IFF_PROMISC, LX_IFF_PROMISC },
+ { IFF_ALLMULTI, LX_IFF_ALLMULTI },
+ { IFF_MULTICAST, LX_IFF_MULTICAST },
+ { 0 }
+ };
+
+ /*
+ * illumos interfaces that contain a ':' are non-zero logical
+ * interfaces. We should only emit the name of the zeroth logical
+ * interface, since RTM_GETLINK only expects to see the name of
+ * devices. The addresses of all logical devices will be
+ * returned via an RTM_GETADDR.
+ */
+ if (strchr(lifr->lifr_name, ':') != NULL)
+ return;
+
+ /*
+ * Most of the lx_netlink module is architected to emit information in
+ * an illumos-native manner. Socket syscalls such as getsockname will
+ * not translate fields to values Linux programs would expect since
+ * that conversion is performed by the generic socket emulation.
+ *
+ * This is _not_ true of the actual protocol output from lx_netlink.
+ * Since translating it at the socket layer would be onerous, all
+ * output (including constants and names) is pre-translated to values
+ * valid for Linux.
+ */
+
+ bzero(&ifi, sizeof (ifi));
+ ifi.lxnl_ifi_family = AF_UNSPEC;
+ ifi.lxnl_ifi_change = (uint32_t)-1;
+
+ /* Convert the name to be Linux-friendly */
+ (void) strlcpy(if_name, lifr->lifr_name, IFNAMSIZ);
+ lx_ifname_convert(if_name, LX_IF_FROMNATIVE);
+ is_loopback = (strncmp(if_name, "lo", 2) == 0);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+ return;
+
+ ifi.lxnl_ifi_index = lifr->lifr_index;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+ return;
+
+ for (i = 0; flags[i].native; i++) {
+ if (lifr->lifr_flags & flags[i].native)
+ ifi.lxnl_ifi_flags |= flags[i].lx;
+ }
+
+ /*
+ * Query the datalink address.
+ * The interface type will be included in the outgoing infomsg while
+ * the address itself will be output separately.
+ */
+ sdl = (struct sockaddr_dl *)&lifr->lifr_addr;
+ bzero(sdl, sizeof (*sdl));
+ if (!is_loopback) {
+ (void) lx_netlink_reply_ioctl(reply, SIOCGLIFHWADDR, lifr);
+ } else {
+ /* Simulate an empty hwaddr for loopback */
+ sdl->sdl_type = DL_LOOP;
+ sdl->sdl_alen = ETHERADDRL;
+ }
+ lx_stol_hwaddr(sdl, &hwaddr, &hwaddr_size);
+
+ ifi.lxnl_ifi_type = hwaddr.sa_family;
+ lx_netlink_reply_msg(reply, &ifi, sizeof (lx_netlink_ifinfomsg_t));
+
+ lx_netlink_reply_attr_string(reply, LX_NETLINK_IFLA_IFNAME, if_name);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFMTU, lifr) != 0)
+ return;
+
+ lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_MTU, lifr->lifr_mtu);
+
+ if (hwaddr_size != 0) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_IFLA_ADDRESS,
+ hwaddr.sa_data, hwaddr_size);
+ }
+
+ /* Emulate a txqlen of 1. (0 for loopbacks) */
+ lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_TXQLEN,
+ (is_loopback) ? 0 : 1);
+
+ lx_netlink_reply_send(reply);
+}
+
+static void
+lx_netlink_reply_eachfamily(lx_netlink_reply_t *reply,
+ void (*func)(lx_netlink_reply_t *, struct lifreq *), boolean_t distinct)
+{
+ lx_netlink_sock_t *sock = reply->lxnr_sock;
+ int nlifr, i;
+
+ struct {
+ int family;
+ ldi_handle_t handle;
+ struct lifconf lifc;
+ struct lifnum lifn;
+ } families[] = {
+ { AF_INET, sock->lxns_iphandle },
+ { AF_INET6, sock->lxns_ip6handle },
+ { AF_UNSPEC }
+ }, *family, *check;
+
+ for (family = families; family->family != AF_UNSPEC; family++) {
+ struct lifconf *lifc = &family->lifc;
+ struct lifnum *lifn = &family->lifn;
+
+ lifn->lifn_family = family->family;
+ sock->lxns_current = family->handle;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFNUM, lifn) != 0)
+ break;
+
+ lifc->lifc_family = lifn->lifn_family;
+ lifc->lifc_flags = 0;
+ lifc->lifc_len = lifn->lifn_count * sizeof (struct lifreq);
+ if (lifn->lifn_count == 0) {
+ lifc->lifc_buf = NULL;
+ continue;
+ }
+ lifc->lifc_buf = kmem_alloc(lifc->lifc_len, KM_SLEEP);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFCONF, lifc) != 0)
+ break;
+
+ nlifr = lifc->lifc_len / sizeof (lifc->lifc_req[0]);
+
+ for (i = 0; i < nlifr; i++) {
+ if (!distinct) {
+ func(reply, &lifc->lifc_req[i]);
+ continue;
+ }
+
+ /*
+ * If we have been asked to provide each interface
+ * exactly once, we need to (annoyingly) check this
+ * name against others that we've already processed for
+ * other families. Yes, this is quadratic time -- but
+ * the number of interfaces per family is expected to
+ * be very small.
+ */
+ for (check = families; check != family; check++) {
+ struct lifconf *clifc = &check->lifc;
+ int cnlifr = clifc->lifc_len /
+ sizeof (clifc->lifc_req[0]), j;
+ char *nm = lifc->lifc_req[i].lifr_name, *cnm;
+
+ for (j = 0; j < cnlifr; j++) {
+ cnm = clifc->lifc_req[j].lifr_name;
+
+ if (strcmp(nm, cnm) == 0)
+ break;
+ }
+
+ if (j != cnlifr)
+ break;
+ }
+
+ if (check != family)
+ continue;
+
+ func(reply, &lifc->lifc_req[i]);
+ }
+ }
+
+ for (family = families; family->family != AF_UNSPEC; family++) {
+ struct lifconf *lifc = &family->lifc;
+
+ if (lifc->lifc_buf != NULL)
+ kmem_free(lifc->lifc_buf, lifc->lifc_len);
+ }
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getlink(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWLINK);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_eachfamily(reply, lx_netlink_getlink_lifreq, B_TRUE);
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+static void
+lx_netlink_getaddr_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+ lx_netlink_ifaddrmsg_t ifa;
+
+ bzero(&ifa, sizeof (ifa));
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+ return;
+
+ ifa.lxnl_ifa_index = lifr->lifr_index;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+ return;
+
+ /*
+ * Don't report on-link subnets
+ */
+ if ((lifr->lifr_flags & IFF_NOLOCAL) != 0)
+ return;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFSUBNET, lifr) != 0)
+ return;
+
+ ifa.lxnl_ifa_prefixlen = lifr->lifr_addrlen;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFADDR, lifr) != 0)
+ return;
+
+ if (lifr->lifr_addr.ss_family == AF_INET) {
+ struct sockaddr_in *sin;
+
+ ifa.lxnl_ifa_family = LX_AF_INET;
+
+ sin = (struct sockaddr_in *)&lifr->lifr_addr;
+ ifa.lxnl_ifa_scope = lx_ipv4_rtscope(
+ sin->sin_addr.s_addr);
+
+ lx_netlink_reply_msg(reply, &ifa,
+ sizeof (lx_netlink_ifaddrmsg_t));
+
+ lx_netlink_reply_attr_int32(reply,
+ LX_NETLINK_IFA_ADDRESS, sin->sin_addr.s_addr);
+ } else {
+ struct sockaddr_in6 *sin;
+
+ ifa.lxnl_ifa_family = LX_AF_INET6;
+
+ sin = (struct sockaddr_in6 *)&lifr->lifr_addr;
+ ifa.lxnl_ifa_scope = lx_ipv6_rtscope(&sin->sin6_addr);
+
+ lx_netlink_reply_msg(reply, &ifa,
+ sizeof (lx_netlink_ifaddrmsg_t));
+
+ lx_netlink_reply_attr(reply, LX_NETLINK_IFA_ADDRESS,
+ &sin->sin6_addr, sizeof (sin->sin6_addr));
+ }
+
+ lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getaddr(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWADDR);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_eachfamily(reply, lx_netlink_getaddr_lifreq, B_FALSE);
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+struct lx_getroute_ctx {
+ lx_netlink_reply_t *lgrtctx_reply;
+ lx_netlink_rtmsg_t *lgrtctx_rtmsg;
+ lx_netlink_attr_t *lgrtctx_attrs[LX_NETLINK_MAX_RTA];
+ unsigned int lgrtctx_max_attr;
+ lx_netlink_attr_t *lgrtctx_rtadst;
+};
+
+static void
+lx_netlink_getroute_ipv4(ire_t *ire, struct lx_getroute_ctx *ctx)
+{
+ lx_netlink_reply_t *reply = ctx->lgrtctx_reply;
+ lx_netlink_rtmsg_t *rtmsg = ctx->lgrtctx_rtmsg;
+ lx_netlink_attr_t *rtadst = ctx->lgrtctx_rtadst;
+ lx_netlink_rtmsg_t res;
+ ill_t *ill = NULL;
+
+ /* Certain IREs are too specific for netlink */
+ if ((ire->ire_type & (IRE_BROADCAST | IRE_MULTICAST | IRE_NOROUTE |
+ IRE_LOOPBACK | IRE_LOCAL)) != 0 || ire->ire_testhidden != 0) {
+ return;
+ }
+ /*
+ * When listing routes, CLONE entries are undesired.
+ * They are required for 'ip route get' on a local address.
+ */
+ if (rtmsg->rtm_dst_len == 0 && (ire->ire_type & IRE_IF_CLONE) != 0) {
+ return;
+ }
+
+ bzero(&res, sizeof (res));
+ res.rtm_family = LX_AF_INET;
+ res.rtm_table = LX_ROUTE_TABLE_MAIN;
+ res.rtm_type = LX_RTN_UNICAST;
+ res.rtm_dst_len = ire->ire_masklen;
+
+ if (ire->ire_type & (IRE_IF_NORESOLVER|IRE_IF_RESOLVER)) {
+ /* Interface-local networks considered kernel-created */
+ res.rtm_protocol = LX_RTPROT_KERNEL;
+ res.rtm_scope = LX_RTSCOPE_LINK;
+ } else if (ire->ire_flags & RTF_STATIC) {
+ res.rtm_protocol = LX_RTPROT_STATIC;
+ }
+
+ if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+ /*
+ * SpecifY single-destination route.
+ * RTA_DST details will be added later
+ */
+ res.rtm_dst_len = rtmsg->rtm_dst_len;
+ }
+
+
+ lx_netlink_reply_msg(reply, &res, sizeof (res));
+
+ if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+ /* Add RTA_DST details for single-destination route. */
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+ LXATTR_PAYLOAD(rtadst), sizeof (ipaddr_t));
+ } else if (ire->ire_masklen != 0) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+ &ire->ire_addr, sizeof (ire->ire_addr));
+ }
+
+ if (ire->ire_ill != NULL) {
+ ill = ire->ire_ill;
+ } else if (ire->ire_dep_parent != NULL) {
+ ill = ire->ire_dep_parent->ire_ill;
+ }
+
+ if (ill != NULL) {
+ uint32_t ifindex, addr_src;
+
+ ifindex = ill->ill_phyint->phyint_ifindex;
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_OIF,
+ &ifindex, sizeof (ifindex));
+
+ addr_src = ill->ill_ipif->ipif_lcl_addr;
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_PREFSRC,
+ &addr_src, sizeof (addr_src));
+ }
+
+ if (ire->ire_flags & RTF_GATEWAY) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_GATEWAY,
+ &ire->ire_gateway_addr, sizeof (ire->ire_gateway_addr));
+ }
+
+ lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getroute(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr,
+ mblk_t *mp)
+{
+ struct lx_getroute_ctx ctx;
+ lx_netlink_reply_t *reply;
+ lx_netlink_rtmsg_t rtmsg, *rtmsgp;
+ int rtmsg_size = sizeof (rtmsg);
+ netstack_t *ns;
+ int i;
+
+ bzero(&ctx, sizeof (ctx));
+ ctx.lgrtctx_max_attr = LX_NETLINK_MAX_RTA;
+
+ if (lx_netlink_parse_msg_attrs(mp, (void **)&rtmsgp,
+ rtmsg_size, ctx.lgrtctx_attrs, &ctx.lgrtctx_max_attr) != 0) {
+ return (EPROTO);
+ }
+
+ /*
+ * Older version of libnetlink send a truncated rtmsg struct for
+ * certain RTM_GETROUTE queries. We must detect this condition and
+ * truncate our input to prevent later confusion.
+ */
+ if (curproc->p_zone->zone_brand == &lx_brand &&
+ lx_kern_release_cmp(curproc->p_zone, "2.6.32") <= 0 &&
+ rtmsgp->rtm_dst_len == 0) {
+ rtmsg_size = sizeof (rtmsg.rtm_family);
+ }
+ bzero(&rtmsg, sizeof (rtmsg));
+ bcopy(rtmsgp, &rtmsg, rtmsg_size);
+ ctx.lgrtctx_rtmsg = &rtmsg;
+
+ /* If RTA_DST was passed, it effects later decisions */
+ for (i = 0; i < ctx.lgrtctx_max_attr; i++) {
+ lx_netlink_attr_t *attr = ctx.lgrtctx_attrs[i];
+
+ if (attr->lxna_type == LX_NETLINK_RTA_DST &&
+ attr->lxna_len == LXATTR_LEN(sizeof (ipaddr_t))) {
+ ctx.lgrtctx_rtadst = attr;
+ break;
+ }
+ }
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWROUTE);
+ if (reply == NULL) {
+ return (ENOMEM);
+ }
+ ctx.lgrtctx_reply = reply;
+
+ /* Do not report anything outside the main table */
+ if (rtmsg.rtm_table != LX_ROUTE_TABLE_MAIN &&
+ rtmsg.rtm_table != 0) {
+ lx_netlink_reply_done(reply);
+ return (0);
+ }
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lx_netlink_reply_done(reply);
+ return (0);
+ }
+ if (rtmsg.rtm_family == LX_AF_INET || rtmsg.rtm_family == 0) {
+ if (rtmsg.rtm_dst_len == 0x20 && ctx.lgrtctx_rtadst != NULL) {
+ /* resolve route for host */
+ ipaddr_t *dst = LXATTR_PAYLOAD(ctx.lgrtctx_rtadst);
+ ire_t *ire_dst;
+
+ ire_dst = ire_route_recursive_dstonly_v4(*dst, 0, 0,
+ ns->netstack_ip);
+ lx_netlink_getroute_ipv4(ire_dst, &ctx);
+ ire_refrele(ire_dst);
+ } else {
+ /* get route listing */
+ ire_walk_v4(&lx_netlink_getroute_ipv4, &ctx, ALL_ZONES,
+ ns->netstack_ip);
+ }
+ }
+ if (rtmsg.rtm_family == LX_AF_INET6) {
+ /* punt on ipv6 for now */
+ netstack_rele(ns);
+ lx_netlink_reply_done(reply);
+ return (EPROTO);
+ }
+ netstack_rele(ns);
+
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+lx_netlink_audit(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ /*
+ * For all auditing messages, we return ECONNREFUSED, which seems to
+ * keep user-level auditing happy. (Or at least, non-suicidal.)
+ */
+ return (ECONNREFUSED);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_kobject_uevent(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ /*
+ * For udev, we just silently accept all writes and never actually
+ * reply with anything -- which appears to be sufficient for things
+ * to work.
+ */
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_send(sock_lower_handle_t handle, mblk_t *mp,
+ struct nmsghdr *msg, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ int i, rval;
+
+ static struct {
+ int proto;
+ uint16_t type;
+ int (*func)(lx_netlink_sock_t *, lx_netlink_hdr_t *, mblk_t *);
+ } handlers[] = {
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETLINK, lx_netlink_getlink },
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETADDR, lx_netlink_getaddr },
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETROUTE, lx_netlink_getroute },
+ { LX_NETLINK_AUDIT,
+ LX_NETLINK_NLMSG_NONE, lx_netlink_audit },
+ { LX_NETLINK_KOBJECT_UEVENT,
+ LX_NETLINK_NLMSG_NONE, lx_netlink_kobject_uevent },
+ { LX_NETLINK_NLMSG_NOOP, LX_NETLINK_NLMSG_NONE, NULL }
+ };
+
+ if (DB_TYPE(mp) != M_DATA || MBLKL(mp) < sizeof (lx_netlink_hdr_t)) {
+ freemsg(mp);
+ return (EPROTO);
+ }
+
+ for (i = 0; handlers[i].func != NULL; i++) {
+ if (lxsock->lxns_proto != handlers[i].proto)
+ continue;
+
+ if (handlers[i].type != LX_NETLINK_NLMSG_NONE &&
+ hdr->lxnh_type != handlers[i].type)
+ continue;
+
+ rval = handlers[i].func(lxsock, hdr, mp);
+ freemsg(mp);
+
+ return (rval);
+ }
+
+ /*
+ * An unrecognized message. We will bounce up an EOPNOTSUPP reply.
+ */
+ rval = lx_netlink_reply_error(lxsock, hdr, EOPNOTSUPP);
+ freemsg(mp);
+
+ return (rval);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_close(sock_lower_handle_t handle, int flags, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle, *sock, **prev;
+
+ mutex_enter(&lx_netlink_lock);
+
+ prev = &lx_netlink_head;
+
+ for (sock = *prev; sock != lxsock; sock = sock->lxns_next)
+ prev = &sock->lxns_next;
+
+ *prev = sock->lxns_next;
+
+ mutex_exit(&lx_netlink_lock);
+
+ (void) ldi_close(lxsock->lxns_iphandle, FREAD, kcred);
+ (void) ldi_close(lxsock->lxns_ip6handle, FREAD, kcred);
+ kmem_free(lxsock, sizeof (lx_netlink_sock_t));
+
+ return (0);
+}
+
+static sock_downcalls_t sock_lx_netlink_downcalls = {
+ lx_netlink_activate, /* sd_activate */
+ sock_accept_notsupp, /* sd_accept */
+ lx_netlink_bind, /* sd_bind */
+ sock_listen_notsupp, /* sd_listen */
+ sock_connect_notsupp, /* sd_connect */
+ sock_getpeername_notsupp, /* sd_getpeername */
+ lx_netlink_getsockname, /* sd_getsockname */
+ sock_getsockopt_notsupp, /* sd_getsockopt */
+ lx_netlink_setsockopt, /* sd_setsockopt */
+ lx_netlink_send, /* sd_send */
+ NULL, /* sd_send_uio */
+ NULL, /* sd_recv_uio */
+ NULL, /* sd_poll */
+ sock_shutdown_notsupp, /* sd_shutdown */
+ sock_clr_flowctrl_notsupp, /* sd_setflowctrl */
+ sock_ioctl_notsupp, /* sd_ioctl */
+ lx_netlink_close /* sd_close */
+};
+
+/*ARGSUSED*/
+static sock_lower_handle_t
+lx_netlink_create(int family, int type, int proto,
+ sock_downcalls_t **sock_downcalls, uint_t *smodep, int *errorp,
+ int flags, cred_t *credp)
+{
+ lx_netlink_sock_t *lxsock;
+ ldi_handle_t handle, handle6;
+ cred_t *kcred = zone_kcred();
+ int err;
+
+ if (family != AF_LX_NETLINK ||
+ (type != SOCK_DGRAM && type != SOCK_RAW)) {
+ *errorp = EPROTONOSUPPORT;
+ return (NULL);
+ }
+
+ switch (proto) {
+ case LX_NETLINK_ROUTE:
+ case LX_NETLINK_AUDIT:
+ case LX_NETLINK_KOBJECT_UEVENT:
+ break;
+
+ default:
+ *errorp = EPROTONOSUPPORT;
+ return (NULL);
+ }
+
+ if ((err = ldi_open_by_name(DEV_IP, FREAD, kcred,
+ &handle, lx_netlink_ldi)) != 0) {
+ *errorp = err;
+ return (NULL);
+ }
+
+ if ((err = ldi_open_by_name(DEV_IP6, FREAD, kcred,
+ &handle6, lx_netlink_ldi)) != 0) {
+ (void) ldi_close(handle, FREAD, kcred);
+ *errorp = err;
+ return (NULL);
+ }
+
+ *sock_downcalls = &sock_lx_netlink_downcalls;
+ *smodep = SM_ATOMIC;
+
+ lxsock = kmem_zalloc(sizeof (lx_netlink_sock_t), KM_SLEEP);
+ lxsock->lxns_iphandle = handle;
+ lxsock->lxns_ip6handle = handle6;
+ lxsock->lxns_bufsize = lx_netlink_bufsize;
+ lxsock->lxns_proto = proto;
+
+ mutex_enter(&lx_netlink_lock);
+
+ lxsock->lxns_next = lx_netlink_head;
+ lx_netlink_head = lxsock;
+
+ mutex_exit(&lx_netlink_lock);
+
+ return ((sock_lower_handle_t)lxsock);
+}
+
+static void
+lx_netlink_init(void)
+{
+ major_t major = mod_name_to_major("ip");
+ int err;
+
+ VERIFY(major != DDI_MAJOR_T_NONE);
+
+ err = ldi_ident_from_major(major, &lx_netlink_ldi);
+ VERIFY(err == 0);
+}
+
+static void
+lx_netlink_fini(void)
+{
+ ldi_ident_release(lx_netlink_ldi);
+}
+
+static smod_reg_t sinfo = {
+ SOCKMOD_VERSION,
+ "lx_netlink",
+ SOCK_UC_VERSION,
+ SOCK_DC_VERSION,
+ lx_netlink_create,
+ NULL
+};
+
+/* modldrv structure */
+static struct modlsockmod sockmod = {
+ &mod_sockmodops, "AF_LX_NETLINK socket module", &sinfo
+};
+
+/* modlinkage structure */
+static struct modlinkage ml = {
+ MODREV_1,
+ &sockmod,
+ NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ lx_netlink_init();
+
+ if ((err = mod_install(&ml)) != 0)
+ lx_netlink_fini();
+
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&ml, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err = 0;
+
+ mutex_enter(&lx_netlink_lock);
+
+ if (lx_netlink_head != NULL)
+ err = EBUSY;
+
+ mutex_exit(&lx_netlink_lock);
+
+ if (err == 0 && (err = mod_remove(&ml)) == 0)
+ lx_netlink_fini();
+
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/io/lx_ptm.c b/usr/src/uts/common/brand/lx/io/lx_ptm.c
new file mode 100644
index 0000000000..23e0c6f459
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_ptm.c
@@ -0,0 +1,1188 @@
+/*
+ * 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 2016 Joyent, Inc. All rights reserved.
+ */
+
+
+/*
+ * This driver attempts to emulate some of the the behaviors of
+ * Linux terminal devices (/dev/ptmx and /dev/pts/[0-9][0-9]*) on Solaris
+ *
+ * It does this by layering over the /dev/ptmx device and intercepting
+ * opens to it.
+ *
+ * This driver makes the following assumptions about the way the ptm/pts
+ * drivers on Solaris work:
+ *
+ * - all opens of the /dev/ptmx device node return a unique dev_t.
+ *
+ * - the dev_t minor node value for each open ptm instance corrospondes
+ * to it's associated slave terminal device number. ie. the path to
+ * the slave terminal device associated with an open ptm instance
+ * who's dev_t minor node vaue is 5, is /dev/pts/5.
+ *
+ * - the ptm driver always allocates the lowest numbered slave terminal
+ * device possible.
+ */
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/devops.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/kstr.h>
+#include <sys/lx_ptm.h>
+#include <sys/modctl.h>
+#include <sys/pathname.h>
+#include <sys/ptms.h>
+#include <sys/ptyvar.h>
+#include <sys/stat.h>
+#include <sys/stropts.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/sdt.h>
+
+#define LP_PTM_PATH "/dev/ptmx"
+#define LP_PTS_PATH "/dev/pts/"
+#define LP_PTS_DRV_NAME "pts"
+#define LP_PTS_USEC_DELAY (5 * 1000) /* 5 ms */
+#define LP_PTS_USEC_DELAY_MAX (5 * MILLISEC) /* 5 ms */
+
+/*
+ * this driver is layered on top of the ptm driver. we'd like to
+ * make this drivers minor name space a mirror of the ptm drivers
+ * namespace, but we can't actually do this. the reason is that the
+ * ptm driver is opened via the clone driver. there for no minor nodes
+ * of the ptm driver are actually accessible via the filesystem.
+ * since we're not a streams device we can't be opened by the clone
+ * driver. there for we need to have at least minor node accessible
+ * via the filesystem so that consumers can open it. we use the device
+ * node with a minor number of 0 for this purpose. what this means is
+ * that minor node 0 can't be used to map ptm minor node 0. since this
+ * minor node is now reserved we need to shift our ptm minor node
+ * mappings by one. ie. a ptm minor node with a value of 0 will
+ * corrospond to our minor node with a value of 1. these mappings are
+ * managed with the following macros.
+ */
+#define DEVT_TO_INDEX(x) LX_PTM_DEV_TO_PTS(x)
+#define INDEX_TO_MINOR(x) ((x) + 1)
+
+/*
+ * grow our layered handle array by the same size increment that the ptm
+ * driver uses to grow the pty device space - PTY_MAXDELTA
+ */
+#define LP_PTY_INC 128
+
+/*
+ * lx_ptm_ops contains state information about outstanding operations on the
+ * underlying master terminal device. Currently we only track information
+ * for read operations.
+ *
+ * Note that this data has not been rolled directly into the lx_ptm_handle
+ * structure because we can't put mutex's of condition variables into
+ * lx_ptm_handle structure. The reason is that the array of lx_ptm_handle
+ * structures linked to from the global lx_ptm state can be resized
+ * dynamically, and when it's resized, the new array is at a different
+ * memory location and the old array memory is discarded. Mutexs and cvs
+ * are accessed based off their address, so if this array was re-sized while
+ * there were outstanding operations on any mutexs or cvs in the array
+ * then the system would tip over. In the future the lx_ptm_handle structure
+ * array should probably be replaced with either an array of pointers to
+ * lx_ptm_handle structures or some other kind of data structure containing
+ * pointers to lx_ptm_handle structures. Then the lx_ptm_ops structure
+ * could be folded directly into the lx_ptm_handle structures. (This will
+ * also require the definition of a new locking mechanism to protect the
+ * contents of lx_ptm_handle structures.)
+ */
+typedef struct lx_ptm_ops {
+ int lpo_rops;
+ kcondvar_t lpo_rops_cv;
+ kmutex_t lpo_rops_lock;
+} lx_ptm_ops_t;
+
+/*
+ * Every open of the master terminal device in a zone results in a new
+ * lx_ptm_handle handle allocation. These handles are stored in an array
+ * hanging off the lx_ptm_state structure.
+ */
+typedef struct lx_ptm_handle {
+ /* Device handle to the underlying real /dev/ptmx master terminal. */
+ ldi_handle_t lph_handle;
+
+ /* Flag to indicate if TIOCPKT mode has been enabled. */
+ int lph_pktio;
+
+ /* Number of times the slave device has been opened/closed. */
+ int lph_eofed;
+
+ /* Callback handler in the ptm driver to check if slave is open. */
+ ptmptsopencb_t lph_ppocb;
+
+ /* Pointer to state for operations on underlying device. */
+ lx_ptm_ops_t *lph_lpo;
+} lx_ptm_handle_t;
+
+/*
+ * Global state for the lx_ptm driver.
+ */
+typedef struct lx_ptm_state {
+ /* lx_ptm device devinfo pointer */
+ dev_info_t *lps_dip;
+
+ /* LDI ident used to open underlying real /dev/ptmx master terminals. */
+ ldi_ident_t lps_li;
+
+ /* pts drivers major number */
+ major_t lps_pts_major;
+
+ /* rw lock used to manage access and growth of lps_lh_array */
+ krwlock_t lps_lh_rwlock;
+
+ /* number of elements in lps_lh_array */
+ uint_t lps_lh_count;
+
+ /* Array of handles to underlying real /dev/ptmx master terminals. */
+ lx_ptm_handle_t *lps_lh_array;
+} lx_ptm_state_t;
+
+/* Pointer to the lx_ptm global state structure. */
+static lx_ptm_state_t lps;
+
+/*
+ * List of modules to be autopushed onto slave terminal devices when they
+ * are opened in an lx branded zone.
+ */
+static char *lx_pts_mods[] = {
+ "ptem",
+ "ldterm",
+ "ttcompat",
+ NULL
+};
+
+static void
+lx_ptm_lh_grow(uint_t index)
+{
+ uint_t new_lh_count, old_lh_count;
+ lx_ptm_handle_t *new_lh_array, *old_lh_array;
+
+ /*
+ * allocate a new array. we drop the rw lock on the array so that
+ * readers can still access devices in case our memory allocation
+ * blocks.
+ */
+ new_lh_count = MAX(lps.lps_lh_count + LP_PTY_INC, index + 1);
+ new_lh_array =
+ kmem_zalloc(sizeof (lx_ptm_handle_t) * new_lh_count, KM_SLEEP);
+
+ /*
+ * double check that we still actually need to increase the size
+ * of the array
+ */
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+ if (index < lps.lps_lh_count) {
+ /* someone beat us to it so there's nothing more to do */
+ rw_exit(&lps.lps_lh_rwlock);
+ kmem_free(new_lh_array,
+ sizeof (lx_ptm_handle_t) * new_lh_count);
+ return;
+ }
+
+ /* copy the existing data into the new array */
+ ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+ ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+ if (lps.lps_lh_count != 0) {
+ bcopy(lps.lps_lh_array, new_lh_array,
+ sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+ }
+
+ /* save info on the old array */
+ old_lh_array = lps.lps_lh_array;
+ old_lh_count = lps.lps_lh_count;
+
+ /* install the new array */
+ lps.lps_lh_array = new_lh_array;
+ lps.lps_lh_count = new_lh_count;
+
+ rw_exit(&lps.lps_lh_rwlock);
+
+ /* free the old array */
+ if (old_lh_array != NULL) {
+ kmem_free(old_lh_array,
+ sizeof (lx_ptm_handle_t) * old_lh_count);
+ }
+}
+
+static void
+lx_ptm_lh_insert(uint_t index, ldi_handle_t lh)
+{
+ lx_ptm_ops_t *lpo;
+
+ ASSERT(lh != NULL);
+
+ /* Allocate and initialize the ops structure */
+ lpo = kmem_zalloc(sizeof (lx_ptm_ops_t), KM_SLEEP);
+ mutex_init(&lpo->lpo_rops_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&lpo->lpo_rops_cv, NULL, CV_DEFAULT, NULL);
+
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ /* check if we need to grow the size of the layered handle array */
+ if (index >= lps.lps_lh_count) {
+ rw_exit(&lps.lps_lh_rwlock);
+ lx_ptm_lh_grow(index);
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+ }
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle == NULL);
+ ASSERT(lps.lps_lh_array[index].lph_pktio == 0);
+ ASSERT(lps.lps_lh_array[index].lph_eofed == 0);
+ ASSERT(lps.lps_lh_array[index].lph_lpo == NULL);
+
+ /* insert the new handle and return */
+ lps.lps_lh_array[index].lph_handle = lh;
+ lps.lps_lh_array[index].lph_pktio = 0;
+ lps.lps_lh_array[index].lph_eofed = 0;
+ lps.lps_lh_array[index].lph_lpo = lpo;
+
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_remove(uint_t index)
+{
+ ldi_handle_t lh;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+ ASSERT(lps.lps_lh_array[index].lph_lpo->lpo_rops == 0);
+ ASSERT(!MUTEX_HELD(&lps.lps_lh_array[index].lph_lpo->lpo_rops_lock));
+
+ /* free the write handle */
+ kmem_free(lps.lps_lh_array[index].lph_lpo, sizeof (lx_ptm_ops_t));
+ lps.lps_lh_array[index].lph_lpo = NULL;
+
+ /* remove the handle and return it */
+ lh = lps.lps_lh_array[index].lph_handle;
+ lps.lps_lh_array[index].lph_handle = NULL;
+ lps.lps_lh_array[index].lph_pktio = 0;
+ lps.lps_lh_array[index].lph_eofed = 0;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lh);
+}
+
+static void
+lx_ptm_lh_get_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ *ppocb = lps.lps_lh_array[index].lph_ppocb;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static void
+lx_ptm_lh_set_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ lps.lps_lh_array[index].lph_ppocb = *ppocb;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_lookup(uint_t index)
+{
+ ldi_handle_t lh;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the handle */
+ lh = lps.lps_lh_array[index].lph_handle;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lh);
+}
+
+static lx_ptm_ops_t *
+lx_ptm_lpo_lookup(uint_t index)
+{
+ lx_ptm_ops_t *lpo;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_lpo != NULL);
+
+ /* return the handle */
+ lpo = lps.lps_lh_array[index].lph_lpo;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lpo);
+}
+
+static int
+lx_ptm_lh_pktio_get(uint_t index)
+{
+ int pktio;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the pktio state */
+ pktio = lps.lps_lh_array[index].lph_pktio;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (pktio);
+}
+
+static void
+lx_ptm_lh_pktio_set(uint_t index, int pktio)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* set the pktio state */
+ lps.lps_lh_array[index].lph_pktio = pktio;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_lh_eofed_get(uint_t index)
+{
+ int eofed;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the eofed state */
+ eofed = lps.lps_lh_array[index].lph_eofed;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (eofed);
+}
+
+static void
+lx_ptm_lh_eofed_set(uint_t index)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* set the eofed state */
+ lps.lps_lh_array[index].lph_eofed++;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_read_start(dev_t dev)
+{
+ lx_ptm_ops_t *lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+ mutex_enter(&lpo->lpo_rops_lock);
+ ASSERT(lpo->lpo_rops >= 0);
+
+ /* Wait for other read operations to finish */
+ while (lpo->lpo_rops != 0) {
+ if (cv_wait_sig(&lpo->lpo_rops_cv, &lpo->lpo_rops_lock) == 0) {
+ mutex_exit(&lpo->lpo_rops_lock);
+ return (-1);
+ }
+ }
+
+ /* Start a read operation */
+ VERIFY(++lpo->lpo_rops == 1);
+ mutex_exit(&lpo->lpo_rops_lock);
+ return (0);
+}
+
+static void
+lx_ptm_read_end(dev_t dev)
+{
+ lx_ptm_ops_t *lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+ mutex_enter(&lpo->lpo_rops_lock);
+ ASSERT(lpo->lpo_rops >= 0);
+
+ /* End a read operation */
+ VERIFY(--lpo->lpo_rops == 0);
+ cv_signal(&lpo->lpo_rops_cv);
+
+ mutex_exit(&lpo->lpo_rops_lock);
+}
+
+static int
+lx_ptm_pts_isopen(dev_t dev)
+{
+ ptmptsopencb_t ppocb;
+
+ lx_ptm_lh_get_ppocb(DEVT_TO_INDEX(dev), &ppocb);
+ return (ppocb.ppocb_func(ppocb.ppocb_arg));
+}
+
+static void
+lx_ptm_eof_read(ldi_handle_t lh)
+{
+ struct uio uio;
+ iovec_t iov;
+ char junk[1];
+
+ /*
+ * We can remove any EOF message from the head of the stream by
+ * doing a zero byte read from the stream.
+ */
+ iov.iov_len = 0;
+ iov.iov_base = junk;
+ uio.uio_iovcnt = 1;
+ uio.uio_iov = &iov;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_fmode = 0;
+ uio.uio_extflg = 0;
+ uio.uio_llimit = MAXOFFSET_T;
+ (void) ldi_read(lh, &uio, kcred);
+}
+
+static int
+lx_ptm_eof_drop_1(dev_t dev, int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err, msg_size, msg_count;
+
+ *rvalp = 0;
+
+ /*
+ * Check if there is an EOF message (represented by a zero length
+ * data message) at the head of the stream. Note that the
+ * I_NREAD ioctl is a streams framework ioctl so it will succeed
+ * even if there have been previous write errors on this stream.
+ */
+ if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+ FKIOCTL, kcred, &msg_count)) != 0)
+ return (err);
+
+ if ((msg_count == 0) || (msg_size != 0)) {
+ /* No EOF message found */
+ return (0);
+ }
+
+ /* Record the fact that the slave device has been closed. */
+ lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+
+ /* drop the EOF */
+ lx_ptm_eof_read(lh);
+ *rvalp = 1;
+ return (0);
+}
+
+static int
+lx_ptm_eof_drop(dev_t dev, int *rvalp)
+{
+ int rval, err;
+
+ if (rvalp != NULL)
+ *rvalp = 0;
+ for (;;) {
+ if ((err = lx_ptm_eof_drop_1(dev, &rval)) != 0)
+ return (err);
+ if (rval == 0)
+ return (0);
+ if (rvalp != NULL)
+ *rvalp = 1;
+ }
+}
+
+static int
+lx_ptm_data_check(dev_t dev, int ignore_eof, int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ *rvalp = 0;
+ if (ignore_eof) {
+ int size, rval;
+
+ if ((err = ldi_ioctl(lh, FIONREAD, (intptr_t)&size,
+ FKIOCTL, kcred, &rval)) != 0)
+ return (err);
+ if (size != 0)
+ *rvalp = 1;
+ } else {
+ int msg_size, msg_count;
+
+ if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+ FKIOCTL, kcred, &msg_count)) != 0)
+ return (err);
+ if (msg_count != 0)
+ *rvalp = 1;
+ }
+ return (0);
+}
+
+static int
+lx_ptm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int err;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ if (ddi_create_minor_node(dip, LX_PTM_MINOR_NODE, S_IFCHR,
+ ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ err = ldi_ident_from_dip(dip, &lps.lps_li);
+ if (err != 0) {
+ ddi_remove_minor_node(dip, ddi_get_name(dip));
+ return (DDI_FAILURE);
+ }
+
+ lps.lps_dip = dip;
+ lps.lps_pts_major = ddi_name_to_major(LP_PTS_DRV_NAME);
+
+ rw_init(&lps.lps_lh_rwlock, NULL, RW_DRIVER, NULL);
+ lps.lps_lh_count = 0;
+ lps.lps_lh_array = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ ldi_ident_release(lps.lps_li);
+ lps.lps_dip = NULL;
+
+ ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+ ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+ if (lps.lps_lh_array != NULL) {
+ kmem_free(lps.lps_lh_array,
+ sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+ lps.lps_lh_array = NULL;
+ lps.lps_lh_count = 0;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ struct strioctl iocb;
+ ptmptsopencb_t ppocb = { NULL, NULL };
+ ldi_handle_t lh;
+ major_t maj, our_major = getmajor(*devp);
+ minor_t min, lastmin;
+ uint_t index, anchor = 1;
+ dev_t ptm_dev;
+ int err, rval = 0;
+
+ /*
+ * Don't support the FNDELAY flag and FNONBLOCK until we either
+ * find a Linux app that opens /dev/ptmx with the O_NDELAY
+ * or O_NONBLOCK flags explicitly, or until we create test cases
+ * to determine how reads of master terminal devices opened with
+ * these flags behave in different situations on Linux. Supporting
+ * these flags will involve enhancing our read implementation
+ * and changing the way it deals with EOF notifications.
+ */
+ if (flag & (FNDELAY | FNONBLOCK))
+ return (ENOTSUP);
+
+ /*
+ * we're layered on top of the ptm driver so open that driver
+ * first. (note that we're opening /dev/ptmx in the global
+ * zone, not ourselves in the lx zone.)
+ */
+ err = ldi_open_by_name(LP_PTM_PATH, flag, credp, &lh, lps.lps_li);
+ if (err != 0)
+ return (err);
+
+ /* get the devt returned by the ptmx open */
+ err = ldi_get_dev(lh, &ptm_dev);
+ if (err != 0) {
+ (void) ldi_close(lh, flag, credp);
+ return (err);
+ }
+
+ /*
+ * we're a cloning driver so here's where we'll change the devt that we
+ * return. the ptmx is also a cloning driver so we'll just use
+ * it's minor number as our minor number (it already manages it's
+ * minor name space so no reason to duplicate the effort.)
+ */
+ index = getminor(ptm_dev);
+ *devp = makedevice(our_major, INDEX_TO_MINOR(index));
+
+ /* Get a callback function to query if the pts device is open. */
+ iocb.ic_cmd = PTMPTSOPENCB;
+ iocb.ic_timout = 0;
+ iocb.ic_len = sizeof (ppocb);
+ iocb.ic_dp = (char *)&ppocb;
+
+ err = ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred, &rval);
+ if ((err != 0) || (rval != 0)) {
+ (void) ldi_close(lh, flag, credp);
+ return (EIO); /* XXX return something else here? */
+ }
+ ASSERT(ppocb.ppocb_func != NULL);
+
+ /*
+ * now setup autopush for the terminal slave device. this is
+ * necessary so that when a Linux program opens the device we
+ * can push required strmod modules onto the stream. in Solaris
+ * this is normally done by the application that actually
+ * allocates the terminal.
+ */
+ maj = lps.lps_pts_major;
+ min = index;
+ lastmin = 0;
+ err = kstr_autopush(SET_AUTOPUSH, &maj, &min, &lastmin,
+ &anchor, lx_pts_mods);
+ if (err != 0 && err != EEXIST) {
+ (void) ldi_close(lh, flag, credp);
+ return (EIO); /* XXX return something else here? */
+ }
+
+ /* save off this layered handle for future accesses */
+ lx_ptm_lh_insert(index, lh);
+ lx_ptm_lh_set_ppocb(index, &ppocb);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ ldi_handle_t lh;
+ major_t maj;
+ minor_t min, lastmin;
+ uint_t index;
+ int err;
+ int i;
+
+ index = DEVT_TO_INDEX(dev);
+
+ /*
+ * we must cleanup all the state associated with this major/minor
+ * terminal pair before actually closing the ptm master device.
+ * this is required because once the close of the ptm device is
+ * complete major/minor terminal pair is immediatly available for
+ * re-use in any zone.
+ */
+
+ /* free up our saved reference for this layered handle */
+ lh = lx_ptm_lh_remove(index);
+
+ /* unconfigure autopush for the associated terminal slave device */
+ maj = lps.lps_pts_major;
+ min = index;
+ lastmin = 0;
+ for (i = 0; i < 5; i++) {
+ /*
+ * we loop here because we don't want to release this ptm
+ * node if autopush can't be disabled on the associated
+ * slave device because then bad things could happen if
+ * another brand were to get this terminal allocated
+ * to them. If we keep failing we eventually drive on so that
+ * things don't hang.
+ */
+ err = kstr_autopush(CLR_AUTOPUSH, &maj, &min, &lastmin,
+ 0, NULL);
+ if (err == 0)
+ break;
+
+ cmn_err(CE_WARN, "lx zoneid %d: error %d on kstr_autopush",
+ getzoneid(), err);
+
+ /* wait one second and try again */
+ delay(drv_usectohz(1000000));
+ }
+
+ err = ldi_close(lh, flag, credp);
+
+ /*
+ * note that we don't have to bother with changing the permissions
+ * on the associated slave device here. the reason is that no one
+ * can actually open the device untill it's associated master
+ * device is re-opened, which will result in the permissions on
+ * it being reset.
+ */
+ return (err);
+}
+
+static int
+lx_ptm_read_loop(dev_t dev, struct uio *uiop, cred_t *credp, int *loop)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err, rval;
+ struct uio uio = *uiop;
+
+ *loop = 0;
+
+ /*
+ * Here's another way that Linux master terminals behave differently
+ * from Solaris master terminals. If you do a read on a Linux
+ * master terminal (that was opened witout NDELAY and NONBLOCK)
+ * who's corrosponding slave terminal is currently closed and
+ * has been opened and closed at least once, Linux return -1 and
+ * set errno to EIO where as Solaris blocks.
+ */
+ if (lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev))) {
+ /* Slave has been opened and closed at least once. */
+ if (lx_ptm_pts_isopen(dev) == 0) {
+ /*
+ * Slave is closed. Make sure that data is avaliable
+ * before attempting a read.
+ */
+ if ((err = lx_ptm_data_check(dev, 0, &rval)) != 0)
+ return (err);
+
+ /* If there is no data available then return. */
+ if (rval == 0)
+ return (EIO);
+ }
+ }
+
+ /* Actually do the read operation. */
+ if ((err = ldi_read(lh, uiop, credp)) != 0)
+ return (err);
+
+ /* If read returned actual data then return. */
+ if (uio.uio_resid != uiop->uio_resid)
+ return (0);
+
+ /*
+ * This was a zero byte read (ie, an EOF). This indicates
+ * that the slave terinal device has been closed. Record
+ * the fact that the slave device has been closed and retry
+ * the read operation.
+ */
+ lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+ *loop = 1;
+ return (0);
+}
+
+static int
+lx_ptm_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int pktio = lx_ptm_lh_pktio_get(DEVT_TO_INDEX(dev));
+ int err, loop;
+ struct uio uio;
+ struct iovec iovp;
+
+ ASSERT(uiop->uio_iovcnt > 0);
+
+ /*
+ * If packet mode has been enabled (via TIOCPKT) we need to pad
+ * all read requests with a leading byte that indicates any
+ * relevant control status information.
+ */
+ if (pktio != 0) {
+ /*
+ * We'd like to write the control information into
+ * the current buffer but we can't yet. We don't
+ * want to modify userspace memory here only to have
+ * the read operation fail later. So instead
+ * what we'll do here is read one character from the
+ * beginning of the memory pointed to by the uio
+ * structure. This will advance the output pointer
+ * by one. Then when the read completes successfully
+ * we can update the byte that we passed over. Before
+ * we do the read make a copy of the current uiop and
+ * iovec structs so we can write to them later.
+ */
+ uio = *uiop;
+ iovp = *uiop->uio_iov;
+ uio.uio_iov = &iovp;
+
+ if (uwritec(uiop) == -1)
+ return (EFAULT);
+ }
+
+ do {
+ /*
+ * Before we actually attempt a read operation we need
+ * to make sure there's some buffer space to actually
+ * read in some data. We do this because if we're in
+ * pktio mode and the caller only requested one byte,
+ * then we've already used up that one byte and we
+ * don't want to pass this read request. Doing a 0
+ * byte read (unless there is a problem with the stream
+ * head) always returns succcess. Normally when a streams
+ * read returns 0 bytes we interpret that as an EOF on
+ * the stream (ie, the slave side has been opened and
+ * closed) and we ignore it and re-try the read operation.
+ * So if we pass on a 0 byte read here lx_ptm_read_loop()
+ * will tell us to loop around and we'll end up in an
+ * infinite loop.
+ */
+ if (uiop->uio_resid == 0)
+ break;
+
+ /*
+ * Serialize all reads. We need to do this so that we can
+ * properly emulate the behavior of master terminals on Linux.
+ * In reality this serializaion should not pose any kind of
+ * performance problem since it would be very strange to have
+ * multiple threads trying to read from the same master
+ * terminal device concurrently.
+ */
+ if (lx_ptm_read_start(dev) != 0)
+ return (EINTR);
+
+ err = lx_ptm_read_loop(dev, uiop, credp, &loop);
+ lx_ptm_read_end(dev);
+ if (err != 0)
+ return (err);
+ } while (loop != 0);
+
+ if (pktio != 0) {
+ uint8_t pktio_data = TIOCPKT_DATA;
+
+ /*
+ * Note that the control status information we
+ * pass back is faked up in the sense that we
+ * don't actually report any events, we always
+ * report a status of 0.
+ */
+ if (uiomove(&pktio_data, 1, UIO_READ, &uio) != 0)
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+lx_ptm_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ err = ldi_write(lh, uiop, credp);
+
+ return (err);
+}
+
+static int
+lx_ptm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ /*
+ * here we need to make sure that we never allow the
+ * I_SETSIG and I_ESETSIG ioctls to pass through. we
+ * do this because we can't support them.
+ *
+ * the native Solaris ptm device supports these ioctls because
+ * they are streams framework ioctls and all streams devices
+ * support them by default. these ioctls cause the current
+ * process to be registered with a stream and receive signals
+ * when certain stream events occur.
+ *
+ * a problem arises with cleanup of these registrations
+ * for layered drivers.
+ *
+ * normally the streams framework is notified whenever a
+ * process closes any reference to a stream and it goes ahead
+ * and cleans up these registrations. but actual device drivers
+ * are not notified when a process performs a close operation
+ * unless the process is closing the last opened reference to
+ * the device on the entire system.
+ *
+ * so while we could pass these ioctls on and allow processes
+ * to register for signal delivery, we would never receive
+ * any notification when those processes exit (or close a
+ * stream) and we wouldn't be able to unregister them.
+ *
+ * luckily these operations are streams specific and Linux
+ * doesn't support streams devices. so it doesn't actually
+ * seem like we need to support these ioctls. if it turns
+ * out that we do need to support them for some reason in
+ * the future, the current driver model will have to be
+ * enhanced to better support streams device layering.
+ */
+ if ((cmd == I_SETSIG) || (cmd == I_ESETSIG))
+ return (EINVAL);
+
+ /*
+ * here we fake up support for TIOCPKT. Linux applications expect
+ * /etc/ptmx to support this ioctl, but on Solaris it doesn't.
+ * (it is supported on older bsd style ptys.) so we'll fake
+ * up support for it here.
+ *
+ * the reason that this ioctl is emulated here instead of in
+ * userland is that this ioctl affects the results returned
+ * from read() operations. if this ioctl was emulated in
+ * userland the brand library would need to intercept all
+ * read operations and check to see if pktio was enabled
+ * for the fd being read from. since this ioctl only needs
+ * to be supported on the ptmx device it makes more sense
+ * to support it here where we can easily update the results
+ * returned for read() operations performed on ourselves.
+ */
+ if (cmd == TIOCPKT) {
+ int pktio;
+
+ if (ddi_copyin((void *)arg, &pktio, sizeof (pktio),
+ mode) != DDI_SUCCESS)
+ return (EFAULT);
+
+ if (pktio == 0)
+ lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 0);
+ else
+ lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 1);
+
+ return (0);
+ }
+
+ err = ldi_ioctl(lh, cmd, arg, mode, credp, rvalp);
+
+ /*
+ * On recent versions of Linux some apps issue the following ioctls to
+ * the master side of the ptm before opening the slave side. Because
+ * our streams modules (specifically ptem) aren't autopushed until the
+ * slave side has been opened, these ioctls will fail. To alleviate the
+ * issue we simply pretend that these ioctls have succeeded.
+ *
+ * We could push our own "lx_ptem" module onto the master side of the
+ * stream in lx_ptm_open if we need better emulation, but that would
+ * require an "lx_ptem" module which duplicates most of ptem. ptem
+ * doesn't work properly when pushed on the master side.
+ */
+ if (err == EINVAL && (cmd == TIOCSWINSZ || cmd == TCSETS) &&
+ lx_ptm_pts_isopen(dev) == 0) {
+ /* slave side not open, assume we need to succeed */
+ DTRACE_PROBE1(lx_ptm_ioctl__override, int, cmd);
+ return (0);
+ }
+
+ return (err);
+}
+
+static int
+lx_ptm_poll_loop(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp, int *loop)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ short reventsp2;
+ int err, rval;
+
+ *loop = 0;
+
+ /*
+ * If the slave device has been opened and closed at least
+ * once and the slave device is currently closed, then poll
+ * always needs to returns immediatly.
+ */
+ if ((lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev)) != 0) &&
+ (lx_ptm_pts_isopen(dev) == 0)) {
+ /* In this case always return POLLHUP */
+ *reventsp = POLLHUP;
+
+ /*
+ * Check if there really is data on the stream.
+ * If so set the correct return flags.
+ */
+ if ((err = lx_ptm_data_check(dev, 1, &rval)) != 0) {
+ /* Something went wrong. */
+ return (err);
+ }
+ if (rval != 0)
+ *reventsp |= (events & (POLLIN | POLLRDNORM));
+
+ /*
+ * Is the user checking for writability? Note that for ptm
+ * devices Linux seems to ignore the POLLWRBAND write flag.
+ */
+ if ((events & POLLWRNORM) == 0)
+ return (0);
+
+ /*
+ * To check if the stream is writable we have to actually
+ * call poll, but make sure to set anyyet to 1 to prevent
+ * the streams framework from setting up callbacks.
+ */
+ if ((err = ldi_poll(lh, POLLWRNORM, 1, &reventsp2, NULL)) != 0)
+ return (err);
+
+ *reventsp |= (reventsp2 & POLLWRNORM);
+ } else {
+ int lockstate;
+
+ /* The slave device is open, do the poll */
+ if ((err = ldi_poll(lh, events, anyyet, reventsp, phpp)) != 0)
+ return (err);
+
+ /*
+ * Drop any leading EOFs on the stream.
+ *
+ * Note that we have to use pollunlock() here to avoid
+ * recursive mutex enters in the poll framework. The
+ * reason is that if there is an EOF message on the stream
+ * then the act of reading from the queue to remove the
+ * message can cause the ptm drivers event service
+ * routine to be invoked, and if there is no open
+ * slave device then the ptm driver may generate
+ * error messages and put them on the stream. This
+ * in turn will generate a poll event and the poll
+ * framework will try to invoke any poll callbacks
+ * associated with the stream. In the process of
+ * doing that the poll framework will try to aquire
+ * locks that we are already holding. So we need to
+ * drop those locks here before we do our read.
+ */
+ if (pollunlock(&lockstate) != 0) {
+ *reventsp = POLLNVAL;
+ return (0);
+ }
+ err = lx_ptm_eof_drop(dev, &rval);
+ pollrelock(lockstate);
+ if (err)
+ return (err);
+
+ /* If no EOF was dropped then return */
+ if (rval == 0)
+ return (0);
+
+ /*
+ * An EOF was removed from the stream. Retry the entire
+ * poll operation from the top because polls on the ptm
+ * device should behave differently now.
+ */
+ *loop = 1;
+ }
+ return (0);
+}
+
+static int
+lx_ptm_poll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ int loop, err;
+
+ do {
+ /* Serialize ourself wrt read operations. */
+ if (lx_ptm_read_start(dev) != 0)
+ return (EINTR);
+
+ err = lx_ptm_poll_loop(dev,
+ events, anyyet, reventsp, phpp, &loop);
+ lx_ptm_read_end(dev);
+ if (err != 0)
+ return (err);
+ } while (loop != 0);
+ return (0);
+}
+
+static struct cb_ops lx_ptm_cb_ops = {
+ lx_ptm_open, /* open */
+ lx_ptm_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ lx_ptm_read, /* read */
+ lx_ptm_write, /* write */
+ lx_ptm_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ lx_ptm_poll, /* chpoll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* cb_str */
+ D_NEW | D_MP,
+ CB_REV,
+ NULL,
+ NULL
+};
+
+static struct dev_ops lx_ptm_ops = {
+ DEVO_REV,
+ 0,
+ ddi_getinfo_1to1,
+ nulldev,
+ nulldev,
+ lx_ptm_attach,
+ lx_ptm_detach,
+ nodev,
+ &lx_ptm_cb_ops,
+ NULL,
+ NULL,
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* type of module */
+ "Linux master terminal driver", /* description of module */
+ &lx_ptm_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/brand/lx/io/lx_ptm.conf b/usr/src/uts/common/brand/lx/io/lx_ptm.conf
new file mode 100644
index 0000000000..481b4e3c74
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_ptm.conf
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="lx_ptm" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c
new file mode 100644
index 0000000000..8b2c5d39c4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c
@@ -0,0 +1,2630 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * The LX Brand: emulation of a Linux operating environment within a zone.
+ *
+ * OVERVIEW
+ *
+ * The LX brand enables a full Linux userland -- including a C library,
+ * init(1) framework, and some set of applications -- to run unmodified
+ * within an illumos zone. Unlike illumos, where applications are expected
+ * to link against and consume functions exported from libraries, the
+ * supported Linux binary compatibility boundary is the system call
+ * interface. By accurately emulating the behaviour of Linux system calls,
+ * Linux software can be executed in this environment as if it were running
+ * on a native Linux system.
+ *
+ * EMULATING LINUX SYSTEM CALLS
+ *
+ * Linux system calls are made in 32-bit processes via the "int 0x80"
+ * instruction; in 64-bit processes the "syscall" instruction is used, as it
+ * is with native illumos processes. In both cases, arguments to system
+ * calls are generally passed in registers and the usermode stack is not
+ * interpreted or modified by the Linux kernel.
+ *
+ * When the emulated Linux process makes a system call, it traps into the
+ * illumos kernel. The in-kernel brand module contains various emulation
+ * routines, and can fully service some emulated system calls; e.g. read(2)
+ * and write(2). Other system calls require assistance from the illumos
+ * libc, bouncing back out to the brand library ("lx_brand.so.1") for
+ * emulation.
+ *
+ * The brand mechanism allows for the provision of an alternative trap
+ * handler for the various system call mechanisms. Traditionally this was
+ * used to immediately revector execution to the usermode emulation library,
+ * which was responsible for handling all system calls. In the interests of
+ * more accurate emulation and increased performance, much of the regular
+ * illumos system call path is now invoked. Only the argument processing and
+ * handler dispatch are replaced by the brand, via the per-LWP
+ * "lwp_brand_syscall" interposition function pointer.
+ *
+ * THE NATIVE AND BRAND STACKS
+ *
+ * Some runtime environments (e.g. the Go language) allocate very small
+ * thread stacks, preferring to grow or split the stack as necessary. The
+ * Linux kernel generally does not use the usermode stack when servicing
+ * system calls, so this is not a problem. In order for our emulation to
+ * have the same zero stack impact, we must execute usermode emulation
+ * routines on an _alternate_ stack. This is similar, in principle, to the
+ * use of sigaltstack(3C) to run signal handlers off the main thread stack.
+ *
+ * To this end, the brand library allocates and installs an alternate stack
+ * (called the "native" stack) for each LWP. The in-kernel brand code uses
+ * this stack for usermode emulation calls and interposed signal delivery,
+ * while the emulated Linux process sees only the data on the main thread
+ * stack, known as the "brand" stack. The stack mode is tracked in the
+ * per-LWP brand-private data, using the LX_STACK_MODE_* enum.
+ *
+ * The stack mode doubles as a system call "mode bit". When in the
+ * LX_STACK_MODE_BRAND mode, system calls are processed as emulated Linux
+ * system calls. In other modes, system calls are assumed to be native
+ * illumos system calls as made during brand library initialisation and
+ * usermode emulation.
+ *
+ * USERMODE EMULATION
+ *
+ * When a Linux system call cannot be emulated within the kernel, we preserve
+ * the register state of the Linux process and revector the LWP to the brand
+ * library usermode emulation handler: the "lx_emulate()" function in
+ * "lx_brand.so.1". This revectoring is modelled on the delivery of signals,
+ * and is performed in "lx_emulate_user()".
+ *
+ * First, the emulated process state is written out to the usermode stack of
+ * the process as a "ucontext_t" object. Arguments to the emulation routine
+ * are passed on the stack or in registers, depending on the ABI. When the
+ * usermode emulation is complete, the result is passed back to the kernel
+ * (via the "B_EMULATION_DONE" brandsys subcommand) with the saved context
+ * for restoration.
+ *
+ * SIGNAL DELIVERY, SETCONTEXT AND GETCONTEXT
+ *
+ * When servicing emulated system calls in the usermode brand library, or
+ * during signal delivery, various state is preserved by the kernel so that
+ * the running LWP may be revectored to a handling routine. The context
+ * allows the kernel to restart the program at the point of interruption,
+ * either at the return of the signal handler, via setcontext(3C); or after
+ * the usermode emulation request has been serviced, via B_EMULATION_DONE.
+ *
+ * In illumos native processes, the saved context (a "ucontext_t" object)
+ * includes the state of registers and the current signal mask at the point
+ * of interruption. The context also includes a link to the most recently
+ * saved context, forming a chain to be unwound as requests complete. The LX
+ * brand requires additional book-keeping to describe the machine state: in
+ * particular, the current stack mode and the occupied extent of the native
+ * stack.
+ *
+ * The brand code is able to interpose on the context save and restore
+ * operations in the kernel -- see "lx_savecontext()" and
+ * "lx_restorecontext()" -- to enable getcontext(3C) and setcontext(3C) to
+ * function correctly in the face of a dual stack LWP. The brand also
+ * interposes on the signal delivery mechanism -- see "lx_sendsig()" and
+ * "lx_sendsig_stack()" -- to allow all signals to be delivered to the brand
+ * library interposer on the native stack, regardless of the interrupted
+ * execution mode. Linux sigaltstack(2) emulation is performed entirely by
+ * the usermode brand library during signal handler interposition.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/exec.h>
+#include <sys/lx_impl.h>
+#include <sys/machbrand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/param.h>
+#include <sys/termios.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/auxv.h>
+#include <sys/priv.h>
+#include <sys/regset.h>
+#include <sys/privregs.h>
+#include <sys/archsystm.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <sys/core.h>
+#include <sys/stack.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <lx_signum.h>
+#include <util/sscanf.h>
+#include <sys/lx_brand.h>
+#include <sys/zfs_ioctl.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+
+int lx_debug = 0;
+
+void lx_init_brand_data(zone_t *, kmutex_t *);
+void lx_free_brand_data(zone_t *);
+void lx_setbrand(proc_t *);
+int lx_getattr(zone_t *, int, void *, size_t *);
+int lx_setattr(zone_t *, int, void *, size_t);
+int lx_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+void lx_set_kern_version(zone_t *, char *);
+void lx_copy_procdata(proc_t *, proc_t *);
+
+extern int getsetcontext(int, void *);
+extern int waitsys(idtype_t, id_t, siginfo_t *, int);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+extern int waitsys32(idtype_t, id_t, siginfo_t *, int);
+#endif
+
+extern int zvol_name2minor(const char *, minor_t *);
+extern int zvol_create_minor(const char *);
+
+extern void lx_proc_exit(proc_t *);
+extern int lx_sched_affinity(int, uintptr_t, int, uintptr_t, int64_t *);
+
+extern void lx_ioctl_init();
+extern void lx_ioctl_fini();
+extern void lx_socket_init();
+extern void lx_socket_fini();
+
+lx_systrace_f *lx_systrace_entry_ptr;
+lx_systrace_f *lx_systrace_return_ptr;
+
+static int lx_systrace_enabled;
+
+/*
+ * cgroup file system maintenance functions which are set when cgroups loads.
+ */
+void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * While this is effectively mmu.hole_start - PAGESIZE, we don't particularly
+ * want an MMU dependency here (and should there be a microprocessor without
+ * a hole, we don't want to start allocating from the top of the VA range).
+ */
+#define LX_MAXSTACK64 0x7ffffff00000
+
+uint64_t lx_maxstack64 = LX_MAXSTACK64;
+
+static int lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+ struct intpdata *idata, int level, long *execsz, int setid,
+ caddr_t exec_file, struct cred *cred, int *brand_action);
+
+static boolean_t lx_native_exec(uint8_t, const char **);
+static uint32_t lx_map32limit(proc_t *);
+
+static void lx_savecontext(ucontext_t *);
+static void lx_restorecontext(ucontext_t *);
+static caddr_t lx_sendsig_stack(int);
+static void lx_sendsig(int);
+#if defined(_SYSCALL32_IMPL)
+static void lx_savecontext32(ucontext32_t *);
+#endif
+static int lx_setid_clear(vattr_t *, cred_t *);
+#if defined(_LP64)
+static int lx_pagefault(proc_t *, klwp_t *, caddr_t, enum fault_type,
+ enum seg_rw);
+#endif
+
+typedef struct lx_zfs_ds {
+ list_node_t ds_link;
+ char ds_name[MAXPATHLEN];
+ uint64_t ds_cookie;
+} lx_zfs_ds_t;
+
+/* lx brand */
+struct brand_ops lx_brops = {
+ lx_init_brand_data, /* b_init_brand_data */
+ lx_free_brand_data, /* b_free_brand_data */
+ lx_brandsys, /* b_brandsys */
+ lx_setbrand, /* b_setbrand */
+ lx_getattr, /* b_getattr */
+ lx_setattr, /* b_setattr */
+ lx_copy_procdata, /* b_copy_procdata */
+ lx_proc_exit, /* b_proc_exit */
+ lx_exec, /* b_exec */
+ lx_setrval, /* b_lwp_setrval */
+ lx_lwpdata_alloc, /* b_lwpdata_alloc */
+ lx_lwpdata_free, /* b_lwpdata_free */
+ lx_initlwp, /* b_initlwp */
+ lx_initlwp_post, /* b_initlwp_post */
+ lx_forklwp, /* b_forklwp */
+ lx_freelwp, /* b_freelwp */
+ lx_exitlwp, /* b_lwpexit */
+ lx_elfexec, /* b_elfexec */
+ NULL, /* b_sigset_native_to_brand */
+ NULL, /* b_sigset_brand_to_native */
+ lx_sigfd_translate, /* b_sigfd_translate */
+ NSIG, /* b_nsig */
+ lx_exit_with_sig, /* b_exit_with_sig */
+ lx_wait_filter, /* b_wait_filter */
+ lx_native_exec, /* b_native_exec */
+ lx_map32limit, /* b_map32limit */
+ lx_stop_notify, /* b_stop_notify */
+ lx_waitid_helper, /* b_waitid_helper */
+ lx_sigcld_repost, /* b_sigcld_repost */
+ lx_ptrace_issig_stop, /* b_issig_stop */
+ lx_ptrace_sig_ignorable, /* b_sig_ignorable */
+ lx_savecontext, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ lx_savecontext32, /* b_savecontext32 */
+#endif
+ lx_restorecontext, /* b_restorecontext */
+ lx_sendsig_stack, /* b_sendsig_stack */
+ lx_sendsig, /* b_sendsig */
+ lx_setid_clear, /* b_setid_clear */
+#if defined(_LP64)
+ lx_pagefault, /* b_pagefault */
+#else
+ NULL,
+#endif
+ B_FALSE /* b_intp_parse_arg */
+};
+
+struct brand_mach_ops lx_mops = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ lx_fixsegreg,
+ lx_fsbase
+};
+
+struct brand lx_brand = {
+ BRAND_VER_1,
+ "lx",
+ &lx_brops,
+ &lx_mops,
+ sizeof (struct lx_proc_data)
+};
+
+static struct modlbrand modlbrand = {
+ &mod_brandops, "lx brand", &lx_brand
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlbrand, NULL
+};
+
+void
+lx_proc_exit(proc_t *p)
+{
+ lx_proc_data_t *lxpd;
+ proc_t *cp;
+
+ mutex_enter(&p->p_lock);
+ VERIFY((lxpd = ptolxproc(p)) != NULL);
+ VERIFY(lxpd->l_ptrace == 0);
+ if ((lxpd->l_flags & LX_PROC_CHILD_DEATHSIG) == 0) {
+ mutex_exit(&p->p_lock);
+ return;
+ }
+ mutex_exit(&p->p_lock);
+
+ /* Check for children which desire notification of parental death. */
+ mutex_enter(&pidlock);
+ for (cp = p->p_child; cp != NULL; cp = cp->p_sibling) {
+ mutex_enter(&cp->p_lock);
+ if ((lxpd = ptolxproc(cp)) == NULL) {
+ mutex_exit(&cp->p_lock);
+ continue;
+ }
+ if (lxpd->l_parent_deathsig != 0) {
+ sigtoproc(cp, NULL, lxpd->l_parent_deathsig);
+ }
+ mutex_exit(&cp->p_lock);
+ }
+ mutex_exit(&pidlock);
+}
+
+void
+lx_setbrand(proc_t *p)
+{
+ /* Send SIGCHLD to parent by default when child exits */
+ ptolxproc(p)->l_signal = stol_signo[SIGCHLD];
+
+ lx_read_argv_bounds(p);
+}
+
+/* ARGSUSED */
+int
+lx_setattr(zone_t *zone, int attr, void *ubuf, size_t ubufsz)
+{
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+ switch (attr) {
+ case LX_ATTR_KERN_RELEASE: {
+ char buf[LX_KERN_RELEASE_MAX];
+ bzero(buf, LX_KERN_RELEASE_MAX);
+ if (ubufsz >= LX_KERN_RELEASE_MAX) {
+ return (ERANGE);
+ }
+ if (copyin(ubuf, buf, ubufsz) != 0) {
+ return (EFAULT);
+ }
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(lxzd->lxzd_kernel_release, buf,
+ LX_KERN_RELEASE_MAX);
+ mutex_exit(&lxzd->lxzd_lock);
+ return (0);
+ }
+ case LX_ATTR_KERN_VERSION: {
+ char buf[LX_KERN_VERSION_MAX];
+ bzero(buf, LX_KERN_VERSION_MAX);
+ if (ubufsz >= LX_KERN_VERSION_MAX) {
+ return (ERANGE);
+ }
+ if (copyin(ubuf, buf, ubufsz) != 0) {
+ return (EFAULT);
+ }
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(lxzd->lxzd_kernel_version, buf,
+ LX_KERN_VERSION_MAX);
+ mutex_exit(&lxzd->lxzd_lock);
+ return (0);
+ }
+ default:
+ return (EINVAL);
+ }
+}
+
+/* ARGSUSED */
+int
+lx_getattr(zone_t *zone, int attr, void *ubuf, size_t *ubufsz)
+{
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+ int len;
+
+ switch (attr) {
+ case LX_ATTR_KERN_RELEASE: {
+ char buf[LX_KERN_RELEASE_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ len = strnlen(lxzd->lxzd_kernel_release, LX_KERN_RELEASE_MAX);
+ len++;
+ if (*ubufsz < len) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return (ERANGE);
+ }
+ bzero(buf, sizeof (buf));
+ (void) strncpy(buf, lxzd->lxzd_kernel_release, sizeof (buf));
+ mutex_exit(&lxzd->lxzd_lock);
+ if (copyout(buf, ubuf, len) != 0) {
+ return (EFAULT);
+ }
+ *ubufsz = len;
+ return (0);
+ }
+ case LX_ATTR_KERN_VERSION: {
+ char buf[LX_KERN_VERSION_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ len = strnlen(lxzd->lxzd_kernel_version, LX_KERN_VERSION_MAX);
+ len++;
+ if (*ubufsz < len) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return (ERANGE);
+ }
+ bzero(buf, sizeof (buf));
+ (void) strncpy(buf, lxzd->lxzd_kernel_version, sizeof (buf));
+ mutex_exit(&lxzd->lxzd_lock);
+ if (copyout(buf, ubuf, len) != 0) {
+ return (EFAULT);
+ }
+ *ubufsz = len;
+ return (0);
+ }
+ default:
+ return (EINVAL);
+ }
+}
+
+uint32_t
+lx_map32limit(proc_t *p)
+{
+ /*
+ * To be bug-for-bug compatible with Linux, we have MAP_32BIT only
+ * allow mappings in the first 31 bits. This was a nuance in the
+ * original Linux implementation circa 2002, and applications have
+ * come to depend on its behavior.
+ *
+ * This is only relevant for 64-bit processes.
+ */
+ if (p->p_model == DATAMODEL_LP64)
+ return ((uint32_t)1 << 31);
+
+ return ((uint32_t)USERLIMIT32);
+}
+
+void
+lx_brand_systrace_enable(void)
+{
+ VERIFY(!lx_systrace_enabled);
+
+ lx_systrace_enabled = 1;
+}
+
+void
+lx_brand_systrace_disable(void)
+{
+ VERIFY(lx_systrace_enabled);
+
+ lx_systrace_enabled = 0;
+}
+
+void
+lx_lwp_set_native_stack_current(lx_lwp_data_t *lwpd, uintptr_t new_sp)
+{
+ VERIFY(lwpd->br_ntv_stack != 0);
+
+ /*
+ * The "brand-lx-set-ntv-stack-current" probe has arguments:
+ * arg0: stack pointer before change
+ * arg1: stack pointer after change
+ * arg2: current stack base
+ */
+ DTRACE_PROBE3(brand__lx__set__ntv__stack__current,
+ uintptr_t, lwpd->br_ntv_stack_current,
+ uintptr_t, new_sp,
+ uintptr_t, lwpd->br_ntv_stack);
+
+ lwpd->br_ntv_stack_current = new_sp;
+}
+
+#if defined(_LP64)
+static int
+lx_pagefault(proc_t *p, klwp_t *lwp, caddr_t addr, enum fault_type type,
+ enum seg_rw rw)
+{
+ int syscall_num;
+
+ /*
+ * We only want to handle a very specific set of circumstances.
+ * Namely: this is a 64-bit LX-branded process attempting to execute an
+ * address in a page for which it does not have a valid mapping. If
+ * this is not the case, we bail out as fast as possible.
+ */
+ VERIFY(PROC_IS_BRANDED(p));
+ if (type != F_INVAL || rw != S_EXEC || lwp_getdatamodel(lwp) !=
+ DATAMODEL_NATIVE) {
+ return (-1);
+ }
+
+ if (!lx_vsyscall_iscall(lwp, (uintptr_t)addr, &syscall_num)) {
+ return (-1);
+ }
+
+ /*
+ * This is a valid vsyscall address. We service the system call and
+ * return 0 to signal that the pagefault has been handled completely.
+ */
+ lx_vsyscall_enter(p, lwp, syscall_num);
+ return (0);
+}
+#endif
+
+/*
+ * This hook runs prior to sendsig() processing and allows us to nominate
+ * an alternative stack pointer for delivery of the signal handling frame.
+ * Critically, this routine should _not_ modify any LWP state as the
+ * savecontext() does not run until after this hook.
+ */
+/* ARGSUSED */
+static caddr_t
+lx_sendsig_stack(int sig)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * We want to take signal delivery on the native stack, but only if
+ * one has been allocated and installed for this LWP.
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ /*
+ * The program is not running on the native stack. Return
+ * the native stack pointer from our brand-private data so
+ * that we may switch to it for signal handling.
+ */
+ return ((caddr_t)lwpd->br_ntv_stack_current);
+ } else {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * Either the program is already running on the native stack,
+ * or one has not yet been allocated for this LWP. Use the
+ * current stack pointer value.
+ */
+ return ((caddr_t)rp->r_sp);
+ }
+}
+
+/*
+ * This hook runs after sendsig() processing and allows us to update the
+ * per-LWP mode flags for system calls and stacks. The pre-signal
+ * context has already been saved and delivered to the user at this point.
+ */
+/* ARGSUSED */
+static void
+lx_sendsig(int sig)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ case LX_STACK_MODE_NATIVE:
+ /*
+ * In lx_sendsig_stack(), we nominated a stack pointer from the
+ * native stack. Update the stack mode, and the current in-use
+ * extent of the native stack, accordingly:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, rp->r_sp);
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+ break;
+
+ default:
+ /*
+ * Otherwise, the brand library has not yet installed the
+ * alternate stack for this LWP. Signals will be handled on
+ * the regular stack thread.
+ */
+ return;
+ }
+}
+
+/*
+ * This hook runs prior to the context restoration, allowing us to take action
+ * or modify the context before it is loaded.
+ */
+static void
+lx_restorecontext(ucontext_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0];
+ caddr_t sp = ucp->uc_brand_data[1];
+
+ /*
+ * We have a saved native stack pointer value that we must restore
+ * into the per-LWP data.
+ */
+ if (flags & LX_UC_RESTORE_NATIVE_SP) {
+ lx_lwp_set_native_stack_current(lwpd, (uintptr_t)sp);
+ }
+
+ /*
+ * We do not wish to restore the value of uc_link in this context,
+ * so replace it with the value currently in the LWP.
+ */
+ if (flags & LX_UC_IGNORE_LINK) {
+ ucp->uc_link = (ucontext_t *)lwp->lwp_oldcontext;
+ }
+
+ /*
+ * Restore the stack mode:
+ */
+ if (flags & LX_UC_STACK_NATIVE) {
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ } else if (flags & LX_UC_STACK_BRAND) {
+ lwpd->br_stack_mode = LX_STACK_MODE_BRAND;
+ }
+
+#if defined(__amd64)
+ /*
+ * Override the fs/gsbase in the context with the value provided
+ * through the Linux arch_prctl(2) system call.
+ */
+ if (flags & LX_UC_STACK_BRAND) {
+ if (lwpd->br_lx_fsbase != 0) {
+ ucp->uc_mcontext.gregs[REG_FSBASE] = lwpd->br_lx_fsbase;
+ }
+ if (lwpd->br_lx_gsbase != 0) {
+ ucp->uc_mcontext.gregs[REG_GSBASE] = lwpd->br_lx_gsbase;
+ }
+ }
+#endif
+}
+
+static void
+lx_savecontext(ucontext_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ uintptr_t flags = 0;
+
+ /*
+ * The ucontext_t affords us three private pointer-sized members in
+ * "uc_brand_data". We pack a variety of flags into the first element,
+ * and an optional stack pointer in the second element. The flags
+ * determine which stack pointer (native or brand), if any, is stored
+ * in the second element. The third element may contain the system
+ * call number; this is analogous to the "orig_[er]ax" member of a
+ * Linux "user_regs_struct".
+ */
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+ lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ /*
+ * Record the value of the native stack pointer to restore
+ * when returning to this branded context:
+ */
+ flags |= LX_UC_RESTORE_NATIVE_SP;
+ ucp->uc_brand_data[1] = (void *)lwpd->br_ntv_stack_current;
+ }
+
+ /*
+ * Save the stack mode:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+ flags |= LX_UC_STACK_NATIVE;
+ } else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ flags |= LX_UC_STACK_BRAND;
+ }
+
+ /*
+ * If we might need to restart this system call, save that information
+ * in the context:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ ucp->uc_brand_data[2] =
+ (void *)(uintptr_t)lwpd->br_syscall_num;
+ if (lwpd->br_syscall_restart) {
+ flags |= LX_UC_RESTART_SYSCALL;
+ }
+ } else {
+ ucp->uc_brand_data[2] = NULL;
+ }
+
+ ucp->uc_brand_data[0] = (void *)flags;
+}
+
+#if defined(_SYSCALL32_IMPL)
+static void
+lx_savecontext32(ucontext32_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ unsigned int flags = 0;
+
+ /*
+ * The ucontext_t affords us three private pointer-sized members in
+ * "uc_brand_data". We pack a variety of flags into the first element,
+ * and an optional stack pointer in the second element. The flags
+ * determine which stack pointer (native or brand), if any, is stored
+ * in the second element. The third element may contain the system
+ * call number; this is analogous to the "orig_[er]ax" member of a
+ * Linux "user_regs_struct".
+ */
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+ lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ /*
+ * Record the value of the native stack pointer to restore
+ * when returning to this branded context:
+ */
+ flags |= LX_UC_RESTORE_NATIVE_SP;
+ ucp->uc_brand_data[1] = (caddr32_t)lwpd->br_ntv_stack_current;
+ }
+
+ /*
+ * Save the stack mode:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+ flags |= LX_UC_STACK_NATIVE;
+ } else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ flags |= LX_UC_STACK_BRAND;
+ }
+
+ /*
+ * If we might need to restart this system call, save that information
+ * in the context:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ ucp->uc_brand_data[2] = (caddr32_t)lwpd->br_syscall_num;
+ if (lwpd->br_syscall_restart) {
+ flags |= LX_UC_RESTART_SYSCALL;
+ }
+ } else {
+ ucp->uc_brand_data[2] = NULL;
+ }
+
+ ucp->uc_brand_data[0] = flags;
+}
+#endif
+
+static int
+lx_zfs_ioctl(ldi_handle_t lh, int cmd, zfs_cmd_t *zc, size_t *dst_alloc_size)
+{
+ uint64_t cookie;
+ size_t dstsize;
+ int rc, unused;
+
+ cookie = zc->zc_cookie;
+
+ dstsize = (dst_alloc_size == NULL ? 0 : 8192);
+
+again:
+ if (dst_alloc_size != NULL) {
+ zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(dstsize,
+ KM_SLEEP);
+ zc->zc_nvlist_dst_size = dstsize;
+ }
+
+ rc = ldi_ioctl(lh, cmd, (intptr_t)zc, FKIOCTL, kcred, &unused);
+ if (rc == ENOMEM && dst_alloc_size != NULL) {
+ /*
+ * Our nvlist_dst buffer was too small, retry with a bigger
+ * buffer. ZFS will tell us the exact needed size.
+ */
+ size_t newsize = zc->zc_nvlist_dst_size;
+ ASSERT(newsize > dstsize);
+
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, dstsize);
+ dstsize = newsize;
+ zc->zc_cookie = cookie;
+
+ goto again;
+ }
+
+ if (dst_alloc_size != NULL) {
+ *dst_alloc_size = dstsize;
+ }
+
+ return (rc);
+}
+
+static int
+lx_zone_zfs_open(ldi_handle_t *lh, dev_t *zfs_dev)
+{
+ ldi_ident_t li;
+
+ if (ldi_ident_from_mod(&modlinkage, &li) != 0) {
+ return (-1);
+ }
+ if (ldi_open_by_name("/dev/zfs", FREAD|FWRITE, kcred, lh, li) != 0) {
+ ldi_ident_release(li);
+ return (-1);
+ }
+ ldi_ident_release(li);
+ if (ldi_get_dev(*lh, zfs_dev) != 0) {
+ (void) ldi_close(*lh, FREAD|FWRITE, kcred);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * We only get the relevant properties for zvols. This is because we're
+ * essentially iterating all of the ZFS datasets/zvols on the entire system
+ * when we boot the zone and there is a significant performance penalty if we
+ * have to retrieve all of the properties for everything. Especially since we
+ * don't care about any of them except the zvols actually in our delegated
+ * datasets.
+ *
+ * Note that the two properties we care about, volsize & volblocksize, are
+ * mandatory for zvols and should always be present. Also, note that the
+ * blocksize property value cannot change after the zvol has been created.
+ */
+static void
+lx_zvol_props(ldi_handle_t lh, zfs_cmd_t *zc, uint64_t *vsz, uint64_t *bsz)
+{
+ int rc;
+ size_t size;
+ nvlist_t *nv = NULL, *nv2;
+
+ rc = lx_zfs_ioctl(lh, ZFS_IOC_OBJSET_STATS, zc, &size);
+ if (rc != 0)
+ return;
+
+ rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, &nv, 0);
+ ASSERT(rc == 0);
+
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ zc->zc_nvlist_dst = NULL;
+ zc->zc_nvlist_dst_size = 0;
+
+ if ((rc = nvlist_lookup_nvlist(nv, "volsize", &nv2)) == 0) {
+ uint64_t val;
+
+ rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+ if (rc == 0) {
+ *vsz = val;
+ }
+ }
+
+ if ((rc = nvlist_lookup_nvlist(nv, "volblocksize", &nv2)) == 0) {
+ uint64_t val;
+
+ rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+ if (rc == 0) {
+ *bsz = val;
+ }
+ }
+
+ nvlist_free(nv);
+}
+
+/*
+ * Unlike ZFS proper, which does dynamic zvols, we currently only generate the
+ * zone's "disk" list once at zone boot time and use that consistently in all
+ * of the various subsystems (devfs, sysfs, procfs). This allows us to avoid
+ * re-iterating the datasets every time one of those subsystems accesses a
+ * "disk" and allows us to keep the view consistent across all subsystems, but
+ * it does mean a reboot is required to see new "disks". This is somewhat
+ * mitigated by its similarity to actual disk drives on a real system.
+ */
+static void
+lx_zone_get_zvols(zone_t *zone, ldi_handle_t lh, minor_t *emul_minor)
+{
+ lx_zone_data_t *lxzd;
+ list_t *zvol_lst, ds_lst;
+ int rc;
+ unsigned int devnum = 0;
+ size_t size;
+ zfs_cmd_t *zc;
+ nvpair_t *elem = NULL;
+ nvlist_t *pnv = NULL;
+
+ lxzd = ztolxzd(zone);
+ ASSERT(lxzd != NULL);
+ zvol_lst = lxzd->lxzd_vdisks;
+
+ zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ if (lx_zfs_ioctl(lh, ZFS_IOC_POOL_CONFIGS, zc, &size) != 0) {
+ goto out;
+ }
+ ASSERT(zc->zc_cookie > 0);
+
+ rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, &pnv, 0);
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ if (rc != 0)
+ goto out;
+
+ /*
+ * We use a dataset list to process all of the datasets in the pool
+ * without doing recursion so that we don't risk blowing the kernel
+ * stack.
+ */
+ list_create(&ds_lst, sizeof (lx_zfs_ds_t),
+ offsetof(lx_zfs_ds_t, ds_link));
+
+ while ((elem = nvlist_next_nvpair(pnv, elem)) != NULL) {
+ lx_zfs_ds_t *ds;
+
+ ds = kmem_zalloc(sizeof (lx_zfs_ds_t), KM_SLEEP);
+ (void) strcpy(ds->ds_name, nvpair_name(elem));
+ list_insert_head(&ds_lst, ds);
+
+ while (ds != NULL) {
+ int w; /* dummy variable */
+
+ bzero(zc, sizeof (zfs_cmd_t));
+ zc->zc_cookie = ds->ds_cookie;
+ (void) strcpy(zc->zc_name, ds->ds_name);
+
+ rc = lx_zfs_ioctl(lh, ZFS_IOC_DATASET_LIST_NEXT,
+ zc, NULL);
+ /* Update the cookie before doing anything else. */
+ ds->ds_cookie = zc->zc_cookie;
+
+ if (rc != 0) {
+ list_remove(&ds_lst, ds);
+ kmem_free(ds, sizeof (lx_zfs_ds_t));
+ ds = list_tail(&ds_lst);
+ continue;
+ }
+
+ /* Reserved internal names, skip over these. */
+ if (strchr(zc->zc_name, '$') != NULL ||
+ strchr(zc->zc_name, '%') != NULL)
+ continue;
+
+ if (!zone_dataset_visible_inzone(zone, zc->zc_name, &w))
+ continue;
+
+ if (zc->zc_objset_stats.dds_type == DMU_OST_ZVOL) {
+ lx_virt_disk_t *vd;
+ minor_t m = 0;
+ char *znm = zc->zc_name;
+
+ /* Create a virtual disk entry for the zvol */
+ vd = kmem_zalloc(sizeof (lx_virt_disk_t),
+ KM_SLEEP);
+ vd->lxvd_type = LXVD_ZVOL;
+ (void) snprintf(vd->lxvd_name,
+ sizeof (vd->lxvd_name),
+ "zvol%u", devnum++);
+ (void) strlcpy(vd->lxvd_real_name,
+ zc->zc_name,
+ sizeof (vd->lxvd_real_name));
+
+ /* Record emulated and real dev_t values */
+ vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK,
+ (*emul_minor)++);
+ if (zvol_name2minor(znm, &m) != 0) {
+ (void) zvol_create_minor(znm);
+ VERIFY(zvol_name2minor(znm, &m) == 0);
+ }
+ if (m != 0) {
+ vd->lxvd_real_dev = makedevice(
+ getmajor(lxzd->lxzd_zfs_dev), m);
+ }
+
+ /* Query volume size properties */
+ lx_zvol_props(lh, zc, &vd->lxvd_volsize,
+ &vd->lxvd_blksize);
+
+ list_insert_tail(zvol_lst, vd);
+ } else {
+ lx_zfs_ds_t *nds;
+
+ /* Create a new ds_t for the child. */
+ nds = kmem_zalloc(sizeof (lx_zfs_ds_t),
+ KM_SLEEP);
+ (void) strcpy(nds->ds_name, zc->zc_name);
+ list_insert_after(&ds_lst, ds, nds);
+
+ /* Depth-first, so do the one just created. */
+ ds = nds;
+ }
+ }
+
+ ASSERT(list_is_empty(&ds_lst));
+ }
+
+ list_destroy(&ds_lst);
+
+out:
+ nvlist_free(pnv);
+ kmem_free(zc, sizeof (zfs_cmd_t));
+}
+
+static void
+lx_zone_get_zfsds(zone_t *zone, minor_t *emul_minor)
+{
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ vfs_t *vfsp = zone->zone_rootvp->v_vfsp;
+
+ /*
+ * Only the root will be mounted at zone init time.
+ * Finding means of discovering other datasets mounted in the zone
+ * would be a good enhancement later.
+ */
+ if (getmajor(vfsp->vfs_dev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = kmem_zalloc(sizeof (lx_virt_disk_t), KM_SLEEP);
+ vd->lxvd_type = LXVD_ZFS_DS;
+ vd->lxvd_real_dev = vfsp->vfs_dev;
+ vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK, (*emul_minor)++);
+ (void) snprintf(vd->lxvd_name, sizeof (vd->lxvd_name),
+ "zfsds%u", 0);
+ (void) strlcpy(vd->lxvd_real_name,
+ refstr_value(vfsp->vfs_resource),
+ sizeof (vd->lxvd_real_name));
+
+ list_insert_tail(lxzd->lxzd_vdisks, vd);
+ }
+}
+
+/* Cleanup virtual disk list */
+static void
+lx_zone_cleanup_vdisks(lx_zone_data_t *lxzd)
+{
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+ vd = (list_remove_head(lxzd->lxzd_vdisks));
+ while (vd != NULL) {
+ kmem_free(vd, sizeof (lx_virt_disk_t));
+ vd = list_remove_head(lxzd->lxzd_vdisks);
+ }
+
+ list_destroy(lxzd->lxzd_vdisks);
+ kmem_free(lxzd->lxzd_vdisks, sizeof (list_t));
+ lxzd->lxzd_vdisks = NULL;
+}
+
+/*
+ * See mod_set_extra_privports. By default illumos restricts access to
+ * ULP_DEF_EPRIV_PORT1 and ULP_DEF_EPRIV_PORT2 for TCP and UDP, even though
+ * these ports are outside of the privileged port range. Linux does not do
+ * this, so we need to remove these defaults.
+ */
+static void
+lx_fix_netstack()
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ udp_stack_t *udps;
+ in_port_t *ports;
+ uint_t i, nports;
+ kmutex_t *lock;
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+
+ tcps = ns->netstack_tcp;
+ ports = tcps->tcps_g_epriv_ports;
+ nports = tcps->tcps_g_num_epriv_ports;
+ lock = &tcps->tcps_epriv_port_lock;
+
+ mutex_enter(lock);
+ for (i = 0; i < nports; i++)
+ ports[i] = 0;
+ mutex_exit(lock);
+
+ udps = ns->netstack_udp;
+ ports = udps->us_epriv_ports;
+ nports = udps->us_num_epriv_ports;
+ lock = &udps->us_epriv_port_lock;
+
+ mutex_enter(lock);
+ for (i = 0; i < nports; i++)
+ ports[i] = 0;
+ mutex_exit(lock);
+
+ netstack_rele(ns);
+}
+
+void
+lx_init_brand_data(zone_t *zone, kmutex_t *zsl)
+{
+ lx_zone_data_t *data;
+ ldi_handle_t lh;
+
+ ASSERT(MUTEX_HELD(zsl));
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(zone->zone_brand_data == NULL);
+
+ data = (lx_zone_data_t *)kmem_zalloc(sizeof (lx_zone_data_t), KM_SLEEP);
+ mutex_init(&data->lxzd_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /* No need to hold mutex now since zone_brand_data is not set yet. */
+
+ /*
+ * Set the default lxzd_kernel_version to 2.4.
+ * This can be changed by a call to setattr() during zone boot.
+ */
+ (void) strlcpy(data->lxzd_kernel_release, "2.4.21",
+ LX_KERN_RELEASE_MAX);
+ (void) strlcpy(data->lxzd_kernel_version, "BrandZ virtual linux",
+ LX_KERN_VERSION_MAX);
+
+ zone->zone_brand_data = data;
+
+ /*
+ * In Linux, if the init(1) process terminates the system panics.
+ * The zone must reboot to simulate this behaviour.
+ */
+ zone->zone_reboot_on_init_exit = B_TRUE;
+
+ /*
+ * We cannot hold the zone_status_lock while performing zfs operations
+ * so we drop the lock, get the zfs devs as the last step in this
+ * function, then reaquire the lock. Don't add any code after this
+ * which requires that the zone_status_lock was continuously held.
+ */
+ mutex_exit(zsl);
+
+ data->lxzd_vdisks = kmem_alloc(sizeof (list_t), KM_SLEEP);
+ list_create(data->lxzd_vdisks, sizeof (lx_virt_disk_t),
+ offsetof(lx_virt_disk_t, lxvd_link));
+
+ if (lx_zone_zfs_open(&lh, &data->lxzd_zfs_dev) == 0) {
+ minor_t emul_minor = 1;
+
+ lx_zone_get_zfsds(zone, &emul_minor);
+ lx_zone_get_zvols(zone, lh, &emul_minor);
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ } else {
+ /* Avoid matching any devices */
+ data->lxzd_zfs_dev = makedevice(-1, 0);
+ }
+ mutex_enter(zsl);
+}
+
+void
+lx_free_brand_data(zone_t *zone)
+{
+ lx_zone_data_t *data = ztolxzd(zone);
+ ASSERT(data != NULL);
+ mutex_enter(&data->lxzd_lock);
+ if (data->lxzd_ioctl_sock != NULL) {
+ /*
+ * Since zone_kcred has been cleaned up already, close the
+ * socket using the global kcred.
+ */
+ (void) ksocket_close(data->lxzd_ioctl_sock, kcred);
+ data->lxzd_ioctl_sock = NULL;
+ }
+ ASSERT(data->lxzd_cgroup == NULL);
+
+ lx_zone_cleanup_vdisks(data);
+
+ mutex_exit(&data->lxzd_lock);
+ zone->zone_brand_data = NULL;
+ mutex_destroy(&data->lxzd_lock);
+ kmem_free(data, sizeof (*data));
+}
+
+void
+lx_unsupported(char *dmsg)
+{
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ DTRACE_PROBE1(brand__lx__unsupported, char *, dmsg);
+
+ if (pd != NULL && (pd->l_flags & LX_PROC_STRICT_MODE) != 0) {
+ /*
+ * If this process was run with strict mode enabled
+ * (via LX_STRICT in the environment), we mark this
+ * LWP as having triggered an unsupported behaviour.
+ * This flag will be checked at an appropriate point
+ * by lx_check_strict_failure().
+ */
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ lwpd->br_strict_failure = B_TRUE;
+ }
+}
+
+void
+lx_check_strict_failure(lx_lwp_data_t *lwpd)
+{
+ proc_t *p;
+
+ if (!lwpd->br_strict_failure) {
+ return;
+ }
+
+ lwpd->br_strict_failure = B_FALSE;
+
+ /*
+ * If this process is operating in strict mode (via LX_STRICT in
+ * the environment), and has triggered a call to
+ * lx_unsupported(), we drop SIGSYS on it as we return.
+ */
+ p = curproc;
+ mutex_enter(&p->p_lock);
+ sigtoproc(p, curthread, SIGSYS);
+ mutex_exit(&p->p_lock);
+}
+
+void
+lx_trace_sysenter(int syscall_num, uintptr_t *args)
+{
+ if (lx_systrace_enabled) {
+ VERIFY(lx_systrace_entry_ptr != NULL);
+
+ (*lx_systrace_entry_ptr)(syscall_num, args[0], args[1],
+ args[2], args[3], args[4], args[5]);
+ }
+}
+
+void
+lx_trace_sysreturn(int syscall_num, long ret)
+{
+ if (lx_systrace_enabled) {
+ VERIFY(lx_systrace_return_ptr != NULL);
+
+ (*lx_systrace_return_ptr)(syscall_num, ret, ret, 0, 0, 0, 0);
+ }
+}
+
+/*
+ * Get the addresses of the user-space system call handler and attach it to
+ * the proc structure. Returning 0 indicates success; the value returned
+ * by the system call is the value stored in rval. Returning a non-zero
+ * value indicates a failure; the value returned is used to set errno, -1
+ * is returned from the syscall and the contents of rval are ignored. To
+ * set errno and have the syscall return a value other than -1 we can
+ * manually set errno and rval and return 0.
+ */
+int
+lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
+ uintptr_t arg3, uintptr_t arg4)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ lx_proc_data_t *pd;
+ struct termios *termios;
+ uint_t termios_len;
+ int error;
+ int code;
+ int sig;
+ lx_brand_registration_t reg;
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * There is one operation that is suppored for non-branded
+ * process. B_EXEC_BRAND. This is the equilivant of an
+ * exec call, but the new process that is created will be
+ * a branded process.
+ */
+ if (cmd == B_EXEC_BRAND) {
+ VERIFY(p->p_zone != NULL);
+ VERIFY(p->p_zone->zone_brand == &lx_brand);
+ return (exec_common(
+ (char *)arg1, (const char **)arg2, (const char **)arg3,
+ EBA_BRAND));
+ }
+
+ /* For all other operations this must be a branded process. */
+ if (p->p_brand == NULL)
+ return (ENOSYS);
+
+ VERIFY(p->p_brand == &lx_brand);
+ VERIFY(p->p_brand_data != NULL);
+
+ switch (cmd) {
+ case B_REGISTER:
+ if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ lx_print("stack mode was not PREINIT during "
+ "REGISTER\n");
+ return (EINVAL);
+ }
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ if (copyin((void *)arg1, &reg, sizeof (reg)) != 0) {
+ lx_print("Failed to copyin brand registration "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+ }
+#ifdef _LP64
+ else {
+ /* 32-bit userland on 64-bit kernel */
+ lx_brand_registration32_t reg32;
+
+ if (copyin((void *)arg1, &reg32, sizeof (reg32)) != 0) {
+ lx_print("Failed to copyin brand registration "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+
+ reg.lxbr_version = (uint_t)reg32.lxbr_version;
+ reg.lxbr_handler =
+ (void *)(uintptr_t)reg32.lxbr_handler;
+ reg.lxbr_flags = reg32.lxbr_flags;
+ }
+#endif
+
+ if (reg.lxbr_version != LX_VERSION_1) {
+ lx_print("Invalid brand library version (%u)\n",
+ reg.lxbr_version);
+ return (EINVAL);
+ }
+
+ if ((reg.lxbr_flags & ~LX_PROC_ALL) != 0) {
+ lx_print("Invalid brand flags (%u)\n",
+ reg.lxbr_flags);
+ return (EINVAL);
+ }
+
+ lx_print("Assigning brand 0x%p and handler 0x%p to proc 0x%p\n",
+ (void *)&lx_brand, (void *)reg.lxbr_handler, (void *)p);
+ pd = p->p_brand_data;
+ pd->l_handler = (uintptr_t)reg.lxbr_handler;
+ pd->l_flags = reg.lxbr_flags & LX_PROC_ALL;
+
+ /*
+ * We can't fix up our netstack from the lx_init_brand_data
+ * hook since that hook is run by zoneadmd (which has the GZ's
+ * stack). Instead, we fix it up when the init process starts
+ * inside the zone since it will have the proper stack.
+ * Note that it is conceivable that a Linux init could be
+ * illumos-aware and re-enable additional privileged ports,
+ * then exec(2) over itself. This would cause those settings to
+ * be lost, but this scenario is considered unlikely so we
+ * don't worry about it.
+ */
+ if (p->p_pid == p->p_zone->zone_proc_initpid)
+ lx_fix_netstack();
+
+ return (0);
+
+ case B_TTYMODES:
+ /* This is necessary for emulating TCGETS ioctls. */
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+ DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&termios,
+ &termios_len) != DDI_SUCCESS)
+ return (EIO);
+
+ ASSERT(termios_len == sizeof (*termios));
+
+ if (copyout(&termios, (void *)arg1, sizeof (termios)) != 0) {
+ ddi_prop_free(termios);
+ return (EFAULT);
+ }
+
+ ddi_prop_free(termios);
+ return (0);
+
+ case B_ELFDATA: {
+ mutex_enter(&p->p_lock);
+ pd = curproc->p_brand_data;
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_elf_data_t led;
+
+ bcopy(&pd->l_elf_data, &led, sizeof (led));
+ mutex_exit(&p->p_lock);
+
+ if (copyout(&led, (void *)arg1,
+ sizeof (lx_elf_data_t)) != 0) {
+ return (EFAULT);
+ }
+ }
+#if defined(_LP64)
+ else {
+ /* 32-bit userland on 64-bit kernel */
+ lx_elf_data32_t led32;
+
+ led32.ed_phdr = (int)pd->l_elf_data.ed_phdr;
+ led32.ed_phent = (int)pd->l_elf_data.ed_phent;
+ led32.ed_phnum = (int)pd->l_elf_data.ed_phnum;
+ led32.ed_entry = (int)pd->l_elf_data.ed_entry;
+ led32.ed_base = (int)pd->l_elf_data.ed_base;
+ led32.ed_ldentry = (int)pd->l_elf_data.ed_ldentry;
+ mutex_exit(&p->p_lock);
+
+ if (copyout(&led32, (void *)arg1,
+ sizeof (led32)) != 0) {
+ return (EFAULT);
+ }
+ }
+#endif
+ return (0);
+ }
+
+ case B_EXEC_NATIVE:
+ return (exec_common((char *)arg1, (const char **)arg2,
+ (const char **)arg3, EBA_NATIVE));
+
+ /*
+ * The B_TRUSS_POINT subcommand is used so that we can make a no-op
+ * syscall for debugging purposes (dtracing) from within the user-level
+ * emulation.
+ */
+ case B_TRUSS_POINT:
+ return (0);
+
+ case B_LPID_TO_SPAIR: {
+ /*
+ * Given a Linux pid as arg1, return the Solaris pid in arg2 and
+ * the Solaris LWP in arg3. We also translate pid 1 (which is
+ * hardcoded in many applications) to the zone's init process.
+ */
+ pid_t s_pid;
+ id_t s_tid;
+
+ if ((pid_t)arg1 == 1) {
+ s_pid = p->p_zone->zone_proc_initpid;
+ /* handle the dead/missing init(1M) case */
+ if (s_pid == -1)
+ s_pid = 1;
+ s_tid = 1;
+ } else if (lx_lpid_to_spair((pid_t)arg1, &s_pid, &s_tid) < 0) {
+ return (ESRCH);
+ }
+
+ if (copyout(&s_pid, (void *)arg2, sizeof (s_pid)) != 0 ||
+ copyout(&s_tid, (void *)arg3, sizeof (s_tid)) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+ }
+
+ case B_SIGEV_THREAD_ID: {
+ /*
+ * Emulate Linux's timer_create(2) SIGEV_THREAD_ID
+ * notification method. This mechanism is only meant
+ * for userland threading libraries such as glibc and
+ * is documented as such. Therefore, assume this is
+ * only ever invoked for the purpose of alerting a
+ * Linux threading library. Assume that the tid is a
+ * member of the caller's process and the signal
+ * number is valid. See lx_sigev_thread_id() for the
+ * userland side of this emulation.
+ *
+ * The return code from this function is not checked
+ * by the caller since it executes in an asynchronous
+ * context and there is nothing much to be done. If
+ * this function does fail then it will manifest as
+ * Linux threads waiting for a signal they will never
+ * receive.
+ *
+ * arg1 -- Linux tid
+ * arg2 -- Linux signal number
+ * arg3 -- sigval pointer
+ */
+
+ int native_sig = lx_ltos_signo((int)arg2, 0);
+ pid_t spid;
+ int stid;
+ sigqueue_t *sqp;
+
+ if (native_sig == 0)
+ return (EINVAL);
+
+ if (lx_lpid_to_spair((pid_t)arg1, &spid, &stid) != 0) {
+ return (ESRCH);
+ }
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+ mutex_enter(&curproc->p_lock);
+ if ((t = idtot(curproc, stid)) == NULL) {
+ mutex_exit(&curproc->p_lock);
+ kmem_free(sqp, sizeof (sigqueue_t));
+ return (ESRCH);
+ }
+
+ sqp->sq_info.si_signo = native_sig;
+ sqp->sq_info.si_code = SI_TIMER;
+ sqp->sq_info.si_pid = curproc->p_pid;
+ sqp->sq_info.si_zoneid = getzoneid();
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sqp->sq_info.si_value.sival_ptr = (void *)arg3;
+ sigaddqa(curproc, t, sqp);
+
+ mutex_exit(&curproc->p_lock);
+
+ return (0);
+ }
+
+ case B_SET_AFFINITY_MASK:
+ case B_GET_AFFINITY_MASK:
+ /*
+ * Retrieve or store the CPU affinity mask for the
+ * requested linux pid.
+ *
+ * arg1 is a linux PID (0 means curthread).
+ * arg2 is the size of the given mask.
+ * arg3 is the address of the affinity mask.
+ */
+ return (lx_sched_affinity(cmd, arg1, arg2, arg3, rval));
+
+ case B_PTRACE_STOP_FOR_OPT:
+ return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE, (ulong_t)arg3, arg4));
+
+ case B_PTRACE_CLONE_BEGIN:
+ return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE));
+
+ case B_PTRACE_SIG_RETURN: {
+ /*
+ * Our ptrace emulation must emit PR_SYSEXIT for rt_sigreturn.
+ * Since that syscall does not pass through the normal
+ * emulation, which would call lx_syscall_return, the event is
+ * emitted manually. A successful result of the syscall is
+ * assumed since there is little to be done in the face of
+ * failure.
+ */
+ struct regs *rp = lwptoregs(lwp);
+
+ rp->r_r0 = 0;
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+ return (0);
+ }
+
+ case B_UNSUPPORTED: {
+ char dmsg[256];
+
+ if (copyin((void *)arg1, &dmsg, sizeof (dmsg)) != 0) {
+ lx_print("Failed to copyin unsupported msg "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+ dmsg[255] = '\0';
+ lx_unsupported(dmsg);
+
+ lx_check_strict_failure(lwpd);
+
+ return (0);
+ }
+
+ case B_STORE_ARGS: {
+ /*
+ * B_STORE_ARGS subcommand
+ * arg1 = address of struct to be copied in
+ * arg2 = size of the struct being copied in
+ * arg3-arg6 ignored
+ * rval = the amount of data copied.
+ */
+ void *buf;
+
+ /* only have upper limit because arg2 is unsigned */
+ if (arg2 > LX_BR_ARGS_SIZE_MAX) {
+ return (EINVAL);
+ }
+
+ buf = kmem_alloc(arg2, KM_SLEEP);
+ if (copyin((void *)arg1, buf, arg2) != 0) {
+ lx_print("Failed to copyin scall arg at 0x%p\n",
+ (void *) arg1);
+ kmem_free(buf, arg2);
+ /*
+ * Purposely not setting br_scall_args to NULL
+ * to preserve data for debugging.
+ */
+ return (EFAULT);
+ }
+
+ if (lwpd->br_scall_args != NULL) {
+ ASSERT(lwpd->br_args_size > 0);
+ kmem_free(lwpd->br_scall_args,
+ lwpd->br_args_size);
+ }
+
+ lwpd->br_scall_args = buf;
+ lwpd->br_args_size = arg2;
+ *rval = arg2;
+ return (0);
+ }
+
+ case B_HELPER_CLONE:
+ return (lx_helper_clone(rval, arg1, (void *)arg2, (void *)arg3,
+ (void *)arg4));
+
+ case B_HELPER_SETGROUPS:
+ return (lx_helper_setgroups(arg1, (gid_t *)arg2));
+
+ case B_HELPER_SIGQUEUE:
+ return (lx_helper_rt_sigqueueinfo(arg1, arg2,
+ (siginfo_t *)arg3));
+
+ case B_HELPER_TGSIGQUEUE:
+ return (lx_helper_rt_tgsigqueueinfo(arg1, arg2, arg3,
+ (siginfo_t *)arg4));
+
+ case B_SET_THUNK_PID:
+ lwpd->br_lx_thunk_pid = arg1;
+ return (0);
+
+ case B_GETPID:
+ /*
+ * The usermode clone(2) code needs to be able to call
+ * lx_getpid() from native code:
+ */
+ *rval = lx_getpid();
+ return (0);
+
+ case B_SET_NATIVE_STACK:
+ /*
+ * B_SET_NATIVE_STACK subcommand
+ * arg1 = the base of the stack to use for emulation
+ */
+ if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ lx_print("B_SET_NATIVE_STACK when stack was already "
+ "set to %p\n", (void *)arg1);
+ return (EEXIST);
+ }
+
+ /*
+ * We move from the PREINIT state, where we have no brand
+ * emulation stack, to the INIT state. Here, we are still
+ * running on what will become the BRAND stack, but are running
+ * emulation (i.e. native) code. Once the initialisation
+ * process for this thread has finished, we will jump to
+ * brand-specific code, while moving to the BRAND mode.
+ *
+ * When a new LWP is created, lx_initlwp() will clear the
+ * stack data. If that LWP is actually being duplicated
+ * into a child process by fork(2), lx_forklwp() will copy
+ * it so that the cloned thread will keep using the same
+ * alternate stack.
+ */
+ lwpd->br_ntv_stack = arg1;
+ lwpd->br_stack_mode = LX_STACK_MODE_INIT;
+ lx_lwp_set_native_stack_current(lwpd, arg1);
+
+ return (0);
+
+ case B_GET_CURRENT_CONTEXT:
+ /*
+ * B_GET_CURRENT_CONTEXT subcommand:
+ * arg1 = address for pointer to current ucontext_t
+ */
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ caddr32_t addr = (caddr32_t)lwp->lwp_oldcontext;
+
+ error = copyout(&addr, (void *)arg1, sizeof (addr));
+ } else
+#endif
+ {
+ error = copyout(&lwp->lwp_oldcontext, (void *)arg1,
+ sizeof (lwp->lwp_oldcontext));
+ }
+
+ return (error != 0 ? EFAULT : 0);
+
+ case B_JUMP_TO_LINUX:
+ /*
+ * B_JUMP_TO_LINUX subcommand:
+ * arg1 = ucontext_t pointer for jump state
+ */
+
+ if (arg1 == NULL)
+ return (EINVAL);
+
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_NATIVE: {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * We are on the NATIVE stack, so we must preserve
+ * the extent of that stack. The pointer will be
+ * reset by a future setcontext().
+ */
+ lx_lwp_set_native_stack_current(lwpd,
+ (uintptr_t)rp->r_sp);
+ break;
+ }
+
+ case LX_STACK_MODE_INIT:
+ /*
+ * The LWP is transitioning to Linux code for the first
+ * time.
+ */
+ break;
+
+ case LX_STACK_MODE_PREINIT:
+ /*
+ * This LWP has not installed an alternate stack for
+ * usermode emulation handling.
+ */
+ return (ENOENT);
+
+ case LX_STACK_MODE_BRAND:
+ /*
+ * The LWP should not be on the BRAND stack.
+ */
+ exit(CLD_KILLED, SIGSYS);
+ return (0);
+ }
+
+ /*
+ * Transfer control to Linux:
+ */
+ return (lx_runexe(lwp, (void *)arg1));
+
+ case B_EMULATION_DONE:
+ /*
+ * B_EMULATION_DONE subcommand:
+ * arg1 = ucontext_t * to restore
+ * arg2 = system call number
+ * arg3 = return code
+ * arg4 = if operation failed, the errno value
+ */
+
+ /*
+ * The first part of this operation is a setcontext() to
+ * restore the register state to the copy we preserved
+ * before vectoring to the usermode emulation routine.
+ * If that fails, we return (hopefully) to the emulation
+ * routine and it will handle the error.
+ */
+#if (_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ error = getsetcontext32(SETCONTEXT, (void *)arg1);
+ } else
+#endif
+ {
+ error = getsetcontext(SETCONTEXT, (void *)arg1);
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * The saved Linux context has been restored. We handle the
+ * return value or errno with code common to the in-kernel
+ * system call emulation.
+ */
+ if ((error = (int)arg4) != 0) {
+ /*
+ * lx_syscall_return() looks at the errno in the LWP,
+ * so set it here:
+ */
+ (void) set_errno(error);
+ }
+ lx_syscall_return(ttolwp(curthread), (int)arg2, (long)arg3);
+
+ return (0);
+
+ case B_EXIT_AS_SIG:
+ code = CLD_KILLED;
+ sig = (int)arg1;
+ proc_is_exiting(p);
+ if (exitlwps(1) != 0) {
+ mutex_enter(&p->p_lock);
+ lwp_exit();
+ }
+ ttolwp(curthread)->lwp_cursig = sig;
+ if (sig == SIGSEGV) {
+ if (core(sig, 0) == 0)
+ code = CLD_DUMPED;
+ }
+ exit(code, sig);
+ /* NOTREACHED */
+ break;
+
+ case B_OVERRIDE_KERN_VER: {
+ void *urel = (void *)arg1;
+ void *uver = (void *)arg2;
+ size_t len;
+
+ pd = ptolxproc(p);
+ if (urel != NULL) {
+ if (copyinstr(urel, pd->l_uname_release,
+ LX_KERN_RELEASE_MAX, &len) != 0) {
+ return (EFAULT);
+ }
+ pd->l_uname_release[LX_KERN_RELEASE_MAX - 1] = '\0';
+ }
+ if (uver != NULL) {
+ if (copyinstr(uver, pd->l_uname_version,
+ LX_KERN_VERSION_MAX, &len) != 0) {
+ return (EFAULT);
+ }
+ pd->l_uname_version[LX_KERN_VERSION_MAX - 1] = '\0';
+ }
+
+ return (0);
+ }
+
+ case B_GET_PERSONALITY: {
+ unsigned int result;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ result = pd->l_personality;
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * Compare linux kernel version to the one set for the zone.
+ * Returns greater than 0 if zone version is higher, less than 0 if the zone
+ * version is lower, and 0 if the versions are equal.
+ */
+int
+lx_kern_release_cmp(zone_t *zone, const char *vers)
+{
+ int zvers[3] = {0, 0, 0};
+ int cvers[3] = {0, 0, 0};
+ int i;
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+ VERIFY(zone->zone_brand == &lx_brand);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) sscanf(lxzd->lxzd_kernel_release, "%d.%d.%d", &zvers[0],
+ &zvers[1], &zvers[2]);
+ mutex_exit(&lxzd->lxzd_lock);
+ (void) sscanf(vers, "%d.%d.%d", &cvers[0], &cvers[1], &cvers[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (zvers[i] > cvers[i]) {
+ return (1);
+ } else if (zvers[i] < cvers[i]) {
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Linux unconditionally removes the setuid and setgid bits when changing
+ * file ownership. This brand hook overrides the illumos native behaviour,
+ * which is based on the PRIV_FILE_SETID privilege.
+ */
+/* ARGSUSED */
+static int
+lx_setid_clear(vattr_t *vap, cred_t *cr)
+{
+ if (S_ISDIR(vap->va_mode)) {
+ return (0);
+ }
+
+ if (vap->va_mode & S_ISUID) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~S_ISUID;
+ }
+ if ((vap->va_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~S_ISGID;
+ }
+
+ return (0);
+}
+
+/*
+ * Copy the per-process brand data from a parent proc to a child.
+ */
+void
+lx_copy_procdata(proc_t *cp, proc_t *pp)
+{
+ lx_proc_data_t *cpd, *ppd;
+
+ /*
+ * Since b_copy_procdata is called during getproc(), while the child
+ * process is still being initialized, acquiring cp->p_lock should not
+ * be required.
+ */
+ VERIFY(cp->p_brand == &lx_brand);
+ VERIFY((cpd = cp->p_brand_data) != NULL);
+
+ mutex_enter(&pp->p_lock);
+ VERIFY(pp->p_brand == &lx_brand);
+ VERIFY((ppd = pp->p_brand_data) != NULL);
+
+ bcopy(ppd, cpd, sizeof (lx_proc_data_t));
+ mutex_exit(&pp->p_lock);
+
+ /*
+ * The l_ptrace count is normally manipulated only while under holding
+ * p_lock. Since this is a freshly created process, it's safe to zero
+ * out. If it is to be inherited, the attach will occur later.
+ */
+ cpd->l_ptrace = 0;
+
+ cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = LX_RLIM64_INFINITY;
+
+ cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = 20;
+ cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = 20;
+
+ cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = LX_RLIM64_INFINITY;
+
+ cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = LX_RLIM64_INFINITY;
+}
+
+#if defined(_LP64)
+static void
+Ehdr32to64(Elf32_Ehdr *src, Ehdr *dst)
+{
+ bcopy(src->e_ident, dst->e_ident, sizeof (src->e_ident));
+ dst->e_type = src->e_type;
+ dst->e_machine = src->e_machine;
+ dst->e_version = src->e_version;
+ dst->e_entry = src->e_entry;
+ dst->e_phoff = src->e_phoff;
+ dst->e_shoff = src->e_shoff;
+ dst->e_flags = src->e_flags;
+ dst->e_ehsize = src->e_ehsize;
+ dst->e_phentsize = src->e_phentsize;
+ dst->e_phnum = src->e_phnum;
+ dst->e_shentsize = src->e_shentsize;
+ dst->e_shnum = src->e_shnum;
+ dst->e_shstrndx = src->e_shstrndx;
+}
+#endif /* _LP64 */
+
+static void
+restoreexecenv(struct execenv *ep, stack_t *sp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+
+ setexecenv(ep);
+ lwp->lwp_sigaltstack.ss_sp = sp->ss_sp;
+ lwp->lwp_sigaltstack.ss_size = sp->ss_size;
+ lwp->lwp_sigaltstack.ss_flags = sp->ss_flags;
+}
+
+extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
+ long *, int, caddr_t, cred_t *, int *);
+
+extern int elf32exec(struct vnode *, execa_t *, uarg_t *, intpdata_t *, int,
+ long *, int, caddr_t, cred_t *, int *);
+
+static uintptr_t
+lx_map_vdso(struct uarg *args, struct cred *cred)
+{
+ int err;
+ char *fpath = LX_VDSO_PATH;
+ vnode_t *vp;
+ vattr_t attr;
+ caddr_t addr;
+
+#if defined(_LP64)
+ if (args->to_model != DATAMODEL_NATIVE) {
+ fpath = LX_VDSO_PATH32;
+ }
+#endif
+
+ /*
+ * The comm page should have been mapped in already.
+ */
+ if (args->commpage == NULL) {
+ return (NULL);
+ }
+
+ /*
+ * Ensure the VDSO library is present and appropriately sized.
+ * This lookup is started at the zone root to avoid complications for
+ * processes which have chrooted. For the specified lookup root to be
+ * used, the leading slash must be dropped from the path.
+ */
+ ASSERT(fpath[0] == '/');
+ fpath++;
+ if (lookupnameat(fpath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp,
+ curzone->zone_rootvp) != 0) {
+ return (NULL);
+ }
+
+ /*
+ * The VDSO requires data exposed via the comm page in order to
+ * function properly. The VDSO is always mapped in at a fixed known
+ * offset from the comm page, providing an easy means to locate it.
+ */
+ addr = (caddr_t)(args->commpage - LX_VDSO_SIZE);
+ attr.va_mask = AT_SIZE;
+ if (VOP_GETATTR(vp, &attr, 0, cred, NULL) != 0 ||
+ attr.va_size > LX_VDSO_SIZE) {
+ VN_RELE(vp);
+ return (NULL);
+ }
+
+ err = execmap(vp, addr, attr.va_size, 0, 0,
+ PROT_USER|PROT_READ|PROT_EXEC, 1, 0);
+ VN_RELE(vp);
+ if (err != 0) {
+ return (NULL);
+ }
+ return ((uintptr_t)addr);
+}
+
+/*
+ * Exec routine called by elfexec() to load either 32-bit or 64-bit Linux
+ * binaries.
+ */
+/* ARGSUSED4 */
+static int
+lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+ struct intpdata *idata, int level, long *execsz, int setid,
+ caddr_t exec_file, struct cred *cred, int *brand_action)
+{
+ int error, i;
+ vnode_t *nvp;
+ Ehdr ehdr;
+ Addr uphdr_vaddr;
+ intptr_t voffset;
+ char *interp = NULL;
+ uintptr_t ldaddr = NULL;
+ proc_t *p = ttoproc(curthread);
+ klwp_t *lwp = ttolwp(curthread);
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ struct execenv env, origenv;
+ stack_t orig_sigaltstack;
+ struct user *up = PTOU(ttoproc(curthread));
+ lx_elf_data_t edp;
+ char *lib_path = LX_LIB_PATH;
+ boolean_t execstk = B_TRUE;
+ unsigned int personality;
+
+ ASSERT(p->p_brand == &lx_brand);
+ ASSERT(lxpd != NULL);
+
+ /*
+ * Start with a separate struct for ELF data instead of inheriting
+ * values from the currently running binary. This ensures that fields
+ * such as ed_base are cleared if the new binary does not utilize an
+ * interpreter.
+ */
+ bzero(&edp, sizeof (edp));
+
+#if defined(_LP64)
+ if (args->to_model != DATAMODEL_NATIVE) {
+ lib_path = LX_LIB_PATH32;
+ }
+#endif
+
+ /*
+ * Set the brandname and library name for the new process so that
+ * elfexec() puts them onto the stack.
+ */
+ args->brandname = LX_BRANDNAME;
+ args->emulator = lib_path;
+
+#if defined(_LP64)
+ /*
+ * To conform with the way Linux lays out the address space, we clamp
+ * the stack to be the top of the lower region of the x86-64 canonical
+ * form address space -- which has the side-effect of laying out the
+ * entire address space in that lower region. Note that this only
+ * matters on 64-bit processes (this value will always be greater than
+ * the size of a 32-bit address space) and doesn't actually affect
+ * USERLIMIT: if a Linux-branded processes wishes to map something
+ * into the top half of the address space, it can do so -- but with
+ * the user stack starting at the top of the bottom region, those high
+ * virtual addresses won't be used unless explicitly directed.
+ */
+ args->maxstack = lx_maxstack64;
+#endif
+
+ /*
+ * Search the binary for a PT_GNU_STACK header. The PF_X bit contained
+ * within is used to dictate protection defaults for the stack, among
+ * other things.
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ Ehdr ehdr;
+ Phdr *phdrp;
+ caddr_t phdrbase = NULL;
+ ssize_t phdrsize = 0;
+ int nphdrs, hsize;
+
+ if ((error = elfreadhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+ &phdrsize)) != 0) {
+ return (error);
+ }
+
+ hsize = ehdr.e_phentsize;
+ /* LINTED: alignment */
+ phdrp = (Phdr *)phdrbase;
+ for (i = nphdrs; i > 0; i--) {
+ switch (phdrp->p_type) {
+ case PT_GNU_STACK:
+ if ((phdrp->p_flags & PF_X) == 0) {
+ execstk = B_FALSE;
+ }
+ break;
+ }
+ /* LINTED: alignment */
+ phdrp = (Phdr *)((caddr_t)phdrp + hsize);
+ }
+ kmem_free(phdrbase, phdrsize);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdrp;
+ caddr_t phdrbase = NULL;
+ ssize_t phdrsize = 0;
+ int nphdrs, hsize;
+
+ if ((error = elf32readhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+ &phdrsize)) != 0) {
+ return (error);
+ }
+
+ hsize = ehdr.e_phentsize;
+ /* LINTED: alignment */
+ phdrp = (Elf32_Phdr *)phdrbase;
+ for (i = nphdrs; i > 0; i--) {
+ switch (phdrp->p_type) {
+ case PT_GNU_STACK:
+ if ((phdrp->p_flags & PF_X) == 0) {
+ execstk = B_FALSE;
+ }
+ break;
+ }
+ /* LINTED: alignment */
+ phdrp = (Elf32_Phdr *)((caddr_t)phdrp + hsize);
+ }
+ kmem_free(phdrbase, phdrsize);
+ }
+#endif
+
+ /*
+ * Revert the base personality while maintaining any existing flags.
+ */
+ personality = LX_PER_LINUX | (lxpd->l_personality & ~LX_PER_MASK);
+
+ /*
+ * Linux defaults to an executable stack unless the aformentioned
+ * PT_GNU_STACK entry in the elf header dictates otherwise. Enabling
+ * the READ_IMPLIES_EXEC personality flag is also implied in this case.
+ */
+ if (execstk) {
+ args->stk_prot |= PROT_EXEC;
+ args->stk_prot_override = B_TRUE;
+ personality |= LX_PER_READ_IMPLIES_EXEC;
+ }
+
+ /*
+ * We will first exec the brand library, then map in the linux
+ * executable and the linux linker.
+ */
+ if ((error = lookupname(lib_path, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &nvp))) {
+ uprintf("%s: not found.", lib_path);
+ return (error);
+ }
+
+ /*
+ * We will eventually set the p_exec member to be the vnode for the new
+ * executable when we call setexecenv(). However, if we get an error
+ * before that call we need to restore the execenv to its original
+ * values so that when we return to the caller fop_close() works
+ * properly while cleaning up from the failed exec(). Restoring the
+ * original value will also properly decrement the 2nd VN_RELE that we
+ * took on the brand library.
+ */
+ origenv.ex_bssbase = p->p_bssbase;
+ origenv.ex_brkbase = p->p_brkbase;
+ origenv.ex_brksize = p->p_brksize;
+ origenv.ex_vp = p->p_exec;
+ orig_sigaltstack.ss_sp = lwp->lwp_sigaltstack.ss_sp;
+ orig_sigaltstack.ss_size = lwp->lwp_sigaltstack.ss_size;
+ orig_sigaltstack.ss_flags = lwp->lwp_sigaltstack.ss_flags;
+
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = elfexec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+ execsz, setid, exec_file, cred, brand_action);
+ }
+#if defined(_LP64)
+ else {
+ error = elf32exec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+ execsz, setid, exec_file, cred, brand_action);
+ }
+#endif
+ VN_RELE(nvp);
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ return (error);
+ }
+
+ /*
+ * exec-ed in the brand library above.
+ * The u_auxv vectors are now setup by elfexec to point to the
+ * brand emulation library and its linker.
+ */
+
+ /*
+ * After execing the brand library (which should have implicitly mapped
+ * in the comm page), map the VDSO into the approprate place in the AS.
+ */
+ lxpd->l_vdso = lx_map_vdso(args, cred);
+
+ bzero(&env, sizeof (env));
+
+ /*
+ * map in the the Linux executable
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
+ &voffset, exec_file, &interp, &env.ex_bssbase,
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr32;
+ Elf32_Addr uphdr_vaddr32;
+
+ error = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
+ &voffset, exec_file, &interp, &env.ex_bssbase,
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+
+ Ehdr32to64(&ehdr32, &ehdr);
+
+ if (uphdr_vaddr32 == (Elf32_Addr)-1)
+ uphdr_vaddr = (Addr)-1;
+ else
+ uphdr_vaddr = uphdr_vaddr32;
+ }
+#endif
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+
+ if (interp != NULL)
+ kmem_free(interp, MAXPATHLEN);
+
+ return (error);
+ }
+
+ /*
+ * Save off the important properties of the lx executable. The brand
+ * library will ask us for this data later, when it is ready to set
+ * things up for the lx executable.
+ */
+ edp.ed_phdr = (uphdr_vaddr == -1) ? voffset + ehdr.e_phoff :
+ voffset + uphdr_vaddr;
+ edp.ed_entry = voffset + ehdr.e_entry;
+ edp.ed_phent = ehdr.e_phentsize;
+ edp.ed_phnum = ehdr.e_phnum;
+
+ if (interp != NULL) {
+ if (ehdr.e_type == ET_DYN) {
+ /*
+ * This is a shared object executable, so we need to
+ * pick a reasonable place to put the heap. Just don't
+ * use the first page.
+ */
+ env.ex_brkbase = (caddr_t)PAGESIZE;
+ env.ex_bssbase = (caddr_t)PAGESIZE;
+ }
+
+ /*
+ * If the program needs an interpreter (most do), map it in and
+ * store relevant information about it in the aux vector, where
+ * the brand library can find it.
+ */
+ if ((error = lookupname(interp, UIO_SYSSPACE, FOLLOW,
+ NULLVPP, &nvp))) {
+ uprintf("%s: not found.", interp);
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ kmem_free(interp, MAXPATHLEN);
+ return (error);
+ }
+
+ kmem_free(interp, MAXPATHLEN);
+ interp = NULL;
+
+ /*
+ * map in the Linux linker
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = mapexec_brand(nvp, args, &ehdr,
+ &uphdr_vaddr, &voffset, exec_file, NULL, NULL,
+ NULL, NULL, NULL, &ldaddr);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr32;
+ Elf32_Addr uphdr_vaddr32;
+
+ error = mapexec32_brand(nvp, args, &ehdr32,
+ &uphdr_vaddr32, &voffset, exec_file, NULL, NULL,
+ NULL, NULL, NULL, &ldaddr);
+
+ Ehdr32to64(&ehdr32, &ehdr);
+
+ if (uphdr_vaddr32 == (Elf32_Addr)-1)
+ uphdr_vaddr = (Addr)-1;
+ else
+ uphdr_vaddr = uphdr_vaddr32;
+ }
+#endif
+
+ VN_RELE(nvp);
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ return (error);
+ }
+
+ /*
+ * Now that we know the base address of the brand's linker,
+ * we also save this for later use by the brand library.
+ */
+ edp.ed_base = voffset;
+ edp.ed_ldentry = voffset + ehdr.e_entry;
+ } else {
+ /*
+ * This program has no interpreter. The lx brand library will
+ * jump to the address in the AT_SUN_BRAND_LDENTRY aux vector,
+ * so in this case, put the entry point of the main executable
+ * there.
+ */
+ if (ehdr.e_type == ET_EXEC) {
+ /*
+ * An executable with no interpreter, this must be a
+ * statically linked executable, which means we loaded
+ * it at the address specified in the elf header, in
+ * which case the e_entry field of the elf header is an
+ * absolute address.
+ */
+ edp.ed_ldentry = ehdr.e_entry;
+ edp.ed_entry = ehdr.e_entry;
+ } else {
+ /*
+ * A shared object with no interpreter, we use the
+ * calculated address from above.
+ */
+ edp.ed_ldentry = edp.ed_entry;
+
+ /*
+ * In all situations except an ET_DYN elf object with no
+ * interpreter, we want to leave the brk and base
+ * values set by mapexec_brand alone. Normally when
+ * running ET_DYN objects on Solaris (most likely
+ * /lib/ld.so.1) the kernel sets brk and base to 0 since
+ * it doesn't know where to put the heap, and later the
+ * linker will call brk() to initialize the heap in:
+ * usr/src/cmd/sgs/rtld/common/setup.c:setup()
+ * after it has determined where to put it. (This
+ * decision is made after the linker loads and inspects
+ * elf properties of the target executable being run.)
+ *
+ * So for ET_DYN Linux executables, we also don't know
+ * where the heap should go, so we'll set the brk and
+ * base to 0. But in this case the Solaris linker will
+ * not initialize the heap, so when the Linux linker
+ * starts running there is no heap allocated. This
+ * seems to be ok on Linux 2.4 based systems because the
+ * Linux linker/libc fall back to using mmap() to
+ * allocate memory. But on 2.6 systems, running
+ * applications by specifying them as command line
+ * arguments to the linker results in segfaults for an
+ * as yet undetermined reason (which seems to indicatej
+ * that a more permanent fix for heap initalization in
+ * these cases may be necessary).
+ */
+ if (ehdr.e_type == ET_DYN) {
+ env.ex_bssbase = (caddr_t)0;
+ env.ex_brkbase = (caddr_t)0;
+ env.ex_brksize = 0;
+ }
+ }
+ }
+
+ env.ex_vp = vp;
+ setexecenv(&env);
+
+ /*
+ * We try to keep /proc's view of the aux vector consistent with
+ * what's on the process stack.
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ auxv_t phdr_auxv[4] = {
+ { AT_SUN_BRAND_LX_PHDR, 0 },
+ { AT_SUN_BRAND_LX_INTERP, 0 },
+ { AT_SUN_BRAND_LX_CLKTCK, 0 },
+ { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+ };
+ phdr_auxv[0].a_un.a_val = edp.ed_phdr;
+ phdr_auxv[1].a_un.a_val = ldaddr;
+ phdr_auxv[2].a_un.a_val = hz;
+ phdr_auxv[3].a_un.a_val = lxpd->l_vdso;
+
+ if (copyout(&phdr_auxv, args->auxp_brand,
+ sizeof (phdr_auxv)) == -1)
+ return (EFAULT);
+ }
+#if defined(_LP64)
+ else {
+ auxv32_t phdr_auxv32[4] = {
+ { AT_SUN_BRAND_LX_PHDR, 0 },
+ { AT_SUN_BRAND_LX_INTERP, 0 },
+ { AT_SUN_BRAND_LX_CLKTCK, 0 },
+ { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+ };
+ phdr_auxv32[0].a_un.a_val = edp.ed_phdr;
+ phdr_auxv32[1].a_un.a_val = ldaddr;
+ phdr_auxv32[2].a_un.a_val = hz;
+ phdr_auxv32[3].a_un.a_val = lxpd->l_vdso;
+
+ if (copyout(&phdr_auxv32, args->auxp_brand,
+ sizeof (phdr_auxv32)) == -1)
+ return (EFAULT);
+ }
+#endif
+
+ /*
+ * /proc uses the AT_ENTRY aux vector entry to deduce
+ * the location of the executable in the address space. The user
+ * structure contains a copy of the aux vector that needs to have those
+ * entries patched with the values of the real lx executable (they
+ * currently contain the values from the lx brand library that was
+ * elfexec'd, above).
+ *
+ * For live processes, AT_BASE is used to locate the linker segment,
+ * which /proc and friends will later use to find Solaris symbols
+ * (such as rtld_db_preinit). However, for core files, /proc uses
+ * AT_ENTRY to find the right segment to label as the executable.
+ * So we set AT_ENTRY to be the entry point of the linux executable,
+ * but leave AT_BASE to be the address of the Solaris linker.
+ */
+ for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
+ switch (up->u_auxv[i].a_type) {
+ case AT_ENTRY:
+ up->u_auxv[i].a_un.a_val = edp.ed_entry;
+ break;
+
+ case AT_SUN_BRAND_LX_PHDR:
+ up->u_auxv[i].a_un.a_val = edp.ed_phdr;
+ break;
+
+ case AT_SUN_BRAND_LX_INTERP:
+ up->u_auxv[i].a_un.a_val = ldaddr;
+ break;
+
+ case AT_SUN_BRAND_LX_CLKTCK:
+ up->u_auxv[i].a_un.a_val = hz;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Record the brand ELF data and new personality now that the exec has
+ * proceeded successfully.
+ */
+ bcopy(&edp, &lxpd->l_elf_data, sizeof (edp));
+ lxpd->l_personality = personality;
+
+ return (0);
+}
+
+boolean_t
+lx_native_exec(uint8_t osabi, const char **interp)
+{
+ if (osabi != ELFOSABI_SOLARIS)
+ return (B_FALSE);
+
+ /*
+ * If the process root matches the zone root, prepend /native to the
+ * interpreter path for native executables. Absolute precision from
+ * VN_CMP is not necessary since any change of process root is likely
+ * to make native binaries inaccessible via /native.
+ *
+ * Processes which chroot directly into /native will be able to
+ * function as expected with no need for the prefix.
+ */
+ if (VN_CMP(curproc->p_user.u_rdir, curproc->p_zone->zone_rootvp)) {
+ *interp = "/native";
+ }
+
+ return (B_TRUE);
+}
+
+static void
+lx_syscall_init(void)
+{
+ int i;
+
+ /*
+ * Count up the 32-bit Linux system calls. Note that lx_sysent32
+ * has (LX_NSYSCALLS + 1) entries.
+ */
+ for (i = 0; i <= LX_NSYSCALLS && lx_sysent32[i].sy_name != NULL; i++)
+ continue;
+ lx_nsysent32 = i;
+
+#if defined(_LP64)
+ /*
+ * Count up the 64-bit Linux system calls. Note that lx_sysent64
+ * has (LX_NSYSCALLS + 1) entries.
+ */
+ for (i = 0; i <= LX_NSYSCALLS && lx_sysent64[i].sy_name != NULL; i++)
+ continue;
+ lx_nsysent64 = i;
+#endif
+}
+
+int
+_init(void)
+{
+ int err = 0;
+
+ lx_syscall_init();
+ lx_pid_init();
+ lx_ioctl_init();
+ lx_futex_init();
+ lx_ptrace_init();
+ lx_socket_init();
+
+ err = mod_install(&modlinkage);
+ if (err != 0) {
+ cmn_err(CE_WARN, "Couldn't install lx brand module");
+
+ /*
+ * This looks drastic, but it should never happen. These
+ * two data structures should be completely free-able until
+ * they are used by Linux processes. Since the brand
+ * wasn't loaded there should be no Linux processes, and
+ * thus no way for these data structures to be modified.
+ */
+ lx_pid_fini();
+ lx_ioctl_fini();
+ if (lx_futex_fini())
+ panic("lx brand module cannot be loaded or unloaded.");
+ }
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err;
+ int futex_done = 0;
+
+ /*
+ * If there are any zones using this brand, we can't allow it to be
+ * unloaded.
+ */
+ if (brand_zone_count(&lx_brand))
+ return (EBUSY);
+
+ lx_ptrace_fini();
+ lx_pid_fini();
+ lx_ioctl_fini();
+ lx_socket_fini();
+
+ if ((err = lx_futex_fini()) != 0) {
+ goto done;
+ }
+ futex_done = 1;
+
+ err = mod_remove(&modlinkage);
+
+done:
+ if (err) {
+ /*
+ * If we can't unload the module, then we have to get it
+ * back into a sane state.
+ */
+ lx_ptrace_init();
+ lx_pid_init();
+ lx_ioctl_init();
+ lx_socket_init();
+
+ if (futex_done) {
+ lx_futex_init();
+ }
+ }
+
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_misc.c b/usr/src/uts/common/brand/lx/os/lx_misc.c
new file mode 100644
index 0000000000..01fbfca382
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_misc.c
@@ -0,0 +1,1124 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016, Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/archsystm.h>
+#include <sys/privregs.h>
+#include <sys/exec.h>
+#include <sys/lwp.h>
+#include <sys/sem.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_siginfo.h>
+#include <sys/lx_futex.h>
+#include <lx_errno.h>
+#include <sys/cmn_err.h>
+#include <sys/siginfo.h>
+#include <sys/contract/process_impl.h>
+#include <sys/x86_archext.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <net/if.h>
+#include <inet/ip6.h>
+#include <sys/sunddi.h>
+#include <sys/dlpi.h>
+#include <sys/sysmacros.h>
+
+/* Linux specific functions and definitions */
+static void lx_save(klwp_t *);
+static void lx_restore(klwp_t *);
+
+/*
+ * Set the return code for the forked child, always zero
+ */
+/*ARGSUSED*/
+void
+lx_setrval(klwp_t *lwp, int v1, int v2)
+{
+ lwptoregs(lwp)->r_r0 = 0;
+}
+
+/*
+ * Reset process state on exec(2)
+ */
+void
+lx_exec()
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = ttoproc(curthread);
+ lx_proc_data_t *pd = ptolxproc(p);
+ struct regs *rp = lwptoregs(lwp);
+
+ /* b_exec is called without p_lock held */
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * Any l_handler handlers set as a result of B_REGISTER are now
+ * invalid; clear them.
+ */
+ pd->l_handler = NULL;
+
+ /*
+ * If this was a multi-threaded Linux process and this lwp wasn't the
+ * main lwp, then we need to make its Illumos and Linux PIDs match.
+ */
+ if (curthread->t_tid != 1) {
+ lx_pid_reassign(curthread);
+ }
+
+ /*
+ * Inform ptrace(2) that we are processing an execve(2) call so that if
+ * we are traced we can post either the PTRACE_EVENT_EXEC event or the
+ * legacy SIGTRAP.
+ */
+ (void) lx_ptrace_stop_for_option(LX_PTRACE_O_TRACEEXEC, B_FALSE, 0, 0);
+
+ /* clear the fs/gsbase values until the app. can reinitialize them */
+ lwpd->br_lx_fsbase = NULL;
+ lwpd->br_ntv_fsbase = NULL;
+ lwpd->br_lx_gsbase = NULL;
+ lwpd->br_ntv_gsbase = NULL;
+
+ /*
+ * Clear the native stack flags. This will be reinitialised by
+ * lx_init() in the new process image.
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+ lwpd->br_ntv_stack = 0;
+ lwpd->br_ntv_stack_current = 0;
+
+ installctx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL, lx_save,
+ NULL);
+
+ /*
+ * clear out the tls array
+ */
+ bzero(lwpd->br_tls, sizeof (lwpd->br_tls));
+
+ /*
+ * reset the tls entries in the gdt
+ */
+ kpreempt_disable();
+ lx_restore(lwp);
+ kpreempt_enable();
+
+ /* Grab the updated argv bounds */
+ mutex_enter(&p->p_lock);
+ lx_read_argv_bounds(p);
+ mutex_exit(&p->p_lock);
+
+ /*
+ * The exec syscall doesn't return (so we don't call lx_syscall_return)
+ * but for our ptrace emulation we need to do this so that a tracer
+ * does not get out of sync. We know that by the time this lx_exec
+ * function is called that the exec has succeeded.
+ */
+ rp->r_r0 = 0;
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+}
+
+static void
+lx_cleanlwp(klwp_t *lwp, proc_t *p)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ void *rb_list = NULL;
+
+ VERIFY(lwpd != NULL);
+
+ mutex_enter(&p->p_lock);
+ if ((lwpd->br_ptrace_flags & LX_PTF_EXITING) == 0) {
+ lx_ptrace_exit(p, lwp);
+ }
+
+ /*
+ * While we have p_lock, safely grab any robust_list references and
+ * clear the lwp field.
+ */
+ sprlock_proc(p);
+ rb_list = lwpd->br_robust_list;
+ lwpd->br_robust_list = NULL;
+ sprunlock(p);
+
+ if (rb_list != NULL) {
+ lx_futex_robust_exit((uintptr_t)rb_list, lwpd->br_pid);
+ }
+}
+
+void
+lx_exitlwp(klwp_t *lwp)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ kthread_t *t;
+ sigqueue_t *sqp = NULL;
+ pid_t ppid;
+ id_t ptid;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ if (lwpd == NULL) {
+ /* second time thru' */
+ return;
+ }
+
+ lx_cleanlwp(lwp, p);
+
+ if (lwpd->br_clear_ctidp != NULL) {
+ (void) suword32(lwpd->br_clear_ctidp, 0);
+ (void) lx_futex((uintptr_t)lwpd->br_clear_ctidp, FUTEX_WAKE, 1,
+ NULL, NULL, 0);
+ lwpd->br_clear_ctidp = NULL;
+ }
+
+ if (lwpd->br_signal != 0) {
+ /*
+ * The first thread in a process doesn't cause a signal to
+ * be sent when it exits. It was created by a fork(), not
+ * a clone(), so the parent should get signalled when the
+ * process exits.
+ */
+ if (lwpd->br_ptid == -1)
+ goto free;
+
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+ /*
+ * If br_ppid is 0, it means this is a CLONE_PARENT thread,
+ * so the signal goes to the parent process - not to a
+ * specific thread in this process.
+ */
+ p = lwptoproc(lwp);
+ if (lwpd->br_ppid == 0) {
+ mutex_enter(&p->p_lock);
+ ppid = p->p_ppid;
+ t = NULL;
+ } else {
+ /*
+ * If we have been reparented to init or if our
+ * parent thread is gone, then nobody gets
+ * signaled.
+ */
+ if ((lx_lwp_ppid(lwp, &ppid, &ptid) == 1) ||
+ (ptid == -1))
+ goto free;
+
+ mutex_enter(&pidlock);
+ if ((p = prfind(ppid)) == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ goto free;
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if ((t = idtot(p, ptid)) == NULL) {
+ mutex_exit(&p->p_lock);
+ goto free;
+ }
+ }
+
+ sqp->sq_info.si_signo = lwpd->br_signal;
+ sqp->sq_info.si_code = lwpd->br_exitwhy;
+ sqp->sq_info.si_status = lwpd->br_exitwhat;
+ sqp->sq_info.si_pid = lwpd->br_pid;
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(p, t, sqp);
+ mutex_exit(&p->p_lock);
+ sqp = NULL;
+ }
+
+free:
+ if (lwpd->br_scall_args != NULL) {
+ ASSERT(lwpd->br_args_size > 0);
+ kmem_free(lwpd->br_scall_args, lwpd->br_args_size);
+ }
+ if (sqp)
+ kmem_free(sqp, sizeof (sigqueue_t));
+}
+
+void
+lx_freelwp(klwp_t *lwp)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ lx_zone_data_t *lxzdata;
+ vfs_t *cgrp;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ if (lwpd == NULL) {
+ /*
+ * There is one case where an LX branded process will possess
+ * LWPs which lack their own brand data. During the course of
+ * executing native binary, the process will be preemptively
+ * branded to allow hooks such as b_native_exec to function.
+ * If that process possesses multiple LWPS, they will _not_ be
+ * branded since they will exit if the exec succeeds. It's
+ * during this LWP exit that lx_freelwp would be called on an
+ * unbranded LWP. When that is the case, it is acceptable to
+ * bypass the hook.
+ */
+ return;
+ }
+
+ /* cgroup integration */
+ lxzdata = ztolxzd(p->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ cgrp = lxzdata->lxzd_cgroup;
+ if (cgrp != NULL) {
+ VFS_HOLD(cgrp);
+ mutex_exit(&lxzdata->lxzd_lock);
+ ASSERT(lx_cgrp_freelwp != NULL);
+ (*lx_cgrp_freelwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+ lwpd->br_pid);
+ VFS_RELE(cgrp);
+ } else {
+ mutex_exit(&lxzdata->lxzd_lock);
+ }
+
+ /*
+ * It is possible for the lx_freelwp hook to be called without a prior
+ * call to lx_exitlwp being made. This happens as part of lwp
+ * de-branding when a native binary is executed from a branded process.
+ *
+ * To cover all cases, lx_cleanlwp is called from lx_exitlwp as well
+ * here in lx_freelwp. When the second call is redundant, the
+ * resources will already be freed and no work will be needed.
+ */
+ lx_cleanlwp(lwp, p);
+
+ /*
+ * Remove our system call interposer.
+ */
+ lwp->lwp_brand_syscall = NULL;
+
+ (void) removectx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL,
+ lx_save, NULL);
+ if (lwpd->br_pid != 0) {
+ lx_pid_rele(lwptoproc(lwp)->p_pid, lwptot(lwp)->t_tid);
+ }
+
+ /*
+ * Ensure that lx_ptrace_exit() has been called to detach
+ * ptrace(2) tracers and tracees.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == NULL);
+ VERIFY(lwpd->br_ptrace_accord == NULL);
+
+ lwp->lwp_brand = NULL;
+ kmem_free(lwpd, sizeof (struct lx_lwp_data));
+}
+
+void *
+lx_lwpdata_alloc(proc_t *p)
+{
+ lx_lwp_data_t *lwpd;
+ struct lx_pid *lpidp;
+ pid_t newpid = 0;
+ struct pid *pidp = NULL;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * LWPs beyond the first will require a pid to be allocated to emulate
+ * Linux's goofy thread model. While this allocation may be
+ * unnecessary when a single-lwp process undergoes branding, it cannot
+ * be performed during b_initlwp due to p_lock being held.
+ */
+ if (p->p_lwpcnt > 0) {
+ if ((newpid = pid_allocate(p, 0, 0)) < 0) {
+ return (NULL);
+ }
+ pidp = pid_find(newpid);
+ }
+
+ lwpd = kmem_zalloc(sizeof (struct lx_lwp_data), KM_SLEEP);
+ lpidp = kmem_zalloc(sizeof (struct lx_pid), KM_SLEEP);
+
+ lpidp->l_pid = newpid;
+ lpidp->l_pidp = pidp;
+ lwpd->br_lpid = lpidp;
+ return (lwpd);
+}
+
+/*
+ * Free lwp brand data if an error occurred during lwp_create.
+ * Otherwise, lx_freelwp will be used to free the resources after they're
+ * associated with the lwp via lx_initlwp.
+ */
+void
+lx_lwpdata_free(void *lwpbd)
+{
+ lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+ VERIFY(lwpd != NULL);
+ VERIFY(lwpd->br_lpid != NULL);
+
+ if (lwpd->br_lpid->l_pidp != NULL) {
+ (void) pid_rele(lwpd->br_lpid->l_pidp);
+ }
+ kmem_free(lwpd->br_lpid, sizeof (*lwpd->br_lpid));
+ kmem_free(lwpd, sizeof (*lwpd));
+}
+
+void
+lx_initlwp(klwp_t *lwp, void *lwpbd)
+{
+ lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ kthread_t *tp = lwptot(lwp);
+ proc_t *p = lwptoproc(lwp);
+ lx_zone_data_t *lxzdata;
+ vfs_t *cgrp;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(lwp->lwp_brand == NULL);
+
+ lwpd->br_exitwhy = CLD_EXITED;
+ lwpd->br_lwp = lwp;
+ lwpd->br_clear_ctidp = NULL;
+ lwpd->br_set_ctidp = NULL;
+ lwpd->br_signal = 0;
+ lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+ /*
+ * lwpd->br_affinitymask was zeroed by kmem_zalloc()
+ * as was lwpd->br_scall_args and lwpd->br_args_size.
+ */
+
+ /*
+ * The first thread in a process has ppid set to the parent
+ * process's pid, and ptid set to -1. Subsequent threads in the
+ * process have their ppid set to the pid of the thread that
+ * created them, and their ptid to that thread's tid.
+ */
+ if (tp->t_next == tp) {
+ lwpd->br_ppid = tp->t_procp->p_ppid;
+ lwpd->br_ptid = -1;
+ } else if (plwpd != NULL) {
+ bcopy(plwpd->br_tls, lwpd->br_tls, sizeof (lwpd->br_tls));
+ lwpd->br_ppid = plwpd->br_pid;
+ lwpd->br_ptid = curthread->t_tid;
+ /* The child inherits the fs/gsbase values from the parent */
+ lwpd->br_lx_fsbase = plwpd->br_lx_fsbase;
+ lwpd->br_ntv_fsbase = plwpd->br_ntv_fsbase;
+ lwpd->br_lx_gsbase = plwpd->br_lx_gsbase;
+ lwpd->br_ntv_gsbase = plwpd->br_ntv_gsbase;
+ } else {
+ /*
+ * Oddball case: the parent thread isn't a Linux process.
+ */
+ lwpd->br_ppid = 0;
+ lwpd->br_ptid = -1;
+ }
+ lwp->lwp_brand = lwpd;
+
+ /*
+ * When during lx_lwpdata_alloc, we must decide whether or not to
+ * allocate a new pid to associate with the lwp. Since p_lock is not
+ * held at that point, the only time we can guarantee a new pid isn't
+ * needed is when p_lwpcnt == 0. This is because other lwps won't be
+ * present to race with us with regards to pid allocation.
+ *
+ * This means that in all other cases (where p_lwpcnt > 0), we expect
+ * that lx_lwpdata_alloc will allocate a pid for us to use here, even
+ * if it is uneeded. If this process is undergoing an exec, for
+ * example, the single existing lwp will not need a new pid when it is
+ * rebranded. In that case, lx_pid_assign will free the uneeded pid.
+ */
+ VERIFY(lwpd->br_lpid->l_pidp != NULL || p->p_lwpcnt == 0);
+
+ lx_pid_assign(tp, lwpd->br_lpid);
+ lwpd->br_tgid = lwpd->br_pid;
+ /*
+ * Having performed the lx pid assignement, the lpid reference is no
+ * longer needed. The underlying data will be freed during lx_freelwp.
+ */
+ lwpd->br_lpid = NULL;
+
+ installctx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL,
+ lx_save, NULL);
+
+ /*
+ * Install branded system call hooks for this LWP:
+ */
+ lwp->lwp_brand_syscall = lx_syscall_enter;
+
+ /*
+ * The new LWP inherits the parent LWP cgroup ID.
+ */
+ if (plwpd != NULL) {
+ lwpd->br_cgroupid = plwpd->br_cgroupid;
+ }
+ lxzdata = ztolxzd(p->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ cgrp = lxzdata->lxzd_cgroup;
+ if (cgrp != NULL) {
+ VFS_HOLD(cgrp);
+ mutex_exit(&lxzdata->lxzd_lock);
+ ASSERT(lx_cgrp_initlwp != NULL);
+ (*lx_cgrp_initlwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+ lwpd->br_pid);
+ VFS_RELE(cgrp);
+ } else {
+ mutex_exit(&lxzdata->lxzd_lock);
+ }
+}
+
+void
+lx_initlwp_post(klwp_t *lwp)
+{
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ /*
+ * If the parent LWP has a ptrace(2) tracer, the new LWP may
+ * need to inherit that same tracer.
+ */
+ if (plwpd != NULL) {
+ lx_ptrace_inherit_tracer(plwpd, lwptolxlwp(lwp));
+ }
+}
+
+/*
+ * There is no need to have any locking for either the source or
+ * destination struct lx_lwp_data structs. This is always run in the
+ * thread context of the source thread, and the destination thread is
+ * always newly created and not referred to from anywhere else.
+ */
+void
+lx_forklwp(klwp_t *srclwp, klwp_t *dstlwp)
+{
+ struct lx_lwp_data *src = srclwp->lwp_brand;
+ struct lx_lwp_data *dst = dstlwp->lwp_brand;
+
+ dst->br_ppid = src->br_pid;
+ dst->br_ptid = lwptot(srclwp)->t_tid;
+ bcopy(src->br_tls, dst->br_tls, sizeof (dst->br_tls));
+
+ switch (src->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ case LX_STACK_MODE_NATIVE:
+ /*
+ * The parent LWP has an alternate stack installed.
+ * The child LWP should have the same stack base and extent.
+ */
+ dst->br_stack_mode = src->br_stack_mode;
+ dst->br_ntv_stack = src->br_ntv_stack;
+ dst->br_ntv_stack_current = src->br_ntv_stack_current;
+ break;
+
+ default:
+ /*
+ * Otherwise, clear the stack data for this LWP.
+ */
+ dst->br_stack_mode = LX_STACK_MODE_PREINIT;
+ dst->br_ntv_stack = 0;
+ dst->br_ntv_stack_current = 0;
+ }
+
+ /*
+ * copy only these flags
+ */
+ dst->br_lwp_flags = src->br_lwp_flags & BR_CPU_BOUND;
+ dst->br_scall_args = NULL;
+
+ /*
+ * Flag so child doesn't ptrace-stop on syscall exit.
+ */
+ dst->br_ptrace_flags |= LX_PTF_NOSTOP;
+}
+
+/*
+ * When switching a Linux process off the CPU, clear its GDT entries.
+ */
+/* ARGSUSED */
+static void
+lx_save(klwp_t *t)
+{
+ int i;
+
+#if defined(__amd64)
+ reset_sregs();
+#endif
+ for (i = 0; i < LX_TLSNUM; i++)
+ gdt_update_usegd(GDT_TLSMIN + i, &null_udesc);
+}
+
+/*
+ * When switching a Linux process on the CPU, set its GDT entries.
+ *
+ * For 64-bit code we don't have to worry about explicitly setting the
+ * %fsbase via wrmsr(MSR_AMD_FSBASE) here. Instead, that should happen
+ * automatically in update_sregs if we are executing in user-land. If this
+ * is the case then pcb_rupdate should be set.
+ */
+static void
+lx_restore(klwp_t *t)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(t);
+ user_desc_t *tls;
+ int i;
+
+ ASSERT(lwpd);
+
+ tls = lwpd->br_tls;
+ for (i = 0; i < LX_TLSNUM; i++)
+ gdt_update_usegd(GDT_TLSMIN + i, &tls[i]);
+}
+
+void
+lx_set_gdt(int entry, user_desc_t *descrp)
+{
+
+ gdt_update_usegd(entry, descrp);
+}
+
+void
+lx_clear_gdt(int entry)
+{
+ gdt_update_usegd(entry, &null_udesc);
+}
+
+longlong_t
+lx_nosys()
+{
+ return (set_errno(ENOSYS));
+}
+
+/*
+ * Brand-specific routine to check if given non-Solaris standard segment
+ * register values should be modified to other values.
+ */
+/*ARGSUSED*/
+greg_t
+lx_fixsegreg(greg_t sr, model_t datamodel)
+{
+ uint16_t idx = SELTOIDX(sr);
+
+ ASSERT(sr == (sr & 0xffff));
+
+ /*
+ * If the segment selector is a valid TLS selector, just return it.
+ */
+ if (!SELISLDT(sr) && idx >= GDT_TLSMIN && idx <= GDT_TLSMAX)
+ return (sr | SEL_UPL);
+
+ /*
+ * Force the SR into the LDT in ring 3 for 32-bit processes.
+ *
+ * 64-bit processes get the null GDT selector since they are not
+ * allowed to have a private LDT.
+ */
+#if defined(__amd64)
+ return (datamodel == DATAMODEL_ILP32 ? (sr | SEL_TI_LDT | SEL_UPL) : 0);
+#elif defined(__i386)
+ datamodel = datamodel; /* datamodel currently unused for 32-bit */
+ return (sr | SEL_TI_LDT | SEL_UPL);
+#endif /* __amd64 */
+}
+
+/*
+ * Brand-specific function to convert the fsbase as pulled from the register
+ * into a native fsbase suitable for locating the ulwp_t from the kernel.
+ */
+uintptr_t
+lx_fsbase(klwp_t *lwp, uintptr_t fsbase)
+{
+ lx_lwp_data_t *lwpd = lwp->lwp_brand;
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND ||
+ lwpd->br_ntv_fsbase == NULL) {
+ return (fsbase);
+ }
+
+ return (lwpd->br_ntv_fsbase);
+}
+
+/*
+ * These two functions simulate winfo and post_sigcld for the lx brand. The
+ * difference is delivering a designated signal as opposed to always SIGCLD.
+ */
+static void
+lx_winfo(proc_t *pp, k_siginfo_t *ip, struct lx_proc_data *dat)
+{
+ ASSERT(MUTEX_HELD(&pidlock));
+ bzero(ip, sizeof (k_siginfo_t));
+ ip->si_signo = ltos_signo[dat->l_signal];
+ ip->si_code = pp->p_wcode;
+ ip->si_pid = pp->p_pid;
+ ip->si_ctid = PRCTID(pp);
+ ip->si_zoneid = pp->p_zone->zone_id;
+ ip->si_status = pp->p_wdata;
+ ip->si_stime = pp->p_stime;
+ ip->si_utime = pp->p_utime;
+}
+
+static void
+lx_post_exit_sig(proc_t *cp, sigqueue_t *sqp, struct lx_proc_data *dat)
+{
+ proc_t *pp = cp->p_parent;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+ mutex_enter(&pp->p_lock);
+ /*
+ * Since Linux doesn't queue SIGCHLD, or any other non RT
+ * signals, we just blindly deliver whatever signal we can.
+ */
+ ASSERT(sqp != NULL);
+ lx_winfo(cp, &sqp->sq_info, dat);
+ sigaddqa(pp, NULL, sqp);
+ sqp = NULL;
+ mutex_exit(&pp->p_lock);
+}
+
+
+/*
+ * Brand specific code for exiting and sending a signal to the parent, as
+ * opposed to sigcld().
+ */
+void
+lx_exit_with_sig(proc_t *cp, sigqueue_t *sqp)
+{
+ proc_t *pp = cp->p_parent;
+ lx_proc_data_t *lx_brand_data = ptolxproc(cp);
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ switch (cp->p_wcode) {
+ case CLD_EXITED:
+ case CLD_DUMPED:
+ case CLD_KILLED:
+ ASSERT(cp->p_stat == SZOMB);
+ /*
+ * The broadcast on p_srwchan_cv is a kludge to
+ * wakeup a possible thread in uadmin(A_SHUTDOWN).
+ */
+ cv_broadcast(&cp->p_srwchan_cv);
+
+ /*
+ * Add to newstate list of the parent
+ */
+ add_ns(pp, cp);
+
+ cv_broadcast(&pp->p_cv);
+ if ((pp->p_flag & SNOWAIT) ||
+ PTOU(pp)->u_signal[SIGCLD - 1] == SIG_IGN) {
+ if (!(cp->p_pidflag & CLDWAITPID))
+ freeproc(cp);
+ } else if (!(cp->p_pidflag & CLDNOSIGCHLD) &&
+ lx_brand_data->l_signal != 0) {
+ lx_post_exit_sig(cp, sqp, lx_brand_data);
+ sqp = NULL;
+ }
+ break;
+
+ case CLD_STOPPED:
+ case CLD_CONTINUED:
+ case CLD_TRAPPED:
+ panic("Should not be called in this case");
+ }
+
+ if (sqp)
+ siginfofree(sqp);
+}
+
+/*
+ * Filters based on arguments that have been passed in by a separate syscall
+ * using the B_STORE_ARGS mechanism. if the __WALL flag is set, no filter is
+ * applied, otherwise we look at the difference between a clone and non-clone
+ * process.
+ * The definition of a clone process in Linux is a thread that does not deliver
+ * SIGCHLD to its parent. The option __WCLONE indicates to wait only on clone
+ * processes. Without that option, a process should only wait on normal
+ * children. The following table shows the cases.
+ *
+ * default __WCLONE
+ * no SIGCHLD - X
+ * SIGCHLD X -
+ *
+ * This is an XOR of __WCLONE being set, and SIGCHLD being the signal sent on
+ * process exit.
+ *
+ * More information on wait in lx brands can be found at
+ * usr/src/lib/brand/lx/lx_brand/common/wait.c.
+ */
+/* ARGSUSED */
+boolean_t
+lx_wait_filter(proc_t *pp, proc_t *cp)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ int flags = lwpd->br_waitid_flags;
+ boolean_t ret;
+
+ if (!lwpd->br_waitid_emulate) {
+ return (B_TRUE);
+ }
+
+ mutex_enter(&cp->p_lock);
+ if (flags & LX_WALL) {
+ ret = B_TRUE;
+ } else {
+ lx_proc_data_t *pd = ptolxproc(cp);
+ boolean_t is_sigchld = B_TRUE;
+ boolean_t match_wclone = B_FALSE;
+
+ /*
+ * When calling clone, an alternate signal can be chosen to
+ * deliver to the parent when the child exits.
+ */
+ if (pd != NULL && pd->l_signal != stol_signo[SIGCHLD]) {
+ is_sigchld = B_FALSE;
+ }
+ if ((flags & LX_WCLONE) != 0) {
+ match_wclone = B_TRUE;
+ }
+
+ ret = (match_wclone ^ is_sigchld) ? B_TRUE : B_FALSE;
+ }
+ mutex_exit(&cp->p_lock);
+
+ return (ret);
+}
+
+void
+lx_ifname_convert(char *ifname, lx_if_action_t act)
+{
+ if (act == LX_IF_TONATIVE) {
+ if (strncmp(ifname, "lo", IFNAMSIZ) == 0)
+ (void) strlcpy(ifname, "lo0", IFNAMSIZ);
+ } else {
+ if (strncmp(ifname, "lo0", IFNAMSIZ) == 0)
+ (void) strlcpy(ifname, "lo", IFNAMSIZ);
+ }
+}
+
+void
+lx_ifflags_convert(uint64_t *flags, lx_if_action_t act)
+{
+ uint64_t buf;
+
+ buf = *flags & (IFF_UP | IFF_BROADCAST | IFF_DEBUG |
+ IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS |
+ IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+
+ /* Linux has different shift for multicast flag */
+ if (act == LX_IF_TONATIVE) {
+ if (*flags & 0x1000)
+ buf |= IFF_MULTICAST;
+ } else {
+ if (*flags & IFF_MULTICAST)
+ buf |= 0x1000;
+ }
+ *flags = buf;
+}
+
+/*
+ * Convert an IPv6 address into the numbers used by /proc/net/if_inet6
+ */
+unsigned int
+lx_ipv6_scope_convert(const in6_addr_t *addr)
+{
+ if (IN6_IS_ADDR_V4COMPAT(addr)) {
+ return (LX_IPV6_ADDR_COMPATv4);
+ } else if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+ return (LX_IPV6_ADDR_LOOPBACK);
+ } else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+ return (LX_IPV6_ADDR_LINKLOCAL);
+ } else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+ return (LX_IPV6_ADDR_SITELOCAL);
+ } else {
+ return (0x0000U);
+ }
+}
+
+
+void
+lx_stol_hwaddr(const struct sockaddr_dl *src, struct sockaddr *dst, int *size)
+{
+ int copy_size = MIN(src->sdl_alen, sizeof (dst->sa_data));
+
+ switch (src->sdl_type) {
+ case DL_ETHER:
+ dst->sa_family = LX_ARPHRD_ETHER;
+ break;
+ case DL_LOOP:
+ dst->sa_family = LX_ARPHRD_LOOPBACK;
+ break;
+ default:
+ dst->sa_family = LX_ARPHRD_VOID;
+ }
+
+ bcopy(LLADDR(src), dst->sa_data, copy_size);
+ *size = copy_size;
+}
+
+/*
+ * Brand hook to convert native kernel siginfo signal number, errno, code, pid
+ * and si_status to Linux values. Similar to the stol_ksiginfo function but
+ * this one converts in-place, converts the pid, and does not copyout.
+ */
+void
+lx_sigfd_translate(k_siginfo_t *infop)
+{
+ infop->si_signo = lx_stol_signo(infop->si_signo, LX_SIGKILL);
+
+ infop->si_status = lx_stol_status(infop->si_status, LX_SIGKILL);
+
+ infop->si_code = lx_stol_sigcode(infop->si_code);
+
+ infop->si_errno = lx_errno(infop->si_errno, EINVAL);
+
+ if (infop->si_pid == curproc->p_zone->zone_proc_initpid) {
+ infop->si_pid = 1;
+ } else if (infop->si_pid == curproc->p_zone->zone_zsched->p_pid) {
+ infop->si_pid = 0;
+ }
+}
+
+int
+stol_ksiginfo_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+ lx_siginfo_t lsi;
+
+ bzero(&lsi, sizeof (lsi));
+ lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+ lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+ lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+ switch (lsi.lsi_signo) {
+ case LX_SIGPOLL:
+ lsi.lsi_band = sip->si_band;
+ lsi.lsi_fd = sip->si_fd;
+ break;
+
+ case LX_SIGCHLD:
+ lsi.lsi_pid = sip->si_pid;
+ if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+ lsi.lsi_status = sip->si_status;
+ } else {
+ lsi.lsi_status = lx_stol_status(sip->si_status,
+ SIGKILL);
+ }
+ lsi.lsi_utime = sip->si_utime;
+ lsi.lsi_stime = sip->si_stime;
+ break;
+
+ case LX_SIGILL:
+ case LX_SIGBUS:
+ case LX_SIGFPE:
+ case LX_SIGSEGV:
+ lsi.lsi_addr = sip->si_addr;
+ break;
+
+ default:
+ lsi.lsi_pid = sip->si_pid;
+ lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+ }
+
+ if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+int
+stol_ksiginfo32_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+ lx_siginfo32_t lsi;
+
+ bzero(&lsi, sizeof (lsi));
+ lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+ lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+ lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+ switch (lsi.lsi_signo) {
+ case LX_SIGPOLL:
+ lsi.lsi_band = sip->si_band;
+ lsi.lsi_fd = sip->si_fd;
+ break;
+
+ case LX_SIGCHLD:
+ lsi.lsi_pid = sip->si_pid;
+ if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+ lsi.lsi_status = sip->si_status;
+ } else {
+ lsi.lsi_status = lx_stol_status(sip->si_status,
+ SIGKILL);
+ }
+ lsi.lsi_utime = sip->si_utime;
+ lsi.lsi_stime = sip->si_stime;
+ break;
+
+ case LX_SIGILL:
+ case LX_SIGBUS:
+ case LX_SIGFPE:
+ case LX_SIGSEGV:
+ lsi.lsi_addr = (caddr32_t)(uintptr_t)sip->si_addr;
+ break;
+
+ default:
+ lsi.lsi_pid = sip->si_pid;
+ lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+ }
+
+ if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+#endif
+
+/*
+ * Linux uses the original bounds of the argv array when determining the
+ * contents of /proc/<pid/cmdline. We mimic those bounds using argv[0] and
+ * envp[0] as the beginning and end, respectively.
+ */
+void
+lx_read_argv_bounds(proc_t *p)
+{
+ user_t *up = PTOU(p);
+ lx_proc_data_t *pd = ptolxproc(p);
+ uintptr_t addr_arg = up->u_argv;
+ uintptr_t addr_env = up->u_envp;
+ uintptr_t arg_start = 0, env_start = 0, env_end = 0;
+ int i = 0;
+
+ VERIFY(pd != NULL);
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Use AT_SUN_PLATFORM in the aux vector to find the end of the envp
+ * strings.
+ */
+ for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
+ if (up->u_auxv[i].a_type == AT_SUN_PLATFORM) {
+ env_end = (uintptr_t)up->u_auxv[i].a_un.a_val;
+ }
+ }
+
+ mutex_exit(&p->p_lock);
+#if defined(_LP64)
+ if (p->p_model != DATAMODEL_NATIVE) {
+ uint32_t buf32;
+ if (copyin((void *)addr_arg, &buf32, sizeof (buf32)) == 0) {
+ arg_start = (uintptr_t)buf32;
+ }
+ if (copyin((void *)addr_env, &buf32, sizeof (buf32)) == 0) {
+ env_start = (uintptr_t)buf32;
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ uintptr_t buf;
+ if (copyin((void *)addr_arg, &buf, sizeof (buf)) == 0) {
+ arg_start = buf;
+ }
+ if (copyin((void *)addr_env, &buf, sizeof (buf)) == 0) {
+ env_start = buf;
+ }
+ }
+ mutex_enter(&p->p_lock);
+ pd->l_args_start = arg_start;
+ pd->l_envs_start = env_start;
+ pd->l_envs_end = env_end;
+}
+
+/* Given an LX LWP, determine where user register state is stored. */
+lx_regs_location_t
+lx_regs_location(lx_lwp_data_t *lwpd, void **ucp, boolean_t for_write)
+{
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ /*
+ * The LWP was stopped with the brand stack and register state
+ * loaded, e.g. during a syscall emulated within the kernel.
+ */
+ return (LX_REG_LOC_LWP);
+
+ case LX_STACK_MODE_PREINIT:
+ if (for_write) {
+ /* setting registers not allowed in this state */
+ break;
+ }
+ if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED ||
+ lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ /* The LWP was stopped by tracing on exec. */
+ return (LX_REG_LOC_LWP);
+ }
+ break;
+
+ case LX_STACK_MODE_NATIVE:
+ if (for_write) {
+ /* setting registers not allowed in this state */
+ break;
+ }
+ if (lwpd->br_ptrace_whystop == PR_BRAND) {
+ /* Called while ptrace-event-stopped by lx_exec. */
+ if (lwpd->br_ptrace_whatstop == LX_PR_EVENT) {
+ return (LX_REG_LOC_LWP);
+ }
+
+ /* Called while ptrace-event-stopped after clone. */
+ if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED &&
+ lwpd->br_ptrace_stopsig == LX_SIGSTOP &&
+ (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+ return (LX_REG_LOC_LWP);
+ }
+
+ /*
+ * Called to obtain syscall exit for other cases
+ * (e.g. pseudo return from rt_sigreturn).
+ */
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT &&
+ (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+ return (LX_REG_LOC_LWP);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (lwpd->br_ptrace_stopucp != NULL) {
+ /*
+ * The LWP was stopped in the usermode emulation library
+ * but a ucontext_t for the preserved brand stack and
+ * register state was provided. Return the register state
+ * from that ucontext_t.
+ */
+ VERIFY(ucp != NULL);
+ *ucp = (void *)lwpd->br_ptrace_stopucp;
+ return (LX_REG_LOC_UCP);
+ }
+
+ return (LX_REG_LOC_UNAVAIL);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_pid.c b/usr/src/uts/common/brand/lx/os/lx_pid.c
new file mode 100644
index 0000000000..40179bbdaf
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_pid.c
@@ -0,0 +1,395 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/bitmap.h>
+#include <sys/var.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+
+#define LINUX_PROC_FACTOR 8 /* factor down the hash table by this */
+static int hash_len = 4; /* desired average hash chain length */
+static int hash_size; /* no of buckets in the hash table */
+
+static struct lx_pid **stol_pid_hash;
+static struct lx_pid **ltos_pid_hash;
+
+#define LTOS_HASH(pid) ((pid) & (hash_size - 1))
+#define STOL_HASH(pid, tid) (((pid) + (tid)) & (hash_size - 1))
+
+static kmutex_t hash_lock;
+
+static void
+lx_pid_insert_hash(struct lx_pid *lpidp)
+{
+ int shash = STOL_HASH(lpidp->s_pid, lpidp->s_tid);
+ int lhash = LTOS_HASH(lpidp->l_pid);
+
+ ASSERT(MUTEX_HELD(&hash_lock));
+
+ lpidp->stol_next = stol_pid_hash[shash];
+ stol_pid_hash[shash] = lpidp;
+
+ lpidp->ltos_next = ltos_pid_hash[lhash];
+ ltos_pid_hash[lhash] = lpidp;
+}
+
+static struct lx_pid *
+lx_pid_remove_hash(pid_t pid, id_t tid)
+{
+ struct lx_pid **hpp;
+ struct lx_pid *lpidp = NULL;
+
+ ASSERT(MUTEX_HELD(&hash_lock));
+
+ hpp = &stol_pid_hash[STOL_HASH(pid, tid)];
+ while (*hpp) {
+ if ((*hpp)->s_pid == pid && (*hpp)->s_tid == tid) {
+ lpidp = *hpp;
+ *hpp = (*hpp)->stol_next;
+ break;
+ }
+ hpp = &(*hpp)->stol_next;
+ }
+
+ /*
+ * when called during error recovery the pid may already
+ * be released
+ */
+ if (lpidp == NULL)
+ return (NULL);
+
+ hpp = &ltos_pid_hash[LTOS_HASH(lpidp->l_pid)];
+ while (*hpp) {
+ if (*hpp == lpidp) {
+ *hpp = lpidp->ltos_next;
+ break;
+ }
+ hpp = &(*hpp)->ltos_next;
+ }
+
+ return (lpidp);
+}
+
+/*
+ * given a solaris pid/tid pair, create a linux pid
+ */
+void
+lx_pid_assign(kthread_t *t, struct lx_pid *lpidp)
+{
+ proc_t *p = ttoproc(t);
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ pid_t s_pid = p->p_pid;
+ id_t s_tid = t->t_tid;
+
+ /*
+ * When lx_initlwp is called from lx_setbrand, p_lwpcnt will already be
+ * equal to 1. Since lx_initlwp is being called against an lwp that
+ * already exists, an additional pid allocation is not necessary.
+ *
+ * We check for this by testing br_ppid == 0.
+ */
+ if (p->p_lwpcnt > 0 && lwpd->br_ppid != 0) {
+ /*
+ * Assign allocated pid to any thread other than the first.
+ * The l_pid and l_pidp fields should be populated.
+ */
+ VERIFY(lpidp->l_pidp != NULL);
+ VERIFY(lpidp->l_pid != 0);
+ } else {
+ /*
+ * There are cases where a pid is speculatively allocated but
+ * is not needed. We are obligated to free it here.
+ */
+ if (lpidp->l_pidp != NULL) {
+ (void) pid_rele(lpidp->l_pidp);
+ }
+ lpidp->l_pidp = NULL;
+ lpidp->l_pid = s_pid;
+ }
+
+ lpidp->s_pid = s_pid;
+ lpidp->s_tid = s_tid;
+ lpidp->l_start = t->t_start;
+
+ /*
+ * now put the pid into the linux-solaris and solaris-linux
+ * conversion hash tables
+ */
+ mutex_enter(&hash_lock);
+ lx_pid_insert_hash(lpidp);
+ mutex_exit(&hash_lock);
+
+ lwpd->br_pid = lpidp->l_pid;
+}
+
+/*
+ * If we are exec()ing the process, this thread's tid is about to be reset
+ * to 1. Make sure the Linux PID bookkeeping reflects that change.
+ */
+void
+lx_pid_reassign(kthread_t *t)
+{
+ proc_t *p = ttoproc(t);
+ struct pid *old_pidp;
+ struct lx_pid *lpidp;
+
+ ASSERT(p->p_lwpcnt == 1);
+
+ mutex_enter(&hash_lock);
+
+ /*
+ * Clean up all the traces of this thread's 'fake' Linux PID.
+ */
+ lpidp = lx_pid_remove_hash(p->p_pid, t->t_tid);
+ ASSERT(lpidp != NULL);
+ old_pidp = lpidp->l_pidp;
+ lpidp->l_pidp = NULL;
+
+ /*
+ * Now register this thread as (pid, 1).
+ */
+ lpidp->l_pid = p->p_pid;
+ lpidp->s_pid = p->p_pid;
+ lpidp->s_tid = 1;
+ lx_pid_insert_hash(lpidp);
+
+ mutex_exit(&hash_lock);
+
+ if (old_pidp)
+ (void) pid_rele(old_pidp);
+}
+
+/*
+ * release a solaris pid/tid pair
+ */
+void
+lx_pid_rele(pid_t pid, id_t tid)
+{
+ struct lx_pid *lpidp;
+
+ mutex_enter(&hash_lock);
+ lpidp = lx_pid_remove_hash(pid, tid);
+ mutex_exit(&hash_lock);
+
+ if (lpidp) {
+ if (lpidp->l_pidp)
+ (void) pid_rele(lpidp->l_pidp);
+
+ kmem_free(lpidp, sizeof (*lpidp));
+ }
+}
+
+/*
+ * given a linux pid, return the solaris pid/tid pair
+ */
+int
+lx_lpid_to_spair(pid_t l_pid, pid_t *s_pid, id_t *s_tid)
+{
+ struct lx_pid *hp;
+
+ if (l_pid == 1) {
+ pid_t initpid;
+
+ /*
+ * We are trying to look up the Linux init process for the
+ * current zone, which we pretend has pid 1.
+ */
+ if ((initpid = curzone->zone_proc_initpid) == -1) {
+ /*
+ * We could not find the init process for this zone.
+ */
+ return (-1);
+ }
+
+ if (s_pid != NULL)
+ *s_pid = initpid;
+ if (s_tid != NULL)
+ *s_tid = 1;
+
+ return (0);
+ }
+
+ mutex_enter(&hash_lock);
+ for (hp = ltos_pid_hash[LTOS_HASH(l_pid)]; hp; hp = hp->ltos_next) {
+ if (l_pid == hp->l_pid) {
+ if (s_pid)
+ *s_pid = hp->s_pid;
+ if (s_tid)
+ *s_tid = hp->s_tid;
+ break;
+ }
+ }
+ mutex_exit(&hash_lock);
+ if (hp != NULL)
+ return (0);
+
+ /*
+ * We didn't find this pid in our translation table.
+ * But this still could be the pid of a native process
+ * running in the current zone so check for that here.
+ *
+ * Note that prfind() only searches for processes in the current zone.
+ */
+ mutex_enter(&pidlock);
+ if (prfind(l_pid) != NULL) {
+ mutex_exit(&pidlock);
+ if (s_pid)
+ *s_pid = l_pid;
+ if (s_tid)
+ *s_tid = 0;
+ return (0);
+ }
+ mutex_exit(&pidlock);
+
+ return (-1);
+}
+
+/*
+ * Given an lwp, return the Linux pid of its parent. If the caller
+ * wants them, we return the Solaris (pid, tid) as well.
+ */
+pid_t
+lx_lwp_ppid(klwp_t *lwp, pid_t *ppidp, id_t *ptidp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ struct lx_pid *hp;
+ pid_t zoneinit = curproc->p_zone->zone_proc_initpid;
+ pid_t lppid, ppid;
+
+ /*
+ * Be sure not to return a parent pid that should be invisible
+ * within this zone.
+ */
+ ppid = ((p->p_flag & SZONETOP)
+ ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * If the parent process's pid is the zone's init process, force it
+ * to the Linux init pid value of 1.
+ */
+ if (ppid == zoneinit)
+ ppid = 1;
+
+ /*
+ * There are two cases in which the Linux definition of a 'parent'
+ * matches that of Solaris:
+ *
+ * - if our tgid is the same as our PID, then we are either the
+ * first thread in the process or a CLONE_THREAD thread.
+ *
+ * - if the brand lwp value for ppid is 0, then we are either the
+ * child of a differently-branded process or a CLONE_PARENT thread.
+ */
+ if (p->p_pid == lwpd->br_tgid || lwpd->br_ppid == 0) {
+ if (ppidp != NULL)
+ *ppidp = ppid;
+ if (ptidp != NULL)
+ *ptidp = -1;
+ return (ppid);
+ }
+
+ /*
+ * Set the default Linux parent pid to be the pid of the zone's init
+ * process; this will get converted back to the Linux default of 1
+ * later.
+ */
+ lppid = zoneinit;
+
+ /*
+ * If the process's parent isn't init, try and look up the Linux "pid"
+ * corresponding to the process's parent.
+ */
+ if (ppid != 1) {
+ /*
+ * In all other cases, we are looking for the parent of this
+ * specific thread, which in Linux refers to the thread that
+ * clone()d it. We stashed that thread's PID away when this
+ * thread was created.
+ */
+ mutex_enter(&hash_lock);
+ for (hp = ltos_pid_hash[LTOS_HASH(lwpd->br_ppid)]; hp;
+ hp = hp->ltos_next) {
+ if (lwpd->br_ppid == hp->l_pid) {
+ /*
+ * We found the PID we were looking for, but
+ * since we cached its value in this LWP's brand
+ * structure, it has exited and been reused by
+ * another process.
+ */
+ if (hp->l_start > lwptot(lwp)->t_start)
+ break;
+
+ lppid = lwpd->br_ppid;
+ if (ppidp != NULL)
+ *ppidp = hp->s_pid;
+ if (ptidp != NULL)
+ *ptidp = hp->s_tid;
+
+ break;
+ }
+ }
+ mutex_exit(&hash_lock);
+ }
+
+ if (lppid == zoneinit) {
+ lppid = 1;
+
+ if (ppidp != NULL)
+ *ppidp = lppid;
+ if (ptidp != NULL)
+ *ptidp = -1;
+ }
+
+ return (lppid);
+}
+
+void
+lx_pid_init(void)
+{
+ hash_size = 1 << highbit(v.v_proc / (hash_len * LINUX_PROC_FACTOR));
+
+ stol_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+ KM_SLEEP);
+ ltos_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+ KM_SLEEP);
+
+ mutex_init(&hash_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+void
+lx_pid_fini(void)
+{
+ kmem_free(stol_pid_hash, sizeof (struct lx_pid *) * hash_size);
+ kmem_free(ltos_pid_hash, sizeof (struct lx_pid *) * hash_size);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_ptrace.c b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
new file mode 100644
index 0000000000..b22f11be6a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
@@ -0,0 +1,2638 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Emulation of the Linux ptrace(2) interface.
+ *
+ * OVERVIEW
+ *
+ * The Linux process model is somewhat different from the illumos native
+ * model. One critical difference is that each Linux thread has a unique
+ * identifier in the pid namespace. The lx brand assigns a pid to each LWP
+ * within the emulated process, giving the pid of the process itself to the
+ * first LWP.
+ *
+ * The Linux ptrace(2) interface allows for any LWP in a branded process to
+ * exert control over any other LWP within the same zone. Control is exerted
+ * by the use of the ptrace(2) system call itself, which accepts a number of
+ * request codes. Feedback on traced events is primarily received by the
+ * tracer through SIGCLD and the emulated waitpid(2) and waitid(2) system
+ * calls. Many of the possible ptrace(2) requests will only succeed if the
+ * target LWP is in a "ptrace-stop" condition.
+ *
+ * HISTORY
+ *
+ * The brand support for ptrace(2) was originally built on top of the rich
+ * support for debugging and tracing provided through the illumos /proc
+ * interfaces, mounted at /native/proc within the zone. The native legacy
+ * ptrace(3C) functionality was used as a starting point, but was generally
+ * insufficient for complete and precise emulation. The extant legacy
+ * interface, and indeed our native SIGCLD and waitid(2) facilities, are
+ * focused on _process_ level concerns -- the Linux interface has been
+ * extended to be aware of LWPs as well.
+ *
+ * In order to allow us to focus on providing more complete and accurate
+ * emulation without extensive and undesirable changes to the native
+ * facilities, this second generation ptrace(2) emulation is mostly separate
+ * from any other tracing or debugging framework in the system.
+ *
+ * ATTACHING TRACERS TO TRACEES
+ *
+ * There are several ways that a child LWP may becomed traced by a tracer.
+ * To determine which attach method caused a tracee to become attached, one
+ * may inspect the "br_ptrace_attach" member of the LWP-specific brand data
+ * with the debugger.
+ *
+ * The first attach methods to consider are the attaching ptrace(2) requests:
+ *
+ * PTRACE_TRACEME
+ *
+ * If an LWP makes a PTRACE_TRACEME call, it will be attached as a tracee
+ * to its parent LWP (br_ppid). Using PTRACE_TRACEME does _not_ cause the
+ * tracee to be held in a stop condition. It is common practice for
+ * consumers to raise(SIGSTOP) immediately afterward.
+ *
+ * PTRACE_ATTACH
+ *
+ * An LWP may attempt to trace any other LWP in this, or another, process.
+ * We currently allow any attach where the process containing the tracer
+ * LWP has permission to write to /proc for the process containing the
+ * intended tracer. This action also sends a SIGSTOP to the newly attached
+ * tracee.
+ *
+ * The second class of attach methods are the clone(2)/fork(2) inheritance
+ * options that may be set on a tracee with PTRACE_SETOPTIONS:
+ *
+ * PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK and PTRACE_O_TRACECLONE
+ *
+ * If these options have been set on a tracee, then a fork(2), vfork(2) or
+ * clone(2) respectively will cause the newly created LWP to be traced by
+ * the same tracer. The same set of ptrace(2) options will also be set on
+ * the new child.
+ *
+ * The third class of attach method is the PTRACE_CLONE flag to clone(2).
+ * This flag induces the same inheritance as PTRACE_O_TRACECLONE, but is
+ * passed by the tracee as an argument to clone(2).
+ *
+ * DETACHING TRACEES
+ *
+ * Tracees can be detached by the tracer with the PTRACE_DETACH request.
+ * This request is only valid when the tracee is in a ptrace(2) stop
+ * condition, and is itself a restarting action.
+ *
+ * If the tracer exits without detaching all of its tracees, then all of the
+ * tracees are automatically detached and restarted. If a tracee was in
+ * "signal-delivery-stop" at the time the tracer exited, the signal will be
+ * released to the child unless it is a SIGSTOP. We drop this instance of
+ * SIGSTOP in order to prevent the child from becoming stopped by job
+ * control.
+ *
+ * ACCORD ALLOCATION AND MANAGEMENT
+ *
+ * The "lx_ptrace_accord_t" object tracks the agreement between a tracer LWP
+ * and zero or more tracee LWPs. It is explicitly illegal for a tracee to
+ * trace its tracer, and we block this in PTRACE_ATTACH/PTRACE_TRACEME.
+ *
+ * An LWP starts out without an accord. If a child of that LWP calls
+ * ptrace(2) with the PTRACE_TRACEME subcommand, or if the LWP itself uses
+ * PTRACE_ATTACH, an accord will be allocated and stored on that LWP. The
+ * accord structure is not released from that LWP until it arrives in
+ * lx_exitlwp(), as called by lwp_exit(). A new accord will not be
+ * allocated, even if one does not exist, once an LWP arrives in lx_exitlwp()
+ * and sets the LX_PTF_EXITING flag. An LWP will have at most one accord
+ * structure throughout its entire lifecycle; once it has one, it has the
+ * same one until death.
+ *
+ * The accord is reference counted (lxpa_refcnt), starting at a count of one
+ * at creation to represent the link from the tracer LWP to its accord. The
+ * accord is not freed until the reference count falls to zero.
+ *
+ * To make mutual exclusion between a detaching tracer and various notifying
+ * tracees simpler, the tracer will hold "pidlock" while it clears the
+ * accord members that point back to the tracer LWP and CV.
+ *
+ * SIGNALS AND JOB CONTROL
+ *
+ * Various actions, either directly ptrace(2) related or commonly associated
+ * with tracing, cause process- or thread-directed SIGSTOP signals to be sent
+ * to tracees (a "signal-delivery-stop"). These signals, and indeed any signal
+ * other than SIGKILL, can be suppressed by the tracer when using a restarting
+ * request (including PTRACE_DETACH) on a child. The signal may also be
+ * substituted for a different signal.
+ *
+ * If a SIGSTOP (or other stopping signal) is not suppressed by the tracer,
+ * it will induce the regular illumos native job control stop of the entire
+ * traced process. This is at least passingly similar to the Linux "group
+ * stop" ptrace(2) condition.
+ *
+ * SYSTEM CALL TRACING
+ *
+ * The ptrace(2) interface enables the tracer to hold the tracee on entry and
+ * exit from system calls. When a stopped tracee is restarted through the
+ * PTRACE_SYSCALL request, the LX_PTF_SYSCALL flag is set until the next
+ * system call boundary. Whether this is a "syscall-entry-stop" or
+ * "syscall-exit-stop", the tracee is held and the tracer is notified via
+ * SIGCLD/waitpid(2) in the usual way. The flag LX_PTF_SYSCALL flag is
+ * cleared after each stop; for ongoing system call tracing the tracee must
+ * be continuously restarted with PTRACE_SYSCALL.
+ *
+ * SPECIAL CASES FOR STOP EVENTS
+ *
+ * The strace command is one of the primary consumers of ptrace. In order for
+ * strace to properly understand what is actually happening when it receives a
+ * signal associated with a stop event, these signals must match Linux behavior
+ * exactly or the strace consumer will get out of sync and report incorrect
+ * state. There are a couple of special cases we have to handle to provide
+ * proper interaction of the syscall-entry-stop, syscall-exit-stop, and
+ * signal-delivery-stop events:
+ * 1) The child process of a clone/fork does not emit a syscall-exit-stop event.
+ * 2) A signal that arrives between syscall-enter-stop & syscall-exit-stop must
+ * not immediately emit signal-delivery-stop. This event must be emitted
+ * after the syscall is interrupted and syscall-exit-stop has been emitted.
+ *
+ * EVENT STOPS
+ *
+ * Various events (particularly FORK, VFORK, CLONE, EXEC and EXIT) are
+ * enabled by the tracer through PTRACE_SETOPTIONS. Once enabled, the tracee
+ * will be stopped at the nominated points of interest and the tracer
+ * notified. The tracer may request additional information about the event,
+ * such as the pid of new LWPs and processes, via PTRACE_GETEVENTMSG.
+ *
+ * LOCK ORDERING RULES
+ *
+ * It is not safe, in general, to hold p_lock for two different processes at
+ * the same time. This constraint is the primary reason for the existence
+ * (and complexity) of the ptrace(2) accord mechanism.
+ *
+ * In order to facilitate looking up accords by the "pid" of a tracer LWP,
+ * p_lock for the tracer process may be held while entering the accord mutex
+ * (lxpa_lock). This mutex protects the accord flags and reference count.
+ * The reference count is manipulated through lx_ptrace_accord_hold() and
+ * lx_ptrace_accord_rele().
+ *
+ * DO NOT interact with the accord mutex (lxpa_lock) directly. The
+ * lx_ptrace_accord_enter() and lx_ptrace_accord_exit() functions do various
+ * book-keeping and lock ordering enforcement and MUST be used.
+ *
+ * It is NOT legal to take ANY p_lock while holding the accord mutex
+ * (lxpa_lock). If the lxpa_tracees_lock is to be held concurrently with
+ * lxpa_lock, lxpa_lock MUST be taken first and dropped before taking p_lock
+ * of any processes from the tracee list.
+ *
+ * It is NOT legal to take a tracee p_lock and then attempt to enter the
+ * accord mutex (or tracee list mutex) of its tracer. When running as the
+ * tracee LWP, the tracee's hold will prevent the accord from being freed.
+ * Use of the LX_PTF_STOPPING or LX_PTF_CLONING flag in the LWP-specific brand
+ * data prevents an exiting tracer from altering the tracee until the tracee
+ * has come to an orderly stop, without requiring the tracee to hold its own
+ * p_lock the entire time it is stopping.
+ *
+ * It is not safe, in general, to enter "pidlock" while holding the p_lock of
+ * any process. It is similarly illegal to hold any accord locks (lxpa_lock
+ * or lxpa_sublock) while attempting to enter "pidlock". As "pidlock" is a
+ * global mutex, it should be held for the shortest possible time.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/procfs.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/wait.h>
+#include <sys/prsystm.h>
+#include <sys/note.h>
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_syscall.h>
+#include <lx_signum.h>
+
+
+typedef enum lx_ptrace_cont_flags_t {
+ LX_PTC_NONE = 0x00,
+ LX_PTC_SYSCALL = 0x01,
+ LX_PTC_SINGLESTEP = 0x02
+} lx_ptrace_cont_flags_t;
+
+
+extern int lx_user_regs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_regs_copyout(lx_lwp_data_t *, void *);
+extern int lx_ptrace_peekuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_ptrace_pokeuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_user_fpregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpregs_copyout(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyout(lx_lwp_data_t *, void *);
+
+/*
+ * Macros for checking the state of an LWP via "br_ptrace_flags":
+ */
+#define LX_PTRACE_BUSY \
+ (LX_PTF_EXITING | LX_PTF_STOPPING | LX_PTF_CLONING)
+
+#define VISIBLE(a) (((a)->br_ptrace_flags & LX_PTF_EXITING) == 0)
+#define TRACEE_BUSY(a) (((a)->br_ptrace_flags & LX_PTRACE_BUSY) != 0)
+
+#define ACCORD_HELD(a) MUTEX_HELD(&(a)->lxpa_lock)
+
+#define LX_PID_TO_INIT(x) ((x) == curproc->p_zone->zone_proc_initpid ? \
+ 1 : (x))
+#define LX_INIT_TO_PID(x) ((x) == 1 ? \
+ curproc->p_zone->zone_proc_initpid : (x))
+
+static kcondvar_t lx_ptrace_busy_cv;
+static kmem_cache_t *lx_ptrace_accord_cache;
+
+/*
+ * Enter the accord mutex.
+ */
+static void
+lx_ptrace_accord_enter(lx_ptrace_accord_t *accord)
+{
+ VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+
+ mutex_enter(&accord->lxpa_lock);
+}
+
+/*
+ * Exit the accord mutex. If the reference count has dropped to zero,
+ * free the accord.
+ */
+static void
+lx_ptrace_accord_exit(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ if (accord->lxpa_refcnt > 0) {
+ mutex_exit(&accord->lxpa_lock);
+ return;
+ }
+
+ /*
+ * When the reference count drops to zero we must free the accord.
+ */
+ VERIFY(accord->lxpa_tracer == NULL);
+ VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+ VERIFY(list_is_empty(&accord->lxpa_tracees));
+ VERIFY(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+
+ mutex_destroy(&accord->lxpa_lock);
+ mutex_destroy(&accord->lxpa_tracees_lock);
+
+ kmem_cache_free(lx_ptrace_accord_cache, accord);
+}
+
+/*
+ * Drop our reference to this accord. If this drops the reference count
+ * to zero, the next lx_ptrace_accord_exit() will free the accord.
+ */
+static void
+lx_ptrace_accord_rele(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ VERIFY(accord->lxpa_refcnt > 0);
+ accord->lxpa_refcnt--;
+}
+
+/*
+ * Place an additional hold on an accord.
+ */
+static void
+lx_ptrace_accord_hold(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ accord->lxpa_refcnt++;
+}
+
+/*
+ * Fetch the accord for this LWP. If one has not yet been created, and the
+ * process is not exiting, allocate it now. Must be called with p_lock held
+ * for the process containing the target LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_locked(klwp_t *lwp, lx_ptrace_accord_t **accordp,
+ boolean_t allocate_one)
+{
+ lx_ptrace_accord_t *lxpa;
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * If this LWP does not have an accord, we wish to allocate
+ * and install one.
+ */
+ if ((lxpa = lwpd->br_ptrace_accord) == NULL) {
+ if (!allocate_one || !VISIBLE(lwpd)) {
+ /*
+ * Either we do not wish to allocate an accord, or this
+ * LWP has already begun exiting from a ptrace
+ * perspective.
+ */
+ *accordp = NULL;
+ return (ESRCH);
+ }
+
+ lxpa = kmem_cache_alloc(lx_ptrace_accord_cache, KM_SLEEP);
+ bzero(lxpa, sizeof (*lxpa));
+
+ /*
+ * The initial reference count is 1 because we are referencing
+ * it in from the soon-to-be tracer LWP.
+ */
+ lxpa->lxpa_refcnt = 1;
+ mutex_init(&lxpa->lxpa_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxpa->lxpa_tracees_lock, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&lxpa->lxpa_tracees, sizeof (lx_lwp_data_t),
+ offsetof(lx_lwp_data_t, br_ptrace_linkage));
+ lxpa->lxpa_cvp = &p->p_cv;
+
+ lxpa->lxpa_tracer = lwpd;
+ lwpd->br_ptrace_accord = lxpa;
+ }
+
+ /*
+ * Lock the accord before returning it to the caller.
+ */
+ lx_ptrace_accord_enter(lxpa);
+
+ /*
+ * There should be at least one active reference to this accord,
+ * otherwise it should have been freed.
+ */
+ VERIFY(lxpa->lxpa_refcnt > 0);
+
+ *accordp = lxpa;
+ return (0);
+}
+
+/*
+ * Accords belong to the tracer LWP. Get the accord for this tracer or return
+ * an error if it was not possible. To prevent deadlocks, the caller MUST NOT
+ * hold p_lock on its own or any other process.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_by_pid(pid_t lxpid, lx_ptrace_accord_t **accordp)
+{
+ int ret = ESRCH;
+ pid_t apid;
+ id_t atid;
+ proc_t *aproc;
+ kthread_t *athr;
+ klwp_t *alwp;
+ lx_lwp_data_t *alwpd;
+
+ VERIFY(MUTEX_NOT_HELD(&curproc->p_lock));
+
+ /*
+ * Locate the process containing the tracer LWP based on its Linux pid
+ * and lock it.
+ */
+ if (lx_lpid_to_spair(lxpid, &apid, &atid) != 0 ||
+ (aproc = sprlock(apid)) == NULL) {
+ return (ESRCH);
+ }
+
+ /*
+ * Locate the tracer LWP itself and ensure that it is visible to
+ * ptrace(2).
+ */
+ if ((athr = idtot(aproc, atid)) == NULL ||
+ (alwp = ttolwp(athr)) == NULL ||
+ (alwpd = lwptolxlwp(alwp)) == NULL ||
+ !VISIBLE(alwpd)) {
+ sprunlock(aproc);
+ return (ESRCH);
+ }
+
+ /*
+ * We should not fetch our own accord this way.
+ */
+ if (athr == curthread) {
+ sprunlock(aproc);
+ return (EPERM);
+ }
+
+ /*
+ * Fetch (or allocate) the accord owned by this tracer LWP:
+ */
+ ret = lx_ptrace_accord_get_locked(alwp, accordp, B_TRUE);
+
+ /*
+ * Unlock the process and return.
+ */
+ sprunlock(aproc);
+ return (ret);
+}
+
+/*
+ * Get (or allocate) the ptrace(2) accord for the current LWP, acting as a
+ * tracer. The caller MUST NOT currently hold p_lock on the process containing
+ * this LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get(lx_ptrace_accord_t **accordp, boolean_t allocate_one)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ int ret;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * Lock the tracer (this LWP).
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Fetch (or allocate) the accord for this LWP:
+ */
+ ret = lx_ptrace_accord_get_locked(lwp, accordp, allocate_one);
+
+ mutex_exit(&p->p_lock);
+
+ return (ret);
+}
+
+/*
+ * Restart an LWP if it is in "ptrace-stop". This function may induce sleep,
+ * so the caller MUST NOT hold any mutexes other than p_lock for the process
+ * containing the LWP.
+ */
+static void
+lx_ptrace_restart_lwp(klwp_t *lwp)
+{
+ kthread_t *rt = lwptot(lwp);
+ proc_t *rproc = lwptoproc(lwp);
+ lx_lwp_data_t *rlwpd = lwptolxlwp(lwp);
+
+ VERIFY(rt != curthread);
+ VERIFY(MUTEX_HELD(&rproc->p_lock));
+
+ /*
+ * Exclude potential meddling from procfs.
+ */
+ prbarrier(rproc);
+
+ /*
+ * Check that the LWP is still in "ptrace-stop" and, if so, restart it.
+ */
+ thread_lock(rt);
+ if (BSTOPPED(rt) && rt->t_whystop == PR_BRAND) {
+ rt->t_schedflag |= TS_BSTART;
+ setrun_locked(rt);
+
+ /*
+ * Clear stop reason.
+ */
+ rlwpd->br_ptrace_whystop = 0;
+ rlwpd->br_ptrace_whatstop = 0;
+ rlwpd->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+ }
+ thread_unlock(rt);
+}
+
+static void
+lx_ptrace_winfo(lx_lwp_data_t *remote, k_siginfo_t *ip, boolean_t waitflag,
+ pid_t *event_ppid, pid_t *event_pid)
+{
+ int signo;
+
+ /*
+ * Populate our k_siginfo_t with data about this "ptrace-stop"
+ * condition:
+ */
+ bzero(ip, sizeof (*ip));
+ ip->si_signo = SIGCLD;
+ ip->si_pid = LX_PID_TO_INIT(remote->br_pid);
+ ip->si_code = CLD_TRAPPED;
+
+ switch (remote->br_ptrace_whatstop) {
+ case LX_PR_SYSENTRY:
+ case LX_PR_SYSEXIT:
+ ip->si_status = SIGTRAP;
+ if (remote->br_ptrace_options & LX_PTRACE_O_TRACESYSGOOD) {
+ ip->si_status |= 0x80;
+ }
+ break;
+
+ case LX_PR_SIGNALLED:
+ signo = remote->br_ptrace_stopsig;
+ if (signo < 1 || signo >= LX_NSIG) {
+ /*
+ * If this signal number is not valid, pretend it
+ * was a SIGTRAP.
+ */
+ ip->si_status = SIGTRAP;
+ } else {
+ ip->si_status = ltos_signo[signo];
+ }
+ break;
+
+ case LX_PR_EVENT:
+ ip->si_status = SIGTRAP | remote->br_ptrace_event;
+ /*
+ * Record the Linux pid of both this LWP and the create
+ * event we are dispatching. We will use this information
+ * to unblock any subsequent ptrace(2) events that depend
+ * on this one.
+ */
+ if (event_ppid != NULL)
+ *event_ppid = remote->br_pid;
+ if (event_pid != NULL)
+ *event_pid = (pid_t)remote->br_ptrace_eventmsg;
+ break;
+
+ default:
+ cmn_err(CE_PANIC, "unxpected stop subreason: %d",
+ remote->br_ptrace_whatstop);
+ }
+
+ /*
+ * If WNOWAIT was specified, do not mark the event as posted
+ * so that it may be re-fetched on another call to waitid().
+ */
+ if (waitflag)
+ remote->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+}
+
+/*
+ * Receive notification from stop() of a PR_BRAND stop.
+ */
+void
+lx_stop_notify(proc_t *p, klwp_t *lwp, ushort_t why, ushort_t what)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+ klwp_t *plwp = NULL;
+ proc_t *pp = NULL;
+ lx_lwp_data_t *parent;
+ boolean_t cldpend = B_TRUE;
+ boolean_t cldpost = B_FALSE;
+ sigqueue_t *sqp = NULL;
+
+ /*
+ * We currently only care about LX-specific stop reasons.
+ */
+ if (why != PR_BRAND)
+ return;
+
+ switch (what) {
+ case LX_PR_SYSENTRY:
+ case LX_PR_SYSEXIT:
+ case LX_PR_SIGNALLED:
+ case LX_PR_EVENT:
+ break;
+ default:
+ cmn_err(CE_PANIC, "unexpected subreason for PR_BRAND"
+ " stop: %d", (int)what);
+ }
+
+ /*
+ * We should be holding the lock on our containing process. The
+ * STOPPING flag should have been set by lx_ptrace_stop() for all
+ * PR_BRAND stops.
+ */
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+ VERIFY((accord = lwpd->br_ptrace_tracer) != NULL);
+
+ /*
+ * We must drop our process lock to take "pidlock". The
+ * LX_PTF_STOPPING flag protects us from an exiting tracer.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Allocate before we enter any mutexes.
+ */
+ sqp = kmem_zalloc(sizeof (*sqp), KM_SLEEP);
+
+ /*
+ * We take pidlock now, which excludes all callers of waitid() and
+ * prevents a detaching tracer from clearing critical accord members.
+ */
+ mutex_enter(&pidlock);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Get the ptrace(2) "parent" process, to which we may send
+ * a SIGCLD signal later.
+ */
+ if ((parent = accord->lxpa_tracer) != NULL &&
+ (plwp = parent->br_lwp) != NULL) {
+ pp = lwptoproc(plwp);
+ }
+
+ /*
+ * Our tracer should not have been modified in our absence; the
+ * LX_PTF_STOPPING flag prevents it.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+
+ /*
+ * Stash data for this stop condition in the LWP data while we hold
+ * both pidlock and our p_lock.
+ */
+ lwpd->br_ptrace_whystop = why;
+ lwpd->br_ptrace_whatstop = what;
+ lwpd->br_ptrace_flags |= LX_PTF_WAITPEND;
+
+ /*
+ * If this event does not depend on an event from the parent LWP,
+ * populate the siginfo_t for the event pending on this tracee LWP.
+ */
+ if (!(lwpd->br_ptrace_flags & LX_PTF_PARENT_WAIT) && pp != NULL) {
+ cldpost = B_TRUE;
+ lx_ptrace_winfo(lwpd, &sqp->sq_info, B_FALSE, NULL, NULL);
+ }
+
+ /*
+ * Drop our p_lock so that we may lock the tracer.
+ */
+ mutex_exit(&p->p_lock);
+ if (cldpost && pp != NULL) {
+ /*
+ * Post the SIGCLD to the tracer.
+ */
+ mutex_enter(&pp->p_lock);
+ if (!sigismember(&pp->p_sig, SIGCLD)) {
+ sigaddqa(pp, plwp->lwp_thread, sqp);
+ cldpend = B_FALSE;
+ sqp = NULL;
+ }
+ mutex_exit(&pp->p_lock);
+ }
+
+ /*
+ * We re-take our process lock now. The lock will be held until
+ * the thread is actually marked stopped, so we will not race with
+ * lx_ptrace_lock_if_stopped() or lx_waitid_helper().
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * We clear the STOPPING flag; stop() continues to hold our p_lock
+ * until our thread stop state is visible.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_STOPPING;
+ lwpd->br_ptrace_flags |= LX_PTF_STOPPED;
+ if (cldpend) {
+ /*
+ * We sent the SIGCLD for this new wait condition already.
+ */
+ lwpd->br_ptrace_flags |= LX_PTF_CLDPEND;
+ }
+
+ /*
+ * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+ * be sleeping on this CV until LX_PTF_STOPPING is clear. Wake it
+ * now.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+ /*
+ * While still holding pidlock, we attempt to wake our tracer from a
+ * potential waitid() slumber.
+ */
+ if (accord->lxpa_cvp != NULL) {
+ cv_broadcast(accord->lxpa_cvp);
+ }
+
+ /*
+ * We release pidlock and return as we were called: with our p_lock
+ * held.
+ */
+ mutex_exit(&pidlock);
+
+ if (sqp != NULL) {
+ kmem_free(sqp, sizeof (*sqp));
+ }
+}
+
+/*
+ * For any restarting action (e.g. PTRACE_CONT, PTRACE_SYSCALL or
+ * PTRACE_DETACH) to be allowed, the tracee LWP must be in "ptrace-stop". This
+ * check must ONLY be run on tracees of the current LWP. If the check is
+ * successful, we return with the tracee p_lock held.
+ *
+ * In the case of PTRACE_DETACH, we can return with the tracee locked even if
+ * it is not in "ptrace-stop". This can happen for various reasons, such as if
+ * the remote process is already job-stopped in the kernel. We must still be
+ * able to detach from this process. We return ENOENT in this case.
+ */
+static int
+lx_ptrace_lock_if_stopped(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote,
+ boolean_t detaching)
+{
+ klwp_t *rlwp = remote->br_lwp;
+ proc_t *rproc = lwptoproc(rlwp);
+ kthread_t *rt = lwptot(rlwp);
+
+ /*
+ * We must never check that we, ourselves, are stopped. We must also
+ * have the accord tracee list locked while we lock our tracees.
+ */
+ VERIFY(curthread != rt);
+ VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+ VERIFY(accord->lxpa_tracer == ttolxlwp(curthread));
+
+ /*
+ * Lock the process containing the tracee LWP.
+ */
+ mutex_enter(&rproc->p_lock);
+ if (!VISIBLE(remote)) {
+ /*
+ * The tracee LWP is currently detaching itself as it exits.
+ * It is no longer visible to ptrace(2).
+ */
+ mutex_exit(&rproc->p_lock);
+ return (ESRCH);
+ }
+
+ /*
+ * We must only check whether tracees of the current LWP are stopped.
+ * We check this condition after confirming visibility as an exiting
+ * tracee may no longer be completely consistent.
+ */
+ VERIFY(remote->br_ptrace_tracer == accord);
+
+ if (!(remote->br_ptrace_flags & LX_PTF_STOPPED)) {
+ if (detaching) {
+ /*
+ * The tracee is not in "ptrace-stop", but we still
+ * return with the locked process. This is indicated
+ * by ENOENT.
+ */
+ return (ENOENT);
+ }
+
+ /*
+ * The tracee is not in "ptrace-stop", so we release the
+ * process.
+ */
+ mutex_exit(&rproc->p_lock);
+ return (ESRCH);
+ }
+
+ /*
+ * The tracee is stopped. We return holding its process lock so that
+ * the caller may manipulate it.
+ */
+ return (0);
+}
+
+static int
+lx_ptrace_setoptions(lx_lwp_data_t *remote, uintptr_t options)
+{
+ /*
+ * Check for valid options.
+ */
+ if ((options & ~LX_PTRACE_O_ALL) != 0) {
+ return (EINVAL);
+ }
+
+ /*
+ * Set ptrace options on the target LWP.
+ */
+ remote->br_ptrace_options = (lx_ptrace_options_t)options;
+
+ return (0);
+}
+
+static int
+lx_ptrace_geteventmsg(lx_lwp_data_t *remote, void *umsgp)
+{
+ int error;
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ uint32_t tmp = remote->br_ptrace_eventmsg;
+
+ error = copyout(&tmp, umsgp, sizeof (uint32_t));
+ } else
+#endif
+ {
+ error = copyout(&remote->br_ptrace_eventmsg, umsgp,
+ sizeof (ulong_t));
+ }
+
+ return (error);
+}
+
+static int
+lx_ptrace_getsiginfo(lx_lwp_data_t *remote, void *usiginfo)
+{
+ klwp_t *lwp = remote->br_lwp;
+ int lx_sig;
+
+ lx_sig = lx_stol_signo(lwp->lwp_cursig, 0);
+ if (lx_sig < 1 || lwp->lwp_curinfo == NULL) {
+ return (EINVAL);
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ if (stol_ksiginfo32_copyout(&lwp->lwp_curinfo->sq_info,
+ usiginfo) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif
+ {
+ if (stol_ksiginfo_copyout(&lwp->lwp_curinfo->sq_info,
+ usiginfo) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Implements the PTRACE_CONT subcommand of the Linux ptrace(2) interface.
+ */
+static int
+lx_ptrace_cont(lx_lwp_data_t *remote, lx_ptrace_cont_flags_t flags, int signo)
+{
+ klwp_t *lwp = remote->br_lwp;
+
+ if (flags & LX_PTC_SINGLESTEP) {
+ /*
+ * We do not currently support single-stepping.
+ */
+ lx_unsupported("PTRACE_SINGLESTEP not currently implemented");
+ return (EINVAL);
+ }
+
+ /*
+ * The tracer may choose to suppress the delivery of a signal, or
+ * select an alternative signal for delivery. If this is an
+ * appropriate ptrace(2) "signal-delivery-stop", br_ptrace_stopsig
+ * will be used as the new signal number.
+ *
+ * As with so many other aspects of the Linux ptrace(2) interface, this
+ * may fail silently if the state machine is not aligned correctly.
+ */
+ remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
+
+ /*
+ * Handle the syscall-stop flag if this is a PTRACE_SYSCALL restart:
+ */
+ if (flags & LX_PTC_SYSCALL) {
+ remote->br_ptrace_flags |= LX_PTF_SYSCALL;
+ } else {
+ remote->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+ }
+
+ lx_ptrace_restart_lwp(lwp);
+
+ return (0);
+}
+
+/*
+ * Implements the PTRACE_DETACH subcommand of the Linux ptrace(2) interface.
+ *
+ * The LWP identified by the Linux pid "lx_pid" will, if it as a tracee of the
+ * current LWP, be detached and (optionally) set runnable.
+ */
+static void
+lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo,
+ boolean_t restart)
+{
+ klwp_t *rlwp = remote->br_lwp;
+
+ /*
+ * The tracee LWP was in "ptrace-stop" and we now hold its p_lock.
+ * Detach the LWP from the accord and set it running.
+ */
+ VERIFY(!TRACEE_BUSY(remote));
+ VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+ remote->br_ptrace_flags &= ~(LX_PTF_SYSCALL | LX_PTF_INHERIT);
+ VERIFY(list_link_active(&remote->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, remote);
+
+ remote->br_ptrace_attach = LX_PTA_NONE;
+ remote->br_ptrace_tracer = NULL;
+ remote->br_ptrace_flags = 0;
+
+ /*
+ * Decrement traced-lwp count for the process.
+ */
+ ASSERT(MUTEX_HELD(&rlwp->lwp_procp->p_lock));
+ VERIFY(ptolxproc(rlwp->lwp_procp)->l_ptrace-- >= 1);
+
+ /*
+ * The tracer may, as described in lx_ptrace_cont(), choose to suppress
+ * or modify the delivered signal.
+ */
+ remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
+
+ if (restart) {
+ lx_ptrace_restart_lwp(rlwp);
+ }
+}
+
+/*
+ * This routine implements the PTRACE_ATTACH operation of the Linux ptrace(2)
+ * interface.
+ *
+ * This LWP is requesting to be attached as a tracer to another LWP -- the
+ * tracee. If a ptrace accord to track the list of tracees has not yet been
+ * allocated, one will be allocated and attached to this LWP now.
+ *
+ * The "br_ptrace_tracer" on the tracee LWP is set to this accord, and the
+ * tracee LWP is then added to the "lxpa_tracees" list in the accord. We drop
+ * locks between these two phases; the only consumer of trace events from this
+ * accord is this LWP, which obviously cannot be running waitpid(2) at the same
+ * time as this call to ptrace(2).
+ */
+static int
+lx_ptrace_attach(pid_t lx_pid)
+{
+ int error = ESRCH;
+ /*
+ * Our (Tracer) LWP:
+ */
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ /*
+ * Remote (Tracee) LWP:
+ */
+ pid_t rpid;
+ id_t rtid;
+ proc_t *rproc;
+ kthread_t *rthr;
+ klwp_t *rlwp;
+ lx_lwp_data_t *rlwpd;
+
+ if (lwpd->br_pid == lx_pid) {
+ /*
+ * We cannot trace ourselves.
+ */
+ return (EPERM);
+ }
+
+ /*
+ * Ensure that we have an accord and obtain a lock on it. This
+ * routine should not fail because the LWP cannot make ptrace(2) system
+ * calls after it has begun exiting.
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+ /*
+ * Place speculative hold in case the attach is successful.
+ */
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Locate the process containing the tracee LWP based on its Linux pid
+ * and lock it.
+ */
+ if (lx_lpid_to_spair(lx_pid, &rpid, &rtid) != 0 ||
+ (rproc = sprlock(rpid)) == NULL) {
+ /*
+ * We could not find the target process.
+ */
+ goto errout;
+ }
+
+ /*
+ * Locate the tracee LWP.
+ */
+ if ((rthr = idtot(rproc, rtid)) == NULL ||
+ (rlwp = ttolwp(rthr)) == NULL ||
+ (rlwpd = lwptolxlwp(rlwp)) == NULL ||
+ !VISIBLE(rlwpd)) {
+ /*
+ * The LWP could not be found, was not branded, or is not
+ * visible to ptrace(2) at this time.
+ */
+ goto unlock_errout;
+ }
+
+ /*
+ * We now hold the lock on the tracee. Attempt to install ourselves
+ * as the tracer.
+ */
+ if (curproc != rproc && priv_proc_cred_perm(curproc->p_cred, rproc,
+ NULL, VWRITE) != 0) {
+ /*
+ * This process does not have permission to trace the remote
+ * process.
+ */
+ error = EPERM;
+ } else if (rlwpd->br_ptrace_tracer != NULL) {
+ /*
+ * This LWP is already being traced.
+ */
+ VERIFY(list_link_active(&rlwpd->br_ptrace_linkage));
+ VERIFY(rlwpd->br_ptrace_attach != LX_PTA_NONE);
+ error = EPERM;
+ } else {
+ lx_proc_data_t *rprocd = ptolxproc(rproc);
+
+ /*
+ * Bond the tracee to the accord.
+ */
+ VERIFY0(rlwpd->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(rlwpd->br_ptrace_attach == LX_PTA_NONE);
+ rlwpd->br_ptrace_attach = LX_PTA_ATTACH;
+ rlwpd->br_ptrace_tracer = accord;
+
+ /* Don't emit ptrace syscall-stop-exit event on kernel exit. */
+ rlwpd->br_ptrace_flags |= LX_PTF_NOSTOP;
+
+ /*
+ * We had no tracer, and are thus not in the tracees list.
+ * It is safe to take the tracee list lock while we insert
+ * ourselves.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ VERIFY(!list_link_active(&rlwpd->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, rlwpd);
+ /*
+ * Bump traced-lwp count for the remote process.
+ */
+ rprocd->l_ptrace++;
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Send a thread-directed SIGSTOP.
+ */
+ sigtoproc(rproc, rthr, SIGSTOP);
+
+
+ error = 0;
+ }
+
+unlock_errout:
+ /*
+ * Unlock the process containing the tracee LWP and the accord.
+ */
+ sprunlock(rproc);
+
+errout:
+ if (error != 0) {
+ /*
+ * The attach was not successful. Remove our speculative
+ * hold.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+ }
+
+ return (error);
+}
+
+int
+lx_ptrace_set_clone_inherit(int option, boolean_t inherit_flag)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ mutex_enter(&p->p_lock);
+
+ switch (option) {
+ case LX_PTRACE_O_TRACEFORK:
+ case LX_PTRACE_O_TRACEVFORK:
+ case LX_PTRACE_O_TRACECLONE:
+ lwpd->br_ptrace_clone_option = option;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ if (inherit_flag) {
+ lwpd->br_ptrace_flags |= LX_PTF_INHERIT;
+ } else {
+ lwpd->br_ptrace_flags &= ~LX_PTF_INHERIT;
+ }
+
+ mutex_exit(&p->p_lock);
+ return (0);
+}
+
+/*
+ * If the parent LWP is being traced, we want to attach ourselves to the
+ * same accord.
+ */
+void
+lx_ptrace_inherit_tracer(lx_lwp_data_t *src, lx_lwp_data_t *dst)
+{
+ proc_t *srcp = lwptoproc(src->br_lwp);
+ proc_t *dstp = lwptoproc(dst->br_lwp);
+ lx_ptrace_accord_t *accord;
+ boolean_t is_fork = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&dstp->p_lock));
+ if (srcp != dstp) {
+ /*
+ * In the case of being called via forklwp, some lock shuffling
+ * is required. The destination p_lock must be dropped to
+ * avoid deadlocks when locking the source and manipulating
+ * ptrace accord resources.
+ */
+ is_fork = B_TRUE;
+ sprlock_proc(dstp);
+ mutex_exit(&dstp->p_lock);
+ mutex_enter(&srcp->p_lock);
+ }
+
+ if ((accord = src->br_ptrace_tracer) == NULL) {
+ /*
+ * The source LWP does not have a tracer to inherit.
+ */
+ goto out;
+ }
+
+ /*
+ * There are two conditions to check when determining if the new
+ * child should inherit the same tracer (and tracing options) as its
+ * parent. Either condition is sufficient to trigger inheritance.
+ */
+ dst->br_ptrace_attach = LX_PTA_NONE;
+ if ((src->br_ptrace_options & src->br_ptrace_clone_option) != 0) {
+ /*
+ * Condition 1:
+ * The clone(2), fork(2) and vfork(2) emulated system calls
+ * populate "br_ptrace_clone_option" with the specific
+ * ptrace(2) SETOPTIONS option that applies to this
+ * operation. If the relevant option has been enabled by the
+ * tracer then we inherit.
+ */
+ dst->br_ptrace_attach |= LX_PTA_INHERIT_OPTIONS;
+
+ } else if ((src->br_ptrace_flags & LX_PTF_INHERIT) != 0) {
+ /*
+ * Condition 2:
+ * If the caller opted in to inheritance with the
+ * PTRACE_CLONE flag to clone(2), the LX_PTF_INHERIT flag
+ * will be set and we inherit.
+ */
+ dst->br_ptrace_attach |= LX_PTA_INHERIT_CLONE;
+ }
+
+ /*
+ * These values only apply for the duration of a single clone(2), et
+ * al, system call.
+ */
+ src->br_ptrace_flags &= ~LX_PTF_INHERIT;
+ src->br_ptrace_clone_option = 0;
+
+ if (dst->br_ptrace_attach == LX_PTA_NONE) {
+ /*
+ * No condition triggered inheritance.
+ */
+ goto out;
+ }
+
+ /*
+ * Set the LX_PTF_CLONING flag to prevent us from being detached
+ * while our p_lock is dropped.
+ */
+ src->br_ptrace_flags |= LX_PTF_CLONING;
+ mutex_exit(&srcp->p_lock);
+
+ /*
+ * Hold the accord for the new LWP.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Install the tracer and copy the current PTRACE_SETOPTIONS options.
+ */
+ dst->br_ptrace_tracer = accord;
+ dst->br_ptrace_options = src->br_ptrace_options;
+
+ /*
+ * This flag prevents waitid() from seeing events for the new child
+ * until the parent is able to post the relevant ptrace event to
+ * the tracer.
+ */
+ dst->br_ptrace_flags |= LX_PTF_PARENT_WAIT;
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ VERIFY(list_link_active(&src->br_ptrace_linkage));
+ VERIFY(!list_link_active(&dst->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, dst);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Relock our process and clear our busy flag.
+ */
+ mutex_enter(&srcp->p_lock);
+ src->br_ptrace_flags &= ~LX_PTF_CLONING;
+
+ /*
+ * Bump traced-lwp count for the process.
+ */
+ ptolxproc(dstp)->l_ptrace++;
+
+ /*
+ * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+ * be sleeping on this CV until LX_PTF_CLONING is clear. Wake it
+ * now.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+out:
+ if (is_fork) {
+ mutex_exit(&srcp->p_lock);
+ mutex_enter(&dstp->p_lock);
+ sprunprlock(dstp);
+ }
+}
+
+static int
+lx_ptrace_traceme(void)
+{
+ int error;
+ boolean_t did_attach = B_FALSE;
+ /*
+ * Our (Tracee) LWP:
+ */
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ /*
+ * Remote (Tracer) LWP:
+ */
+ lx_ptrace_accord_t *accord;
+
+ /*
+ * We are intending to be the tracee. Fetch (or allocate) the accord
+ * for our parent LWP.
+ */
+ if ((error = lx_ptrace_accord_get_by_pid(lx_lwp_ppid(lwp, NULL,
+ NULL), &accord)) != 0) {
+ /*
+ * Could not determine the Linux pid of the parent LWP, or
+ * could not get the accord for that LWP.
+ */
+ return (error);
+ }
+
+ /*
+ * We now hold the accord lock.
+ */
+ if (accord->lxpa_flags & LX_ACC_TOMBSTONE) {
+ /*
+ * The accord is marked for death; give up now.
+ */
+ lx_ptrace_accord_exit(accord);
+ return (ESRCH);
+ }
+
+ /*
+ * Bump the reference count so that the accord is not freed. We need
+ * to drop the accord lock before we take our own p_lock.
+ */
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * We now lock _our_ process and determine if we can install our parent
+ * as our tracer.
+ */
+ mutex_enter(&p->p_lock);
+ if (lwpd->br_ptrace_tracer != NULL) {
+ /*
+ * This LWP is already being traced.
+ */
+ VERIFY(lwpd->br_ptrace_attach != LX_PTA_NONE);
+ error = EPERM;
+ } else {
+ /*
+ * Bond ourselves to the accord. We already bumped the accord
+ * reference count.
+ */
+ VERIFY(lwpd->br_ptrace_attach == LX_PTA_NONE);
+ lwpd->br_ptrace_attach = LX_PTA_TRACEME;
+ lwpd->br_ptrace_tracer = accord;
+ did_attach = B_TRUE;
+ error = 0;
+
+ /*
+ * Speculatively bump l_ptrace now before dropping p_lock.
+ * It will be reverted if the tracee attachment fails.
+ */
+ ptolxproc(p)->l_ptrace++;
+ }
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Lock the accord tracee list and add this LWP. Once we are in the
+ * tracee list, it is the responsibility of the tracer to detach us.
+ */
+ if (error == 0) {
+ lx_ptrace_accord_enter(accord);
+ mutex_enter(&accord->lxpa_tracees_lock);
+
+ if (!(accord->lxpa_flags & LX_ACC_TOMBSTONE)) {
+ /*
+ * Put ourselves in the tracee list for this accord.
+ */
+ VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, lwpd);
+ mutex_exit(&accord->lxpa_tracees_lock);
+ lx_ptrace_accord_exit(accord);
+
+ return (0);
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * The accord has been marked for death. We must
+ * untrace ourselves.
+ */
+ error = ESRCH;
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Undo speculative increment of ptracer count.
+ */
+ mutex_enter(&p->p_lock);
+ ptolxproc(p)->l_ptrace--;
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Our optimism was unjustified: We were unable to attach. We need to
+ * lock the process containing this LWP again in order to remove the
+ * tracer.
+ */
+ VERIFY(error != 0);
+ mutex_enter(&p->p_lock);
+ if (did_attach) {
+ /*
+ * Verify that things were as we left them:
+ */
+ VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+
+ lwpd->br_ptrace_attach = LX_PTA_NONE;
+ lwpd->br_ptrace_tracer = NULL;
+ }
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Remove our speculative hold on the accord, possibly causing it to be
+ * freed in the process.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ return (error);
+}
+
+static boolean_t
+lx_ptrace_stop_common(proc_t *p, lx_lwp_data_t *lwpd, ushort_t what)
+{
+ boolean_t reset_nostop = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Mark this LWP as stopping and call stop() to enter "ptrace-stop".
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+ lwpd->br_ptrace_flags |= LX_PTF_STOPPING;
+
+ if (lwpd->br_lwp->lwp_nostop == 1 &&
+ lwpd->br_ptrace_event == LX_PTRACE_EVENT_EXEC) {
+ /* We need to clear this to get the signal delivered. */
+ lwpd->br_lwp->lwp_nostop = 0;
+ reset_nostop = B_TRUE;
+ }
+
+ stop(PR_BRAND, what);
+
+ if (reset_nostop) {
+ VERIFY(lwpd->br_lwp->lwp_nostop == 0);
+ lwpd->br_lwp->lwp_nostop = 1;
+ }
+
+ /*
+ * We are back from "ptrace-stop" with our process lock held.
+ */
+ lwpd->br_ptrace_flags &= ~(LX_PTF_STOPPING | LX_PTF_STOPPED |
+ LX_PTF_CLDPEND);
+ lwpd->br_ptrace_stopucp = NULL;
+ cv_broadcast(&lx_ptrace_busy_cv);
+ mutex_exit(&p->p_lock);
+
+ return (B_TRUE);
+}
+
+int
+lx_ptrace_stop_for_option(int option, boolean_t child, ulong_t msg,
+ uintptr_t ucp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ mutex_enter(&p->p_lock);
+ if (lwpd->br_ptrace_tracer == NULL) {
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+ }
+
+ if (!child) {
+ /*
+ * Only the first event posted by a new process is to be held
+ * until the matching parent event is dispatched, and only if
+ * it is a "child" event. This is not a child event, so we
+ * clear the wait flag.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+ } else if (option == LX_PTRACE_O_TRACEVFORK) {
+ /*
+ * For a child, we have to handle vfork as a special case. In
+ * lx_ptrace_inherit_tracer() we set LX_PTF_PARENT_WAIT to
+ * force events to be delayed until the parent posts its event.
+ * This flag is cleared in lx_waitid_helper() to enforce a
+ * "happens after" relationship. However, this obviously cannot
+ * work for the vfork case. Thus, we clear our flag now so that
+ * we can deliver the signal in lx_stop_notify(), if necessary.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+ }
+
+ if (!(lwpd->br_ptrace_options & option)) {
+ if (option == LX_PTRACE_O_TRACEEXEC) {
+ /*
+ * Without PTRACE_O_TRACEEXEC, the Linux kernel will
+ * send SIGTRAP to the process.
+ */
+ sigtoproc(p, t, SIGTRAP);
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ /*
+ * The flag for this trace event is not enabled, so we will not
+ * stop.
+ */
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+ }
+
+ if (child) {
+ switch (option) {
+ case LX_PTRACE_O_TRACECLONE:
+ case LX_PTRACE_O_TRACEFORK:
+ case LX_PTRACE_O_TRACEVFORK:
+ /*
+ * Send the child LWP a directed SIGSTOP.
+ */
+ sigtoproc(p, t, SIGSTOP);
+ mutex_exit(&p->p_lock);
+ return (0);
+ default:
+ goto nostop;
+ }
+ }
+
+ lwpd->br_ptrace_eventmsg = msg;
+
+ switch (option) {
+ case LX_PTRACE_O_TRACECLONE:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_CLONE;
+ break;
+ case LX_PTRACE_O_TRACEEXEC:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXEC;
+ lwpd->br_ptrace_eventmsg = 0;
+ break;
+ case LX_PTRACE_O_TRACEEXIT:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXIT;
+ break;
+ case LX_PTRACE_O_TRACEFORK:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_FORK;
+ break;
+ case LX_PTRACE_O_TRACEVFORK:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK;
+ break;
+ case LX_PTRACE_O_TRACEVFORKDONE:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK_DONE;
+ lwpd->br_ptrace_eventmsg = 0;
+ break;
+ default:
+ goto nostop;
+ }
+
+ /*
+ * Userland may have passed in a ucontext_t pointer for
+ * PTRACE_GETREGS/PTRACE_SETREGS usage while stopped.
+ */
+ lwpd->br_ptrace_stopucp = ucp;
+
+ /*
+ * p_lock for the process containing the tracee will be dropped by
+ * lx_ptrace_stop_common().
+ */
+ return (lx_ptrace_stop_common(p, lwpd, LX_PR_EVENT) ? 0 : ESRCH);
+
+nostop:
+ lwpd->br_ptrace_event = 0;
+ lwpd->br_ptrace_eventmsg = 0;
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+}
+
+boolean_t
+lx_ptrace_stop(ushort_t what)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ VERIFY(what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT ||
+ what == LX_PR_SIGNALLED);
+
+ /*
+ * If we do not have an accord, bail out early.
+ */
+ if (lwpd->br_ptrace_tracer == NULL)
+ return (B_FALSE);
+
+ /*
+ * Lock this process and re-check the condition.
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * The child after a fork/clone doesn't emit syscall-exit-stop event.
+ */
+ if (what == LX_PR_SYSEXIT && (lwpd->br_ptrace_flags & LX_PTF_NOSTOP)) {
+ lwpd->br_ptrace_flags &= ~LX_PTF_NOSTOP;
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ if (lwpd->br_ptrace_tracer == NULL) {
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_SYSCALL);
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ if (what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT) {
+ if (what == LX_PR_SYSENTRY) {
+ lwpd->br_ptrace_flags |= LX_PTF_INSYSCALL;
+ } else {
+ lwpd->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+ }
+
+ /*
+ * This is a syscall-entry-stop or syscall-exit-stop point.
+ */
+ if (!(lwpd->br_ptrace_flags & LX_PTF_SYSCALL)) {
+ /*
+ * A system call stop has not been requested.
+ */
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ /*
+ * The PTRACE_SYSCALL restart command applies only to the next
+ * system call entry or exit. The tracer must restart us with
+ * PTRACE_SYSCALL while we are in ptrace-stop for us to fire
+ * again at the next system call boundary.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+ }
+
+ /*
+ * p_lock for the process containing the tracee will be dropped by
+ * lx_ptrace_stop_common().
+ */
+ return (lx_ptrace_stop_common(p, lwpd, what));
+}
+
+int
+lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ int lx_sig;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * In very rare circumstances, a process which is almost completely
+ * through proc_exit() may incur issig checks in the current thread via
+ * clean-up actions. The process will still be branded, but the thread
+ * will have already been stripped of any LX-specific data on its way
+ * to the grave. Bail early if the brand data is missing.
+ */
+ if (lwpd == NULL) {
+ return (0);
+ }
+
+ /*
+ * If we do not have an accord, bail out now. Additionally, if there
+ * is no valid signal then we have no reason to stop.
+ */
+ if (lwpd->br_ptrace_tracer == NULL || lwp->lwp_cursig == SIGKILL ||
+ (lwp->lwp_cursig == 0 || lwp->lwp_cursig > NSIG) ||
+ (lx_sig = stol_signo[lwp->lwp_cursig]) < 1) {
+ if (lwp->lwp_cursig == 0) {
+ /*
+ * If this lwp has no current signal, it means that any
+ * signal ignorance enabled by br_ptrace_donesig has
+ * already taken place (the signal was consumed).
+ * By clearing donesig, we declare desire to ignore no
+ * signals for accurate ptracing.
+ */
+ lwpd->br_ptrace_donesig = 0;
+ }
+ return (0);
+ }
+
+ /*
+ * We can't deliver the signal-delivery-stop condition while we're
+ * between the syscall-enter-stop and syscall-exit-stop conditions.
+ * We must first let the signal interrupt the in-progress syscall, let
+ * it emit syscall-exit-stop with the interrupted result, then we'll
+ * come back here to emit signal-delivery-stop.
+ */
+ if (lwpd->br_ptrace_flags & LX_PTF_INSYSCALL) {
+ return (0);
+ }
+
+ /*
+ * We stash the signal on the LWP where our waitid_helper will find it
+ * and enter the ptrace "signal-delivery-stop" condition.
+ */
+ lwpd->br_ptrace_stopsig = lx_sig;
+ lwpd->br_ptrace_donesig = 0;
+ (void) lx_ptrace_stop_common(p, lwpd, LX_PR_SIGNALLED);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * When we return, the signal may have been altered or suppressed.
+ */
+ if (lwpd->br_ptrace_stopsig != lx_sig) {
+ int native_sig;
+ lx_sig = lwpd->br_ptrace_stopsig;
+
+ if (lx_sig >= LX_NSIG) {
+ lx_sig = 0;
+ }
+
+ /*
+ * Translate signal from Linux signal number back to
+ * an illumos native signal.
+ */
+ if (lx_sig >= LX_NSIG || lx_sig < 0 || (native_sig =
+ ltos_signo[lx_sig]) < 1) {
+ /*
+ * The signal is not deliverable.
+ */
+ lwp->lwp_cursig = 0;
+ lwp->lwp_extsig = 0;
+ if (lwp->lwp_curinfo) {
+ siginfofree(lwp->lwp_curinfo);
+ lwp->lwp_curinfo = NULL;
+ }
+ } else {
+ /*
+ * Alter the currently dispatching signal.
+ */
+ if (native_sig == SIGKILL) {
+ /*
+ * We mark ourselves the victim and request
+ * a restart of signal processing.
+ */
+ p->p_flag |= SKILLED;
+ p->p_flag &= ~SEXTKILLED;
+ return (-1);
+ }
+ lwp->lwp_cursig = native_sig;
+ lwp->lwp_extsig = 0;
+ if (lwp->lwp_curinfo != NULL) {
+ lwp->lwp_curinfo->sq_info.si_signo = native_sig;
+ }
+ }
+ }
+
+ lwpd->br_ptrace_donesig = lwp->lwp_cursig;
+ lwpd->br_ptrace_stopsig = 0;
+ return (0);
+}
+
+boolean_t
+lx_ptrace_sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+ lx_proc_data_t *lxpd = ptolxproc(p);
+
+ /*
+ * Ignored signals and ptrace:
+ *
+ * When a process is being ptraced by another, special care is needed
+ * while handling signals. Since the tracer is interested in all
+ * signals sent to the tracee, an effort must be made to initially
+ * bypass signal ignorance logic. This allows the signal to be placed
+ * in the tracee's sigqueue to be inspected and potentially altered by
+ * the tracer.
+ *
+ * A critical detail in this procedure is how a signal is handled after
+ * tracer has completed processing for the event. If the signal would
+ * have been ignored, were it not for the initial ptrace override, then
+ * lx_ptrace_sig_ignorable must report B_TRUE when the tracee is
+ * restarted and resumes signal processing. This is done by recording
+ * the most recent tracee signal consumed by ptrace.
+ */
+
+ if (lxpd->l_ptrace != 0 && lx_stol_signo(sig, 0) != 0) {
+ /*
+ * This process is being ptraced. Bypass signal ignorance for
+ * anything that maps to a valid Linux signal...
+ */
+ if (lwp != NULL && lwptolxlwp(lwp)->br_ptrace_donesig == sig) {
+ /*
+ * ...Unless it is a signal which has already been
+ * processed by the tracer.
+ */
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+static void
+lx_ptrace_exit_tracer(proc_t *p, lx_lwp_data_t *lwpd,
+ lx_ptrace_accord_t *accord)
+{
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ lx_ptrace_accord_enter(accord);
+ /*
+ * Mark this accord for death. This means no new tracees can be
+ * attached to this accord.
+ */
+ VERIFY0(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+ accord->lxpa_flags |= LX_ACC_TOMBSTONE;
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Walk the list of tracees, detaching them and setting them runnable
+ * if they are stopped.
+ */
+ for (;;) {
+ klwp_t *rlwp;
+ proc_t *rproc;
+ lx_lwp_data_t *remote;
+ kmutex_t *rmp;
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ if (list_is_empty(&accord->lxpa_tracees)) {
+ mutex_exit(&accord->lxpa_tracees_lock);
+ break;
+ }
+
+ /*
+ * Fetch the first tracee LWP in the list and lock the process
+ * which contains it.
+ */
+ remote = list_head(&accord->lxpa_tracees);
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+ /*
+ * The p_lock mutex persists beyond the life of the process
+ * itself. We save the address, here, to prevent the need to
+ * dereference the proc_t after awaking from sleep.
+ */
+ rmp = &rproc->p_lock;
+ mutex_enter(rmp);
+
+ if (TRACEE_BUSY(remote)) {
+ /*
+ * This LWP is currently detaching itself on exit, or
+ * mid-way through stop(). We must wait for this
+ * action to be completed. While we wait on the CV, we
+ * must drop the accord tracee list lock.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ cv_wait(&lx_ptrace_busy_cv, rmp);
+
+ /*
+ * While we were waiting, some state may have changed.
+ * Restart the walk to be sure we don't miss anything.
+ */
+ mutex_exit(rmp);
+ continue;
+ }
+
+ /*
+ * We now hold p_lock on the process. Remove the tracee from
+ * the list.
+ */
+ VERIFY(list_link_active(&remote->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, remote);
+
+ /*
+ * Unlink the accord and clear our trace flags.
+ */
+ remote->br_ptrace_attach = LX_PTA_NONE;
+ remote->br_ptrace_tracer = NULL;
+ remote->br_ptrace_flags = 0;
+
+ /*
+ * Let go of the list lock before we restart the LWP. We must
+ * not hold any locks other than the process p_lock when
+ * we call lx_ptrace_restart_lwp() as it will thread_lock
+ * the tracee.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Decrement traced-lwp count for the remote process.
+ */
+ VERIFY(ptolxproc(rproc)->l_ptrace-- >= 1);
+
+ /*
+ * Ensure that the LWP is not stopped on our account.
+ */
+ lx_ptrace_restart_lwp(rlwp);
+
+ /*
+ * Unlock the former tracee.
+ */
+ mutex_exit(rmp);
+
+ /*
+ * Drop the hold this tracee had on the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+ }
+
+ mutex_enter(&p->p_lock);
+ lwpd->br_ptrace_accord = NULL;
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Clean up and release our hold on the accord If we completely
+ * detached all tracee LWPs, this will free the accord. Otherwise, it
+ * will be freed when they complete their cleanup.
+ *
+ * We hold "pidlock" while clearing these members for easy exclusion of
+ * waitid(), etc.
+ */
+ mutex_enter(&pidlock);
+ lx_ptrace_accord_enter(accord);
+ accord->lxpa_cvp = NULL;
+ accord->lxpa_tracer = NULL;
+ mutex_exit(&pidlock);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+}
+
+static void
+lx_ptrace_exit_tracee(proc_t *p, lx_lwp_data_t *lwpd,
+ lx_ptrace_accord_t *accord)
+{
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * We are the tracee LWP. Lock the accord tracee list and then our
+ * containing process.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Remove our reference to the accord. We will release our hold
+ * later.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+ lwpd->br_ptrace_attach = LX_PTA_NONE;
+ lwpd->br_ptrace_tracer = NULL;
+
+ /*
+ * Remove this LWP from the accord tracee list:
+ */
+ VERIFY(list_link_active(&lwpd->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, lwpd);
+
+ /*
+ * Wake up any tracers waiting for us to detach from the accord.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+ /*
+ * Decrement traced-lwp count for the process.
+ */
+ VERIFY(ptolxproc(p)->l_ptrace-- >= 1);
+
+ mutex_exit(&p->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Grab "pidlock" and wake the tracer if it is blocked in waitid().
+ */
+ mutex_enter(&pidlock);
+ if (accord->lxpa_cvp != NULL) {
+ cv_broadcast(accord->lxpa_cvp);
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * Release our hold on the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+}
+
+/*
+ * This routine is called from lx_exitlwp() when an LWP is ready to exit. If
+ * this LWP is being traced, it will be detached from the tracer's accord. The
+ * routine will also detach any LWPs being traced by this LWP.
+ */
+void
+lx_ptrace_exit(proc_t *p, klwp_t *lwp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Mark our LWP as exiting from a ptrace perspective. This will
+ * prevent a new accord from being allocated if one does not exist
+ * already, and will make us invisible to PTRACE_ATTACH/PTRACE_TRACEME.
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+ lwpd->br_ptrace_flags |= LX_PTF_EXITING;
+
+ if ((accord = lwpd->br_ptrace_tracer) != NULL) {
+ /*
+ * We are traced by another LWP and must detach ourselves.
+ */
+ mutex_exit(&p->p_lock);
+ lx_ptrace_exit_tracee(p, lwpd, accord);
+ mutex_enter(&p->p_lock);
+ }
+
+ if ((accord = lwpd->br_ptrace_accord) != NULL) {
+ /*
+ * We have been tracing other LWPs, and must detach from
+ * them and clean up our accord.
+ */
+ mutex_exit(&p->p_lock);
+ lx_ptrace_exit_tracer(p, lwpd, accord);
+ mutex_enter(&p->p_lock);
+ }
+}
+
+/*
+ * Called when a SIGCLD signal is dispatched so that we may enqueue another.
+ * Return 0 if we enqueued a signal, or -1 if not.
+ */
+int
+lx_sigcld_repost(proc_t *pp, sigqueue_t *sqp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *remote;
+ klwp_t *rlwp;
+ proc_t *rproc;
+ boolean_t found = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&pidlock));
+ VERIFY(MUTEX_NOT_HELD(&pp->p_lock));
+ VERIFY(lwptoproc(lwp) == pp);
+
+ mutex_enter(&pp->p_lock);
+ if ((accord = lwpd->br_ptrace_accord) == NULL) {
+ /*
+ * This LWP is not a tracer LWP, so there will be no
+ * SIGCLD.
+ */
+ mutex_exit(&pp->p_lock);
+ return (-1);
+ }
+ mutex_exit(&pp->p_lock);
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ /*
+ * Check if this LWP is in "ptrace-stop". If in the correct
+ * stop condition, lock the process containing the tracee LWP.
+ */
+ if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+ /*
+ * This event depends on waitid() clearing out the
+ * event of another LWP. Skip it for now.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_CLDPEND)) {
+ /*
+ * No SIGCLD is required for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+ remote->br_ptrace_whystop == 0 ||
+ remote->br_ptrace_whatstop == 0) {
+ /*
+ * No (new) stop reason to post for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ /*
+ * We found a process of interest. Leave the process
+ * containing the tracee LWP locked and break out of the loop.
+ */
+ found = B_TRUE;
+ break;
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ if (!found) {
+ return (-1);
+ }
+
+ /*
+ * Generate siginfo for this tracee LWP.
+ */
+ lx_ptrace_winfo(remote, &sqp->sq_info, B_FALSE, NULL, NULL);
+ remote->br_ptrace_flags &= ~LX_PTF_CLDPEND;
+ mutex_exit(&rproc->p_lock);
+
+ mutex_enter(&pp->p_lock);
+ if (sigismember(&pp->p_sig, SIGCLD)) {
+ mutex_exit(&pp->p_lock);
+
+ mutex_enter(&rproc->p_lock);
+ remote->br_ptrace_flags |= LX_PTF_CLDPEND;
+ mutex_exit(&rproc->p_lock);
+
+ return (-1);
+ }
+ sigaddqa(pp, curthread, sqp);
+ mutex_exit(&pp->p_lock);
+
+ return (0);
+}
+
+/*
+ * Consume the next available ptrace(2) event queued against the accord for
+ * this LWP. The event will be emitted as if through waitid(), and converted
+ * by lx_waitpid() and friends before the return to usermode.
+ */
+int
+lx_waitid_helper(idtype_t idtype, id_t id, k_siginfo_t *ip, int options,
+ boolean_t *brand_wants_wait, int *rval)
+{
+ lx_ptrace_accord_t *accord;
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *local = lwptolxlwp(lwp);
+ lx_lwp_data_t *remote;
+ boolean_t found = B_FALSE;
+ klwp_t *rlwp = NULL;
+ proc_t *rproc = NULL;
+ pid_t event_pid = 0, event_ppid = 0;
+ boolean_t waitflag = !(options & WNOWAIT);
+ boolean_t target_found = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&pidlock));
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * By default, we do not expect waitid() to block on our account.
+ */
+ *brand_wants_wait = B_FALSE;
+
+ if (!local->br_waitid_emulate) {
+ /*
+ * This waitid() call is not expecting emulated results.
+ */
+ return (-1);
+ }
+
+ switch (idtype) {
+ case P_ALL:
+ case P_PID:
+ case P_PGID:
+ break;
+ default:
+ /*
+ * This idtype has no power here.
+ */
+ return (-1);
+ }
+
+ if (lx_ptrace_accord_get(&accord, B_FALSE) != 0) {
+ /*
+ * This LWP does not have an accord; it cannot be tracing.
+ */
+ return (-1);
+ }
+
+ /*
+ * We do not need an additional hold on the accord as it belongs to
+ * the running, tracer, LWP.
+ */
+ lx_ptrace_accord_exit(accord);
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ if (list_is_empty(&accord->lxpa_tracees)) {
+ /*
+ * Though it has an accord, there are currently no tracees in
+ * the list for this LWP.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (-1);
+ }
+
+ /*
+ * Walk the list of tracees and determine if any of them have events to
+ * report.
+ */
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ /*
+ * We check to see if this LWP matches an id we are waiting for.
+ */
+ switch (idtype) {
+ case P_ALL:
+ break;
+ case P_PID:
+ if (remote->br_pid != id)
+ continue;
+ break;
+ case P_PGID:
+ if (rproc->p_pgrp != id)
+ continue;
+ break;
+ default:
+ cmn_err(CE_PANIC, "unexpected idtype: %d", idtype);
+ }
+
+ /* This tracee matches provided idtype and id */
+ target_found = B_TRUE;
+
+ /*
+ * Check if this LWP is in "ptrace-stop". If in the correct
+ * stop condition, lock the process containing the tracee LWP.
+ */
+ if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+ /*
+ * This event depends on waitid() clearing out the
+ * event of another LWP. Skip it for now.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+ remote->br_ptrace_whystop == 0 ||
+ remote->br_ptrace_whatstop == 0) {
+ /*
+ * No (new) stop reason to post for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ /*
+ * We found a process of interest. Leave the process
+ * containing the tracee LWP locked and break out of the loop.
+ */
+ found = B_TRUE;
+ break;
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ if (!found) {
+ /*
+ * There were no events of interest, but we have tracees.
+ * If any of the tracees matched the spcified criteria, signal
+ * to waitid() that it should block if the provided flags allow
+ * for it.
+ */
+ if (target_found) {
+ *brand_wants_wait = B_TRUE;
+ }
+
+ return (-1);
+ }
+
+ /*
+ * Populate the signal information.
+ */
+ lx_ptrace_winfo(remote, ip, waitflag, &event_ppid, &event_pid);
+
+ /*
+ * Unlock the tracee.
+ */
+ mutex_exit(&rproc->p_lock);
+
+ if (event_pid != 0 && event_ppid != 0) {
+ /*
+ * We need to do another pass around the tracee list and
+ * unblock any events that have a "happens after" relationship
+ * with this event.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ mutex_enter(&rproc->p_lock);
+
+ if (remote->br_pid != event_pid ||
+ remote->br_ppid != event_ppid) {
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ remote->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+ mutex_exit(&rproc->p_lock);
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+ }
+
+ /*
+ * If we are consuming this wait state, we remove the SIGCLD from
+ * the queue and post another.
+ */
+ if (waitflag) {
+ mutex_exit(&pidlock);
+ sigcld_delete(ip);
+ sigcld_repost();
+ mutex_enter(&pidlock);
+ }
+
+ *rval = 0;
+ return (0);
+}
+
+static int
+lx_ptrace_peek(lx_lwp_data_t *lwpd, uintptr_t addr, void *data)
+{
+ proc_t *p = lwptoproc(lwpd->br_lwp);
+ long buf;
+ int error = 0, size = sizeof (buf);
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ size = sizeof (uint32_t);
+ }
+#endif
+ if ((addr & (size - 1)) != 0) {
+ /* unaligned access */
+ return (EINVAL);
+ }
+
+ mutex_exit(&p->p_lock);
+ error = uread(p, &buf, size, addr);
+ mutex_enter(&p->p_lock);
+
+ if (error != 0) {
+ return (EIO);
+ }
+ if (copyout(&buf, data, size) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+lx_ptrace_poke(lx_lwp_data_t *lwpd, uintptr_t addr, uintptr_t data)
+{
+ proc_t *p = lwptoproc(lwpd->br_lwp);
+ int error = 0, size = sizeof (data);
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ size = sizeof (uint32_t);
+ }
+#endif
+ if ((addr & (size - 1)) != 0) {
+ /* unaligned access */
+ return (EINVAL);
+ }
+
+ mutex_exit(&p->p_lock);
+ error = uwrite(p, &data, size, addr);
+ mutex_enter(&p->p_lock);
+
+ if (error != 0) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+lx_ptrace_kill(lx_lwp_data_t *lwpd)
+{
+ sigtoproc(lwptoproc(lwpd->br_lwp), NULL, SIGKILL);
+
+ return (0);
+}
+
+static int
+lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+ lx_lwp_data_t *local = ttolxlwp(curthread);
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *remote;
+ klwp_t *rlwp;
+ proc_t *rproc;
+ int error;
+ boolean_t found = B_FALSE, restart = B_TRUE;
+
+ /*
+ * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of
+ * one LWP by another. The target LWP must not be traced already.
+ */
+ switch (ptrace_op) {
+ case LX_PTRACE_TRACEME:
+ return (lx_ptrace_traceme());
+
+ case LX_PTRACE_ATTACH:
+ return (lx_ptrace_attach(lxpid));
+ }
+
+ /*
+ * Ensure that we have an accord and obtain a lock on it. This routine
+ * should not fail because the LWP cannot make ptrace(2) system calls
+ * after it has begun exiting.
+ */
+ VERIFY0(local->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+ /*
+ * The accord belongs to this (the tracer) LWP, and we have a hold on
+ * it. We drop the lock so that we can take other locks.
+ */
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Does the tracee list contain the pid in question?
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ if (remote->br_pid == lxpid) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ /*
+ * The requested pid does not appear in the tracee list.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (ESRCH);
+ }
+
+ if (ptrace_op == LX_PTRACE_DETACH) {
+ /*
+ * We're detaching, make sure in-syscall flag is off so that
+ * signal will stop the process directly.
+ */
+ remote->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+ }
+
+ /*
+ * Attempt to lock the target LWP.
+ */
+ if ((error = lx_ptrace_lock_if_stopped(accord, remote,
+ (ptrace_op == LX_PTRACE_DETACH))) != 0) {
+ /*
+ * The LWP was not in "ptrace-stop". For detach, ENOENT
+ * indicates that the LWP was not in "ptrace-stop", but is
+ * still locked.
+ */
+ if (ptrace_op == LX_PTRACE_DETACH && error == ENOENT) {
+ /*
+ * We're detaching, but the process was not in
+ * ptrace_stop, so we don't want to try to restart it.
+ */
+ restart = B_FALSE;
+ } else {
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (error);
+ }
+ }
+
+ /*
+ * The target LWP is in "ptrace-stop". We have the containing process
+ * locked.
+ */
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+
+ if (ptrace_op == LX_PTRACE_DETACH) {
+ lx_ptrace_detach(accord, remote, (int)data, restart);
+ /*
+ * Drop the lock on both the tracee process and the tracee list.
+ */
+ mutex_exit(&rproc->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Release a hold from the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ return (0);
+ }
+
+ /*
+ * The tracees lock is not needed for any of the other operations.
+ * Drop it so further actions can avoid deadlock.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Process the ptrace(2) request:
+ */
+ switch (ptrace_op) {
+ case LX_PTRACE_CONT:
+ error = lx_ptrace_cont(remote, LX_PTC_NONE, (int)data);
+ break;
+
+ case LX_PTRACE_SYSCALL:
+ error = lx_ptrace_cont(remote, LX_PTC_SYSCALL, (int)data);
+ break;
+
+ case LX_PTRACE_SINGLESTEP:
+ error = lx_ptrace_cont(remote, LX_PTC_SINGLESTEP, (int)data);
+ break;
+
+ case LX_PTRACE_SETOPTIONS:
+ error = lx_ptrace_setoptions(remote, data);
+ break;
+
+ case LX_PTRACE_GETEVENTMSG:
+ error = lx_ptrace_geteventmsg(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETREGS:
+ error = lx_user_regs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETREGS:
+ error = lx_user_regs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETSIGINFO:
+ error = lx_ptrace_getsiginfo(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_PEEKTEXT:
+ case LX_PTRACE_PEEKDATA:
+ error = lx_ptrace_peek(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_POKETEXT:
+ case LX_PTRACE_POKEDATA:
+ error = lx_ptrace_poke(remote, addr, data);
+ break;
+
+ case LX_PTRACE_PEEKUSER:
+ error = lx_ptrace_peekuser(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_POKEUSER:
+ error = lx_ptrace_pokeuser(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_GETFPREGS:
+ error = lx_user_fpregs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETFPREGS:
+ error = lx_user_fpregs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETFPXREGS:
+ error = lx_user_fpxregs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETFPXREGS:
+ error = lx_user_fpxregs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_KILL:
+ error = lx_ptrace_kill(remote);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+
+ /*
+ * Drop the lock on both the tracee process and the tracee list.
+ */
+ mutex_exit(&rproc->p_lock);
+
+ return (error);
+}
+
+int
+lx_ptrace(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+ int error;
+
+ error = lx_ptrace_kernel(ptrace_op, LX_INIT_TO_PID(lxpid), addr, data);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+void
+lx_ptrace_init(void)
+{
+ cv_init(&lx_ptrace_busy_cv, NULL, CV_DEFAULT, NULL);
+
+ lx_ptrace_accord_cache = kmem_cache_create("lx_ptrace_accord",
+ sizeof (lx_ptrace_accord_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+}
+
+void
+lx_ptrace_fini(void)
+{
+ cv_destroy(&lx_ptrace_busy_cv);
+
+ kmem_cache_destroy(lx_ptrace_accord_cache);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_signal.c b/usr/src/uts/common/brand/lx/os/lx_signal.c
new file mode 100644
index 0000000000..53e0cecc14
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_signal.c
@@ -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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/sunddi.h>
+#include <lx_signum.h>
+
+void
+lx_ltos_sigset(lx_sigset_t *lsigp, k_sigset_t *ssigp)
+{
+ int lx_sig, sig;
+
+ sigemptyset(ssigp);
+ for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+ if (lx_sigismember(lsigp, lx_sig) &&
+ ((sig = ltos_signo[lx_sig]) > 0))
+ sigaddset(ssigp, sig);
+ }
+
+ /* Emulate sigutok() restrictions */
+ ssigp->__sigbits[0] &= (FILLSET0 & ~CANTMASK0);
+ ssigp->__sigbits[1] &= (FILLSET1 & ~CANTMASK1);
+ ssigp->__sigbits[2] &= (FILLSET2 & ~CANTMASK2);
+}
+
+void
+lx_stol_sigset(k_sigset_t *ssigp, lx_sigset_t *lsigp)
+{
+ int sig, lx_sig;
+
+ bzero(lsigp, sizeof (lx_sigset_t));
+ for (sig = 1; sig < NSIG; sig++) {
+ if (sigismember(ssigp, sig) &&
+ ((lx_sig = stol_signo[sig]) > 0))
+ lx_sigaddset(lsigp, lx_sig);
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
new file mode 100644
index 0000000000..037cdebcb9
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -0,0 +1,1221 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/privregs.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
+#include <sys/sdt.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_errno.h>
+
+
+/*
+ * Flags for sysent entries:
+ */
+#define LX_SYS_NOSYS_REASON 0x07
+#define LX_SYS_EBPARG6 0x08
+
+/*
+ * Flags that denote the specific reason we do not have a particular system
+ * call. These reasons are only valid if the function is NULL.
+ */
+#define NOSYS_USERMODE 0
+#define NOSYS_NULL 1
+#define NOSYS_NONE 2
+#define NOSYS_NO_EQUIV 3
+#define NOSYS_KERNEL 4
+#define NOSYS_UNDOC 5
+#define NOSYS_OBSOLETE 6
+#define NOSYS_MAX NOSYS_OBSOLETE
+
+#if NOSYS_MAX > LX_SYS_NOSYS_REASON
+#error NOSYS reason codes must fit in LX_SYS_NOSYS_REASON
+#endif
+
+/*
+ * Strings describing the reason we do not emulate a particular system call
+ * in the kernel.
+ */
+static char *nosys_reasons[] = {
+ NULL, /* NOSYS_USERMODE means this call is emulated in usermode */
+ "Not done yet",
+ "No such Linux system call",
+ "No equivalent illumos functionality",
+ "Reads/modifies Linux kernel state",
+ "Undocumented and/or rarely used system call",
+ "Unsupported, obsolete system call"
+};
+
+
+#if defined(_LP64)
+/*
+ * System call handler table and entry count for Linux x86_64 (amd64):
+ */
+lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+int lx_nsysent64;
+#endif
+/*
+ * System call handler table and entry count for Linux x86 (i386):
+ */
+lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+int lx_nsysent32;
+
+#if defined(_LP64)
+struct lx_vsyscall
+{
+ uintptr_t lv_addr;
+ uintptr_t lv_scnum;
+} lx_vsyscalls[] = {
+ { LX_VSYS_gettimeofday, LX_SYS_gettimeofday },
+ { LX_VSYS_time, LX_SYS_time },
+ { LX_VSYS_getcpu, LX_SYS_getcpu },
+ { NULL, NULL }
+};
+#endif
+
+#if defined(__amd64)
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+ struct regs *rp = lwptoregs(lwp);
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ /*
+ * Note: Syscall argument passing is different from function
+ * call argument passing on amd64. For function calls, the
+ * fourth arg is passed via %rcx, but for system calls the 4th
+ * arg is passed via %r10. This is because in amd64, the
+ * syscall instruction puts the lower 32 bits of %rflags in
+ * %r11 and puts the %rip value to %rcx.
+ *
+ * Appendix A of the amd64 ABI (Linux conventions) states that
+ * syscalls are limited to 6 args and no arg is passed on the
+ * stack.
+ */
+ args[0] = rp->r_rdi;
+ args[1] = rp->r_rsi;
+ args[2] = rp->r_rdx;
+ args[3] = rp->r_r10;
+ args[4] = rp->r_r8;
+ args[5] = rp->r_r9;
+ } else {
+ /*
+ * If the system call takes 6 args, then libc has stashed them
+ * in memory at the address contained in %ebx. Except for some
+ * syscalls which store the 6th argument in %ebp.
+ */
+ if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+ uint32_t args32[6];
+
+ if (copyin((void *)rp->r_rbx, &args32,
+ sizeof (args32)) != 0) {
+ /*
+ * Clear the argument vector so that the
+ * trace probe does not expose kernel
+ * memory.
+ */
+ bzero(args, 6 * sizeof (uintptr_t));
+ return (set_errno(EFAULT));
+ }
+
+ args[0] = args32[0];
+ args[1] = args32[1];
+ args[2] = args32[2];
+ args[3] = args32[3];
+ args[4] = args32[4];
+ args[5] = args32[5];
+ } else {
+ args[0] = rp->r_rbx;
+ args[1] = rp->r_rcx;
+ args[2] = rp->r_rdx;
+ args[3] = rp->r_rsi;
+ args[4] = rp->r_rdi;
+ args[5] = rp->r_rbp;
+ }
+ }
+
+ return (0);
+}
+
+#else /* !__amd64 */
+
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * If the system call takes 6 args, then libc has stashed them
+ * in memory at the address contained in %ebx. Except for some
+ * syscalls which store the 6th argument in %ebp.
+ */
+ if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+ if (copyin((void *)rp->r_ebx, args, 6 * sizeof (uintptr_t)) !=
+ 0) {
+ /*
+ * Clear the argument vector so that the trace probe
+ * does not expose kernel memory.
+ */
+ bzero(args, 6 * sizeof (uintptr_t));
+ return (set_errno(EFAULT));
+ }
+ } else {
+ args[0] = rp->r_ebx;
+ args[1] = rp->r_ecx;
+ args[2] = rp->r_edx;
+ args[3] = rp->r_esi;
+ args[4] = rp->r_edi;
+ args[5] = rp->r_ebp;
+ }
+
+ return (0);
+}
+#endif
+
+void
+lx_syscall_return(klwp_t *lwp, int syscall_num, long ret)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ int error = lwp->lwp_errno;
+
+ if (error != EINTR) {
+ /*
+ * If this system call was not interrupted, clear the system
+ * call restart flag before lx_setcontext() can pass it to
+ * usermode.
+ */
+ lwpd->br_syscall_restart = B_FALSE;
+ }
+
+ if (error != 0) {
+ /*
+ * Convert from illumos to Linux errno:
+ */
+ ret = -lx_errno(error, EINVAL);
+ }
+
+ /*
+ * 32-bit Linux system calls return via %eax; 64-bit calls return via
+ * %rax.
+ */
+ rp->r_r0 = ret;
+
+ /*
+ * Hold for the ptrace(2) "syscall-exit-stop" condition if required by
+ * PTRACE_SYSCALL. Note that the register state may be modified by
+ * tracer.
+ */
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+
+ /*
+ * Fire the DTrace "lx-syscall:::return" probe:
+ */
+ lx_trace_sysreturn(syscall_num, ret);
+
+ /*
+ * Clear errno for next time. We do not clear "br_syscall_restart" or
+ * "br_syscall_num" as they are potentially used by "lx_savecontext()"
+ * in the signal delivery path.
+ */
+ lwp->lwp_errno = 0;
+
+ lx_check_strict_failure(lwpd);
+
+ /*
+ * We want complete control of the registers on return from this
+ * emulated Linux system call:
+ */
+ lwp->lwp_eosys = JUSTRETURN;
+ curthread->t_post_sys = 1;
+ aston(curthread);
+}
+
+static void
+lx_syscall_unsup_msg(lx_sysent_t *s, int syscall_num, int unsup_reason)
+{
+ char buf[100];
+
+ if (s == NULL) {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%d): out of bounds",
+ syscall_num);
+ } else {
+ VERIFY(unsup_reason < (sizeof (nosys_reasons) /
+ sizeof (*nosys_reasons)));
+
+ if (s->sy_name == NULL) {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%d): %s",
+ syscall_num, nosys_reasons[unsup_reason]);
+ } else {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%s): %s",
+ s->sy_name, nosys_reasons[unsup_reason]);
+ }
+ }
+
+ lx_unsupported(buf);
+}
+
+/*
+ * This function is used to override the processing of arguments and
+ * invocation of a handler for emulated system calls, installed on each
+ * branded LWP as "lwp_brand_syscall". If this system call should use the
+ * native path, we return 1. If we handled this system call (and have made
+ * arrangements with respect to post-return usermode register state) we
+ * return 0.
+ */
+int
+lx_syscall_enter(void)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ int syscall_num;
+ int error;
+ long ret = 0;
+ lx_sysent_t *s;
+ uintptr_t args[6];
+ unsigned int unsup_reason;
+
+ /*
+ * If we got here, we should have an LWP-specific brand data
+ * structure.
+ */
+ VERIFY(lwpd != NULL);
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+ /*
+ * The lwp is not in in BRAND execution mode, so we return
+ * to the regular native system call path.
+ */
+ DTRACE_PROBE(brand__lx__syscall__hook__skip);
+ return (1);
+ }
+
+ /*
+ * Clear the restartable system call flag. This flag will be set
+ * on in the system call handler if the call is a candidate for
+ * a restart. It will be saved by lx_setcontext() in the event
+ * that we take a signal, and used in the signal handling path
+ * to restart the system call iff SA_RESTART was set for this
+ * signal. Save the system call number so that we can store it
+ * in the saved context if required.
+ */
+ lwpd->br_syscall_restart = B_FALSE;
+ lwpd->br_syscall_num = (int)rp->r_r0;
+
+ /*
+ * Hold for the ptrace(2) "syscall-entry-stop" condition if traced by
+ * PTRACE_SYSCALL. The system call number and arguments may be
+ * modified by the tracer.
+ */
+ (void) lx_ptrace_stop(LX_PR_SYSENTRY);
+
+ /*
+ * Check that the system call number is within the bounds we expect.
+ */
+ syscall_num = lwpd->br_syscall_num;
+ if (syscall_num < 0 || syscall_num > LX_MAX_SYSCALL(lwp)) {
+ lx_syscall_unsup_msg(NULL, syscall_num, 0);
+
+ (void) set_errno(ENOTSUP);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+
+#if defined(_LP64)
+ if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+ s = &lx_sysent64[syscall_num];
+ } else
+#endif
+ {
+ s = &lx_sysent32[syscall_num];
+ }
+
+ /*
+ * Process the arguments for this system call and fire the DTrace
+ * "lx-syscall:::entry" probe:
+ */
+ error = lx_emulate_args(lwp, s, args);
+ lx_trace_sysenter(syscall_num, args);
+ if (error != 0) {
+ /*
+ * Could not read and process the arguments. Return the error
+ * to the process.
+ */
+ (void) set_errno(error);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+
+ if (s->sy_callc != NULL) {
+ /*
+ * Call the in-kernel handler for this Linux system call:
+ */
+ lwpd->br_eosys = NORMALRETURN;
+ ret = s->sy_callc(args[0], args[1], args[2], args[3], args[4],
+ args[5]);
+ if (lwpd->br_eosys == NORMALRETURN) {
+ lx_syscall_return(lwp, syscall_num, ret);
+ }
+ return (0);
+ }
+
+ /*
+ * There is no in-kernel handler.
+ */
+ switch (unsup_reason = (s->sy_flags & LX_SYS_NOSYS_REASON)) {
+ case NOSYS_USERMODE:
+ /*
+ * Pass to the usermode emulation routine.
+ */
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(lwp, syscall_num, args);
+ } else
+#endif
+ {
+ lx_emulate_user(lwp, syscall_num, args);
+ }
+ return (0);
+
+ default:
+ /*
+ * We are not emulating this system call at all.
+ */
+ lx_syscall_unsup_msg(s, syscall_num, unsup_reason);
+
+ (void) set_errno(ENOTSUP);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+}
+
+#if defined(_LP64)
+/*
+ * Emulate vsyscall support.
+ *
+ * Linux magically maps a single page into the address space of each process,
+ * allowing them to make 'vsyscalls'. Originally designed to counteract the
+ * perceived overhead of regular system calls, vsyscalls were implemented as
+ * code residing in userspace which could be called directly. The userspace
+ * implementations of these vsyscalls which have now been replaced by
+ * instructions which vector into the normal syscall path.
+ *
+ * Implementing vsyscalls on Illumos is complicated by the fact that the
+ * required static address region resides inside the kernel address space.
+ * Rather than mapping a user-accessible page into the KAS, a different
+ * approach is taken. The vsyscall gate is emulated by interposing on
+ * pagefaults in trap(). An attempt to execute a known vsyscall address will
+ * result in emulating the appropriate system call rather than inducing a
+ * SIGSEGV.
+ */
+void
+lx_vsyscall_enter(proc_t *p, klwp_t *lwp, int scnum)
+{
+ struct regs *rp = lwptoregs(lwp);
+ uintptr_t raddr;
+
+ /*
+ * Fetch the return address from the process stack.
+ */
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ if (copyin((void *)rp->r_rsp, &raddr, sizeof (raddr)) != 0) {
+#if DEBUG
+ printf("lx_vsyscall_call: bad brand stack at vsyscall "
+ "cmd=%s, pid=%d, sp=0x%p\n", PTOU(p)->u_comm,
+ p->p_pid, (void *)rp->r_rsp);
+#endif
+
+ /*
+ * The process jumped to the vsyscall address without a
+ * correctly configured stack. Terminate the process.
+ */
+ exit(CLD_KILLED, SIGSEGV);
+ return;
+ }
+
+ DTRACE_PROBE1(brand__lx__vsyscall, int, scnum);
+
+ /* Simulate vectoring into the syscall */
+ rp->r_rax = scnum;
+ rp->r_rip = raddr;
+ rp->r_rsp += sizeof (uintptr_t);
+
+ (void) lx_syscall_enter();
+}
+
+boolean_t
+lx_vsyscall_iscall(klwp_t *lwp, uintptr_t addr, int *scnum)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ int i;
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+ /*
+ * We only handle vsyscalls when running Linux code.
+ */
+ return (B_FALSE);
+ }
+
+ if (addr < LX_VSYSCALL_ADDR ||
+ addr >= (LX_VSYSCALL_ADDR + LX_VSYSCALL_SIZE)) {
+ /*
+ * Ignore faults outside the vsyscall page.
+ */
+ return (B_FALSE);
+ }
+
+ for (i = 0; lx_vsyscalls[i].lv_addr != NULL; i++) {
+ if (addr == lx_vsyscalls[i].lv_addr) {
+ /*
+ * This is a valid vsyscall address.
+ */
+ *scnum = lx_vsyscalls[i].lv_scnum;
+ return (B_TRUE);
+ }
+ }
+
+ lx_unsupported("bad vsyscall access");
+ return (B_FALSE);
+}
+#endif
+
+/*
+ * Linux defines system call numbers for 32-bit x86 in the file:
+ * arch/x86/syscalls/syscall_32.tbl
+ */
+lx_sysent_t lx_sysent32[] = {
+ {"nosys", NULL, NOSYS_NONE, 0}, /* 0 */
+ {"exit", NULL, 0, 1}, /* 1 */
+ {"fork", NULL, 0, 0}, /* 2 */
+ {"read", lx_read, 0, 3}, /* 3 */
+ {"write", lx_write, 0, 3}, /* 4 */
+ {"open", lx_open, 0, 3}, /* 5 */
+ {"close", lx_close, 0, 1}, /* 6 */
+ {"waitpid", lx_waitpid, 0, 3}, /* 7 */
+ {"creat", lx_creat, 0, 2}, /* 8 */
+ {"link", lx_link, 0, 2}, /* 9 */
+ {"unlink", lx_unlink, 0, 1}, /* 10 */
+ {"execve", NULL, 0, 3}, /* 11 */
+ {"chdir", lx_chdir, 0, 1}, /* 12 */
+ {"time", lx_time, 0, 1}, /* 13 */
+ {"mknod", NULL, 0, 3}, /* 14 */
+ {"chmod", lx_chmod, 0, 2}, /* 15 */
+ {"lchown16", lx_lchown16, 0, 3}, /* 16 */
+ {"break", NULL, NOSYS_OBSOLETE, 0}, /* 17 */
+ {"stat", NULL, NOSYS_OBSOLETE, 0}, /* 18 */
+ {"lseek", lx_lseek32, 0, 3}, /* 19 */
+ {"getpid", lx_getpid, 0, 0}, /* 20 */
+ {"mount", lx_mount, 0, 5}, /* 21 */
+ {"umount", lx_umount, 0, 1}, /* 22 */
+ {"setuid16", lx_setuid16, 0, 1}, /* 23 */
+ {"getuid16", lx_getuid16, 0, 0}, /* 24 */
+ {"stime", lx_stime, 0, 1}, /* 25 */
+ {"ptrace", lx_ptrace, 0, 4}, /* 26 */
+ {"alarm", lx_alarm, 0, 1}, /* 27 */
+ {"fstat", NULL, NOSYS_OBSOLETE, 0}, /* 28 */
+ {"pause", lx_pause, 0, 0}, /* 29 */
+ {"utime", NULL, 0, 2}, /* 30 */
+ {"stty", NULL, NOSYS_OBSOLETE, 0}, /* 31 */
+ {"gtty", NULL, NOSYS_OBSOLETE, 0}, /* 32 */
+ {"access", lx_access, 0, 2}, /* 33 */
+ {"nice", lx_nice, 0, 1}, /* 34 */
+ {"ftime", NULL, NOSYS_OBSOLETE, 0}, /* 35 */
+ {"sync", lx_sync, 0, 0}, /* 36 */
+ {"kill", lx_kill, 0, 2}, /* 37 */
+ {"rename", lx_rename, 0, 2}, /* 38 */
+ {"mkdir", lx_mkdir, 0, 2}, /* 39 */
+ {"rmdir", NULL, 0, 1}, /* 40 */
+ {"dup", lx_dup, 0, 1}, /* 41 */
+ {"pipe", lx_pipe, 0, 1}, /* 42 */
+ {"times", NULL, 0, 1}, /* 43 */
+ {"prof", NULL, NOSYS_OBSOLETE, 0}, /* 44 */
+ {"brk", lx_brk, 0, 1}, /* 45 */
+ {"setgid16", lx_setgid16, 0, 1}, /* 46 */
+ {"getgid16", lx_getgid16, 0, 0}, /* 47 */
+ {"signal", NULL, 0, 2}, /* 48 */
+ {"geteuid16", lx_geteuid16, 0, 0}, /* 49 */
+ {"getegid16", lx_getegid16, 0, 0}, /* 50 */
+ {"acct", NULL, NOSYS_NO_EQUIV, 0}, /* 51 */
+ {"umount2", lx_umount2, 0, 2}, /* 52 */
+ {"lock", NULL, NOSYS_OBSOLETE, 0}, /* 53 */
+ {"ioctl", lx_ioctl, 0, 3}, /* 54 */
+ {"fcntl", lx_fcntl, 0, 3}, /* 55 */
+ {"mpx", NULL, NOSYS_OBSOLETE, 0}, /* 56 */
+ {"setpgid", lx_setpgid, 0, 2}, /* 57 */
+ {"ulimit", NULL, NOSYS_OBSOLETE, 0}, /* 58 */
+ {"olduname", NULL, NOSYS_OBSOLETE, 0}, /* 59 */
+ {"umask", lx_umask, 0, 1}, /* 60 */
+ {"chroot", lx_chroot, 0, 1}, /* 61 */
+ {"ustat", NULL, NOSYS_OBSOLETE, 2}, /* 62 */
+ {"dup2", lx_dup2, 0, 2}, /* 63 */
+ {"getppid", lx_getppid, 0, 0}, /* 64 */
+ {"getpgrp", lx_getpgrp, 0, 0}, /* 65 */
+ {"setsid", lx_setsid, 0, 0}, /* 66 */
+ {"sigaction", NULL, 0, 3}, /* 67 */
+ {"sgetmask", NULL, NOSYS_OBSOLETE, 0}, /* 68 */
+ {"ssetmask", NULL, NOSYS_OBSOLETE, 0}, /* 69 */
+ {"setreuid16", lx_setreuid16, 0, 2}, /* 70 */
+ {"setregid16", lx_setregid16, 0, 2}, /* 71 */
+ {"sigsuspend", NULL, 0, 1}, /* 72 */
+ {"sigpending", NULL, 0, 1}, /* 73 */
+ {"sethostname", lx_sethostname, 0, 2}, /* 74 */
+ {"setrlimit", lx_setrlimit, 0, 2}, /* 75 */
+ {"getrlimit", lx_oldgetrlimit, 0, 2}, /* 76 */
+ {"getrusage", lx_getrusage, 0, 2}, /* 77 */
+ {"gettimeofday", lx_gettimeofday, 0, 2}, /* 78 */
+ {"settimeofday", NULL, 0, 2}, /* 79 */
+ {"getgroups16", NULL, 0, 2}, /* 80 */
+ {"setgroups16", NULL, 0, 2}, /* 81 */
+ {"select", NULL, NOSYS_OBSOLETE, 0}, /* 82 */
+ {"symlink", lx_symlink, 0, 2}, /* 83 */
+ {"oldlstat", NULL, NOSYS_OBSOLETE, 0}, /* 84 */
+ {"readlink", lx_readlink, 0, 3}, /* 85 */
+ {"uselib", NULL, NOSYS_KERNEL, 0}, /* 86 */
+ {"swapon", NULL, NOSYS_KERNEL, 0}, /* 87 */
+ {"reboot", lx_reboot, 0, 4}, /* 88 */
+ {"readdir", NULL, 0, 3}, /* 89 */
+ {"mmap", NULL, 0, 6}, /* 90 */
+ {"munmap", lx_munmap, 0, 2}, /* 91 */
+ {"truncate", NULL, 0, 2}, /* 92 */
+ {"ftruncate", NULL, 0, 2}, /* 93 */
+ {"fchmod", lx_fchmod, 0, 2}, /* 94 */
+ {"fchown16", lx_fchown16, 0, 3}, /* 95 */
+ {"getpriority", NULL, 0, 2}, /* 96 */
+ {"setpriority", NULL, 0, 3}, /* 97 */
+ {"profil", NULL, NOSYS_NO_EQUIV, 0}, /* 98 */
+ {"statfs", NULL, 0, 2}, /* 99 */
+ {"fstatfs", NULL, 0, 2}, /* 100 */
+ {"ioperm", NULL, NOSYS_NO_EQUIV, 0}, /* 101 */
+ {"socketcall", lx_socketcall, 0, 2}, /* 102 */
+ {"syslog", lx_syslog, 0, 3}, /* 103 */
+ {"setitimer", NULL, 0, 3}, /* 104 */
+ {"getitimer", lx_getitimer, 0, 2}, /* 105 */
+ {"stat", lx_stat32, 0, 2}, /* 106 */
+ {"lstat", lx_lstat32, 0, 2}, /* 107 */
+ {"fstat", lx_fstat32, 0, 2}, /* 108 */
+ {"uname", NULL, NOSYS_OBSOLETE, 0}, /* 109 */
+ {"oldiopl", NULL, NOSYS_NO_EQUIV, 0}, /* 110 */
+ {"vhangup", lx_vhangup, 0, 0}, /* 111 */
+ {"idle", NULL, NOSYS_NO_EQUIV, 0}, /* 112 */
+ {"vm86old", NULL, NOSYS_OBSOLETE, 0}, /* 113 */
+ {"wait4", lx_wait4, 0, 4}, /* 114 */
+ {"swapoff", NULL, NOSYS_KERNEL, 0}, /* 115 */
+ {"sysinfo", lx_sysinfo32, 0, 1}, /* 116 */
+ {"ipc", NULL, 0, 5}, /* 117 */
+ {"fsync", NULL, 0, 1}, /* 118 */
+ {"sigreturn", NULL, 0, 1}, /* 119 */
+ {"clone", NULL, 0, 5}, /* 120 */
+ {"setdomainname", lx_setdomainname, 0, 2}, /* 121 */
+ {"uname", lx_uname, 0, 1}, /* 122 */
+ {"modify_ldt", lx_modify_ldt, 0, 3}, /* 123 */
+ {"adjtimex", NULL, 0, 1}, /* 124 */
+ {"mprotect", NULL, 0, 3}, /* 125 */
+ {"sigprocmask", NULL, 0, 3}, /* 126 */
+ {"create_module", NULL, NOSYS_KERNEL, 0}, /* 127 */
+ {"init_module", NULL, NOSYS_KERNEL, 0}, /* 128 */
+ {"delete_module", NULL, NOSYS_KERNEL, 0}, /* 129 */
+ {"get_kernel_syms", NULL, NOSYS_KERNEL, 0}, /* 130 */
+ {"quotactl", NULL, NOSYS_KERNEL, 0}, /* 131 */
+ {"getpgid", lx_getpgid, 0, 1}, /* 132 */
+ {"fchdir", lx_fchdir, 0, 1}, /* 133 */
+ {"bdflush", NULL, NOSYS_KERNEL, 0}, /* 134 */
+ {"sysfs", NULL, 0, 3}, /* 135 */
+ {"personality", lx_personality, 0, 1}, /* 136 */
+ {"afs_syscall", NULL, NOSYS_KERNEL, 0}, /* 137 */
+ {"setfsuid16", lx_setfsuid16, 0, 1}, /* 138 */
+ {"setfsgid16", lx_setfsgid16, 0, 1}, /* 139 */
+ {"llseek", lx_llseek, 0, 5}, /* 140 */
+ {"getdents", lx_getdents_32, 0, 3}, /* 141 */
+ {"select", lx_select, 0, 5}, /* 142 */
+ {"flock", NULL, 0, 2}, /* 143 */
+ {"msync", NULL, 0, 3}, /* 144 */
+ {"readv", lx_readv, 0, 3}, /* 145 */
+ {"writev", lx_writev, 0, 3}, /* 146 */
+ {"getsid", lx_getsid, 0, 1}, /* 147 */
+ {"fdatasync", NULL, 0, 1}, /* 148 */
+ {"sysctl", NULL, 0, 1}, /* 149 */
+ {"mlock", NULL, 0, 2}, /* 150 */
+ {"munlock", NULL, 0, 2}, /* 151 */
+ {"mlockall", NULL, 0, 1}, /* 152 */
+ {"munlockall", NULL, 0, 0}, /* 153 */
+ {"sched_setparam", NULL, 0, 2}, /* 154 */
+ {"sched_getparam", NULL, 0, 2}, /* 155 */
+ {"sched_setscheduler", NULL, 0, 3}, /* 156 */
+ {"sched_getscheduler", NULL, 0, 1}, /* 157 */
+ {"sched_yield", lx_sched_yield, 0, 0}, /* 158 */
+ {"sched_get_priority_max", NULL, 0, 1}, /* 159 */
+ {"sched_get_priority_min", NULL, 0, 1}, /* 160 */
+ {"sched_rr_get_interval", NULL, 0, 2}, /* 161 */
+ {"nanosleep", lx_nanosleep, 0, 2}, /* 162 */
+ {"mremap", NULL, 0, 5}, /* 163 */
+ {"setresuid16", lx_setresuid16, 0, 3}, /* 164 */
+ {"getresuid16", lx_getresuid16, 0, 3}, /* 165 */
+ {"vm86", NULL, NOSYS_NO_EQUIV, 0}, /* 166 */
+ {"query_module", NULL, 0, 5}, /* 167 */
+ {"poll", lx_poll, 0, 3}, /* 168 */
+ {"nfsservctl", NULL, NOSYS_KERNEL, 0}, /* 169 */
+ {"setresgid16", lx_setresgid16, 0, 3}, /* 170 */
+ {"getresgid16", lx_getresgid16, 0, 3}, /* 171 */
+ {"prctl", lx_prctl, 0, 5}, /* 172 */
+ {"rt_sigreturn", NULL, 0, 0}, /* 173 */
+ {"rt_sigaction", NULL, 0, 4}, /* 174 */
+ {"rt_sigprocmask", NULL, 0, 4}, /* 175 */
+ {"rt_sigpending", NULL, 0, 2}, /* 176 */
+ {"rt_sigtimedwait", NULL, 0, 4}, /* 177 */
+ {"rt_sigqueueinfo", NULL, 0, 3}, /* 178 */
+ {"rt_sigsuspend", NULL, 0, 2}, /* 179 */
+ {"pread64", lx_pread32, 0, 5}, /* 180 */
+ {"pwrite64", lx_pwrite32, 0, 5}, /* 181 */
+ {"chown16", lx_chown16, 0, 3}, /* 182 */
+ {"getcwd", lx_getcwd, 0, 2}, /* 183 */
+ {"capget", NULL, 0, 2}, /* 184 */
+ {"capset", NULL, 0, 2}, /* 185 */
+ {"sigaltstack", NULL, 0, 2}, /* 186 */
+ {"sendfile", NULL, 0, 4}, /* 187 */
+ {"getpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 188 */
+ {"putpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 189 */
+ {"vfork", NULL, 0, 0}, /* 190 */
+ {"getrlimit", lx_getrlimit, 0, 2}, /* 191 */
+ {"mmap2", NULL, LX_SYS_EBPARG6, 6}, /* 192 */
+ {"truncate64", NULL, 0, 3}, /* 193 */
+ {"ftruncate64", NULL, 0, 3}, /* 194 */
+ {"stat64", lx_stat64, 0, 2}, /* 195 */
+ {"lstat64", lx_lstat64, 0, 2}, /* 196 */
+ {"fstat64", lx_fstat64, 0, 2}, /* 197 */
+ {"lchown", lx_lchown, 0, 3}, /* 198 */
+ {"getuid", lx_getuid, 0, 0}, /* 199 */
+ {"getgid", lx_getgid, 0, 0}, /* 200 */
+ {"geteuid", lx_geteuid, 0, 0}, /* 201 */
+ {"getegid", lx_getegid, 0, 0}, /* 202 */
+ {"setreuid", lx_setreuid, 0, 0}, /* 203 */
+ {"setregid", lx_setregid, 0, 0}, /* 204 */
+ {"getgroups", NULL, 0, 2}, /* 205 */
+ {"setgroups", NULL, 0, 2}, /* 206 */
+ {"fchown", lx_fchown, 0, 3}, /* 207 */
+ {"setresuid", lx_setresuid, 0, 3}, /* 208 */
+ {"getresuid", lx_getresuid, 0, 3}, /* 209 */
+ {"setresgid", lx_setresgid, 0, 3}, /* 210 */
+ {"getresgid", lx_getresgid, 0, 3}, /* 211 */
+ {"chown", lx_chown, 0, 3}, /* 212 */
+ {"setuid", lx_setuid, 0, 1}, /* 213 */
+ {"setgid", lx_setgid, 0, 1}, /* 214 */
+ {"setfsuid", lx_setfsuid, 0, 1}, /* 215 */
+ {"setfsgid", lx_setfsgid, 0, 1}, /* 216 */
+ {"pivot_root", NULL, NOSYS_KERNEL, 0}, /* 217 */
+ {"mincore", lx_mincore, 0, 3}, /* 218 */
+ {"madvise", NULL, 0, 3}, /* 219 */
+ {"getdents64", lx_getdents64, 0, 3}, /* 220 */
+ {"fcntl64", lx_fcntl64, 0, 3}, /* 221 */
+ {"tux", NULL, NOSYS_NO_EQUIV, 0}, /* 222 */
+ {"security", NULL, NOSYS_NO_EQUIV, 0}, /* 223 */
+ {"gettid", lx_gettid, 0, 0}, /* 224 */
+ {"readahead", NULL, NOSYS_NO_EQUIV, 0}, /* 225 */
+ {"setxattr", lx_setxattr, 0, 5}, /* 226 */
+ {"lsetxattr", lx_lsetxattr, 0, 5}, /* 227 */
+ {"fsetxattr", lx_fsetxattr, 0, 5}, /* 228 */
+ {"getxattr", lx_getxattr, 0, 4}, /* 229 */
+ {"lgetxattr", lx_lgetxattr, 0, 4}, /* 230 */
+ {"fgetxattr", lx_fgetxattr, 0, 4}, /* 231 */
+ {"listxattr", lx_listxattr, 0, 3}, /* 232 */
+ {"llistxattr", lx_llistxattr, 0, 3}, /* 233 */
+ {"flistxattr", lx_flistxattr, 0, 3}, /* 234 */
+ {"removexattr", lx_removexattr, 0, 2}, /* 235 */
+ {"lremovexattr", lx_lremovexattr, 0, 2}, /* 236 */
+ {"fremovexattr", lx_fremovexattr, 0, 2}, /* 237 */
+ {"tkill", lx_tkill, 0, 2}, /* 238 */
+ {"sendfile64", NULL, 0, 4}, /* 239 */
+ {"futex", lx_futex, LX_SYS_EBPARG6, 6}, /* 240 */
+ {"sched_setaffinity", NULL, 0, 3}, /* 241 */
+ {"sched_getaffinity", NULL, 0, 3}, /* 242 */
+ {"set_thread_area", lx_set_thread_area, 0, 1}, /* 243 */
+ {"get_thread_area", lx_get_thread_area, 0, 1}, /* 244 */
+ {"io_setup", lx_io_setup, 0, 2}, /* 245 */
+ {"io_destroy", NULL, 0, 1}, /* 246 */
+ {"io_getevents", NULL, 0, 5}, /* 247 */
+ {"io_submit", NULL, 0, 3}, /* 248 */
+ {"io_cancel", NULL, 0, 3}, /* 249 */
+ {"fadvise64", lx_fadvise64_32, 0, 5}, /* 250 */
+ {"nosys", NULL, 0, 0}, /* 251 */
+ {"group_exit", NULL, 0, 1}, /* 252 */
+ {"lookup_dcookie", NULL, NOSYS_NO_EQUIV, 0}, /* 253 */
+ {"epoll_create", lx_epoll_create, 0, 1}, /* 254 */
+ {"epoll_ctl", lx_epoll_ctl, 0, 4}, /* 255 */
+ {"epoll_wait", lx_epoll_wait, 0, 4}, /* 256 */
+ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 257 */
+ {"set_tid_address", lx_set_tid_address, 0, 1}, /* 258 */
+ {"timer_create", NULL, 0, 3}, /* 259 */
+ {"timer_settime", NULL, 0, 4}, /* 260 */
+ {"timer_gettime", NULL, 0, 2}, /* 261 */
+ {"timer_getoverrun", NULL, 0, 1}, /* 262 */
+ {"timer_delete", NULL, 0, 1}, /* 263 */
+ {"clock_settime", lx_clock_settime, 0, 2}, /* 264 */
+ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 265 */
+ {"clock_getres", lx_clock_getres, 0, 2}, /* 266 */
+ {"clock_nanosleep", NULL, 0, 4}, /* 267 */
+ {"statfs64", NULL, 0, 2}, /* 268 */
+ {"fstatfs64", NULL, 0, 2}, /* 269 */
+ {"tgkill", lx_tgkill, 0, 3}, /* 270 */
+
+/*
+ * The following system calls only exist in kernel 2.6 and greater:
+ */
+ {"utimes", NULL, 0, 2}, /* 271 */
+ {"fadvise64_64", lx_fadvise64_64, LX_SYS_EBPARG6, 6}, /* 272 */
+ {"vserver", NULL, NOSYS_NULL, 0}, /* 273 */
+ {"mbind", NULL, NOSYS_NULL, 0}, /* 274 */
+ {"get_mempolicy", NULL, NOSYS_NULL, 0}, /* 275 */
+ {"set_mempolicy", NULL, NOSYS_NULL, 0}, /* 276 */
+ {"mq_open", NULL, NOSYS_NULL, 0}, /* 277 */
+ {"mq_unlink", NULL, NOSYS_NULL, 0}, /* 278 */
+ {"mq_timedsend", NULL, NOSYS_NULL, 0}, /* 279 */
+ {"mq_timedreceive", NULL, NOSYS_NULL, 0}, /* 280 */
+ {"mq_notify", NULL, NOSYS_NULL, 0}, /* 281 */
+ {"mq_getsetattr", NULL, NOSYS_NULL, 0}, /* 282 */
+ {"kexec_load", NULL, NOSYS_NULL, 0}, /* 283 */
+ {"waitid", lx_waitid, 0, 4}, /* 284 */
+ {"sys_setaltroot", NULL, NOSYS_NULL, 0}, /* 285 */
+ {"add_key", NULL, NOSYS_NULL, 0}, /* 286 */
+ {"request_key", NULL, NOSYS_NULL, 0}, /* 287 */
+ {"keyctl", NULL, NOSYS_NULL, 0}, /* 288 */
+ {"ioprio_set", lx_ioprio_set, 0, 3}, /* 289 */
+ {"ioprio_get", lx_ioprio_get, 0, 2}, /* 290 */
+ {"inotify_init", NULL, 0, 0}, /* 291 */
+ {"inotify_add_watch", NULL, 0, 3}, /* 292 */
+ {"inotify_rm_watch", NULL, 0, 2}, /* 293 */
+ {"migrate_pages", NULL, NOSYS_NULL, 0}, /* 294 */
+ {"openat", lx_openat, 0, 4}, /* 295 */
+ {"mkdirat", lx_mkdirat, 0, 3}, /* 296 */
+ {"mknodat", NULL, 0, 4}, /* 297 */
+ {"fchownat", lx_fchownat, 0, 5}, /* 298 */
+ {"futimesat", NULL, 0, 3}, /* 299 */
+ {"fstatat64", lx_fstatat64, 0, 4}, /* 300 */
+ {"unlinkat", lx_unlinkat, 0, 3}, /* 301 */
+ {"renameat", lx_renameat, 0, 4}, /* 302 */
+ {"linkat", lx_linkat, 0, 5}, /* 303 */
+ {"symlinkat", lx_symlinkat, 0, 3}, /* 304 */
+ {"readlinkat", lx_readlinkat, 0, 4}, /* 305 */
+ {"fchmodat", lx_fchmodat, 0, 3}, /* 306 */
+ {"faccessat", lx_faccessat, 0, 4}, /* 307 */
+ {"pselect6", lx_pselect, LX_SYS_EBPARG6, 6}, /* 308 */
+ {"ppoll", lx_ppoll, 0, 5}, /* 309 */
+ {"unshare", NULL, NOSYS_NULL, 0}, /* 310 */
+ {"set_robust_list", lx_set_robust_list, 0, 2}, /* 311 */
+ {"get_robust_list", lx_get_robust_list, 0, 3}, /* 312 */
+ {"splice", NULL, NOSYS_NULL, 0}, /* 313 */
+ {"sync_file_range", lx_sync_file_range, 0, 4}, /* 314 */
+ {"tee", NULL, NOSYS_NULL, 0}, /* 315 */
+ {"vmsplice", NULL, NOSYS_NULL, 0}, /* 316 */
+ {"move_pages", NULL, NOSYS_NULL, 0}, /* 317 */
+ {"getcpu", lx_getcpu, 0, 3}, /* 318 */
+ {"epoll_pwait", lx_epoll_pwait, 0, 5}, /* 319 */
+ {"utimensat", NULL, 0, 4}, /* 320 */
+ {"signalfd", NULL, 0, 3}, /* 321 */
+ {"timerfd_create", NULL, 0, 2}, /* 322 */
+ {"eventfd", NULL, 0, 1}, /* 323 */
+ {"fallocate", lx_fallocate32, LX_SYS_EBPARG6, 6}, /* 324 */
+ {"timerfd_settime", NULL, 0, 4}, /* 325 */
+ {"timerfd_gettime", NULL, 0, 2}, /* 326 */
+ {"signalfd4", NULL, 0, 4}, /* 327 */
+ {"eventfd2", NULL, 0, 2}, /* 328 */
+ {"epoll_create1", lx_epoll_create1, 0, 1}, /* 329 */
+ {"dup3", lx_dup3, 0, 3}, /* 330 */
+ {"pipe2", lx_pipe2, 0, 2}, /* 331 */
+ {"inotify_init1", NULL, 0, 1}, /* 332 */
+ {"preadv", lx_preadv32, 0, 5}, /* 333 */
+ {"pwritev", lx_pwritev32, 0, 5}, /* 334 */
+ {"rt_tgsigqueueinfo", NULL, 0, 4}, /* 335 */
+ {"perf_event_open", NULL, NOSYS_NULL, 0}, /* 336 */
+ {"recvmmsg", NULL, NOSYS_NULL, 0}, /* 337 */
+ {"fanotify_init", NULL, NOSYS_NULL, 0}, /* 338 */
+ {"fanotify_mark", NULL, NOSYS_NULL, 0}, /* 339 */
+ {"prlimit64", lx_prlimit64, 0, 4}, /* 340 */
+ {"name_to_handle_at", NULL, NOSYS_NULL, 0}, /* 341 */
+ {"open_by_handle_at", NULL, NOSYS_NULL, 0}, /* 342 */
+ {"clock_adjtime", NULL, NOSYS_NULL, 0}, /* 343 */
+ {"syncfs", lx_syncfs, 0, 1}, /* 344 */
+ {"sendmmsg", NULL, NOSYS_NULL, 0}, /* 345 */
+ {"setns", NULL, NOSYS_NULL, 0}, /* 346 */
+ {"process_vm_readv", NULL, NOSYS_NULL, 0}, /* 347 */
+ {"process_vm_writev", NULL, NOSYS_NULL, 0}, /* 348 */
+ {"kcmp", NULL, NOSYS_NULL, 0}, /* 349 */
+ {"finit_module", NULL, NOSYS_NULL, 0}, /* 350 */
+ {"sched_setattr", NULL, NOSYS_NULL, 0}, /* 351 */
+ {"sched_getattr", NULL, NOSYS_NULL, 0}, /* 352 */
+ {"renameat2", NULL, NOSYS_NULL, 0}, /* 353 */
+ {"seccomp", NULL, NOSYS_NULL, 0}, /* 354 */
+ {"getrandom", lx_getrandom, 0, 3}, /* 355 */
+ {"memfd_create", NULL, NOSYS_NULL, 0}, /* 356 */
+ {"bpf", NULL, NOSYS_NULL, 0}, /* 357 */
+ {"execveat", NULL, NOSYS_NULL, 0}, /* 358 */
+};
+
+#if defined(_LP64)
+/*
+ * Linux defines system call numbers for 64-bit x86 in the file:
+ * arch/x86/syscalls/syscall_64.tbl
+ */
+lx_sysent_t lx_sysent64[] = {
+ {"read", lx_read, 0, 3}, /* 0 */
+ {"write", lx_write, 0, 3}, /* 1 */
+ {"open", lx_open, 0, 3}, /* 2 */
+ {"close", lx_close, 0, 1}, /* 3 */
+ {"stat", lx_stat64, 0, 2}, /* 4 */
+ {"fstat", lx_fstat64, 0, 2}, /* 5 */
+ {"lstat", lx_lstat64, 0, 2}, /* 6 */
+ {"poll", lx_poll, 0, 3}, /* 7 */
+ {"lseek", lx_lseek64, 0, 3}, /* 8 */
+ {"mmap", NULL, 0, 6}, /* 9 */
+ {"mprotect", NULL, 0, 3}, /* 10 */
+ {"munmap", lx_munmap, 0, 2}, /* 11 */
+ {"brk", lx_brk, 0, 1}, /* 12 */
+ {"rt_sigaction", NULL, 0, 4}, /* 13 */
+ {"rt_sigprocmask", NULL, 0, 4}, /* 14 */
+ {"rt_sigreturn", NULL, 0, 0}, /* 15 */
+ {"ioctl", lx_ioctl, 0, 3}, /* 16 */
+ {"pread64", lx_pread, 0, 4}, /* 17 */
+ {"pwrite64", lx_pwrite, 0, 4}, /* 18 */
+ {"readv", lx_readv, 0, 3}, /* 19 */
+ {"writev", lx_writev, 0, 3}, /* 20 */
+ {"access", lx_access, 0, 2}, /* 21 */
+ {"pipe", lx_pipe, 0, 1}, /* 22 */
+ {"select", lx_select, 0, 5}, /* 23 */
+ {"sched_yield", lx_sched_yield, 0, 0}, /* 24 */
+ {"mremap", NULL, 0, 5}, /* 25 */
+ {"msync", NULL, 0, 3}, /* 26 */
+ {"mincore", lx_mincore, 0, 3}, /* 27 */
+ {"madvise", NULL, 0, 3}, /* 28 */
+ {"shmget", NULL, 0, 3}, /* 29 */
+ {"shmat", NULL, 0, 4}, /* 30 */
+ {"shmctl", NULL, 0, 3}, /* 31 */
+ {"dup", lx_dup, 0, 1}, /* 32 */
+ {"dup2", lx_dup2, 0, 2}, /* 33 */
+ {"pause", lx_pause, 0, 0}, /* 34 */
+ {"nanosleep", lx_nanosleep, 0, 2}, /* 35 */
+ {"getitimer", lx_getitimer, 0, 2}, /* 36 */
+ {"alarm", lx_alarm, 0, 1}, /* 37 */
+ {"setitimer", NULL, 0, 3}, /* 38 */
+ {"getpid", lx_getpid, 0, 0}, /* 39 */
+ {"sendfile", NULL, 0, 4}, /* 40 */
+ {"socket", lx_socket, 0, 3}, /* 41 */
+ {"connect", lx_connect, 0, 3}, /* 42 */
+ {"accept", lx_accept, 0, 3}, /* 43 */
+ {"sendto", lx_sendto, 0, 6}, /* 44 */
+ {"recvfrom", lx_recvfrom, 0, 6}, /* 45 */
+ {"sendmsg", lx_sendmsg, 0, 3}, /* 46 */
+ {"recvmsg", lx_recvmsg, 0, 3}, /* 47 */
+ {"shutdown", lx_shutdown, 0, 2}, /* 48 */
+ {"bind", lx_bind, 0, 3}, /* 49 */
+ {"listen", lx_listen, 0, 2}, /* 50 */
+ {"getsockname", lx_getsockname, 0, 3}, /* 51 */
+ {"getpeername", lx_getpeername, 0, 3}, /* 52 */
+ {"socketpair", lx_socketpair, 0, 4}, /* 53 */
+ {"setsockopt", lx_setsockopt, 0, 5}, /* 54 */
+ {"getsockopt", lx_getsockopt, 0, 5}, /* 55 */
+ {"clone", NULL, 0, 5}, /* 56 */
+ {"fork", NULL, 0, 0}, /* 57 */
+ {"vfork", NULL, 0, 0}, /* 58 */
+ {"execve", NULL, 0, 3}, /* 59 */
+ {"exit", NULL, 0, 1}, /* 60 */
+ {"wait4", lx_wait4, 0, 4}, /* 61 */
+ {"kill", lx_kill, 0, 2}, /* 62 */
+ {"uname", lx_uname, 0, 1}, /* 63 */
+ {"semget", NULL, 0, 3}, /* 64 */
+ {"semop", NULL, 0, 3}, /* 65 */
+ {"semctl", NULL, 0, 4}, /* 66 */
+ {"shmdt", NULL, 0, 1}, /* 67 */
+ {"msgget", NULL, 0, 2}, /* 68 */
+ {"msgsnd", NULL, 0, 4}, /* 69 */
+ {"msgrcv", NULL, 0, 5}, /* 70 */
+ {"msgctl", NULL, 0, 3}, /* 71 */
+ {"fcntl", lx_fcntl64, 0, 3}, /* 72 */
+ {"flock", NULL, 0, 2}, /* 73 */
+ {"fsync", NULL, 0, 1}, /* 74 */
+ {"fdatasync", NULL, 0, 1}, /* 75 */
+ {"truncate", NULL, 0, 2}, /* 76 */
+ {"ftruncate", NULL, 0, 2}, /* 77 */
+ {"getdents", lx_getdents_64, 0, 3}, /* 78 */
+ {"getcwd", lx_getcwd, 0, 2}, /* 79 */
+ {"chdir", lx_chdir, 0, 1}, /* 80 */
+ {"fchdir", lx_fchdir, 0, 1}, /* 81 */
+ {"rename", lx_rename, 0, 2}, /* 82 */
+ {"mkdir", lx_mkdir, 0, 2}, /* 83 */
+ {"rmdir", NULL, 0, 1}, /* 84 */
+ {"creat", lx_creat, 0, 2}, /* 85 */
+ {"link", lx_link, 0, 2}, /* 86 */
+ {"unlink", lx_unlink, 0, 1}, /* 87 */
+ {"symlink", lx_symlink, 0, 2}, /* 88 */
+ {"readlink", lx_readlink, 0, 3}, /* 89 */
+ {"chmod", lx_chmod, 0, 2}, /* 90 */
+ {"fchmod", lx_fchmod, 0, 2}, /* 91 */
+ {"chown", lx_chown, 0, 3}, /* 92 */
+ {"fchown", lx_fchown, 0, 3}, /* 93 */
+ {"lchown", lx_lchown, 0, 3}, /* 94 */
+ {"umask", lx_umask, 0, 1}, /* 95 */
+ {"gettimeofday", lx_gettimeofday, 0, 2}, /* 96 */
+ {"getrlimit", lx_getrlimit, 0, 2}, /* 97 */
+ {"getrusage", lx_getrusage, 0, 2}, /* 98 */
+ {"sysinfo", lx_sysinfo64, 0, 1}, /* 99 */
+ {"times", NULL, 0, 1}, /* 100 */
+ {"ptrace", lx_ptrace, 0, 4}, /* 101 */
+ {"getuid", lx_getuid, 0, 0}, /* 102 */
+ {"syslog", lx_syslog, 0, 3}, /* 103 */
+ {"getgid", lx_getgid, 0, 0}, /* 104 */
+ {"setuid", lx_setuid, 0, 1}, /* 105 */
+ {"setgid", lx_setgid, 0, 1}, /* 106 */
+ {"geteuid", lx_geteuid, 0, 0}, /* 107 */
+ {"getegid", lx_getegid, 0, 0}, /* 108 */
+ {"setpgid", lx_setpgid, 0, 2}, /* 109 */
+ {"getppid", lx_getppid, 0, 0}, /* 110 */
+ {"getpgrp", lx_getpgrp, 0, 0}, /* 111 */
+ {"setsid", lx_setsid, 0, 0}, /* 112 */
+ {"setreuid", lx_setreuid, 0, 0}, /* 113 */
+ {"setregid", lx_setregid, 0, 0}, /* 114 */
+ {"getgroups", NULL, 0, 2}, /* 115 */
+ {"setgroups", NULL, 0, 2}, /* 116 */
+ {"setresuid", lx_setresuid, 0, 3}, /* 117 */
+ {"getresuid", lx_getresuid, 0, 3}, /* 118 */
+ {"setresgid", lx_setresgid, 0, 3}, /* 119 */
+ {"getresgid", lx_getresgid, 0, 3}, /* 120 */
+ {"getpgid", lx_getpgid, 0, 1}, /* 121 */
+ {"setfsuid", lx_setfsuid, 0, 1}, /* 122 */
+ {"setfsgid", lx_setfsgid, 0, 1}, /* 123 */
+ {"getsid", lx_getsid, 0, 1}, /* 124 */
+ {"capget", NULL, 0, 2}, /* 125 */
+ {"capset", NULL, 0, 2}, /* 126 */
+ {"rt_sigpending", NULL, 0, 2}, /* 127 */
+ {"rt_sigtimedwait", NULL, 0, 4}, /* 128 */
+ {"rt_sigqueueinfo", NULL, 0, 3}, /* 129 */
+ {"rt_sigsuspend", NULL, 0, 2}, /* 130 */
+ {"sigaltstack", NULL, 0, 2}, /* 131 */
+ {"utime", NULL, 0, 2}, /* 132 */
+ {"mknod", NULL, 0, 3}, /* 133 */
+ {"uselib", NULL, NOSYS_KERNEL, 0}, /* 134 */
+ {"personality", lx_personality, 0, 1}, /* 135 */
+ {"ustat", NULL, NOSYS_OBSOLETE, 2}, /* 136 */
+ {"statfs", NULL, 0, 2}, /* 137 */
+ {"fstatfs", NULL, 0, 2}, /* 138 */
+ {"sysfs", NULL, 0, 3}, /* 139 */
+ {"getpriority", NULL, 0, 2}, /* 140 */
+ {"setpriority", NULL, 0, 3}, /* 141 */
+ {"sched_setparam", NULL, 0, 2}, /* 142 */
+ {"sched_getparam", NULL, 0, 2}, /* 143 */
+ {"sched_setscheduler", NULL, 0, 3}, /* 144 */
+ {"sched_getscheduler", NULL, 0, 1}, /* 145 */
+ {"sched_get_priority_max", NULL, 0, 1}, /* 146 */
+ {"sched_get_priority_min", NULL, 0, 1}, /* 147 */
+ {"sched_rr_get_interval", NULL, 0, 2}, /* 148 */
+ {"mlock", NULL, 0, 2}, /* 149 */
+ {"munlock", NULL, 0, 2}, /* 150 */
+ {"mlockall", NULL, 0, 1}, /* 151 */
+ {"munlockall", NULL, 0, 0}, /* 152 */
+ {"vhangup", lx_vhangup, 0, 0}, /* 153 */
+ {"modify_ldt", lx_modify_ldt, 0, 3}, /* 154 */
+ {"pivot_root", NULL, NOSYS_KERNEL, 0}, /* 155 */
+ {"sysctl", NULL, 0, 1}, /* 156 */
+ {"prctl", lx_prctl, 0, 5}, /* 157 */
+ {"arch_prctl", lx_arch_prctl, 0, 2}, /* 158 */
+ {"adjtimex", NULL, 0, 1}, /* 159 */
+ {"setrlimit", lx_setrlimit, 0, 2}, /* 160 */
+ {"chroot", lx_chroot, 0, 1}, /* 161 */
+ {"sync", lx_sync, 0, 0}, /* 162 */
+ {"acct", NULL, NOSYS_NO_EQUIV, 0}, /* 163 */
+ {"settimeofday", NULL, 0, 2}, /* 164 */
+ {"mount", lx_mount, 0, 5}, /* 165 */
+ {"umount2", lx_umount2, 0, 2}, /* 166 */
+ {"swapon", NULL, NOSYS_KERNEL, 0}, /* 167 */
+ {"swapoff", NULL, NOSYS_KERNEL, 0}, /* 168 */
+ {"reboot", lx_reboot, 0, 4}, /* 169 */
+ {"sethostname", lx_sethostname, 0, 2}, /* 170 */
+ {"setdomainname", lx_setdomainname, 0, 2}, /* 171 */
+ {"iopl", NULL, NOSYS_NO_EQUIV, 0}, /* 172 */
+ {"ioperm", NULL, NOSYS_NO_EQUIV, 0}, /* 173 */
+ {"create_module", NULL, NOSYS_KERNEL, 0}, /* 174 */
+ {"init_module", NULL, NOSYS_KERNEL, 0}, /* 175 */
+ {"delete_module", NULL, NOSYS_KERNEL, 0}, /* 176 */
+ {"get_kernel_syms", NULL, NOSYS_KERNEL, 0}, /* 177 */
+ {"query_module", NULL, 0, 5}, /* 178 */
+ {"quotactl", NULL, NOSYS_KERNEL, 0}, /* 179 */
+ {"nfsservctl", NULL, NOSYS_KERNEL, 0}, /* 180 */
+ {"getpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 181 */
+ {"putpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 182 */
+ {"afs_syscall", NULL, NOSYS_KERNEL, 0}, /* 183 */
+ {"tux", NULL, NOSYS_NO_EQUIV, 0}, /* 184 */
+ {"security", NULL, NOSYS_NO_EQUIV, 0}, /* 185 */
+ {"gettid", lx_gettid, 0, 0}, /* 186 */
+ {"readahead", NULL, NOSYS_NO_EQUIV, 0}, /* 187 */
+ {"setxattr", lx_setxattr, 0, 5}, /* 188 */
+ {"lsetxattr", lx_lsetxattr, 0, 5}, /* 189 */
+ {"fsetxattr", lx_fsetxattr, 0, 5}, /* 190 */
+ {"getxattr", lx_getxattr, 0, 4}, /* 191 */
+ {"lgetxattr", lx_lgetxattr, 0, 4}, /* 192 */
+ {"fgetxattr", lx_fgetxattr, 0, 4}, /* 193 */
+ {"listxattr", lx_listxattr, 0, 3}, /* 194 */
+ {"llistxattr", lx_llistxattr, 0, 3}, /* 195 */
+ {"flistxattr", lx_flistxattr, 0, 3}, /* 196 */
+ {"removexattr", lx_removexattr, 0, 2}, /* 197 */
+ {"lremovexattr", lx_lremovexattr, 0, 2}, /* 198 */
+ {"fremovexattr", lx_fremovexattr, 0, 2}, /* 199 */
+ {"tkill", lx_tkill, 0, 2}, /* 200 */
+ {"time", lx_time, 0, 1}, /* 201 */
+ {"futex", lx_futex, 0, 6}, /* 202 */
+ {"sched_setaffinity", NULL, 0, 3}, /* 203 */
+ {"sched_getaffinity", NULL, 0, 3}, /* 204 */
+ {"set_thread_area", lx_set_thread_area, 0, 1}, /* 205 */
+ {"io_setup", lx_io_setup, 0, 2}, /* 206 */
+ {"io_destroy", NULL, 0, 1}, /* 207 */
+ {"io_getevents", NULL, 0, 5}, /* 208 */
+ {"io_submit", NULL, 0, 3}, /* 209 */
+ {"io_cancel", NULL, 0, 3}, /* 210 */
+ {"get_thread_area", lx_get_thread_area, 0, 1}, /* 211 */
+ {"lookup_dcookie", NULL, NOSYS_NO_EQUIV, 0}, /* 212 */
+ {"epoll_create", lx_epoll_create, 0, 1}, /* 213 */
+ {"epoll_ctl_old", NULL, NOSYS_NULL, 0}, /* 214 */
+ {"epoll_wait_old", NULL, NOSYS_NULL, 0}, /* 215 */
+ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 216 */
+ {"getdents64", lx_getdents64, 0, 3}, /* 217 */
+ {"set_tid_address", lx_set_tid_address, 0, 1}, /* 218 */
+ {"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */
+ {"semtimedop", NULL, 0, 4}, /* 220 */
+ {"fadvise64", lx_fadvise64, 0, 4}, /* 221 */
+ {"timer_create", NULL, 0, 3}, /* 222 */
+ {"timer_settime", NULL, 0, 4}, /* 223 */
+ {"timer_gettime", NULL, 0, 2}, /* 224 */
+ {"timer_getoverrun", NULL, 0, 1}, /* 225 */
+ {"timer_delete", NULL, 0, 1}, /* 226 */
+ {"clock_settime", lx_clock_settime, 0, 2}, /* 227 */
+ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 228 */
+ {"clock_getres", lx_clock_getres, 0, 2}, /* 229 */
+ {"clock_nanosleep", NULL, 0, 4}, /* 230 */
+ {"exit_group", NULL, 0, 1}, /* 231 */
+ {"epoll_wait", lx_epoll_wait, 0, 4}, /* 232 */
+ {"epoll_ctl", lx_epoll_ctl, 0, 4}, /* 233 */
+ {"tgkill", lx_tgkill, 0, 3}, /* 234 */
+ {"utimes", NULL, 0, 2}, /* 235 */
+ {"vserver", NULL, NOSYS_NULL, 0}, /* 236 */
+ {"mbind", NULL, NOSYS_NULL, 0}, /* 237 */
+ {"set_mempolicy", NULL, NOSYS_NULL, 0}, /* 238 */
+ {"get_mempolicy", NULL, NOSYS_NULL, 0}, /* 239 */
+ {"mq_open", NULL, NOSYS_NULL, 0}, /* 240 */
+ {"mq_unlink", NULL, NOSYS_NULL, 0}, /* 241 */
+ {"mq_timedsend", NULL, NOSYS_NULL, 0}, /* 242 */
+ {"mq_timedreceive", NULL, NOSYS_NULL, 0}, /* 243 */
+ {"mq_notify", NULL, NOSYS_NULL, 0}, /* 244 */
+ {"mq_getsetattr", NULL, NOSYS_NULL, 0}, /* 245 */
+ {"kexec_load", NULL, NOSYS_NULL, 0}, /* 246 */
+ {"waitid", lx_waitid, 0, 4}, /* 247 */
+ {"add_key", NULL, NOSYS_NULL, 0}, /* 248 */
+ {"request_key", NULL, NOSYS_NULL, 0}, /* 249 */
+ {"keyctl", NULL, NOSYS_NULL, 0}, /* 250 */
+ {"ioprio_set", lx_ioprio_set, 0, 3}, /* 251 */
+ {"ioprio_get", lx_ioprio_get, 0, 2}, /* 252 */
+ {"inotify_init", NULL, 0, 0}, /* 253 */
+ {"inotify_add_watch", NULL, 0, 3}, /* 254 */
+ {"inotify_rm_watch", NULL, 0, 2}, /* 255 */
+ {"migrate_pages", NULL, NOSYS_NULL, 0}, /* 256 */
+ {"openat", lx_openat, 0, 4}, /* 257 */
+ {"mkdirat", lx_mkdirat, 0, 3}, /* 258 */
+ {"mknodat", NULL, 0, 4}, /* 259 */
+ {"fchownat", lx_fchownat, 0, 5}, /* 260 */
+ {"futimesat", NULL, 0, 3}, /* 261 */
+ {"fstatat64", lx_fstatat64, 0, 4}, /* 262 */
+ {"unlinkat", lx_unlinkat, 0, 3}, /* 263 */
+ {"renameat", lx_renameat, 0, 4}, /* 264 */
+ {"linkat", lx_linkat, 0, 5}, /* 265 */
+ {"symlinkat", lx_symlinkat, 0, 3}, /* 266 */
+ {"readlinkat", lx_readlinkat, 0, 4}, /* 267 */
+ {"fchmodat", lx_fchmodat, 0, 3}, /* 268 */
+ {"faccessat", lx_faccessat, 0, 4}, /* 269 */
+ {"pselect6", lx_pselect, 0, 6}, /* 270 */
+ {"ppoll", lx_ppoll, 0, 5}, /* 271 */
+ {"unshare", NULL, NOSYS_NULL, 0}, /* 272 */
+ {"set_robust_list", lx_set_robust_list, 0, 2}, /* 273 */
+ {"get_robust_list", lx_get_robust_list, 0, 3}, /* 274 */
+ {"splice", NULL, NOSYS_NULL, 0}, /* 275 */
+ {"tee", NULL, NOSYS_NULL, 0}, /* 276 */
+ {"sync_file_range", lx_sync_file_range, 0, 4}, /* 277 */
+ {"vmsplice", NULL, NOSYS_NULL, 0}, /* 278 */
+ {"move_pages", NULL, NOSYS_NULL, 0}, /* 279 */
+ {"utimensat", NULL, 0, 4}, /* 280 */
+ {"epoll_pwait", lx_epoll_pwait, 0, 5}, /* 281 */
+ {"signalfd", NULL, 0, 3}, /* 282 */
+ {"timerfd_create", NULL, 0, 2}, /* 283 */
+ {"eventfd", NULL, 0, 1}, /* 284 */
+ {"fallocate", lx_fallocate, 0, 4}, /* 285 */
+ {"timerfd_settime", NULL, 0, 4}, /* 286 */
+ {"timerfd_gettime", NULL, 0, 2}, /* 287 */
+ {"accept4", lx_accept4, 0, 4}, /* 288 */
+ {"signalfd4", NULL, 0, 4}, /* 289 */
+ {"eventfd2", NULL, 0, 2}, /* 290 */
+ {"epoll_create1", lx_epoll_create1, 0, 1}, /* 291 */
+ {"dup3", lx_dup3, 0, 3}, /* 292 */
+ {"pipe2", lx_pipe2, 0, 2}, /* 293 */
+ {"inotify_init1", NULL, 0, 1}, /* 294 */
+ {"preadv", lx_preadv, 0, 4}, /* 295 */
+ {"pwritev", lx_pwritev, 0, 4}, /* 296 */
+ {"rt_tgsigqueueinfo", NULL, 0, 4}, /* 297 */
+ {"perf_event_open", NULL, NOSYS_NULL, 0}, /* 298 */
+ {"recvmmsg", NULL, NOSYS_NULL, 0}, /* 299 */
+ {"fanotify_init", NULL, NOSYS_NULL, 0}, /* 300 */
+ {"fanotify_mark", NULL, NOSYS_NULL, 0}, /* 301 */
+ {"prlimit64", lx_prlimit64, 0, 4}, /* 302 */
+ {"name_to_handle_at", NULL, NOSYS_NULL, 0}, /* 303 */
+ {"open_by_handle_at", NULL, NOSYS_NULL, 0}, /* 304 */
+ {"clock_adjtime", NULL, NOSYS_NULL, 0}, /* 305 */
+ {"syncfs", lx_syncfs, 0, 1}, /* 306 */
+ {"sendmmsg", NULL, NOSYS_NULL, 0}, /* 307 */
+ {"setns", NULL, NOSYS_NULL, 0}, /* 309 */
+ {"getcpu", lx_getcpu, 0, 3}, /* 309 */
+ {"process_vm_readv", NULL, NOSYS_NULL, 0}, /* 310 */
+ {"process_vm_writev", NULL, NOSYS_NULL, 0}, /* 311 */
+ {"kcmp", NULL, NOSYS_NULL, 0}, /* 312 */
+ {"finit_module", NULL, NOSYS_NULL, 0}, /* 313 */
+ {"sched_setattr", NULL, NOSYS_NULL, 0}, /* 314 */
+ {"sched_getattr", NULL, NOSYS_NULL, 0}, /* 315 */
+ {"renameat2", NULL, NOSYS_NULL, 0}, /* 316 */
+ {"seccomp", NULL, NOSYS_NULL, 0}, /* 317 */
+ {"getrandom", lx_getrandom, 0, 3}, /* 318 */
+ {"memfd_create", NULL, NOSYS_NULL, 0}, /* 319 */
+ {"kexec_file_load", NULL, NOSYS_NULL, 0}, /* 320 */
+ {"bpf", NULL, NOSYS_NULL, 0}, /* 321 */
+ {"execveat", NULL, NOSYS_NULL, 0}, /* 322 */
+
+ /* XXX TBD gap then x32 syscalls from 512 - 544 */
+};
+#endif
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_proc.h b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
new file mode 100644
index 0000000000..65996c7e8f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
@@ -0,0 +1,351 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_PROC_H
+#define _LX_PROC_H
+
+#ifdef _LXPROC_NATIVE_H
+#error Attempted to include branded lx_proc.h after native lxproc.h
+#endif
+
+#define _LXPROC_BRANDED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxproc.h: declarations, data structures and macros for lxprocfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/nvpair.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+
+/*
+ * Convert a vnode into an lxpr_mnt_t
+ */
+#define VTOLXPM(vp) ((lxpr_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxpr_node
+ */
+#define VTOLXP(vp) ((lxpr_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxprnode into a vnode
+ */
+#define LXPTOV(lxpnp) ((lxpnp)->lxpr_vnode)
+
+/*
+ * convert a lxpr_node into zone for fs
+ */
+#define LXPTOZ(lxpnp) \
+ (((lxpr_mnt_t *)(lxpnp)->lxpr_vnode->v_vfsp->vfs_data)->lxprm_zone)
+
+#define LXPNSIZ 256 /* max size of lx /proc file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXPR_SDSIZE 16
+
+/*
+ * Node/file types for lx /proc files
+ * (directories and files contained therein).
+ */
+typedef enum lxpr_nodetype {
+ LXPR_INVALID, /* nodes start at 1 */
+ LXPR_PROCDIR, /* /proc */
+ LXPR_PIDDIR, /* /proc/<pid> */
+ LXPR_PID_AUXV, /* /proc/<pid>/auxv */
+ LXPR_PID_CGROUP, /* /proc/<pid>/cgroup */
+ LXPR_PID_CMDLINE, /* /proc/<pid>/cmdline */
+ LXPR_PID_COMM, /* /proc/<pid>/comm */
+ LXPR_PID_CPU, /* /proc/<pid>/cpu */
+ LXPR_PID_CURDIR, /* /proc/<pid>/cwd */
+ LXPR_PID_ENV, /* /proc/<pid>/environ */
+ LXPR_PID_EXE, /* /proc/<pid>/exe */
+ LXPR_PID_LIMITS, /* /proc/<pid>/limits */
+ LXPR_PID_LOGINUID, /* /proc/<pid>/loginuid */
+ LXPR_PID_MAPS, /* /proc/<pid>/maps */
+ LXPR_PID_MEM, /* /proc/<pid>/mem */
+ LXPR_PID_MOUNTINFO, /* /proc/<pid>/mountinfo */
+ LXPR_PID_MOUNTS, /* /proc/<pid>/mounts */
+ LXPR_PID_OOM_SCR_ADJ, /* /proc/<pid>/oom_score_adj */
+ LXPR_PID_PERSONALITY, /* /proc/<pid>/personality */
+ LXPR_PID_ROOTDIR, /* /proc/<pid>/root */
+ LXPR_PID_STAT, /* /proc/<pid>/stat */
+ LXPR_PID_STATM, /* /proc/<pid>/statm */
+ LXPR_PID_STATUS, /* /proc/<pid>/status */
+ LXPR_PID_TASKDIR, /* /proc/<pid>/task */
+ LXPR_PID_TASK_IDDIR, /* /proc/<pid>/task/<tid> */
+ LXPR_PID_FDDIR, /* /proc/<pid>/fd */
+ LXPR_PID_FD_FD, /* /proc/<pid>/fd/nn */
+ LXPR_PID_TID_AUXV, /* /proc/<pid>/task/<tid>/auxv */
+ LXPR_PID_TID_CGROUP, /* /proc/<pid>/task/<tid>/cgroup */
+ LXPR_PID_TID_CMDLINE, /* /proc/<pid>/task/<tid>/cmdline */
+ LXPR_PID_TID_COMM, /* /proc/<pid>/task/<tid>/comm */
+ LXPR_PID_TID_CPU, /* /proc/<pid>/task/<tid>/cpu */
+ LXPR_PID_TID_CURDIR, /* /proc/<pid>/task/<tid>/cwd */
+ LXPR_PID_TID_ENV, /* /proc/<pid>/task/<tid>/environ */
+ LXPR_PID_TID_EXE, /* /proc/<pid>/task/<tid>/exe */
+ LXPR_PID_TID_LIMITS, /* /proc/<pid>/task/<tid>/limits */
+ LXPR_PID_TID_LOGINUID, /* /proc/<pid>/task/<tid>/loginuid */
+ LXPR_PID_TID_MAPS, /* /proc/<pid>/task/<tid>/maps */
+ LXPR_PID_TID_MEM, /* /proc/<pid>/task/<tid>/mem */
+ LXPR_PID_TID_MOUNTINFO, /* /proc/<pid>/task/<tid>/mountinfo */
+ LXPR_PID_TID_OOM_SCR_ADJ, /* /proc/<pid>/task/<tid>/oom_score_adj */
+ LXPR_PID_TID_PERSONALITY, /* /proc/<pid>/task/<tid>/personality */
+ LXPR_PID_TID_ROOTDIR, /* /proc/<pid>/task/<tid>/root */
+ LXPR_PID_TID_STAT, /* /proc/<pid>/task/<tid>/stat */
+ LXPR_PID_TID_STATM, /* /proc/<pid>/task/<tid>/statm */
+ LXPR_PID_TID_STATUS, /* /proc/<pid>/task/<tid>/status */
+ LXPR_PID_TID_FDDIR, /* /proc/<pid>/task/<tid>/fd */
+ LXPR_PID_TID_FD_FD, /* /proc/<pid>/task/<tid>/fd/nn */
+ LXPR_CGROUPS, /* /proc/cgroups */
+ LXPR_CMDLINE, /* /proc/cmdline */
+ LXPR_CPUINFO, /* /proc/cpuinfo */
+ LXPR_DEVICES, /* /proc/devices */
+ LXPR_DISKSTATS, /* /proc/diskstats */
+ LXPR_DMA, /* /proc/dma */
+ LXPR_FILESYSTEMS, /* /proc/filesystems */
+ LXPR_INTERRUPTS, /* /proc/interrupts */
+ LXPR_IOPORTS, /* /proc/ioports */
+ LXPR_KCORE, /* /proc/kcore */
+ LXPR_KMSG, /* /proc/kmsg */
+ LXPR_LOADAVG, /* /proc/loadavg */
+ LXPR_MEMINFO, /* /proc/meminfo */
+ LXPR_MODULES, /* /proc/modules */
+ LXPR_MOUNTS, /* /proc/mounts */
+ LXPR_NETDIR, /* /proc/net */
+ LXPR_NET_ARP, /* /proc/net/arp */
+ LXPR_NET_DEV, /* /proc/net/dev */
+ LXPR_NET_DEV_MCAST, /* /proc/net/dev_mcast */
+ LXPR_NET_IF_INET6, /* /proc/net/if_inet6 */
+ LXPR_NET_IGMP, /* /proc/net/igmp */
+ LXPR_NET_IP_MR_CACHE, /* /proc/net/ip_mr_cache */
+ LXPR_NET_IP_MR_VIF, /* /proc/net/ip_mr_vif */
+ LXPR_NET_IPV6_ROUTE, /* /proc/net/ipv6_route */
+ LXPR_NET_MCFILTER, /* /proc/net/mcfilter */
+ LXPR_NET_NETSTAT, /* /proc/net/netstat */
+ LXPR_NET_RAW, /* /proc/net/raw */
+ LXPR_NET_ROUTE, /* /proc/net/route */
+ LXPR_NET_RPC, /* /proc/net/rpc */
+ LXPR_NET_RT_CACHE, /* /proc/net/rt_cache */
+ LXPR_NET_SOCKSTAT, /* /proc/net/sockstat */
+ LXPR_NET_SNMP, /* /proc/net/snmp */
+ LXPR_NET_STAT, /* /proc/net/stat */
+ LXPR_NET_TCP, /* /proc/net/tcp */
+ LXPR_NET_TCP6, /* /proc/net/tcp6 */
+ LXPR_NET_UDP, /* /proc/net/udp */
+ LXPR_NET_UDP6, /* /proc/net/udp6 */
+ LXPR_NET_UNIX, /* /proc/net/unix */
+ LXPR_PARTITIONS, /* /proc/partitions */
+ LXPR_SELF, /* /proc/self */
+ LXPR_STAT, /* /proc/stat */
+ LXPR_SWAPS, /* /proc/swaps */
+ LXPR_SYSDIR, /* /proc/sys/ */
+ LXPR_SYS_FSDIR, /* /proc/sys/fs/ */
+ LXPR_SYS_FS_INOTIFYDIR, /* /proc/sys/fs/inotify */
+ LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS, /* inotify/max_queued_events */
+ LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES, /* inotify/max_user_instances */
+ LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES, /* inotify/max_user_watches */
+ LXPR_SYS_KERNELDIR, /* /proc/sys/kernel/ */
+ LXPR_SYS_KERNEL_CAPLCAP, /* /proc/sys/kernel/cap_last_cap */
+ LXPR_SYS_KERNEL_COREPATT, /* /proc/sys/kernel/core_pattern */
+ LXPR_SYS_KERNEL_HOSTNAME, /* /proc/sys/kernel/hostname */
+ LXPR_SYS_KERNEL_MSGMNI, /* /proc/sys/kernel/msgmni */
+ LXPR_SYS_KERNEL_NGROUPS_MAX, /* /proc/sys/kernel/ngroups_max */
+ LXPR_SYS_KERNEL_OSREL, /* /proc/sys/kernel/osrelease */
+ LXPR_SYS_KERNEL_PID_MAX, /* /proc/sys/kernel/pid_max */
+ LXPR_SYS_KERNEL_RANDDIR, /* /proc/sys/kernel/random */
+ LXPR_SYS_KERNEL_RAND_BOOTID, /* /proc/sys/kernel/random/boot_id */
+ LXPR_SYS_KERNEL_SEM, /* /proc/sys/kernel/sem */
+ LXPR_SYS_KERNEL_SHMALL, /* /proc/sys/kernel/shmall */
+ LXPR_SYS_KERNEL_SHMMAX, /* /proc/sys/kernel/shmmax */
+ LXPR_SYS_KERNEL_SHMMNI, /* /proc/sys/kernel/shmmni */
+ LXPR_SYS_KERNEL_THREADS_MAX, /* /proc/sys/kernel/threads-max */
+ LXPR_SYS_NETDIR, /* /proc/sys/net */
+ LXPR_SYS_NET_COREDIR, /* /proc/sys/net/core */
+ LXPR_SYS_NET_CORE_SOMAXCON, /* /proc/sys/net/core/somaxconn */
+ LXPR_SYS_NET_IPV4DIR, /* /proc/sys/net/ipv4 */
+ LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, /* .../net/ipv4/ip_local_port_range */
+ LXPR_SYS_NET_IPV4_TCP_FIN_TO, /* /proc/sys/net/ipv4/tcp_fin_timeout */
+ LXPR_SYS_NET_IPV4_TCP_KA_INT, /* .../net/ipv4/tcp_keepalive_intvl */
+ LXPR_SYS_NET_IPV4_TCP_KA_TIM, /* .../net/ipv4/tcp_keepalive_time */
+ LXPR_SYS_NET_IPV4_TCP_SACK, /* /proc/sys/net/ipv4/tcp_sack */
+ LXPR_SYS_NET_IPV4_TCP_WINSCALE, /* .../net/ipv4/tcp_window_scaling */
+ LXPR_SYS_VMDIR, /* /proc/sys/vm */
+ LXPR_SYS_VM_MAX_MAP_CNT, /* /proc/sys/vm/max_map_count */
+ LXPR_SYS_VM_MINFR_KB, /* /proc/sys/vm/min_free_kbytes */
+ LXPR_SYS_VM_NHUGEP, /* /proc/sys/vm/nr_hugepages */
+ LXPR_SYS_VM_OVERCOMMIT_MEM, /* /proc/sys/vm/overcommit_memory */
+ LXPR_SYS_VM_SWAPPINESS, /* /proc/sys/vm/swappiness */
+ LXPR_UPTIME, /* /proc/uptime */
+ LXPR_VERSION, /* /proc/version */
+ LXPR_VMSTAT, /* /proc/vmstat */
+ LXPR_NFILES /* number of lx /proc file types */
+} lxpr_nodetype_t;
+
+
+/*
+ * Number of fds allowed for in the inode number calculation
+ * per process (if a process has more fds then inode numbers
+ * may be duplicated)
+ */
+#define LXPR_FD_PERPROC 2000
+
+/*
+ * Linux sector size for /proc/diskstats
+ */
+#define LXPR_SECTOR_SIZE 512
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+ lxpr_nodetype_t d_type;
+ char *d_name;
+} lxpr_dirent_t;
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+typedef struct lxpr_node {
+ lxpr_nodetype_t lxpr_type; /* type of this node */
+ vnode_t *lxpr_vnode; /* vnode for the node */
+ vnode_t *lxpr_parent; /* parent directory */
+ vnode_t *lxpr_realvp; /* real vnode, file in dirs */
+ timestruc_t lxpr_time; /* creation etc time for file */
+ mode_t lxpr_mode; /* file mode bits */
+ uid_t lxpr_uid; /* file owner */
+ gid_t lxpr_gid; /* file group owner */
+ pid_t lxpr_pid; /* pid of proc referred to */
+ uint_t lxpr_desc; /* addl. descriptor (fd or tid) */
+ ino_t lxpr_ino; /* node id */
+} lxpr_node_t;
+
+struct zone; /* forward declaration */
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxpr_mnt {
+ lxpr_node_t *lxprm_node; /* node at root of proc mount */
+ struct zone *lxprm_zone; /* zone for this mount */
+ ldi_ident_t lxprm_li; /* ident for ldi */
+} lxpr_mnt_t;
+
+extern vnodeops_t *lxpr_vnodeops;
+extern int nproc_highbit; /* highbit(v.v_nproc) */
+
+typedef struct mounta mounta_t;
+
+extern void lxpr_initnodecache();
+extern void lxpr_fininodecache();
+extern void lxpr_initrootnode(lxpr_node_t **, vfs_t *);
+extern ino_t lxpr_inode(lxpr_nodetype_t, pid_t, int);
+extern ino_t lxpr_parentinode(lxpr_node_t *);
+extern boolean_t lxpr_is_writable(lxpr_nodetype_t);
+extern lxpr_node_t *lxpr_getnode(vnode_t *, lxpr_nodetype_t, proc_t *, int);
+extern void lxpr_freenode(lxpr_node_t *);
+extern vnode_t *lxpr_lookup_fdnode(vnode_t *, const char *);
+extern int lxpr_readlink_fdnode(lxpr_node_t *, char *, size_t);
+
+typedef struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t buffsize;
+ char *pos;
+ size_t beg;
+ int error;
+} lxpr_uiobuf_t;
+
+extern lxpr_uiobuf_t *lxpr_uiobuf_new(uio_t *);
+extern void lxpr_uiobuf_free(lxpr_uiobuf_t *);
+extern int lxpr_uiobuf_flush(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_seek(lxpr_uiobuf_t *, offset_t);
+extern boolean_t lxpr_uiobuf_nonblock(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_write(lxpr_uiobuf_t *, const char *, size_t);
+extern void lxpr_uiobuf_printf(lxpr_uiobuf_t *, const char *, ...);
+extern void lxpr_uiobuf_seterr(lxpr_uiobuf_t *, int);
+
+extern int lxpr_core_path_l2s(const char *, char *, size_t);
+extern int lxpr_core_path_s2l(const char *, char *, size_t);
+
+typedef enum lxpr_zombok {
+ NO_ZOMB = 0,
+ ZOMB_OK
+} zombok_t;
+
+proc_t *lxpr_lock(pid_t, zombok_t);
+void lxpr_unlock(proc_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef islower
+#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LX_PROC_H */
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c b/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
new file mode 100644
index 0000000000..37573a81f6
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
@@ -0,0 +1,852 @@
+/*
+ * 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 2016 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * lxprsubr.c: Various functions for the /lxproc vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+#include <sys/zfs_ioctl.h>
+
+#include "lx_proc.h"
+
+#define LXPRCACHE_NAME "lxbpr_cache"
+
+static int lxpr_node_constructor(void *, void *, int);
+static void lxpr_node_destructor(void *, void *);
+
+static kmem_cache_t *lxpr_node_cache;
+
+int lx_pr_bufsize = 4000;
+
+struct lxpr_zfs_ds {
+ list_node_t ds_link;
+ char ds_name[MAXPATHLEN];
+ uint64_t ds_cookie;
+};
+
+struct lxpr_uiobuf *
+lxpr_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxpr_uiobuf and output buffer */
+ int bufsize = lx_pr_bufsize;
+ struct lxpr_uiobuf *uiobuf =
+ kmem_alloc(sizeof (struct lxpr_uiobuf) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->buffsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize);
+}
+
+void
+lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset)
+{
+ uiobuf->uiop->uio_offset = (off_t)offset;
+}
+
+boolean_t
+lxpr_uiobuf_nonblock(struct lxpr_uiobuf *uiobuf)
+{
+ if ((uiobuf->uiop->uio_fmode & FNONBLOCK) != 0)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+void
+lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->buffsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxpr_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxpr_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxpr_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
+
+/*
+ * lxpr_lock():
+ *
+ * Lookup process from pid and return with p_plock and P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock(pid_t pid, zombok_t zombie_ok)
+{
+ proc_t *p;
+ kmutex_t *mp;
+ pid_t find_pid;
+ uint_t flags;
+
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ for (;;) {
+ mutex_enter(&pidlock);
+
+ /*
+ * If the pid is 1, we really want the zone's init process;
+ * if 0 we want zsched.
+ */
+ if (pid == 1) {
+ find_pid = curproc->p_zone->zone_proc_initpid;
+ } else if (pid == 0) {
+ find_pid = curproc->p_zone->zone_zsched->p_pid;
+ } else {
+ find_pid = pid;
+ }
+ p = prfind(find_pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+
+ /*
+ * p_lock is persistent, but p itself is not -- it could
+ * vanish during cv_wait(). Load p->p_lock now so we can
+ * drop it after cv_wait() without referencing p.
+ */
+ mp = &p->p_lock;
+ mutex_enter(mp);
+
+ mutex_exit(&pidlock);
+
+ /*
+ * Filter out exiting or zombie processes, if requested.
+ */
+ if (zombie_ok == NO_ZOMB &&
+ ((p->p_flag & SEXITING) || p->p_stat == SZOMB)) {
+ mutex_exit(mp);
+ return (NULL);
+ }
+
+ flags = p->p_proc_flag & (P_PR_LOCK | P_PR_EXEC);
+ if (flags == 0) {
+ break;
+ } else if (flags == P_PR_EXEC && p == curproc) {
+ /*
+ * Forward progress with (only) the PR_EXEC flag is
+ * safe if a process is accessing resources in its own
+ * piddir. Executing its own /proc/<pid>/exe symlink
+ * is one potential example.
+ *
+ * For all other processes, it is necessary to wait
+ * until the exec is completed.
+ */
+ break;
+ }
+
+ cv_wait(&pr_pid_cv[p->p_slot], mp);
+ mutex_exit(mp);
+ }
+
+ p->p_proc_flag |= P_PR_LOCK;
+ THREAD_KPRI_REQUEST();
+ return (p);
+}
+
+/*
+ * lxpr_unlock()
+ *
+ * Unlock locked process
+ */
+void
+lxpr_unlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+ mutex_exit(&p->p_lock);
+ THREAD_KPRI_RELEASE();
+}
+
+void
+lxpr_initnodecache()
+{
+ lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME,
+ sizeof (lxpr_node_t), 0,
+ lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxpr_fininodecache()
+{
+ kmem_cache_destroy(lxpr_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxpr_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxpr_node_t *lxpnp = buf;
+ vnode_t *vp;
+
+ vp = lxpnp->lxpr_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxpr_vnodeops);
+ vp->v_data = lxpnp;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_node_destructor(void *buf, void *un)
+{
+ lxpr_node_t *lxpnp = buf;
+
+ vn_free(LXPTOV(lxpnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxproc node
+ */
+ino_t
+lxpr_inode(lxpr_nodetype_t type, pid_t pid, int desc)
+{
+ if (pid == 1) {
+ pid = curproc->p_zone->zone_proc_initpid;
+ } else if (pid == 0) {
+ pid = curproc->p_zone->zone_zsched->p_pid;
+ }
+
+ switch (type) {
+ case LXPR_PIDDIR:
+ return (maxpid + pid + 1);
+ case LXPR_PID_TASK_IDDIR:
+ return (maxpid + (desc * 10));
+ case LXPR_PROCDIR:
+ return (maxpid + 2);
+ case LXPR_PID_FD_FD:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ LXPR_NFILES + desc);
+ default:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ type);
+ }
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxpr_parentinode(lxpr_node_t *lxpnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxpnp->lxpr_type != LXPR_PROCDIR)
+ return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino);
+ else
+ return (lxpnp->lxpr_ino);
+}
+
+/*
+ * Allocate a new lxproc node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxpr_node_t *
+lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int desc)
+{
+ lxpr_node_t *lxpnp;
+ vnode_t *vp;
+ user_t *up;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_innactive
+ */
+ lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxpnp->lxpr_type = type;
+ lxpnp->lxpr_realvp = NULL;
+ lxpnp->lxpr_parent = dp;
+ lxpnp->lxpr_desc = desc;
+ VN_HOLD(dp);
+ if (p != NULL) {
+ if (p->p_pid == curproc->p_zone->zone_proc_initpid) {
+ lxpnp->lxpr_pid = 1;
+ } else if (p->p_pid == curproc->p_zone->zone_zsched->p_pid) {
+ lxpnp->lxpr_pid = 0;
+ } else {
+ lxpnp->lxpr_pid = p->p_pid;
+ }
+
+ lxpnp->lxpr_time = PTOU(p)->u_start;
+ lxpnp->lxpr_uid = crgetruid(p->p_cred);
+ lxpnp->lxpr_gid = crgetrgid(p->p_cred);
+ lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, desc);
+ } else {
+ /* Pretend files without a proc belong to sched */
+ lxpnp->lxpr_pid = 0;
+ lxpnp->lxpr_time = now;
+ lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0;
+ lxpnp->lxpr_ino = lxpr_inode(type, 0, 0);
+ }
+
+ /* initialize the vnode data */
+ vp = lxpnp->lxpr_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Do node specific stuff
+ */
+ if (lxpr_is_writable(type)) {
+ /* These two have different modes; handled later. */
+ if (type != LXPR_PID_FD_FD && type != LXPR_PID_TID_FD_FD) {
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0644;
+ return (lxpnp);
+ }
+ }
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vp->v_flag |= VROOT;
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_CURDIR:
+ ASSERT(p != NULL);
+
+ /*
+ * Zombie check. p_stat is officially protected by pidlock,
+ * but we can't grab pidlock here because we already hold
+ * p_lock. Luckily if we look at the process exit code
+ * we see that p_stat only transisions from SRUN to SZOMB
+ * while p_lock is held. Aside from this, the only other
+ * p_stat transition that we need to be aware about is
+ * SIDL to SRUN, but that's not a problem since lxpr_lock()
+ * ignores nodes in the SIDL state so we'll never get a node
+ * that isn't already in the SRUN state.
+ */
+ if (p->p_stat == SZOMB) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ up = PTOU(p);
+ lxpnp->lxpr_realvp = up->u_cdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_ROOTDIR:
+ ASSERT(p != NULL);
+ /* Zombie check. see locking comment above */
+ if (p->p_stat == SZOMB) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ up = PTOU(p);
+ lxpnp->lxpr_realvp =
+ up->u_rdir != NULL ? up->u_rdir : rootdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_EXE:
+ ASSERT(p != NULL);
+ lxpnp->lxpr_realvp = p->p_exec;
+ if (lxpnp->lxpr_realvp != NULL) {
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777;
+ break;
+
+ case LXPR_SELF:
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_TASKDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_TASK_IDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_FD_FD:
+ case LXPR_PID_TID_FD_FD:
+ ASSERT(p != NULL);
+ /* lxpr_realvp is set after we return */
+ lxpnp->lxpr_mode = 0700; /* read-write-exe owner only */
+ vp->v_type = VLNK;
+ break;
+
+ case LXPR_PID_FDDIR:
+ case LXPR_PID_TID_FDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0500; /* read-search by owner only */
+ break;
+
+ case LXPR_PIDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0511;
+ break;
+
+ case LXPR_NETDIR:
+ case LXPR_SYSDIR:
+ case LXPR_SYS_FSDIR:
+ case LXPR_SYS_FS_INOTIFYDIR:
+ case LXPR_SYS_KERNELDIR:
+ case LXPR_SYS_KERNEL_RANDDIR:
+ case LXPR_SYS_NETDIR:
+ case LXPR_SYS_NET_COREDIR:
+ case LXPR_SYS_NET_IPV4DIR:
+ case LXPR_SYS_VMDIR:
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by all */
+ break;
+
+ case LXPR_PID_ENV:
+ case LXPR_PID_MEM:
+ ASSERT(p != NULL);
+ /*FALLTHRU*/
+ case LXPR_KCORE:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0400; /* read-only by owner only */
+ break;
+
+ default:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0444; /* read-only by all */
+ break;
+ }
+
+ return (lxpnp);
+}
+
+
+/*
+ * Free the storage obtained from lxpr_getnode().
+ */
+void
+lxpr_freenode(lxpr_node_t *lxpnp)
+{
+ ASSERT(lxpnp != NULL);
+ ASSERT(LXPTOV(lxpnp) != NULL);
+
+ /*
+ * delete any association with realvp
+ */
+ if (lxpnp->lxpr_realvp != NULL)
+ VN_RELE(lxpnp->lxpr_realvp);
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxpnp->lxpr_parent != NULL)
+ VN_RELE(lxpnp->lxpr_parent);
+
+ /*
+ * Release the lxprnode.
+ */
+ kmem_cache_free(lxpr_node_cache, lxpnp);
+}
+
+/*
+ * Attempt to locate vnode for /proc/<pid>/fd/<#>.
+ */
+vnode_t *
+lxpr_lookup_fdnode(vnode_t *dvp, const char *name)
+{
+ lxpr_node_t *lxdp = VTOLXP(dvp);
+ lxpr_node_t *lxfp;
+ char *endptr = NULL;
+ long num;
+ int fd;
+ proc_t *p;
+ vnode_t *vp = NULL;
+ file_t *fp;
+ uf_entry_t *ufp;
+ uf_info_t *fip;
+
+ ASSERT(lxdp->lxpr_type == LXPR_PID_FDDIR ||
+ lxdp->lxpr_type == LXPR_PID_TID_FDDIR);
+
+ if (ddi_strtol(name, &endptr, 10, &num) != 0) {
+ return (NULL);
+ } else if (name[0] < '0' || name[0] > '9' || *endptr != '\0') {
+ /*
+ * ddi_strtol allows leading spaces and trailing garbage
+ * We do not tolerate such foolishness.
+ */
+ return (NULL);
+ } else if ((fd = (int)num) < 0) {
+ return (NULL);
+ }
+
+ /* Lock the owner process */
+ p = lxpr_lock(lxdp->lxpr_pid, NO_ZOMB);
+ if ((p == NULL))
+ return (NULL);
+
+ /* Not applicable to processes which are system-owned. */
+ if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ lxfp = lxpr_getnode(dvp, LXPR_PID_FD_FD, p, fd);
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we dereference into fi_list.
+ */
+ fip = P_FINFO(p);
+ mutex_exit(&p->p_lock);
+ mutex_enter(&fip->fi_lock);
+ if (fd < fip->fi_nfiles) {
+ UF_ENTER(ufp, fip, fd);
+ if ((fp = ufp->uf_file) != NULL) {
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ }
+ UF_EXIT(ufp);
+ }
+ mutex_exit(&fip->fi_lock);
+
+ if (vp == NULL) {
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ lxpr_freenode(lxfp);
+ return (NULL);
+ } else {
+ /*
+ * Fill in the lxpr_node so future references will be able to
+ * find the underlying vnode. The vnode is held on the realvp.
+ */
+ lxfp->lxpr_realvp = vp;
+
+ /*
+ * For certain entries (sockets, pipes, etc), Linux expects a
+ * bogus-named symlink. If that's the case, report the type as
+ * VNON to bypass link-following elsewhere in the vfs system.
+ *
+ * See lxpr_readlink for more details.
+ */
+ if (lxpr_readlink_fdnode(lxfp, NULL, 0) == 0)
+ LXPTOV(lxfp)->v_type = VNON;
+ }
+
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ ASSERT(LXPTOV(lxfp) != NULL);
+ return (LXPTOV(lxfp));
+}
+
+/*
+ * Attempt to create Linux-proc-style fake symlinks contents for supported
+ * /proc/<pid>/fd/<#> entries.
+ */
+int
+lxpr_readlink_fdnode(lxpr_node_t *lxpnp, char *bp, size_t len)
+{
+ const char *format;
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ vattr_t attr;
+
+ switch (rvp->v_type) {
+ case VSOCK:
+ format = "socket:[%lu]";
+ break;
+ case VFIFO:
+ format = "pipe:[%lu]";
+ break;
+ default:
+ return (-1);
+ }
+
+ /* Fetch the inode of the underlying vnode */
+ if (VOP_GETATTR(rvp, &attr, 0, CRED(), NULL) != 0)
+ return (-1);
+
+ if (bp != NULL)
+ (void) snprintf(bp, len, format, (ino_t)attr.va_nodeid);
+ return (0);
+}
+
+/*
+ * Translate a Linux core_pattern path to a native Illumos one, by replacing
+ * the appropriate % escape sequences.
+ *
+ * Any % escape sequences that are not recognised are double-escaped so that
+ * they will be inserted literally into the path (to mimic Linux).
+ */
+int
+lxpr_core_path_l2s(const char *inp, char *outp, size_t outsz)
+{
+ int i = 0, j = 0;
+ char x;
+
+ while (j < outsz - 1) {
+ x = inp[i++];
+ if (x == '\0')
+ break;
+ if (x != '%') {
+ outp[j++] = x;
+ continue;
+ }
+
+ x = inp[i++];
+ if (x == '\0')
+ break;
+
+ /* Make sure we have enough space in the output buffer. */
+ if (j + 2 >= outsz - 1)
+ return (EINVAL);
+
+ switch (x) {
+ case 'E':
+ if (j + 4 >= outsz - 1)
+ return (EINVAL);
+ outp[j++] = '%';
+ outp[j++] = 'd';
+ outp[j++] = '%';
+ outp[j++] = 'f';
+ break;
+ case 'e':
+ outp[j++] = '%';
+ outp[j++] = 'f';
+ break;
+ case 'p':
+ case 'g':
+ case 'u':
+ case 't':
+ case '%':
+ outp[j++] = '%';
+ outp[j++] = x;
+ break;
+ case 'h':
+ outp[j++] = '%';
+ outp[j++] = 'n';
+ break;
+ default:
+ /* No translation, make it literal. */
+ if (j + 3 >= outsz - 1)
+ return (EINVAL);
+ outp[j++] = '%';
+ outp[j++] = '%';
+ outp[j++] = x;
+ break;
+ }
+ }
+
+ outp[j] = '\0';
+ return (0);
+}
+
+/*
+ * Translate an Illumos core pattern path back to Linux format.
+ */
+int
+lxpr_core_path_s2l(const char *inp, char *outp, size_t outsz)
+{
+ int i = 0, j = 0;
+ char x;
+
+ while (j < outsz - 1) {
+ x = inp[i++];
+ if (x == '\0')
+ break;
+ if (x != '%') {
+ outp[j++] = x;
+ continue;
+ }
+
+ x = inp[i++];
+ if (x == '\0')
+ break;
+
+ /* Make sure we have enough space in the output buffer. */
+ if (j + 2 >= outsz - 1)
+ return (EINVAL);
+
+ switch (x) {
+ case 'd':
+ /* No Linux equivalent unless it's %d%f. */
+ if (inp[i] == '%' && inp[i + 1] == 'f') {
+ i += 2;
+ outp[j++] = '%';
+ outp[j++] = 'E';
+ }
+ break;
+ case 'f':
+ outp[j++] = '%';
+ outp[j++] = 'e';
+ break;
+ case 'p':
+ case 'P':
+ case 'g':
+ case 'u':
+ case 't':
+ case '%':
+ outp[j++] = '%';
+ outp[j++] = (x == 'P' ? 'p' : x);
+ break;
+ case 'n':
+ outp[j++] = '%';
+ outp[j++] = 'h';
+ break;
+ default:
+ /* No translation. */
+ break;
+ }
+ }
+
+ outp[j] = '\0';
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c
new file mode 100644
index 0000000000..b4dc5091c2
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c
@@ -0,0 +1,377 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lxprvfsops.c: vfs operations for /lxprocfs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+#include "lx_proc.h"
+
+/* Module level parameters */
+static int lxprocfstype;
+static dev_t lxprocdev;
+static kmutex_t lxpr_mount_lock;
+
+int nproc_highbit; /* highbit(v.v_nproc) */
+
+static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxpr_unmount(vfs_t *, int, cred_t *);
+static int lxpr_root(vfs_t *, vnode_t **);
+static int lxpr_statvfs(vfs_t *, statvfs64_t *);
+static int lxpr_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_proc",
+ lxpr_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand procfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ /*
+ * destroy lxpr_node cache
+ */
+ lxpr_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxprocfstype);
+ vn_freevnodeops(lxpr_vnodeops);
+
+ mutex_destroy(&lxpr_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxpr_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxpr_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxpr_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxpr_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxpr_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxpr_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxpr_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ nproc_highbit = highbit(v.v_proc);
+ lxprocfstype = fstype;
+ ASSERT(lxprocfstype != 0);
+
+ mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxprocdev = makedevice(dev, 0);
+
+ /*
+ * Initialise cache for lxpr_nodes
+ */
+ lxpr_initnodecache();
+
+ return (0);
+}
+
+static int
+lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt;
+ zone_t *zone = curproc->p_zone;
+ ldi_ident_t li;
+ int err;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * Mounting lx_proc is not allowed outside an LX zone.
+ */
+ if (zone->zone_brand != &lx_brand) {
+ return (ENOTSUP);
+ }
+
+ /*
+ * Having the resource be anything but "lxproc" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxproc", 0);
+
+ lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
+
+ if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+ return (err);
+ }
+ lxpr_mnt->lxprm_li = li;
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxpr_mount_lock);
+ kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * Hold a zone reference for access to the lxzd structure.
+ */
+ zone_hold(lxpr_mnt->lxprm_zone = zone);
+
+ /*
+ * Allocate the first vnode and arbitrarily set the parent vnode to the
+ * mounted over directory
+ */
+ lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
+
+ /* Correctly set the fs for the root node */
+ lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxprocfstype;
+ vfsp->vfs_data = (caddr_t)lxpr_mnt;
+ vfsp->vfs_dev = lxprocdev;
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+ int count;
+
+ ASSERT(lxpr_mnt != NULL);
+ vp = LXPTOV(lxpr_mnt->lxprm_node);
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * must be root to unmount
+ */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EPERM);
+ }
+
+ /*
+ * forced unmount is not supported by this file system
+ */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxpr_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Ensure that no vnodes are in use on this mount point.
+ */
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EBUSY);
+ }
+
+
+ /*
+ * purge the dnlc cache for vnode entries
+ * associated with this file system
+ */
+ count = dnlc_purge_vfsp(vfsp, 0);
+
+ /*
+ * free up the lxprnode
+ */
+ lxpr_freenode(lxpr_mnt->lxprm_node);
+ zone_rele(lxpr_mnt->lxprm_zone);
+
+ ldi_ident_release(lxpr_mnt->lxprm_li);
+
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
+ vnode_t *vp = LXPTOV(lxpnp);
+
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ int n;
+ dev32_t d32;
+ extern uint_t nproc;
+
+ n = v.v_proc - nproc;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)v.v_proc + 2;
+ sp->f_ffree = (fsfilcnt64_t)n;
+ sp->f_favail = (fsfilcnt64_t)n;
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+ /* We know f_fstr is 32 chars */
+ (void) strcpy(sp->f_fstr, "/proc");
+ (void) strcpy(&sp->f_fstr[6], "/proc");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
new file mode 100644
index 0000000000..7e56d3322b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
@@ -0,0 +1,7344 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_proc -- a Linux-compatible /proc for the LX brand
+ *
+ * We have -- confusingly -- two implementations of Linux /proc. One is to
+ * support native (but Linux-borne) programs that wish to view the native
+ * system through the Linux /proc model; the other -- this one -- is to
+ * support Linux binaries via the LX brand. These two implementations differ
+ * greatly in their aspirations (and their willingness to bend the truth
+ * of the system to accommodate those aspirations); they should not be unified.
+ */
+
+#include <sys/cpupart.h>
+#include <sys/cpuvar.h>
+#include <sys/session.h>
+#include <sys/vmparam.h>
+#include <sys/mman.h>
+#include <vm/rm.h>
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <lx_auxv.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/fcntl.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/rctl.h>
+#include <sys/kstat.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_types.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/corectl.h>
+#include <inet/ip.h>
+#include <inet/ip_ire.h>
+#include <inet/ip6.h>
+#include <inet/ip_if.h>
+#include <inet/tcp.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+#include <inet/ipclassifier.h>
+#include <sys/socketvar.h>
+#include <fs/sockfs/socktpi.h>
+
+/* Dependent on procfs */
+extern kthread_t *prchoose(proc_t *);
+extern int prreadargv(proc_t *, char *, size_t, size_t *);
+extern int prreadenvv(proc_t *, char *, size_t, size_t *);
+extern int prreadbuf(proc_t *, uintptr_t, uint8_t *, size_t, size_t *);
+
+#include "lx_proc.h"
+
+extern pgcnt_t swapfs_minfree;
+extern time_t boot_time;
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxprinit() in lxpr_vfsops.c
+ */
+vnodeops_t *lxpr_vnodeops;
+
+static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_create(struct vnode *, char *, struct vattr *, enum vcexcl,
+ int, struct vnode **, struct cred *, int, caller_context_t *, vsecattr_t *);
+static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_space(vnode_t *, int, flock64_t *, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_setattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxpr_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *);
+static int lxpr_sync(void);
+static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxpr_lookup_procdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_piddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_not_a_dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sysdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fsdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fs_inotifydir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kerneldir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kdir_randdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_coredir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_ipv4dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_vmdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_taskdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_task_tid_dir(vnode_t *, char *);
+
+static int lxpr_readdir_procdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_piddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_not_a_dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sysdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fsdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kerneldir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kdir_randdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_coredir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_vmdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_taskdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_task_tid_dir(lxpr_node_t *, uio_t *, int *);
+
+static void lxpr_read_invalid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_empty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cgroups(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cpuinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_devices(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_diskstats(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_isdir(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fd(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_filesystems(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_kmsg(lxpr_node_t *, lxpr_uiobuf_t *, ldi_handle_t);
+static void lxpr_read_loadavg(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_meminfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_mounts(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_partitions(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_swaps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_uptime(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_version(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_vmstat(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_auxv(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cgroup(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_comm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_env(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_limits(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_loginuid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_mountinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_oom_scr_adj(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_personality(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_tid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_tid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_net_arp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev_mcast(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_if_inet6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_igmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_vif(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ipv6_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_mcfilter(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_netstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_raw(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rpc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rt_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_sockstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_snmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_unix(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_caplcap(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_corepatt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_hostname(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_msgmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_osrel(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_pid_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_sem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmall(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_threads_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_core_somaxc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_minfr_kb(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_nhpages(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_swappiness(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static int lxpr_write_pid_loginuid(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_core_somaxc(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_kernel_corepatt(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+
+/*
+ * Simple conversion
+ */
+#define btok(x) ((x) >> 10) /* bytes to kbytes */
+#define ptok(x) ((x) << (PAGESHIFT - 10)) /* pages to kbytes */
+
+#define ttolxlwp(t) ((struct lx_lwp_data *)ttolwpbrand(t))
+
+extern rctl_hndl_t rc_process_semmsl;
+extern rctl_hndl_t rc_process_semopm;
+extern rctl_hndl_t rc_zone_semmni;
+
+extern rctl_hndl_t rc_zone_msgmni;
+extern rctl_hndl_t rc_zone_shmmax;
+extern rctl_hndl_t rc_zone_shmmni;
+#define FOURGB 4294967295ULL
+
+/*
+ * The maximum length of the concatenation of argument vector strings we
+ * will return to the user via the branded procfs. Likewise for the env vector.
+ */
+int lxpr_maxargvlen = 4096;
+int lxpr_maxenvvlen = 4096;
+
+/*
+ * The lx /proc vnode operations vector
+ */
+const fs_operation_def_t lxpr_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxpr_open },
+ VOPNAME_CLOSE, { .vop_close = lxpr_close },
+ VOPNAME_READ, { .vop_read = lxpr_read },
+ VOPNAME_WRITE, { .vop_read = lxpr_write },
+ VOPNAME_GETATTR, { .vop_getattr = lxpr_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxpr_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxpr_lookup },
+ VOPNAME_CREATE, { .vop_create = lxpr_create },
+ VOPNAME_READDIR, { .vop_readdir = lxpr_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxpr_readlink },
+ VOPNAME_SPACE, { .vop_space = lxpr_space },
+ VOPNAME_SETATTR, { .vop_setattr = lxpr_setattr },
+ VOPNAME_FSYNC, { .error = lxpr_sync },
+ VOPNAME_SEEK, { .error = lxpr_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxpr_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxpr_cmp },
+ VOPNAME_REALVP, { .vop_realvp = lxpr_realvp },
+ NULL, NULL
+};
+
+
+/*
+ * file contents of an lx /proc directory.
+ */
+static lxpr_dirent_t lx_procdir[] = {
+ { LXPR_CGROUPS, "cgroups" },
+ { LXPR_CMDLINE, "cmdline" },
+ { LXPR_CPUINFO, "cpuinfo" },
+ { LXPR_DEVICES, "devices" },
+ { LXPR_DISKSTATS, "diskstats" },
+ { LXPR_DMA, "dma" },
+ { LXPR_FILESYSTEMS, "filesystems" },
+ { LXPR_INTERRUPTS, "interrupts" },
+ { LXPR_IOPORTS, "ioports" },
+ { LXPR_KCORE, "kcore" },
+ { LXPR_KMSG, "kmsg" },
+ { LXPR_LOADAVG, "loadavg" },
+ { LXPR_MEMINFO, "meminfo" },
+ { LXPR_MODULES, "modules" },
+ { LXPR_MOUNTS, "mounts" },
+ { LXPR_NETDIR, "net" },
+ { LXPR_PARTITIONS, "partitions" },
+ { LXPR_SELF, "self" },
+ { LXPR_STAT, "stat" },
+ { LXPR_SWAPS, "swaps" },
+ { LXPR_SYSDIR, "sys" },
+ { LXPR_UPTIME, "uptime" },
+ { LXPR_VERSION, "version" },
+ { LXPR_VMSTAT, "vmstat" }
+};
+
+#define PROCDIRFILES (sizeof (lx_procdir) / sizeof (lx_procdir[0]))
+
+/*
+ * Contents of an lx /proc/<pid> directory.
+ */
+static lxpr_dirent_t piddir[] = {
+ { LXPR_PID_AUXV, "auxv" },
+ { LXPR_PID_CGROUP, "cgroup" },
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_COMM, "comm" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_LIMITS, "limits" },
+ { LXPR_PID_LOGINUID, "loginuid" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_MOUNTINFO, "mountinfo" },
+ { LXPR_PID_MOUNTS, "mounts" },
+ { LXPR_PID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_STATUS, "status" },
+ { LXPR_PID_TASKDIR, "task" },
+ { LXPR_PID_FDDIR, "fd" }
+};
+
+#define PIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]))
+
+/*
+ * Contents of an lx /proc/<pid>/task/<tid> directory.
+ */
+static lxpr_dirent_t tiddir[] = {
+ { LXPR_PID_TID_AUXV, "auxv" },
+ { LXPR_PID_CGROUP, "cgroup" },
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_TID_COMM, "comm" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_LIMITS, "limits" },
+ { LXPR_PID_LOGINUID, "loginuid" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_MOUNTINFO, "mountinfo" },
+ { LXPR_PID_TID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_TID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_TID_STATUS, "status" },
+ { LXPR_PID_FDDIR, "fd" }
+};
+
+#define TIDDIRFILES (sizeof (tiddir) / sizeof (tiddir[0]))
+
+#define LX_RLIM_INFINITY 0xFFFFFFFFFFFFFFFF
+
+#define RCTL_INFINITE(x) \
+ ((x.rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+ (x.rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct lxpr_rlimtab {
+ char *rlim_name; /* limit name */
+ char *rlim_unit; /* limit unit */
+ char *rlim_rctl; /* rctl source */
+} lxpr_rlimtab_t;
+
+static lxpr_rlimtab_t lxpr_rlimtab[] = {
+ { "Max cpu time", "seconds", "process.max-cpu-time" },
+ { "Max file size", "bytes", "process.max-file-size" },
+ { "Max data size", "bytes", "process.max-data-size" },
+ { "Max stack size", "bytes", "process.max-stack-size" },
+ { "Max core file size", "bytes", "process.max-core-size" },
+ { "Max resident set", "bytes", "zone.max-physical-memory" },
+ { "Max processes", "processes", "zone.max-lwps" },
+ { "Max open files", "files", "process.max-file-descriptor" },
+ { "Max locked memory", "bytes", "zone.max-locked-memory" },
+ { "Max address space", "bytes", "process.max-address-space" },
+ { "Max file locks", "locks", NULL },
+ { "Max pending signals", "signals",
+ "process.max-sigqueue-size" },
+ { "Max msgqueue size", "bytes", "process.max-msg-messages" }
+};
+
+#define LX_RLIM_TAB_LEN (sizeof (lxpr_rlimtab) / sizeof (lxpr_rlimtab[0]))
+
+
+/*
+ * contents of lx /proc/net directory
+ */
+static lxpr_dirent_t netdir[] = {
+ { LXPR_NET_ARP, "arp" },
+ { LXPR_NET_DEV, "dev" },
+ { LXPR_NET_DEV_MCAST, "dev_mcast" },
+ { LXPR_NET_IF_INET6, "if_inet6" },
+ { LXPR_NET_IGMP, "igmp" },
+ { LXPR_NET_IP_MR_CACHE, "ip_mr_cache" },
+ { LXPR_NET_IP_MR_VIF, "ip_mr_vif" },
+ { LXPR_NET_IPV6_ROUTE, "ipv6_route" },
+ { LXPR_NET_MCFILTER, "mcfilter" },
+ { LXPR_NET_NETSTAT, "netstat" },
+ { LXPR_NET_RAW, "raw" },
+ { LXPR_NET_ROUTE, "route" },
+ { LXPR_NET_RPC, "rpc" },
+ { LXPR_NET_RT_CACHE, "rt_cache" },
+ { LXPR_NET_SOCKSTAT, "sockstat" },
+ { LXPR_NET_SNMP, "snmp" },
+ { LXPR_NET_STAT, "stat" },
+ { LXPR_NET_TCP, "tcp" },
+ { LXPR_NET_TCP6, "tcp6" },
+ { LXPR_NET_UDP, "udp" },
+ { LXPR_NET_UDP6, "udp6" },
+ { LXPR_NET_UNIX, "unix" }
+};
+
+#define NETDIRFILES (sizeof (netdir) / sizeof (netdir[0]))
+
+/*
+ * contents of /proc/sys directory
+ */
+static lxpr_dirent_t sysdir[] = {
+ { LXPR_SYS_FSDIR, "fs" },
+ { LXPR_SYS_KERNELDIR, "kernel" },
+ { LXPR_SYS_NETDIR, "net" },
+ { LXPR_SYS_VMDIR, "vm" },
+};
+
+#define SYSDIRFILES (sizeof (sysdir) / sizeof (sysdir[0]))
+
+/*
+ * contents of /proc/sys/fs directory
+ */
+static lxpr_dirent_t sys_fsdir[] = {
+ { LXPR_SYS_FS_INOTIFYDIR, "inotify" },
+};
+
+#define SYS_FSDIRFILES (sizeof (sys_fsdir) / sizeof (sys_fsdir[0]))
+
+/*
+ * contents of /proc/sys/fs/inotify directory
+ */
+static lxpr_dirent_t sys_fs_inotifydir[] = {
+ { LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS, "max_queued_events" },
+ { LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES, "max_user_instances" },
+ { LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES, "max_user_watches" },
+};
+
+#define SYS_FS_INOTIFYDIRFILES \
+ (sizeof (sys_fs_inotifydir) / sizeof (sys_fs_inotifydir[0]))
+
+/*
+ * contents of /proc/sys/kernel directory
+ */
+static lxpr_dirent_t sys_kerneldir[] = {
+ { LXPR_SYS_KERNEL_CAPLCAP, "cap_last_cap" },
+ { LXPR_SYS_KERNEL_COREPATT, "core_pattern" },
+ { LXPR_SYS_KERNEL_HOSTNAME, "hostname" },
+ { LXPR_SYS_KERNEL_MSGMNI, "msgmni" },
+ { LXPR_SYS_KERNEL_NGROUPS_MAX, "ngroups_max" },
+ { LXPR_SYS_KERNEL_OSREL, "osrelease" },
+ { LXPR_SYS_KERNEL_PID_MAX, "pid_max" },
+ { LXPR_SYS_KERNEL_RANDDIR, "random" },
+ { LXPR_SYS_KERNEL_SEM, "sem" },
+ { LXPR_SYS_KERNEL_SHMALL, "shmall" },
+ { LXPR_SYS_KERNEL_SHMMAX, "shmmax" },
+ { LXPR_SYS_KERNEL_SHMMNI, "shmmni" },
+ { LXPR_SYS_KERNEL_THREADS_MAX, "threads-max" },
+};
+
+#define SYS_KERNELDIRFILES (sizeof (sys_kerneldir) / sizeof (sys_kerneldir[0]))
+
+/*
+ * contents of /proc/sys/kernel/random directory
+ */
+static lxpr_dirent_t sys_randdir[] = {
+ { LXPR_SYS_KERNEL_RAND_BOOTID, "boot_id" },
+};
+
+#define SYS_RANDDIRFILES (sizeof (sys_randdir) / sizeof (sys_randdir[0]))
+
+/*
+ * contents of /proc/sys/net directory
+ */
+static lxpr_dirent_t sys_netdir[] = {
+ { LXPR_SYS_NET_COREDIR, "core" },
+ { LXPR_SYS_NET_IPV4DIR, "ipv4" },
+};
+
+#define SYS_NETDIRFILES (sizeof (sys_netdir) / sizeof (sys_netdir[0]))
+
+/*
+ * contents of /proc/sys/net/core directory
+ */
+static lxpr_dirent_t sys_net_coredir[] = {
+ { LXPR_SYS_NET_CORE_SOMAXCON, "somaxconn" },
+};
+
+#define SYS_NET_COREDIRFILES \
+ (sizeof (sys_net_coredir) / sizeof (sys_net_coredir[0]))
+
+/*
+ * contents of /proc/sys/net/ipv4 directory
+ * See the Linux ip(7) & tcp(7) man pages for descriptions and the illumos
+ * ip(7p) & tcp(7p) man pages for the native descriptions.
+ */
+static lxpr_dirent_t sys_net_ipv4dir[] = {
+ { LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, "ip_local_port_range" },
+ { LXPR_SYS_NET_IPV4_TCP_FIN_TO, "tcp_fin_timeout" },
+ { LXPR_SYS_NET_IPV4_TCP_KA_INT, "tcp_keepalive_intvl" },
+ { LXPR_SYS_NET_IPV4_TCP_KA_TIM, "tcp_keepalive_time" },
+ { LXPR_SYS_NET_IPV4_TCP_SACK, "tcp_sack" },
+ { LXPR_SYS_NET_IPV4_TCP_WINSCALE, "tcp_window_scaling" },
+};
+
+#define SYS_NET_IPV4DIRFILES \
+ (sizeof (sys_net_ipv4dir) / sizeof (sys_net_ipv4dir[0]))
+
+/*
+ * contents of /proc/sys/vm directory
+ */
+static lxpr_dirent_t sys_vmdir[] = {
+ { LXPR_SYS_VM_MAX_MAP_CNT, "max_map_count" },
+ { LXPR_SYS_VM_MINFR_KB, "min_free_kbytes" },
+ { LXPR_SYS_VM_NHUGEP, "nr_hugepages" },
+ { LXPR_SYS_VM_OVERCOMMIT_MEM, "overcommit_memory" },
+ { LXPR_SYS_VM_SWAPPINESS, "swappiness" },
+};
+
+#define SYS_VMDIRFILES (sizeof (sys_vmdir) / sizeof (sys_vmdir[0]))
+
+/*
+ * Table for standard writable files. Non-standard writable files not in this
+ * table can be handled explicitly as special cases.
+ * This table drives lxpr_is_writable, lxpr_write, and lxpr_create.
+ * Note that the entries LXPR_PID_FD_FD and LXPR_PID_TID_FD_FD exist in the
+ * table both to verify writability and to satisfy opening with O_CREATE.
+ */
+typedef struct wftab {
+ lxpr_nodetype_t wft_type; /* file entry type */
+ int (*wft_wrf)(lxpr_node_t *, struct uio *, cred_t *,
+ caller_context_t *); /* write function */
+} wftab_t;
+
+static wftab_t wr_tab[] = {
+ {LXPR_PID_FD_FD, NULL},
+ {LXPR_PID_LOGINUID, lxpr_write_pid_loginuid},
+ {LXPR_PID_OOM_SCR_ADJ, NULL},
+ {LXPR_PID_TID_FD_FD, NULL},
+ {LXPR_PID_TID_OOM_SCR_ADJ, NULL},
+ {LXPR_SYS_KERNEL_COREPATT, lxpr_write_sys_kernel_corepatt},
+ {LXPR_SYS_KERNEL_SHMALL, NULL},
+ {LXPR_SYS_KERNEL_SHMMAX, NULL},
+ {LXPR_SYS_NET_CORE_SOMAXCON, lxpr_write_sys_net_core_somaxc},
+ {LXPR_SYS_NET_IPV4_IP_LPORT_RANGE,
+ lxpr_write_sys_net_ipv4_ip_lport_range},
+ {LXPR_SYS_NET_IPV4_TCP_FIN_TO, lxpr_write_sys_net_ipv4_tcp_fin_to},
+ {LXPR_SYS_NET_IPV4_TCP_KA_INT, lxpr_write_sys_net_ipv4_tcp_ka_int},
+ {LXPR_SYS_NET_IPV4_TCP_KA_TIM, lxpr_write_sys_net_ipv4_tcp_ka_tim},
+ {LXPR_SYS_NET_IPV4_TCP_SACK, lxpr_write_sys_net_ipv4_tcp_sack},
+ {LXPR_SYS_NET_IPV4_TCP_WINSCALE, lxpr_write_sys_net_ipv4_tcp_winscale},
+ {LXPR_SYS_VM_OVERCOMMIT_MEM, NULL},
+ {LXPR_SYS_VM_SWAPPINESS, NULL},
+ {LXPR_INVALID, NULL}
+};
+
+/*
+ * Centralized test for the standard writable proc files. Other non-standard
+ * writable files might be handled separately.
+ */
+boolean_t
+lxpr_is_writable(lxpr_nodetype_t type)
+{
+ int i;
+
+ for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+ if (wr_tab[i].wft_type == type)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * lxpr_open(): Vnode operation for VOP_OPEN()
+ */
+static int
+lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *vp = *vpp;
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *rvp;
+ int error = 0;
+
+ /* Restrict writes to certain files */
+ if ((flag & FWRITE) && !lxpr_is_writable(type)) {
+ return (EPERM);
+ }
+
+ /*
+ * If we are opening an underlying file only allow regular files,
+ * fifos or sockets; reject the open for anything else.
+ * Just do it if we are opening the current or root directory.
+ */
+ if (lxpnp->lxpr_realvp != NULL) {
+ rvp = lxpnp->lxpr_realvp;
+
+ if (type == LXPR_PID_FD_FD && rvp->v_type != VREG &&
+ rvp->v_type != VFIFO && rvp->v_type != VSOCK) {
+ error = EACCES;
+ } else {
+ if (type == LXPR_PID_FD_FD && rvp->v_type == VFIFO) {
+ /*
+ * This flag lets the fifo open know that
+ * we're using proc/fd to open a fd which we
+ * already have open. Otherwise, the fifo might
+ * reject an open if the other end has closed.
+ */
+ flag |= FKLYR;
+ }
+ /*
+ * Need to hold rvp since VOP_OPEN() may release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ if (error) {
+ VN_RELE(rvp);
+ } else {
+ *vpp = rvp;
+ VN_RELE(vp);
+ }
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * lxpr_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+#ifdef DEBUG
+ lxpr_node_t *lxpr = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpr->lxpr_type;
+
+ /*
+ * we should never get here because the close is done on the realvp
+ * for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR &&
+ type != LXPR_PID_EXE);
+#endif /* DEBUG */
+
+ return (0);
+}
+
+static void (*lxpr_read_function[LXPR_NFILES])() = {
+ NULL, /* invalid */
+ lxpr_read_isdir, /* /proc */
+ lxpr_read_isdir, /* /proc/<pid> */
+ lxpr_read_pid_auxv, /* /proc/<pid>/auxv */
+ lxpr_read_pid_cgroup, /* /proc/<pid>/cgroup */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/cmdline */
+ lxpr_read_pid_comm, /* /proc/<pid>/comm */
+ lxpr_read_empty, /* /proc/<pid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/cwd */
+ lxpr_read_pid_env, /* /proc/<pid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/exe */
+ lxpr_read_pid_limits, /* /proc/<pid>/limits */
+ lxpr_read_pid_loginuid, /* /proc/<pid>/loginuid */
+ lxpr_read_pid_maps, /* /proc/<pid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/mem */
+ lxpr_read_pid_mountinfo, /* /proc/<pid>/mountinfo */
+ lxpr_read_mounts, /* /proc/<pid>/mounts */
+ lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/oom_score_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/personality */
+ lxpr_read_invalid, /* /proc/<pid>/root */
+ lxpr_read_pid_stat, /* /proc/<pid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/statm */
+ lxpr_read_pid_status, /* /proc/<pid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/task */
+ lxpr_read_isdir, /* /proc/<pid>/task/nn */
+ lxpr_read_isdir, /* /proc/<pid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/fd/nn */
+ lxpr_read_pid_auxv, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_read_pid_cgroup, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_read_pid_comm, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_read_empty, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_read_pid_env, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_read_pid_limits, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_read_pid_loginuid, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_read_pid_maps, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_read_pid_mountinfo, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/task/<tid>/personality */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/root */
+ lxpr_read_pid_tid_stat, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_read_pid_tid_status, /* /proc/<pid>/task/<tid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_read_cgroups, /* /proc/cgroups */
+ lxpr_read_cmdline, /* /proc/cmdline */
+ lxpr_read_cpuinfo, /* /proc/cpuinfo */
+ lxpr_read_devices, /* /proc/devices */
+ lxpr_read_diskstats, /* /proc/diskstats */
+ lxpr_read_empty, /* /proc/dma */
+ lxpr_read_filesystems, /* /proc/filesystems */
+ lxpr_read_empty, /* /proc/interrupts */
+ lxpr_read_empty, /* /proc/ioports */
+ lxpr_read_empty, /* /proc/kcore */
+ lxpr_read_invalid, /* /proc/kmsg -- see lxpr_read() */
+ lxpr_read_loadavg, /* /proc/loadavg */
+ lxpr_read_meminfo, /* /proc/meminfo */
+ lxpr_read_empty, /* /proc/modules */
+ lxpr_read_mounts, /* /proc/mounts */
+ lxpr_read_isdir, /* /proc/net */
+ lxpr_read_net_arp, /* /proc/net/arp */
+ lxpr_read_net_dev, /* /proc/net/dev */
+ lxpr_read_net_dev_mcast, /* /proc/net/dev_mcast */
+ lxpr_read_net_if_inet6, /* /proc/net/if_inet6 */
+ lxpr_read_net_igmp, /* /proc/net/igmp */
+ lxpr_read_net_ip_mr_cache, /* /proc/net/ip_mr_cache */
+ lxpr_read_net_ip_mr_vif, /* /proc/net/ip_mr_vif */
+ lxpr_read_net_ipv6_route, /* /proc/net/ipv6_route */
+ lxpr_read_net_mcfilter, /* /proc/net/mcfilter */
+ lxpr_read_net_netstat, /* /proc/net/netstat */
+ lxpr_read_net_raw, /* /proc/net/raw */
+ lxpr_read_net_route, /* /proc/net/route */
+ lxpr_read_net_rpc, /* /proc/net/rpc */
+ lxpr_read_net_rt_cache, /* /proc/net/rt_cache */
+ lxpr_read_net_sockstat, /* /proc/net/sockstat */
+ lxpr_read_net_snmp, /* /proc/net/snmp */
+ lxpr_read_net_stat, /* /proc/net/stat */
+ lxpr_read_net_tcp, /* /proc/net/tcp */
+ lxpr_read_net_tcp6, /* /proc/net/tcp6 */
+ lxpr_read_net_udp, /* /proc/net/udp */
+ lxpr_read_net_udp6, /* /proc/net/udp6 */
+ lxpr_read_net_unix, /* /proc/net/unix */
+ lxpr_read_partitions, /* /proc/partitions */
+ lxpr_read_invalid, /* /proc/self */
+ lxpr_read_stat, /* /proc/stat */
+ lxpr_read_swaps, /* /proc/swaps */
+ lxpr_read_invalid, /* /proc/sys */
+ lxpr_read_invalid, /* /proc/sys/fs */
+ lxpr_read_invalid, /* /proc/sys/fs/inotify */
+ lxpr_read_sys_fs_inotify_max_queued_events, /* max_queued_events */
+ lxpr_read_sys_fs_inotify_max_user_instances, /* max_user_instances */
+ lxpr_read_sys_fs_inotify_max_user_watches, /* max_user_watches */
+ lxpr_read_invalid, /* /proc/sys/kernel */
+ lxpr_read_sys_kernel_caplcap, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_read_sys_kernel_corepatt, /* /proc/sys/kernel/core_pattern */
+ lxpr_read_sys_kernel_hostname, /* /proc/sys/kernel/hostname */
+ lxpr_read_sys_kernel_msgmni, /* /proc/sys/kernel/msgmni */
+ lxpr_read_sys_kernel_ngroups_max, /* /proc/sys/kernel/ngroups_max */
+ lxpr_read_sys_kernel_osrel, /* /proc/sys/kernel/osrelease */
+ lxpr_read_sys_kernel_pid_max, /* /proc/sys/kernel/pid_max */
+ lxpr_read_invalid, /* /proc/sys/kernel/random */
+ lxpr_read_sys_kernel_rand_bootid, /* /proc/sys/kernel/random/boot_id */
+ lxpr_read_sys_kernel_sem, /* /proc/sys/kernel/sem */
+ lxpr_read_sys_kernel_shmall, /* /proc/sys/kernel/shmall */
+ lxpr_read_sys_kernel_shmmax, /* /proc/sys/kernel/shmmax */
+ lxpr_read_sys_kernel_shmmni, /* /proc/sys/kernel/shmmni */
+ lxpr_read_sys_kernel_threads_max, /* /proc/sys/kernel/threads-max */
+ lxpr_read_invalid, /* /proc/sys/net */
+ lxpr_read_invalid, /* /proc/sys/net/core */
+ lxpr_read_sys_net_core_somaxc, /* /proc/sys/net/core/somaxconn */
+ lxpr_read_invalid, /* /proc/sys/net/ipv4 */
+ lxpr_read_sys_net_ipv4_ip_lport_range, /* ../ipv4/ip_local_port_range */
+ lxpr_read_sys_net_ipv4_tcp_fin_to, /* .../ipv4/tcp_fin_timeout */
+ lxpr_read_sys_net_ipv4_tcp_ka_int, /* .../ipv4/tcp_keepalive_intvl */
+ lxpr_read_sys_net_ipv4_tcp_ka_tim, /* .../ipv4/tcp_keepalive_time */
+ lxpr_read_sys_net_ipv4_tcp_sack, /* .../ipv4/tcp_sack */
+ lxpr_read_sys_net_ipv4_tcp_winscale, /* .../ipv4/tcp_window_scaling */
+ lxpr_read_invalid, /* /proc/sys/vm */
+ lxpr_read_sys_vm_max_map_cnt, /* /proc/sys/vm/max_map_count */
+ lxpr_read_sys_vm_minfr_kb, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_read_sys_vm_nhpages, /* /proc/sys/vm/nr_hugepages */
+ lxpr_read_sys_vm_overcommit_mem, /* /proc/sys/vm/overcommit_memory */
+ lxpr_read_sys_vm_swappiness, /* /proc/sys/vm/swappiness */
+ lxpr_read_uptime, /* /proc/uptime */
+ lxpr_read_version, /* /proc/version */
+ lxpr_read_vmstat, /* /proc/vmstat */
+};
+
+/*
+ * Array of lookup functions, indexed by lx /proc file type.
+ */
+static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
+ NULL, /* invalid */
+ lxpr_lookup_procdir, /* /proc */
+ lxpr_lookup_piddir, /* /proc/<pid> */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/auxv */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cgroup */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/comm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/limits */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/loginuid */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mountinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mounts */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/personality */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/status */
+ lxpr_lookup_taskdir, /* /proc/<pid>/task */
+ lxpr_lookup_task_tid_dir, /* /proc/<pid>/task/nn */
+ lxpr_lookup_fddir, /* /proc/<pid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/personality */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/status */
+ lxpr_lookup_fddir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_lookup_not_a_dir, /* /proc/cgroups */
+ lxpr_lookup_not_a_dir, /* /proc/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/cpuinfo */
+ lxpr_lookup_not_a_dir, /* /proc/devices */
+ lxpr_lookup_not_a_dir, /* /proc/diskstats */
+ lxpr_lookup_not_a_dir, /* /proc/dma */
+ lxpr_lookup_not_a_dir, /* /proc/filesystems */
+ lxpr_lookup_not_a_dir, /* /proc/interrupts */
+ lxpr_lookup_not_a_dir, /* /proc/ioports */
+ lxpr_lookup_not_a_dir, /* /proc/kcore */
+ lxpr_lookup_not_a_dir, /* /proc/kmsg */
+ lxpr_lookup_not_a_dir, /* /proc/loadavg */
+ lxpr_lookup_not_a_dir, /* /proc/meminfo */
+ lxpr_lookup_not_a_dir, /* /proc/modules */
+ lxpr_lookup_not_a_dir, /* /proc/mounts */
+ lxpr_lookup_netdir, /* /proc/net */
+ lxpr_lookup_not_a_dir, /* /proc/net/arp */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_lookup_not_a_dir, /* /proc/net/if_inet6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/igmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_lookup_not_a_dir, /* /proc/net/ipv6_route */
+ lxpr_lookup_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_lookup_not_a_dir, /* /proc/net/netstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/raw */
+ lxpr_lookup_not_a_dir, /* /proc/net/route */
+ lxpr_lookup_not_a_dir, /* /proc/net/rpc */
+ lxpr_lookup_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/sockstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/snmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/stat */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/unix */
+ lxpr_lookup_not_a_dir, /* /proc/partitions */
+ lxpr_lookup_not_a_dir, /* /proc/self */
+ lxpr_lookup_not_a_dir, /* /proc/stat */
+ lxpr_lookup_not_a_dir, /* /proc/swaps */
+ lxpr_lookup_sysdir, /* /proc/sys */
+ lxpr_lookup_sys_fsdir, /* /proc/sys/fs */
+ lxpr_lookup_sys_fs_inotifydir, /* /proc/sys/fs/inotify */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_queued_events */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_user_instances */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_user_watches */
+ lxpr_lookup_sys_kerneldir, /* /proc/sys/kernel */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/core_pattern */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/hostname */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/msgmni */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/ngroups_max */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/osrelease */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/pid_max */
+ lxpr_lookup_sys_kdir_randdir, /* /proc/sys/kernel/random */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/random/boot_id */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/sem */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmall */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmmax */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmmni */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/threads-max */
+ lxpr_lookup_sys_netdir, /* /proc/sys/net */
+ lxpr_lookup_sys_net_coredir, /* /proc/sys/net/core */
+ lxpr_lookup_not_a_dir, /* /proc/sys/net/core/somaxconn */
+ lxpr_lookup_sys_net_ipv4dir, /* /proc/sys/net/ipv4 */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/ip_local_port_range */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_fin_timeout */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_keepalive_intvl */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_keepalive_time */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_sack */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_window_scaling */
+ lxpr_lookup_sys_vmdir, /* /proc/sys/vm */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/max_map_count */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/nr_hugepages */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/overcommit_memory */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/swappiness */
+ lxpr_lookup_not_a_dir, /* /proc/uptime */
+ lxpr_lookup_not_a_dir, /* /proc/version */
+ lxpr_lookup_not_a_dir, /* /proc/vmstat */
+};
+
+/*
+ * Array of readdir functions, indexed by /proc file type.
+ */
+static int (*lxpr_readdir_function[LXPR_NFILES])() = {
+ NULL, /* invalid */
+ lxpr_readdir_procdir, /* /proc */
+ lxpr_readdir_piddir, /* /proc/<pid> */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/auxv */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cgroup */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/comm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/limits */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/loginuid */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mountinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mounts */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/personality */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/status */
+ lxpr_readdir_taskdir, /* /proc/<pid>/task */
+ lxpr_readdir_task_tid_dir, /* /proc/<pid>/task/nn */
+ lxpr_readdir_fddir, /* /proc/<pid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/oom_scr_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/personality */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/status */
+ lxpr_readdir_fddir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_readdir_not_a_dir, /* /proc/cgroups */
+ lxpr_readdir_not_a_dir, /* /proc/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/cpuinfo */
+ lxpr_readdir_not_a_dir, /* /proc/devices */
+ lxpr_readdir_not_a_dir, /* /proc/diskstats */
+ lxpr_readdir_not_a_dir, /* /proc/dma */
+ lxpr_readdir_not_a_dir, /* /proc/filesystems */
+ lxpr_readdir_not_a_dir, /* /proc/interrupts */
+ lxpr_readdir_not_a_dir, /* /proc/ioports */
+ lxpr_readdir_not_a_dir, /* /proc/kcore */
+ lxpr_readdir_not_a_dir, /* /proc/kmsg */
+ lxpr_readdir_not_a_dir, /* /proc/loadavg */
+ lxpr_readdir_not_a_dir, /* /proc/meminfo */
+ lxpr_readdir_not_a_dir, /* /proc/modules */
+ lxpr_readdir_not_a_dir, /* /proc/mounts */
+ lxpr_readdir_netdir, /* /proc/net */
+ lxpr_readdir_not_a_dir, /* /proc/net/arp */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_readdir_not_a_dir, /* /proc/net/if_inet6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/igmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_readdir_not_a_dir, /* /proc/net/ipv6_route */
+ lxpr_readdir_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_readdir_not_a_dir, /* /proc/net/netstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/raw */
+ lxpr_readdir_not_a_dir, /* /proc/net/route */
+ lxpr_readdir_not_a_dir, /* /proc/net/rpc */
+ lxpr_readdir_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/sockstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/snmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/stat */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/unix */
+ lxpr_readdir_not_a_dir, /* /proc/partitions */
+ lxpr_readdir_not_a_dir, /* /proc/self */
+ lxpr_readdir_not_a_dir, /* /proc/stat */
+ lxpr_readdir_not_a_dir, /* /proc/swaps */
+ lxpr_readdir_sysdir, /* /proc/sys */
+ lxpr_readdir_sys_fsdir, /* /proc/sys/fs */
+ lxpr_readdir_sys_fs_inotifydir, /* /proc/sys/fs/inotify */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_queued_events */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_user_instances */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_user_watches */
+ lxpr_readdir_sys_kerneldir, /* /proc/sys/kernel */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/core_pattern */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/hostname */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/msgmni */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/ngroups_max */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/osrelease */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/pid_max */
+ lxpr_readdir_sys_kdir_randdir, /* /proc/sys/kernel/random */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/random/boot_id */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/sem */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmall */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmmax */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmmni */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/threads-max */
+ lxpr_readdir_sys_netdir, /* /proc/sys/net */
+ lxpr_readdir_sys_net_coredir, /* /proc/sys/net/core */
+ lxpr_readdir_not_a_dir, /* /proc/sys/net/core/somaxconn */
+ lxpr_readdir_sys_net_ipv4dir, /* /proc/sys/net/ipv4 */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/ip_local_port_range */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_fin_timeout */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_keepalive_intvl */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_keepalive_time */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_sack */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_window_scaling */
+ lxpr_readdir_sys_vmdir, /* /proc/sys/vm */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/max_map_count */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/nr_hugepages */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/overcommit_memory */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/swappiness */
+ lxpr_readdir_not_a_dir, /* /proc/uptime */
+ lxpr_readdir_not_a_dir, /* /proc/version */
+ lxpr_readdir_not_a_dir, /* /proc/vmstat */
+};
+
+
+/*
+ * lxpr_read(): Vnode operation for VOP_READ()
+ *
+ * As the format of all the files that can be read in the lx procfs is human
+ * readable and not binary structures there do not have to be different
+ * read variants depending on whether the reading process model is 32 or 64 bits
+ * (at least in general, and certainly the difference is unlikely to be enough
+ * to justify have different routines for 32 and 64 bit reads
+ */
+/* ARGSUSED */
+static int
+lxpr_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ lxpr_uiobuf_t *uiobuf = lxpr_uiobuf_new(uiop);
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ if (type == LXPR_KMSG) {
+ ldi_ident_t li = VTOLXPM(vp)->lxprm_li;
+ ldi_handle_t ldih;
+ struct strioctl str;
+ int rv;
+
+ /*
+ * Open the zone's console device using the layered driver
+ * interface.
+ */
+ if ((error =
+ ldi_open_by_name("/dev/log", FREAD, cr, &ldih, li)) != 0)
+ return (error);
+
+ /*
+ * Send an ioctl to the underlying console device, letting it
+ * know we're interested in getting console messages.
+ */
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if ((error = ldi_ioctl(ldih, I_STR,
+ (intptr_t)&str, FKIOCTL, cr, &rv)) != 0)
+ return (error);
+
+ lxpr_read_kmsg(lxpnp, uiobuf, ldih);
+
+ if ((error = ldi_close(ldih, FREAD, cr)) != 0)
+ return (error);
+ } else {
+ lxpr_read_function[type](lxpnp, uiobuf);
+ }
+
+ error = lxpr_uiobuf_flush(uiobuf);
+ lxpr_uiobuf_free(uiobuf);
+
+ return (error);
+}
+
+/*
+ * lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
+ *
+ * Various special case reads:
+ * - trying to read a directory
+ * - invalid file (used to mean a file that should be implemented,
+ * but isn't yet)
+ * - empty file
+ * - wait to be able to read a file that will never have anything to read
+ */
+/* ARGSUSED */
+static void
+lxpr_read_isdir(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EISDIR);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_invalid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_empty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_pid_auxv(): read process aux vector
+ */
+static void
+lxpr_read_pid_auxv(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *pd;
+ lx_elf_data_t *edp = NULL;
+ int i, cnt;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_AUXV ||
+ lxpnp->lxpr_type == LXPR_PID_TID_AUXV);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+
+ if (p == NULL) {
+ return;
+ }
+ if ((pd = ptolxproc(p)) == NULL) {
+ /* Emit a single AT_NULL record for non-branded processes */
+ auxv_t buf;
+
+ bzero(&buf, sizeof (buf));
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)&buf, sizeof (buf));
+ return;
+ } else {
+ edp = &pd->l_elf_data;
+ }
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ auxv_t buf[__KERN_NAUXV_IMPL];
+
+ /*
+ * Because a_type is only of size int (not long), the buffer
+ * contents must be zeroed first to ensure cleanliness.
+ */
+ bzero(buf, sizeof (buf));
+ for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+ if (lx_auxv_stol(&p->p_user.u_auxv[i],
+ &buf[cnt], edp) == 0) {
+ cnt++;
+ }
+ if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+ break;
+ }
+ }
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ auxv32_t buf[__KERN_NAUXV_IMPL];
+
+ for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+ auxv_t temp;
+
+ if (lx_auxv_stol(&p->p_user.u_auxv[i],
+ &temp, edp) == 0) {
+ buf[cnt].a_type = (int)temp.a_type;
+ buf[cnt].a_un.a_val = (int)temp.a_un.a_val;
+ cnt++;
+ }
+ if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+ break;
+ }
+ }
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+ }
+#endif /* defined(_SYSCALL32_IMPL) */
+}
+
+/*
+ * lxpr_read_pid_cgroup(): read cgroups for process
+ */
+static void
+lxpr_read_pid_cgroup(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CGROUP ||
+ lxpnp->lxpr_type == LXPR_PID_TID_CGROUP);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ lxpr_unlock(p);
+
+ /* basic stub, 3rd field will need to be populated */
+ lxpr_uiobuf_printf(uiobuf, "1:name=systemd:/\n");
+}
+
+static void
+lxpr_copy_cmdline(proc_t *p, lx_proc_data_t *pd, lxpr_uiobuf_t *uiobuf)
+{
+ uio_t *uiop = uiobuf->uiop;
+ char *buf = uiobuf->buffer;
+ int bsz = uiobuf->buffsize;
+ boolean_t env_overflow = B_FALSE;
+ uintptr_t pos = pd->l_args_start + uiop->uio_offset;
+ uintptr_t estart = pd->l_envs_start;
+ uintptr_t eend = pd->l_envs_end;
+ size_t chunk, copied;
+ int err = 0;
+
+ /* Do not bother with data beyond the end of the envp strings area. */
+ if (pos > eend) {
+ return;
+ }
+ mutex_exit(&p->p_lock);
+
+ /*
+ * If the starting or ending bounds are outside the argv strings area,
+ * check to see if the process has overwritten the terminating NULL.
+ * If not, no data needs to be copied from oustide the argv area.
+ */
+ if (pos >= estart || (pos + uiop->uio_resid) >= estart) {
+ uint8_t term;
+ if (uread(p, &term, sizeof (term), estart - 1) != 0) {
+ err = EFAULT;
+ } else if (term != 0) {
+ env_overflow = B_TRUE;
+ }
+ }
+
+ /* Data between astart and estart-1 can be copied freely. */
+ while (pos < estart && uiop->uio_resid > 0 && err == 0) {
+ chunk = MIN(estart - pos, uiop->uio_resid);
+ chunk = MIN(chunk, bsz);
+
+ if (prreadbuf(p, pos, (uint8_t *)buf, chunk, &copied) != 0 ||
+ copied != chunk) {
+ err = EFAULT;
+ break;
+ }
+ err = uiomove(buf, copied, UIO_READ, uiop);
+ pos += copied;
+ }
+
+ /*
+ * Onward from estart, data is copied as a contiguous string. To
+ * protect env data from potential snooping, only one buffer-sized copy
+ * is allowed to avoid complex seek logic.
+ */
+ if (err == 0 && env_overflow && pos == estart && uiop->uio_resid > 0) {
+ chunk = MIN(eend - pos, uiop->uio_resid);
+ chunk = MIN(chunk, bsz);
+ if (prreadbuf(p, pos, (uint8_t *)buf, chunk, &copied) == 0) {
+ int len = strnlen(buf, copied);
+ if (len > 0) {
+ err = uiomove(buf, len, UIO_READ, uiop);
+ }
+ }
+ }
+
+ uiobuf->error = err;
+ /* reset any uiobuf state */
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+
+ mutex_enter(&p->p_lock);
+}
+
+/*
+ * lxpr_read_pid_cmdline(): read argument vector from process
+ */
+static void
+lxpr_read_pid_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+ size_t asz = lxpr_maxargvlen, sz;
+ lx_proc_data_t *pd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CMDLINE ||
+ lxpnp->lxpr_type == LXPR_PID_TID_CMDLINE);
+
+ buf = kmem_alloc(asz, KM_SLEEP);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+ if (p == NULL) {
+ kmem_free(buf, asz);
+ return;
+ }
+
+ if ((pd = ptolxproc(p)) != NULL && pd->l_args_start != 0 &&
+ pd->l_envs_start != 0 && pd->l_envs_end != 0) {
+ /* Use Linux-style argv bounds if possible. */
+ lxpr_copy_cmdline(p, pd, uiobuf);
+ lxpr_unlock(p);
+ } else {
+ int r;
+
+ r = prreadargv(p, buf, asz, &sz);
+ lxpr_unlock(p);
+
+ if (r != 0) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ } else {
+ lxpr_uiobuf_write(uiobuf, buf, sz);
+ }
+ }
+ kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_comm(): read command from process
+ */
+static void
+lxpr_read_pid_comm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char buf[MAXCOMLEN + 1];
+
+ VERIFY(lxpnp->lxpr_type == LXPR_PID_COMM ||
+ lxpnp->lxpr_type == LXPR_PID_TID_COMM);
+
+ /*
+ * Because prctl(PR_SET_NAME) does not set custom names for threads
+ * (vs processes), there is no need for special handling here.
+ */
+ if ((p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK)) == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ (void) strlcpy(buf, p->p_user.u_comm, sizeof (buf));
+ lxpr_unlock(p);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", buf);
+}
+
+/*
+ * lxpr_read_pid_env(): read env vector from process
+ */
+static void
+lxpr_read_pid_env(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+ size_t asz = lxpr_maxenvvlen, sz;
+ int r;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_ENV);
+
+ buf = kmem_alloc(asz, KM_SLEEP);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+ if (p == NULL) {
+ kmem_free(buf, asz);
+ return;
+ }
+
+ r = prreadenvv(p, buf, asz, &sz);
+ lxpr_unlock(p);
+
+ if (r != 0) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ } else {
+ lxpr_uiobuf_write(uiobuf, buf, sz);
+ }
+ kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_limits(): ulimit file
+ */
+static void
+lxpr_read_pid_limits(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ rctl_qty_t cur[LX_RLIM_TAB_LEN], max[LX_RLIM_TAB_LEN];
+ int i;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LIMITS ||
+ lxpnp->lxpr_type == LXPR_PID_TID_LIMITS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+ if (p == NULL) {
+ return;
+ }
+
+ for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+ char *kname = lxpr_rlimtab[i].rlim_rctl;
+ rctl_val_t nval, *oval = NULL;
+ rctl_hndl_t hndl;
+
+ /* default to unlimited for resources without an analog */
+ cur[i] = RLIM_INFINITY;
+ max[i] = RLIM_INFINITY;
+ if (kname == NULL || (hndl = rctl_hndl_lookup(kname)) == -1) {
+ continue;
+ }
+ while (rctl_local_get(hndl, oval, &nval, p) == 0) {
+ oval = &nval;
+ switch (nval.rcv_privilege) {
+ case RCPRIV_BASIC:
+ if (!RCTL_INFINITE(nval))
+ cur[i] = nval.rcv_value;
+ break;
+ case RCPRIV_PRIVILEGED:
+ if (!RCTL_INFINITE(nval))
+ max[i] = nval.rcv_value;
+ break;
+ }
+ }
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%-25s %-20s %-20s %-10s\n",
+ "Limit", "Soft Limit", "Hard Limit", "Units");
+ for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+ lxpr_uiobuf_printf(uiobuf, "%-25s", lxpr_rlimtab[i].rlim_name);
+ if (cur[i] == RLIM_INFINITY || cur[i] == LX_RLIM_INFINITY) {
+ lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " %-20lu", cur[i]);
+ }
+ if (max[i] == RLIM_INFINITY || max[i] == LX_RLIM_INFINITY) {
+ lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " %-20lu", max[i]);
+ }
+ lxpr_uiobuf_printf(uiobuf, " %-10s\n",
+ lxpr_rlimtab[i].rlim_unit);
+ }
+}
+
+/*
+ * lxpr_read_pid_loginuid(): loginuid file
+ */
+static void
+lxpr_read_pid_loginuid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *pd;
+ uid_t lu = 0;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID ||
+ lxpnp->lxpr_type == LXPR_PID_TID_LOGINUID);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ if ((pd = ptolxproc(p)) != NULL) {
+ lu = pd->l_loginuid;
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%d", lu);
+}
+
+/*
+ * lxpr_read_pid_maps(): memory map file
+ */
+static void
+lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *lxpd;
+ struct as *as;
+ struct seg *seg;
+ char *buf;
+ int buflen = MAXPATHLEN;
+ struct print_data {
+ uintptr_t saddr;
+ uintptr_t eaddr;
+ int type;
+ char prot[5];
+ uintptr_t offset;
+ vnode_t *vp;
+ char *name_override;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *pbuf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MAPS ||
+ lxpnp->lxpr_type == LXPR_PID_TID_MAPS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+ if (p == NULL) {
+ return;
+ }
+
+ as = p->p_as;
+ lxpd = ptolxproc(p);
+
+ if (as == &kas) {
+ lxpr_unlock(p);
+ return;
+ }
+
+ mutex_exit(&p->p_lock);
+
+ /* Iterate over all segments in the address space */
+ AS_LOCK_ENTER(as, RW_READER);
+ for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
+ vnode_t *vp;
+ uint_t protbits;
+
+ pbuf = kmem_alloc(sizeof (*pbuf), KM_SLEEP);
+
+ pbuf->saddr = (uintptr_t)seg->s_base;
+ pbuf->eaddr = pbuf->saddr + seg->s_size;
+ pbuf->type = SEGOP_GETTYPE(seg, seg->s_base);
+
+ /*
+ * Cheat and only use the protection bits of the first page
+ * in the segment
+ */
+ (void) strncpy(pbuf->prot, "----", sizeof (pbuf->prot));
+ (void) SEGOP_GETPROT(seg, seg->s_base, 0, &protbits);
+
+ if (protbits & PROT_READ) pbuf->prot[0] = 'r';
+ if (protbits & PROT_WRITE) pbuf->prot[1] = 'w';
+ if (protbits & PROT_EXEC) pbuf->prot[2] = 'x';
+ if (pbuf->type & MAP_SHARED) pbuf->prot[3] = 's';
+ else if (pbuf->type & MAP_PRIVATE) pbuf->prot[3] = 'p';
+
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG) {
+ VN_HOLD(vp);
+ pbuf->vp = vp;
+ } else {
+ pbuf->vp = NULL;
+ }
+
+ pbuf->offset = SEGOP_GETOFFSET(seg, (caddr_t)pbuf->saddr);
+
+ pbuf->name_override = NULL;
+ if (lxpd != NULL) {
+ if (pbuf->saddr == lxpd->l_vdso) {
+ pbuf->name_override = "[vdso]";
+ } else if (pbuf->saddr == p->p_user.u_commpagep) {
+ pbuf->name_override = "[vvar]";
+ }
+ }
+
+ pbuf->next = NULL;
+ *print_tail = pbuf;
+ print_tail = &pbuf->next;
+ }
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ buf = kmem_alloc(buflen, KM_SLEEP);
+
+ /* print the data we've extracted */
+ pbuf = print_head;
+ while (pbuf != NULL) {
+ struct print_data *pbuf_next;
+ vattr_t vattr;
+
+ int maj = 0;
+ int min = 0;
+ ino_t inode = 0;
+
+ *buf = '\0';
+ if (pbuf->name_override != NULL) {
+ (void) strncpy(buf, pbuf->name_override, buflen);
+ } else if (pbuf->vp != NULL) {
+ vattr.va_mask = AT_FSID | AT_NODEID;
+ if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(),
+ NULL) == 0) {
+ maj = getmajor(vattr.va_fsid);
+ min = getminor(vattr.va_fsid);
+ inode = vattr.va_nodeid;
+ }
+ (void) vnodetopath(NULL, pbuf->vp, buf, buflen, CRED());
+ VN_RELE(pbuf->vp);
+ }
+
+ if (p->p_model == DATAMODEL_LP64) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08llx-%08llx %s %08llx %02x:%02x %llu%s%s\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode, *buf != '\0' ? " " : "", buf);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02x:%02x %llu%s%s\n",
+ (uint32_t)pbuf->saddr, (uint32_t)pbuf->eaddr,
+ pbuf->prot, (uint32_t)pbuf->offset, maj, min,
+ inode, *buf != '\0' ? " " : "", buf);
+ }
+
+ pbuf_next = pbuf->next;
+ kmem_free(pbuf, sizeof (*pbuf));
+ pbuf = pbuf_next;
+ }
+
+ kmem_free(buf, buflen);
+}
+
+/*
+ * Make mount entry look more like Linux. Non-zero return to skip it.
+ */
+static int
+lxpr_clean_mntent(char **mntpt, char **fstype, char **resource)
+{
+ if (strcmp(*mntpt, "/var/ld") == 0 ||
+ strcmp(*fstype, "objfs") == 0 ||
+ strcmp(*fstype, "mntfs") == 0 ||
+ strcmp(*fstype, "ctfs") == 0 ||
+ strncmp(*mntpt, "/native/", 8) == 0) {
+ return (1);
+ }
+
+ if (strcmp(*fstype, "tmpfs") == 0) {
+ *resource = "tmpfs";
+ } else if (strcmp(*fstype, "lx_proc") == 0) {
+ *resource = *fstype = "proc";
+ } else if (strcmp(*fstype, "lx_sysfs") == 0) {
+ *resource = *fstype = "sysfs";
+ } else if (strcmp(*fstype, "lx_devfs") == 0) {
+ *resource = *fstype = "devtmpfs";
+ } else if (strcmp(*fstype, "lx_cgroup") == 0) {
+ *resource = *fstype = "cgroup";
+ } else if (strcmp(*fstype, "lxautofs") == 0) {
+ *fstype = "autofs";
+ }
+
+ return (0);
+}
+
+
+typedef struct lxpr_mount_entry {
+ list_node_t lme_link;
+ uint_t lme_id;
+ uint_t lme_parent_id;
+ refstr_t *lme_mntpt;
+ refstr_t *lme_resource;
+ uint_t lme_flag;
+ int lme_fstype;
+ dev_t lme_dev;
+ boolean_t lme_force;
+} lxpr_mount_entry_t;
+
+static int lxpr_zfs_fstype = -1;
+
+#define LXPR_ROOT_MOUNT_ID 15
+
+static list_t *
+lxpr_enumerate_mounts(zone_t *zone)
+{
+ vfs_t *vfsp, *rvfsp, *vfslist;
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ list_t *result;
+ lxpr_mount_entry_t *lme;
+ lx_virt_disk_t *vd;
+ uint_t root_id, mount_id;
+ char tmppath[MAXPATHLEN];
+
+ result = kmem_alloc(sizeof (list_t), KM_SLEEP);
+ list_create(result, sizeof (lxpr_mount_entry_t),
+ offsetof(lxpr_mount_entry_t, lme_link));
+ /* use an arbitrary start value for the root mount_id */
+ root_id = 15;
+ mount_id = root_id + 1;
+
+ ASSERT(zone != global_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ vfs_list_read_lock();
+ vfsp = vfslist = zone->zone_vfslist;
+
+ /*
+ * If the zone has a root entry, it will be the first in the list.
+ * Conjure one up if needed.
+ */
+ if (vfslist == NULL || strcmp(refstr_value(vfsp->vfs_mntpt),
+ zone->zone_rootpath) != 0) {
+ rvfsp = zone->zone_rootvp->v_vfsp;
+ } else {
+ rvfsp = vfslist;
+ vfsp = vfslist->vfs_zone_next;
+ }
+
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = root_id;
+ lme->lme_parent_id = 0;
+ lme->lme_mntpt = refstr_alloc(zone->zone_rootpath);
+ lme->lme_flag = rvfsp->vfs_flag;
+ lme->lme_fstype = rvfsp->vfs_fstype;
+ lme->lme_force = B_TRUE;
+
+ lme->lme_resource = NULL;
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZFS_DS &&
+ vd->lxvd_real_dev == rvfsp->vfs_dev) {
+ (void) snprintf(tmppath, sizeof (tmppath),
+ "%sdev/%s", zone->zone_rootpath, vd->lxvd_name);
+ lme->lme_resource = refstr_alloc(tmppath);
+ lme->lme_dev = vd->lxvd_emul_dev;
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+ if (lme->lme_resource == NULL) {
+ lme->lme_resource = refstr_alloc(zone->zone_rootpath);
+ lme->lme_dev = rvfsp->vfs_dev;
+ }
+ list_insert_head(result, lme);
+
+ do {
+ if (vfsp == NULL) {
+ break;
+ }
+ /* Skip mounts we shouldn't show */
+ if ((vfsp->vfs_flag & VFS_NOMNTTAB) != 0) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = mount_id++;
+ lme->lme_parent_id = root_id;
+ lme->lme_mntpt = vfsp->vfs_mntpt;
+ refstr_hold(vfsp->vfs_mntpt);
+ lme->lme_flag = vfsp->vfs_flag;
+ lme->lme_fstype = vfsp->vfs_fstype;
+ lme->lme_force = B_FALSE;
+
+ lme->lme_resource = NULL;
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZFS_DS &&
+ vd->lxvd_real_dev == vfsp->vfs_dev) {
+ char vdev[MAXPATHLEN];
+
+ (void) snprintf(vdev, sizeof (vdev),
+ "%sdev/%s",
+ zone->zone_rootpath, vd->lxvd_name);
+ lme->lme_resource = refstr_alloc(vdev);
+ lme->lme_dev = vd->lxvd_emul_dev;
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+ if (lme->lme_resource == NULL) {
+ lme->lme_resource = vfsp->vfs_resource;
+ refstr_hold(vfsp->vfs_resource);
+ lme->lme_dev = vfsp->vfs_dev;
+ }
+ list_insert_tail(result, lme);
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ /* Add a single dummy entry for /native/usr */
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = mount_id++;
+ lme->lme_parent_id = root_id;
+ lme->lme_flag = VFS_RDONLY;
+ lme->lme_dev = makedevice(0, 1);
+ (void) snprintf(tmppath, sizeof (tmppath),
+ "%snative/usr", zone->zone_rootpath);
+ lme->lme_mntpt = refstr_alloc(tmppath);
+ lme->lme_resource = lme->lme_mntpt;
+ refstr_hold(lme->lme_mntpt);
+ if (lxpr_zfs_fstype == -1) {
+ vfssw_t *zfssw = vfs_getvfssw("zfs");
+ VERIFY(zfssw != NULL);
+ lxpr_zfs_fstype = ((uintptr_t)zfssw - (uintptr_t)vfssw) /
+ sizeof (vfssw[0]);
+ VERIFY(&vfssw[lxpr_zfs_fstype] == zfssw);
+ }
+ lme->lme_fstype = lxpr_zfs_fstype;
+ lme->lme_force = B_TRUE;
+ list_insert_tail(result, lme);
+
+ return (result);
+}
+
+/*
+ * lxpr_read_pid_mountinfo(): information about process mount points.
+ */
+static void
+lxpr_read_pid_mountinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ list_t *mounts;
+ lxpr_mount_entry_t *lme;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MOUNTINFO ||
+ lxpnp->lxpr_type == LXPR_PID_TID_MOUNTINFO);
+
+ mounts = lxpr_enumerate_mounts(zone);
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+ while (lme != NULL) {
+ char *resource, *mntpt, *fstype, *rwflag;
+ vnode_t *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(lme->lme_mntpt);
+ resource = (char *)refstr_value(lme->lme_resource);
+
+ if (mntpt == NULL || mntpt[0] == '\0') {
+ goto nextp;
+ }
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ goto nextp;
+ } else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+ }
+ } else {
+ resource = "none";
+ }
+
+ /* Make things look more like Linux. */
+ fstype = vfssw[lme->lme_fstype].vsw_name;
+ if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+ !lme->lme_force) {
+ goto nextp;
+ }
+ rwflag = ((lme->lme_flag & VFS_RDONLY) == 0) ? "rw" : "ro";
+
+ /*
+ * XXX parent ID is not tracked correctly here. Currently we
+ * always assume the parent ID is the root ID.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "%d %d %d:%d / %s %s - %s %s %s\n",
+ lme->lme_id, lme->lme_parent_id,
+ getmajor(lme->lme_dev), getminor(lme->lme_dev),
+ mntpt, rwflag, fstype, resource, rwflag);
+
+nextp:
+ refstr_rele(lme->lme_mntpt);
+ refstr_rele(lme->lme_resource);
+ kmem_free(lme, sizeof (lxpr_mount_entry_t));
+ lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+ }
+
+ list_destroy(mounts);
+ kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_pid_oom_scr_adj(): read oom_score_adj for process
+ */
+static void
+lxpr_read_pid_oom_scr_adj(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_OOM_SCR_ADJ ||
+ lxpnp->lxpr_type == LXPR_PID_TID_OOM_SCR_ADJ);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ lxpr_unlock(p);
+
+ /* always 0 */
+ lxpr_uiobuf_printf(uiobuf, "0\n");
+}
+
+/*
+ * lxpr_read_pid_personality(): read personality for process
+ */
+static void
+lxpr_read_pid_personality(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *lxpd;
+ unsigned int personality;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_PERSONALITY);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ if ((lxpd = ptolxproc(p)) != NULL) {
+ personality = lxpd->l_personality;
+ } else {
+ /* Report native processes as having the SunOS personality */
+ personality = LX_PER_SUNOS;
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%08x\n", personality);
+}
+
+/*
+ * lxpr_read_pid_statm(): memory status file
+ */
+static void
+lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ size_t vsize, rss;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATM ||
+ lxpnp->lxpr_type == LXPR_PID_TID_STATM);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ if (as != &kas) {
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = btopr(as->a_resvsize);
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ } else {
+ vsize = 0;
+ rss = 0;
+ }
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%lu %lu %lu %lu %lu %lu %lu\n",
+ vsize, rss, 0l, rss, 0l, 0l, 0l);
+}
+
+/*
+ * Look for either the main thread (lookup_id is 0) or the specified thread.
+ * If we're looking for the main thread but the proc does not have one, we
+ * fallback to using prchoose to get any thread available.
+ */
+static kthread_t *
+lxpr_get_thread(proc_t *p, uint_t lookup_id)
+{
+ kthread_t *t;
+ uint_t emul_tid;
+ lx_lwp_data_t *lwpd;
+ pid_t pid = p->p_pid;
+ pid_t init_pid = curproc->p_zone->zone_proc_initpid;
+ boolean_t branded = (p->p_brand == &lx_brand);
+
+ /* get specified thread */
+ if ((t = p->p_tlist) == NULL)
+ return (NULL);
+
+ do {
+ if (lookup_id == 0 && t->t_tid == 1) {
+ thread_lock(t);
+ return (t);
+ }
+
+ lwpd = ttolxlwp(t);
+ if (branded && lwpd != NULL) {
+ if (pid == init_pid && lookup_id == 1) {
+ emul_tid = t->t_tid;
+ } else {
+ emul_tid = lwpd->br_pid;
+ }
+ } else {
+ /*
+ * Make only the first (assumed to be main) thread
+ * visible for non-branded processes.
+ */
+ emul_tid = p->p_pid;
+ }
+ if (emul_tid == lookup_id) {
+ thread_lock(t);
+ return (t);
+ }
+ } while ((t = t->t_forw) != p->p_tlist);
+
+ if (lookup_id == 0)
+ return (prchoose(p));
+ return (NULL);
+}
+
+/*
+ * Lookup the real pid for procs 0 or 1.
+ */
+static pid_t
+get_real_pid(pid_t p)
+{
+ pid_t find_pid;
+
+ if (p == 1) {
+ find_pid = curproc->p_zone->zone_proc_initpid;
+ } else if (p == 0) {
+ find_pid = curproc->p_zone->zone_zsched->p_pid;
+ } else {
+ find_pid = p;
+ }
+
+ return (find_pid);
+}
+
+/*
+ * pid/tid common code to read status file
+ */
+static void
+lxpr_read_status_common(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf,
+ uint_t lookup_id)
+{
+ proc_t *p;
+ kthread_t *t;
+ user_t *up;
+ cred_t *cr;
+ const gid_t *groups;
+ struct as *as;
+ char *status;
+ pid_t pid, ppid;
+ k_sigset_t current, ignore, handle;
+ int i, lx_sig, lwpcnt, ngroups;
+ pid_t real_pid;
+ char buf_comm[MAXCOMLEN + 1];
+ rlim64_t fdlim;
+ size_t vsize = 0, nlocked = 0, rss = 0, stksize = 0;
+ boolean_t printsz = B_FALSE;
+
+ real_pid = get_real_pid(lxpnp->lxpr_pid);
+ p = lxpr_lock(real_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ pid = p->p_pid;
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's init
+ * process or if we're the zone's zsched the pid is 0.
+ */
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1;
+ ppid = 0; /* parent pid for init is 0 */
+ } else if (pid == curproc->p_zone->zone_zsched->p_pid) {
+ pid = 0; /* zsched is pid 0 */
+ ppid = 0; /* parent pid for zsched is itself */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP)
+ ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+ }
+
+ t = lxpr_get_thread(p, lookup_id);
+ if (t != NULL) {
+ switch (t->t_state) {
+ case TS_SLEEP:
+ status = "S (sleeping)";
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ status = "R (running)";
+ break;
+ case TS_ZOMB:
+ status = "Z (zombie)";
+ break;
+ case TS_STOPPED:
+ status = "T (stopped)";
+ break;
+ default:
+ status = "! (unknown)";
+ break;
+ }
+ thread_unlock(t);
+ } else {
+ if (lookup_id != 0) {
+ /* we can't find this specific thread */
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ lxpr_unlock(p);
+ return;
+ }
+
+ /*
+ * there is a hole in the exit code, where a proc can have
+ * no threads but it is yet to be flagged SZOMB. We will
+ * assume we are about to become a zombie
+ */
+ status = "Z (zombie)";
+ }
+
+ up = PTOU(p);
+ mutex_enter(&p->p_crlock);
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
+
+ (void) strlcpy(buf_comm, up->u_comm, sizeof (buf_comm));
+ fdlim = p->p_fno_ctl;
+ lwpcnt = p->p_lwpcnt;
+
+ /*
+ * Gather memory information
+ */
+ as = p->p_as;
+ if ((p->p_stat != SZOMB) && !(p->p_flag & (SSYS | SEXITING)) &&
+ (as != &kas)) {
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ nlocked = p->p_locked_mem;
+ stksize = p->p_stksize;
+ printsz = B_TRUE;
+ }
+
+ /*
+ * Gather signal information
+ */
+ sigemptyset(&current);
+ sigemptyset(&ignore);
+ sigemptyset(&handle);
+ for (i = 1; i < NSIG; i++) {
+ lx_sig = stol_signo[i];
+
+ if ((lx_sig > 0) && (lx_sig <= LX_NSIG)) {
+ if (sigismember(&p->p_sig, i))
+ sigaddset(&current, lx_sig);
+
+ if (up->u_signal[i - 1] == SIG_IGN)
+ sigaddset(&ignore, lx_sig);
+ else if (up->u_signal[i - 1] != SIG_DFL)
+ sigaddset(&handle, lx_sig);
+ }
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Name:\t%s\n"
+ "State:\t%s\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "TracerPid:\t%d\n"
+ "Uid:\t%u\t%u\t%u\t%u\n"
+ "Gid:\t%u\t%u\t%u\t%u\n"
+ "FDSize:\t%d\n"
+ "Groups:\t",
+ buf_comm,
+ status,
+ pid, /* thread group id - same as pid */
+ (lookup_id == 0) ? pid : lxpnp->lxpr_desc,
+ ppid,
+ 0,
+ crgetruid(cr), crgetuid(cr), crgetsuid(cr), crgetuid(cr),
+ crgetrgid(cr), crgetgid(cr), crgetsgid(cr), crgetgid(cr),
+ fdlim);
+ ngroups = crgetngroups(cr);
+ groups = crgetgroups(cr);
+ for (i = 0; i < ngroups; i++) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%u ",
+ groups[i]);
+ }
+ crfree(cr);
+ if (printsz) {
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB",
+ btok(vsize),
+ btok(nlocked),
+ ptok(rss),
+ 0l,
+ btok(stksize),
+ ptok(rss),
+ 0l);
+ }
+ lxpr_uiobuf_printf(uiobuf, "\nThreads:\t%u\n", lwpcnt);
+ lxpr_uiobuf_printf(uiobuf,
+ "SigPnd:\t%08x%08x\n"
+ "SigBlk:\t%08x%08x\n"
+ "SigIgn:\t%08x%08x\n"
+ "SigCgt:\t%08x%08x\n",
+ current.__sigbits[1], current.__sigbits[0],
+ 0, 0, /* signals blocked on per thread basis */
+ ignore.__sigbits[1], ignore.__sigbits[0],
+ handle.__sigbits[1], handle.__sigbits[0]);
+ /* Report only the full bounding set for now */
+ lxpr_uiobuf_printf(uiobuf,
+ "CapInh:\t%016x\n"
+ "CapPrm:\t%016x\n"
+ "CapEff:\t%016x\n"
+ "CapBnd:\t%016llx\n",
+ 0, 0, 0, 0x1fffffffffLL);
+}
+
+/*
+ * lxpr_read_pid_status(): status file
+ */
+static void
+lxpr_read_pid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATUS);
+
+ lxpr_read_status_common(lxpnp, uiobuf, 0);
+}
+
+/*
+ * lxpr_read_pid_tid_status(): status file
+ */
+static void
+lxpr_read_pid_tid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TID_STATUS);
+ lxpr_read_status_common(lxpnp, uiobuf, lxpnp->lxpr_desc);
+}
+
+/*
+ * Same logic as the lx devfs lxd_pts_devt_translator.
+ */
+static dev_t
+lxpr_xlate_pts_dev(dev_t dev)
+{
+ minor_t min = getminor(dev);
+ int lx_maj, lx_min;
+
+ lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+ lx_min = min % LX_MAXMIN;
+
+ return (LX_MAKEDEVICE(lx_maj, lx_min));
+}
+
+/*
+ * pid/tid common code to read stat file
+ */
+static void
+lxpr_read_stat_common(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf,
+ uint_t lookup_id)
+{
+ proc_t *p;
+ kthread_t *t;
+ struct as *as;
+ char stat;
+ pid_t pid, ppid, pgpid, spid;
+ gid_t psgid;
+ dev_t psdev;
+ size_t rss, vsize;
+ int nice, pri, lwpcnt;
+ caddr_t wchan, stackbase;
+ processorid_t cpu;
+ pid_t real_pid;
+ clock_t utime, stime, cutime, cstime, ticks, boottime;
+ char buf_comm[MAXCOMLEN + 1];
+ rlim64_t vmem_ctl;
+ int exit_signal = -1;
+
+ real_pid = get_real_pid(lxpnp->lxpr_pid);
+ p = lxpr_lock(real_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ /*
+ * Set Linux defaults if we're the zone's init process
+ */
+ pid = p->p_pid;
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1; /* PID for init */
+ ppid = 0; /* parent PID for init is 0 */
+ pgpid = 0; /* process group for init is 0 */
+ psgid = (gid_t)-1; /* credential GID for init is -1 */
+ spid = 0; /* session id for init is 0 */
+ psdev = 0; /* session device for init is 0 */
+ } else if (pid == curproc->p_zone->zone_zsched->p_pid) {
+ pid = 0; /* PID for zsched */
+ ppid = 0; /* parent PID for zsched is 0 */
+ pgpid = 0; /* process group for zsched is 0 */
+ psgid = (gid_t)-1; /* credential GID for zsched is -1 */
+ spid = 0; /* session id for zsched is 0 */
+ psdev = 0; /* session device for zsched is 0 */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP) ?
+ curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+
+ pgpid = p->p_pgrp;
+
+ mutex_enter(&p->p_splock);
+ mutex_enter(&p->p_sessp->s_lock);
+ spid = p->p_sessp->s_sid;
+ psdev = lxpr_xlate_pts_dev(p->p_sessp->s_dev);
+ if (p->p_sessp->s_cred)
+ psgid = crgetgid(p->p_sessp->s_cred);
+ else
+ psgid = crgetgid(p->p_cred);
+
+ mutex_exit(&p->p_sessp->s_lock);
+ mutex_exit(&p->p_splock);
+ }
+
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ stackbase = 0;
+ } else {
+ /* from prgetstackbase() */
+ stackbase = p->p_usrstack - p->p_stksize;
+ }
+
+ utime = stime = 0;
+ t = lxpr_get_thread(p, lookup_id);
+ if (t != NULL) {
+ klwp_t *lwp = ttolwp(t);
+ struct mstate *ms = &lwp->lwp_mstate;
+ hrtime_t utm, stm;
+
+ /*
+ * For field 38 (the exit signal), some apps explicitly use
+ * this field in a check to distinguish processes from threads,
+ * and assume only processes have a valid signal in this field!
+ */
+ if (t->t_tid == 1) {
+ lx_proc_data_t *lxpd = ptolxproc(p);
+
+ if (lxpd != NULL) {
+ exit_signal = lxpd->l_signal;
+ } else {
+ exit_signal = SIGCHLD;
+ }
+ }
+
+ switch (t->t_state) {
+ case TS_SLEEP:
+ stat = 'S';
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ stat = 'R';
+ break;
+ case TS_ZOMB:
+ stat = 'Z';
+ break;
+ case TS_STOPPED:
+ stat = 'T';
+ break;
+ default:
+ stat = '!';
+ break;
+ }
+
+ if (CL_DONICE(t, NULL, 0, &nice) != 0)
+ nice = 0;
+
+ pri = t->t_pri;
+ wchan = t->t_wchan;
+ cpu = t->t_cpu->cpu_id;
+
+ utm = ms->ms_acct[LMS_USER];
+ stm = ms->ms_acct[LMS_SYSTEM];
+
+ thread_unlock(t);
+
+ /* convert unscaled high-res time to nanoseconds */
+ scalehrtime(&utm);
+ scalehrtime(&stm);
+
+ /* Linux /proc expects these values in ticks */
+ utime = (clock_t)NSEC_TO_TICK(utm);
+ stime = (clock_t)NSEC_TO_TICK(stm);
+ } else {
+ if (lookup_id != 0) {
+ /* we can't find this specific thread */
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ lxpr_unlock(p);
+ return;
+ }
+
+ /* Only zombies have no threads */
+ stat = 'Z';
+ nice = 0;
+ pri = 0;
+ wchan = 0;
+ cpu = 0;
+ }
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ if (as != &kas) {
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ } else {
+ vsize = 0;
+ rss = 0;
+ }
+ mutex_enter(&p->p_lock);
+
+ if (lookup_id == 0) {
+ /* process */
+ utime = p->p_utime;
+ stime = p->p_stime;
+ } else {
+ /* tid: utime & stime for the thread set in block above */
+ /* EMPTY */
+ }
+ cutime = p->p_cutime;
+ cstime = p->p_cstime;
+ lwpcnt = p->p_lwpcnt;
+ vmem_ctl = p->p_vmem_ctl;
+ (void) strlcpy(buf_comm, p->p_user.u_comm, sizeof (buf_comm));
+ ticks = p->p_user.u_ticks; /* lbolt at process start */
+ /* adjust ticks to account for zone boot time */
+ boottime = LXPTOZ(lxpnp)->zone_zsched->p_user.u_ticks;
+ ticks -= boottime;
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%d " /* 1 */
+ "(%s) %c %d %d %d %d %d " /* 2-8 */
+ "%lu %lu %lu %lu %lu " /* 9-13 */
+ "%lu %lu %ld %ld " /* 14-17 */
+ "%d %d %d " /* 18-20 */
+ "%lu " /* 21 */
+ "%lu " /* 22 */
+ "%lu %ld %llu " /* 23-25 */
+ "%lu %lu %llu " /* 26-28 */
+ "%lu %lu " /* 29-30 */
+ "%lu %lu %lu %lu " /* 31-34 */
+ "%lu " /* 35 */
+ "%lu %lu " /* 36-37 */
+ "%d " /* 38 */
+ "%d" /* 39 */
+ "\n",
+ (lookup_id == 0) ? pid : lxpnp->lxpr_desc, /* 1 */
+ buf_comm, stat, ppid, pgpid, spid, psdev, psgid, /* 2-8 */
+ 0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
+ utime, stime, cutime, cstime, /* 14-17 */
+ pri, nice, lwpcnt, /* 18-20 */
+ 0l, /* itrealvalue (time before next SIGALRM) 21 */
+ ticks, /* 22 */
+ vsize, rss, vmem_ctl, /* 23-25 */
+ 0l, 0l, stackbase, /* startcode, endcode, startstack 26-28 */
+ 0l, 0l, /* kstkesp, kstkeip 29-30 */
+ 0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch 31-34 */
+ wchan, /* 35 */
+ 0l, 0l, /* nswap,cnswap 36-37 */
+ exit_signal, /* exit_signal 38 */
+ cpu /* 39 */);
+}
+
+/*
+ * lxpr_read_pid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STAT);
+
+ lxpr_read_stat_common(lxpnp, uiobuf, 0);
+}
+
+/*
+ * lxpr_read_pid_tid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_tid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TID_STAT);
+ lxpr_read_stat_common(lxpnp, uiobuf, lxpnp->lxpr_desc);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_arp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+struct lxpr_ifstat {
+ uint64_t rx_bytes;
+ uint64_t rx_packets;
+ uint64_t rx_errors;
+ uint64_t rx_drop;
+ uint64_t tx_bytes;
+ uint64_t tx_packets;
+ uint64_t tx_errors;
+ uint64_t tx_drop;
+ uint64_t collisions;
+ uint64_t rx_multicast;
+};
+
+static void *
+lxpr_kstat_read(kstat_t *kn, boolean_t byname, size_t *size, int *num)
+{
+ kstat_t *kp;
+ int i, nrec = 0;
+ size_t bufsize;
+ void *buf = NULL;
+
+ if (byname == B_TRUE) {
+ kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+ kn->ks_name, getzoneid());
+ } else {
+ kp = kstat_hold_bykid(kn->ks_kid, getzoneid());
+ }
+ if (kp == NULL) {
+ return (NULL);
+ }
+ if (kp->ks_flags & KSTAT_FLAG_INVALID) {
+ kstat_rele(kp);
+ return (NULL);
+ }
+
+ bufsize = kp->ks_data_size + 1;
+ kstat_rele(kp);
+
+ /*
+ * The kstat in question is released so that kmem_alloc(KM_SLEEP) is
+ * performed without it held. After the alloc, the kstat is reacquired
+ * and its size is checked again. If the buffer is no longer large
+ * enough, the alloc and check are repeated up to three times.
+ */
+ for (i = 0; i < 2; i++) {
+ buf = kmem_alloc(bufsize, KM_SLEEP);
+
+ /* Check if bufsize still appropriate */
+ if (byname == B_TRUE) {
+ kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+ kn->ks_name, getzoneid());
+ } else {
+ kp = kstat_hold_bykid(kn->ks_kid, getzoneid());
+ }
+ if (kp == NULL || kp->ks_flags & KSTAT_FLAG_INVALID) {
+ if (kp != NULL) {
+ kstat_rele(kp);
+ }
+ kmem_free(buf, bufsize);
+ return (NULL);
+ }
+ KSTAT_ENTER(kp);
+ (void) KSTAT_UPDATE(kp, KSTAT_READ);
+ if (bufsize < kp->ks_data_size) {
+ kmem_free(buf, bufsize);
+ buf = NULL;
+ bufsize = kp->ks_data_size + 1;
+ KSTAT_EXIT(kp);
+ kstat_rele(kp);
+ continue;
+ } else {
+ if (KSTAT_SNAPSHOT(kp, buf, KSTAT_READ) != 0) {
+ kmem_free(buf, bufsize);
+ buf = NULL;
+ }
+ nrec = kp->ks_ndata;
+ KSTAT_EXIT(kp);
+ kstat_rele(kp);
+ break;
+ }
+ }
+
+ if (buf != NULL) {
+ *size = bufsize;
+ *num = nrec;
+ }
+ return (buf);
+}
+
+static int
+lxpr_kstat_ifstat(kstat_t *kn, struct lxpr_ifstat *ifs)
+{
+ kstat_named_t *kp;
+ int i, num;
+ size_t size;
+
+ /*
+ * Search by name instead of by kid since there's a small window to
+ * race against kstats being added/removed.
+ */
+ bzero(ifs, sizeof (*ifs));
+ kp = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num);
+ if (kp == NULL)
+ return (-1);
+ for (i = 0; i < num; i++) {
+ if (strncmp(kp[i].name, "rbytes64", KSTAT_STRLEN) == 0)
+ ifs->rx_bytes = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "ipackets64", KSTAT_STRLEN) == 0)
+ ifs->rx_packets = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "ierrors", KSTAT_STRLEN) == 0)
+ ifs->rx_errors = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "norcvbuf", KSTAT_STRLEN) == 0)
+ ifs->rx_drop = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "multircv", KSTAT_STRLEN) == 0)
+ ifs->rx_multicast = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "obytes64", KSTAT_STRLEN) == 0)
+ ifs->tx_bytes = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "opackets64", KSTAT_STRLEN) == 0)
+ ifs->tx_packets = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "oerrors", KSTAT_STRLEN) == 0)
+ ifs->tx_errors = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "noxmtbuf", KSTAT_STRLEN) == 0)
+ ifs->tx_drop = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "collisions", KSTAT_STRLEN) == 0)
+ ifs->collisions = kp[i].value.ui32;
+ }
+ kmem_free(kp, size);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ kstat_t *ksr;
+ kstat_t ks0;
+ int i, nidx;
+ size_t sidx;
+ struct lxpr_ifstat ifs;
+
+ lxpr_uiobuf_printf(uiobuf, "Inter-| Receive "
+ " | Transmit\n");
+ lxpr_uiobuf_printf(uiobuf, " face |bytes packets errs drop fifo"
+ " frame compressed multicast|bytes packets errs drop fifo"
+ " colls carrier compressed\n");
+
+ ks0.ks_kid = 0;
+ ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx);
+ if (ksr == NULL)
+ return;
+
+ for (i = 1; i < nidx; i++) {
+ if (strncmp(ksr[i].ks_module, "link", KSTAT_STRLEN) == 0 ||
+ strncmp(ksr[i].ks_module, "lo", KSTAT_STRLEN) == 0) {
+ if (lxpr_kstat_ifstat(&ksr[i], &ifs) != 0)
+ continue;
+
+ /* Overwriting the name is ok in the local snapshot */
+ lx_ifname_convert(ksr[i].ks_name, LX_IF_FROMNATIVE);
+ lxpr_uiobuf_printf(uiobuf, "%6s: %7llu %7llu %4lu "
+ "%4lu %4u %5u %10u %9lu %8llu %7llu %4lu %4lu %4u "
+ "%5lu %7u %10u\n",
+ ksr[i].ks_name,
+ ifs.rx_bytes, ifs.rx_packets,
+ ifs.rx_errors, ifs.rx_drop,
+ 0, 0, 0, ifs.rx_multicast,
+ ifs.tx_bytes, ifs.tx_packets,
+ ifs.tx_errors, ifs.tx_drop,
+ 0, ifs.collisions, 0, 0);
+ }
+ }
+
+ kmem_free(ksr, sidx);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_inet6_out(const in6_addr_t *addr, char buf[33])
+{
+ const uint8_t *ip = addr->s6_addr;
+ char digits[] = "0123456789abcdef";
+ int i;
+ for (i = 0; i < 16; i++) {
+ buf[2 * i] = digits[ip[i] >> 4];
+ buf[2 * i + 1] = digits[ip[i] & 0xf];
+ }
+ buf[32] = '\0';
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_if_inet6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ ill_t *ill;
+ ipif_t *ipif;
+ ill_walk_context_t ctx;
+ char ifname[LIFNAMSIZ], ip6out[33];
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ ill = ILL_START_WALK_V6(&ctx, ipst);
+
+ for (; ill != NULL; ill = ill_next(&ctx, ill)) {
+ for (ipif = ill->ill_ipif; ipif != NULL;
+ ipif = ipif->ipif_next) {
+ uint_t index = ill->ill_phyint->phyint_ifindex;
+ int plen = ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
+ unsigned int scope = lx_ipv6_scope_convert(
+ &ipif->ipif_v6lcl_addr);
+ /* Always report PERMANENT flag */
+ int flag = 0x80;
+
+ (void) snprintf(ifname, LIFNAMSIZ, "%s", ill->ill_name);
+ lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+ lxpr_inet6_out(&ipif->ipif_v6lcl_addr, ip6out);
+
+ lxpr_uiobuf_printf(uiobuf, "%32s %02x %02x %02x %02x"
+ " %8s\n", ip6out, index, plen, scope, flag, ifname);
+ }
+ }
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_igmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_vif(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_format_route_ipv6(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+ uint32_t flags;
+ char name[IFNAMSIZ];
+ char ipv6addr[33];
+
+ lxpr_inet6_out(&ire->ire_addr_v6, ipv6addr);
+ lxpr_uiobuf_printf(uiobuf, "%s %02x ", ipv6addr,
+ ip_mask_to_plen_v6(&ire->ire_mask_v6));
+
+ /* punt on this for now */
+ lxpr_uiobuf_printf(uiobuf, "%s %02x ",
+ "00000000000000000000000000000000", 0);
+
+ lxpr_inet6_out(&ire->ire_gateway_addr_v6, ipv6addr);
+ lxpr_uiobuf_printf(uiobuf, "%s", ipv6addr);
+
+ flags = ire->ire_flags &
+ (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+ /* Linux's RTF_LOCAL equivalent */
+ if (ire->ire_metrics.iulp_local)
+ flags |= 0x80000000;
+
+ if (ire->ire_ill != NULL) {
+ ill_get_name(ire->ire_ill, name, sizeof (name));
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '\0';
+ }
+
+ lxpr_uiobuf_printf(uiobuf, " %08x %08x %08x %08x %8s\n",
+ 0, /* metric */
+ ire->ire_refcnt,
+ 0,
+ flags,
+ name);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ipv6_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ /*
+ * LX branded zones are expected to have exclusive IP stack, hence
+ * using ALL_ZONES as the zoneid filter.
+ */
+ ire_walk_v6(&lxpr_format_route_ipv6, uiobuf, ALL_ZONES, ipst);
+
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_netstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_raw(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+#define LXPR_SKIP_ROUTE(type) \
+ (((IRE_IF_CLONE | IRE_BROADCAST | IRE_MULTICAST | \
+ IRE_NOROUTE | IRE_LOOPBACK | IRE_LOCAL) & type) != 0)
+
+static void
+lxpr_format_route_ipv4(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+ uint32_t flags;
+ char name[IFNAMSIZ];
+ ill_t *ill;
+ ire_t *nire;
+ ipif_t *ipif;
+ ipaddr_t gateway;
+
+ if (LXPR_SKIP_ROUTE(ire->ire_type) || ire->ire_testhidden != 0)
+ return;
+
+ /* These route flags have direct Linux equivalents */
+ flags = ire->ire_flags &
+ (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+
+ /*
+ * Search for a suitable IRE for naming purposes.
+ * On Linux, the default route is typically associated with the
+ * interface used to access gateway. The default IRE on Illumos
+ * typically lacks an ill reference but its parent might have one.
+ */
+ nire = ire;
+ do {
+ ill = nire->ire_ill;
+ nire = nire->ire_dep_parent;
+ } while (ill == NULL && nire != NULL);
+ if (ill != NULL) {
+ ill_get_name(ill, name, sizeof (name));
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '*';
+ name[1] = '\0';
+ }
+
+ /*
+ * Linux suppresses the gateway address for directly connected
+ * interface networks. To emulate this behavior, we walk all addresses
+ * of a given route interface. If one matches the gateway, it is
+ * displayed as NULL.
+ */
+ gateway = ire->ire_gateway_addr;
+ if ((ill = ire->ire_ill) != NULL) {
+ for (ipif = ill->ill_ipif; ipif != NULL;
+ ipif = ipif->ipif_next) {
+ if (ipif->ipif_lcl_addr == gateway) {
+ gateway = 0;
+ break;
+ }
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
+ "%d\t%08X\t%d\t%u\t%u\n",
+ name,
+ ire->ire_addr,
+ gateway,
+ flags, 0, 0,
+ 0, /* priority */
+ ire->ire_mask,
+ 0, 0, /* mss, window */
+ ire->ire_metrics.iulp_rtt);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ lxpr_uiobuf_printf(uiobuf, "Iface\tDestination\tGateway \tFlags\t"
+ "RefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n");
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ /*
+ * LX branded zones are expected to have exclusive IP stack, hence
+ * using ALL_ZONES as the zoneid filter.
+ */
+ ire_walk_v4(&lxpr_format_route_ipv4, uiobuf, ALL_ZONES, ipst);
+
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rpc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rt_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_sockstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+typedef struct lxpr_snmp_table {
+ const char *lst_proto;
+ const char **lst_fields;
+} lxpr_snmp_table_t;
+
+static const char *lxpr_snmp_ip_fields[] = {
+ "forwarding", "defaultTTL", "inReceives", "inHdrErrors",
+ "inAddrErrors", "forwDatagrams", "inUnknownProtos", "inDiscards",
+ "inDelivers", "outRequests", "outDiscards", "outNoRoutes",
+ "reasmTimeout", "reasmReqds", "reasmOKs", "reasmFails", "fragOKs",
+ "fragFails", "fragCreates",
+ NULL
+};
+
+static const char *lxpr_snmp_icmp_fields[] = {
+ "inMsgs", "inErrors", "inCsumErrors", "inDestUnreachs", "inTimeExcds",
+ "inParmProbs", "inSrcQuenchs", "inRedirects", "inEchos", "inEchoReps",
+ "inTimestamps", "inTimestampReps", "inAddrMasks", "inAddrMaskReps",
+ "outMsgs", "outErrors", "outDestUnreachs", "outTimeExcds",
+ "outParmProbs", "outSrcQuenchs", "outRedirects", "outEchos",
+ "outEchoReps", "outTimestamps", "outTimestampReps", "outAddrMasks",
+ "outAddrMaskReps",
+ NULL
+};
+
+static const char *lxpr_snmp_tcp_fields[] = {
+ "rtoAlgorithm", "rtoMin", "rtoMax", "maxConn", "activeOpens",
+ "passiveOpens", "attemptFails", "estabResets", "currEstab", "inSegs",
+ "outSegs", "retransSegs", "inErrs", "outRsts", "inCsumErrors",
+ NULL
+};
+
+static const char *lxpr_snmp_udp_fields[] = {
+ "inDatagrams", "noPorts", "inErrors", "outDatagrams", "rcvbufErrors",
+ "sndbufErrors", "inCsumErrors",
+ NULL
+};
+
+static lxpr_snmp_table_t lxpr_snmp_ip = { "ip", lxpr_snmp_ip_fields };
+static lxpr_snmp_table_t lxpr_snmp_icmp = { "icmp", lxpr_snmp_icmp_fields };
+static lxpr_snmp_table_t lxpr_snmp_tcp = { "tcp", lxpr_snmp_tcp_fields };
+static lxpr_snmp_table_t lxpr_snmp_udp = { "udp", lxpr_snmp_udp_fields };
+
+static lxpr_snmp_table_t *lxpr_net_snmptab[] = {
+ &lxpr_snmp_ip,
+ &lxpr_snmp_icmp,
+ &lxpr_snmp_tcp,
+ &lxpr_snmp_udp,
+ NULL
+};
+
+static void
+lxpr_kstat_print_tab(lxpr_uiobuf_t *uiobuf, lxpr_snmp_table_t *table,
+ kstat_t *kn)
+{
+ kstat_named_t *klist;
+ char upname[KSTAT_STRLEN], upfield[KSTAT_STRLEN];
+ int i, j, num;
+ size_t size;
+
+ klist = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num);
+ if (klist == NULL)
+ return;
+
+ /* Print the header line, fields capitalized */
+ (void) strncpy(upname, table->lst_proto, KSTAT_STRLEN);
+ upname[0] = toupper(upname[0]);
+ lxpr_uiobuf_printf(uiobuf, "%s:", upname);
+ for (i = 0; table->lst_fields[i] != NULL; i++) {
+ (void) strncpy(upfield, table->lst_fields[i], KSTAT_STRLEN);
+ upfield[0] = toupper(upfield[0]);
+ lxpr_uiobuf_printf(uiobuf, " %s", upfield);
+ }
+ lxpr_uiobuf_printf(uiobuf, "\n%s:", upname);
+
+ /* Then loop back through to print the value line. */
+ for (i = 0; table->lst_fields[i] != NULL; i++) {
+ kstat_named_t *kpoint = NULL;
+ for (j = 0; j < num; j++) {
+ if (strncmp(klist[j].name, table->lst_fields[i],
+ KSTAT_STRLEN) == 0) {
+ kpoint = &klist[j];
+ break;
+ }
+ }
+ if (kpoint == NULL) {
+ /* Output 0 for unknown fields */
+ lxpr_uiobuf_printf(uiobuf, " 0");
+ } else {
+ switch (kpoint->data_type) {
+ case KSTAT_DATA_INT32:
+ lxpr_uiobuf_printf(uiobuf, " %d",
+ kpoint->value.i32);
+ break;
+ case KSTAT_DATA_UINT32:
+ lxpr_uiobuf_printf(uiobuf, " %u",
+ kpoint->value.ui32);
+ break;
+ case KSTAT_DATA_INT64:
+ lxpr_uiobuf_printf(uiobuf, " %ld",
+ kpoint->value.l);
+ break;
+ case KSTAT_DATA_UINT64:
+ lxpr_uiobuf_printf(uiobuf, " %lu",
+ kpoint->value.ul);
+ break;
+ }
+ }
+ }
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ kmem_free(klist, size);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_snmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ kstat_t *ksr;
+ kstat_t ks0;
+ lxpr_snmp_table_t **table = lxpr_net_snmptab;
+ int i, t, nidx;
+ size_t sidx;
+
+ ks0.ks_kid = 0;
+ ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx);
+ if (ksr == NULL)
+ return;
+
+ for (t = 0; table[t] != NULL; t++) {
+ for (i = 0; i < nidx; i++) {
+ if (strncmp(ksr[i].ks_class, "mib2", KSTAT_STRLEN) != 0)
+ continue;
+ if (strncmp(ksr[i].ks_name, table[t]->lst_proto,
+ KSTAT_STRLEN) == 0) {
+ lxpr_kstat_print_tab(uiobuf, table[t], &ksr[i]);
+ break;
+ }
+ }
+ }
+ kmem_free(ksr, sidx);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static int
+lxpr_convert_tcp_state(int st)
+{
+ /*
+ * Derived from the enum located in the Linux kernel sources:
+ * include/net/tcp_states.h
+ */
+ switch (st) {
+ case TCPS_ESTABLISHED:
+ return (1);
+ case TCPS_SYN_SENT:
+ return (2);
+ case TCPS_SYN_RCVD:
+ return (3);
+ case TCPS_FIN_WAIT_1:
+ return (4);
+ case TCPS_FIN_WAIT_2:
+ return (5);
+ case TCPS_TIME_WAIT:
+ return (6);
+ case TCPS_CLOSED:
+ return (7);
+ case TCPS_CLOSE_WAIT:
+ return (8);
+ case TCPS_LAST_ACK:
+ return (9);
+ case TCPS_LISTEN:
+ return (10);
+ case TCPS_CLOSING:
+ return (11);
+ default:
+ /* No translation for TCPS_IDLE, TCPS_BOUND or anything else */
+ return (0);
+ }
+}
+
+static void
+lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+ int i, sl = 0;
+ connf_t *connfp;
+ conn_t *connp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf, " sl local_address rem_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt uid timeout "
+ "inode\n");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " sl "
+ "local_address "
+ "remote_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt "
+ "uid timeout inode\n");
+ }
+ /*
+ * Due to differences between the Linux and illumos TCP
+ * implementations, some data will be omitted from the output here.
+ *
+ * Valid fields:
+ * - local_address
+ * - remote_address
+ * - st
+ * - tx_queue
+ * - rx_queue
+ * - uid
+ * - inode
+ *
+ * Omitted/invalid fields
+ * - tr
+ * - tm->when
+ * - retrnsmt
+ * - timeout
+ */
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+ connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+ connp = NULL;
+ while ((connp =
+ ipcl_get_next_conn(connfp, connp, IPCL_TCPCONN)) != NULL) {
+ tcp_t *tcp;
+ vattr_t attr;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
+ if (connp->conn_ipversion != ipver)
+ continue;
+ tcp = connp->conn_tcp;
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%4d: %08X:%04X %08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v4,
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v4,
+ ntohs(connp->conn_fport));
+ } else {
+ lxpr_uiobuf_printf(uiobuf, "%4d: "
+ "%08X%08X%08X%08X:%04X "
+ "%08X%08X%08X%08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v6.s6_addr32[0],
+ connp->conn_laddr_v6.s6_addr32[1],
+ connp->conn_laddr_v6.s6_addr32[2],
+ connp->conn_laddr_v6.s6_addr32[3],
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v6.s6_addr32[0],
+ connp->conn_faddr_v6.s6_addr32[1],
+ connp->conn_faddr_v6.s6_addr32[2],
+ connp->conn_faddr_v6.s6_addr32[3],
+ ntohs(connp->conn_fport));
+ }
+
+ /* fetch the simulated inode for the socket */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%02X %08X:%08X %02X:%08X %08X "
+ "%5u %8d %lu %d %p %u %u %u %u %d\n",
+ lxpr_convert_tcp_state(tcp->tcp_state),
+ tcp->tcp_rcv_cnt, tcp->tcp_unsent, /* rx/tx queue */
+ 0, 0, /* tr, when */
+ 0, /* per-connection rexmits aren't tracked today */
+ connp->conn_cred->cr_uid,
+ 0, /* timeout */
+ /* inode + more */
+ (ino_t)attr.va_nodeid, 0, NULL, 0, 0, 0, 0, 0);
+ }
+ }
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_tcp(uiobuf, IPV4_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_tcp(uiobuf, IPV6_VERSION);
+}
+
+static void
+lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+ int i, sl = 0;
+ connf_t *connfp;
+ conn_t *connp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf, " sl local_address rem_address"
+ " st tx_queue rx_queue tr tm->when retrnsmt uid"
+ " timeout inode ref pointer drops\n");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " sl "
+ "local_address "
+ "remote_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt "
+ "uid timeout inode ref pointer drops\n");
+ }
+ /*
+ * Due to differences between the Linux and illumos UDP
+ * implementations, some data will be omitted from the output here.
+ *
+ * Valid fields:
+ * - local_address
+ * - remote_address
+ * - st: limited
+ * - uid
+ *
+ * Omitted/invalid fields
+ * - tx_queue
+ * - rx_queue
+ * - tr
+ * - tm->when
+ * - retrnsmt
+ * - timeout
+ * - inode
+ */
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+ connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+ connp = NULL;
+ while ((connp =
+ ipcl_get_next_conn(connfp, connp, IPCL_UDPCONN)) != NULL) {
+ udp_t *udp;
+ int state = 0;
+ vattr_t attr;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
+ if (connp->conn_ipversion != ipver)
+ continue;
+ udp = connp->conn_udp;
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%4d: %08X:%04X %08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v4,
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v4,
+ ntohs(connp->conn_fport));
+ } else {
+ lxpr_uiobuf_printf(uiobuf, "%4d: "
+ "%08X%08X%08X%08X:%04X "
+ "%08X%08X%08X%08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v6.s6_addr32[0],
+ connp->conn_laddr_v6.s6_addr32[1],
+ connp->conn_laddr_v6.s6_addr32[2],
+ connp->conn_laddr_v6.s6_addr32[3],
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v6.s6_addr32[0],
+ connp->conn_faddr_v6.s6_addr32[1],
+ connp->conn_faddr_v6.s6_addr32[2],
+ connp->conn_faddr_v6.s6_addr32[3],
+ ntohs(connp->conn_fport));
+ }
+
+ switch (udp->udp_state) {
+ case TS_UNBND:
+ case TS_IDLE:
+ state = 7;
+ break;
+ case TS_DATA_XFER:
+ state = 1;
+ break;
+ }
+
+ /* fetch the simulated inode for the socket */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%02X %08X:%08X %02X:%08X %08X "
+ "%5u %8d %lu %d %p %d\n",
+ state,
+ 0, 0, /* rx/tx queue */
+ 0, 0, /* tr, when */
+ 0, /* retrans */
+ connp->conn_cred->cr_uid,
+ 0, /* timeout */
+ /* inode, ref, pointer, drops */
+ (ino_t)attr.va_nodeid, 0, NULL, 0);
+ }
+ }
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_udp(uiobuf, IPV4_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_udp(uiobuf, IPV6_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ sonode_t *so;
+ zoneid_t zoneid = getzoneid();
+
+ lxpr_uiobuf_printf(uiobuf, "Num RefCount Protocol Flags Type "
+ "St Inode Path\n");
+
+ mutex_enter(&socklist.sl_lock);
+ for (so = socklist.sl_list; so != NULL;
+ so = _SOTOTPI(so)->sti_next_so) {
+ vnode_t *vp = so->so_vnode;
+ vattr_t attr;
+ sotpi_info_t *sti;
+ const char *name = NULL;
+ int status = 0;
+ int type = 0;
+ int flags = 0;
+
+ /* Only process active sonodes in this zone */
+ if (so->so_count == 0 || so->so_zoneid != zoneid)
+ continue;
+
+ /*
+ * Grab the inode, if possible.
+ * This must be done before entering so_lock.
+ */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
+ mutex_enter(&so->so_lock);
+ sti = _SOTOTPI(so);
+
+ if (sti->sti_laddr_sa != NULL &&
+ sti->sti_laddr_len > 0) {
+ name = sti->sti_laddr_sa->sa_data;
+ } else if (sti->sti_faddr_sa != NULL &&
+ sti->sti_faddr_len > 0) {
+ name = sti->sti_faddr_sa->sa_data;
+ }
+
+ /*
+ * Derived from enum values in Linux kernel source:
+ * include/uapi/linux/net.h
+ */
+ if ((so->so_state & SS_ISDISCONNECTING) != 0) {
+ status = 4;
+ } else if ((so->so_state & SS_ISCONNECTING) != 0) {
+ status = 2;
+ } else if ((so->so_state & SS_ISCONNECTED) != 0) {
+ status = 3;
+ } else {
+ status = 1;
+ /* Add ACC flag for stream-type server sockets */
+ if (so->so_type != SOCK_DGRAM &&
+ sti->sti_laddr_sa != NULL)
+ flags |= 0x10000;
+ }
+
+ /* Convert to Linux type */
+ switch (so->so_type) {
+ case SOCK_DGRAM:
+ type = 2;
+ break;
+ case SOCK_SEQPACKET:
+ type = 5;
+ break;
+ default:
+ type = 1;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%p: %08X %08X %08X %04X %02X %5llu",
+ so,
+ so->so_count,
+ 0, /* proto, always 0 */
+ flags,
+ type,
+ status,
+ (ino_t)attr.va_nodeid);
+
+ /*
+ * Due to shortcomings in the abstract socket emulation, they
+ * cannot be properly represented here (as @<path>).
+ *
+ * This will be the case until they are better implemented.
+ */
+ if (name != NULL)
+ lxpr_uiobuf_printf(uiobuf, " %s\n", name);
+ else
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ mutex_exit(&so->so_lock);
+ }
+ mutex_exit(&socklist.sl_lock);
+}
+
+/*
+ * lxpr_read_kmsg(): read the contents of the kernel message queue. We
+ * translate this into the reception of console messages for this zone; each
+ * read copies out a single zone console message, or blocks until the next one
+ * is produced, unless we're open non-blocking, in which case we return after
+ * 1ms.
+ */
+
+#define LX_KMSG_PRI "<0>"
+
+/* ARGSUSED */
+static void
+lxpr_read_kmsg(lxpr_node_t *lxpnp, struct lxpr_uiobuf *uiobuf, ldi_handle_t lh)
+{
+ mblk_t *mp;
+ timestruc_t to;
+ timestruc_t *tp = NULL;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_KMSG);
+
+ if (lxpr_uiobuf_nonblock(uiobuf)) {
+ to.tv_sec = 0;
+ to.tv_nsec = 1000000; /* 1msec */
+ tp = &to;
+ }
+
+ if (ldi_getmsg(lh, &mp, tp) == 0) {
+ /*
+ * lx procfs doesn't like successive reads to the same file
+ * descriptor unless we do an explicit rewind each time.
+ */
+ lxpr_uiobuf_seek(uiobuf, 0);
+
+ lxpr_uiobuf_printf(uiobuf, "%s%s", LX_KMSG_PRI,
+ mp->b_cont->b_rptr);
+
+ freemsg(mp);
+ }
+}
+
+/*
+ * lxpr_read_loadavg(): read the contents of the "loadavg" file. We do just
+ * enough for uptime and other simple lxproc readers to work
+ */
+extern int nthread;
+
+static void
+lxpr_read_loadavg(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ulong_t avenrun1;
+ ulong_t avenrun5;
+ ulong_t avenrun15;
+ ulong_t avenrun1_cs;
+ ulong_t avenrun5_cs;
+ ulong_t avenrun15_cs;
+ int loadavg[3];
+ int *loadbuf;
+ cpupart_t *cp;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ uint_t nrunnable = 0;
+ rctl_qty_t nlwps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_LOADAVG);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Need to add up values over all CPU partitions. If pools are active,
+ * only report the values of the zone's partition, which by definition
+ * includes the current CPU.
+ */
+ if (pool_pset_enabled()) {
+ psetid_t psetid = zone_pset_get(curproc->p_zone);
+
+ ASSERT(curproc->p_zone != &zone0);
+ cp = CPU->cpu_part;
+
+ nrunnable = cp->cp_nrunning + cp->cp_nrunnable;
+ (void) cpupart_get_loadavg(psetid, &loadavg[0], 3);
+ loadbuf = &loadavg[0];
+ } else {
+ cp = cp_list_head;
+ do {
+ nrunnable += cp->cp_nrunning + cp->cp_nrunnable;
+ } while ((cp = cp->cp_next) != cp_list_head);
+
+ loadbuf = zone == global_zone ?
+ &avenrun[0] : zone->zone_avenrun;
+ }
+
+ /*
+ * If we're in the non-global zone, we'll report the total number of
+ * LWPs in the zone for the "nproc" parameter of /proc/loadavg,
+ * otherwise will just use nthread (which will include kernel threads,
+ * but should be good enough for lxproc).
+ */
+ nlwps = zone == global_zone ? nthread : zone->zone_nlwps;
+
+ mutex_exit(&cpu_lock);
+
+ avenrun1 = loadbuf[0] >> FSHIFT;
+ avenrun1_cs = ((loadbuf[0] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun5 = loadbuf[1] >> FSHIFT;
+ avenrun5_cs = ((loadbuf[1] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun15 = loadbuf[2] >> FSHIFT;
+ avenrun15_cs = ((loadbuf[2] & (FSCALE-1)) * 100) >> FSHIFT;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
+ avenrun1, avenrun1_cs,
+ avenrun5, avenrun5_cs,
+ avenrun15, avenrun15_cs,
+ nrunnable, nlwps, 0);
+}
+
+/*
+ * lxpr_read_meminfo(): read the contents of the "meminfo" file.
+ */
+static void
+lxpr_read_meminfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ int global = zone == global_zone;
+ long total_mem, free_mem, total_swap;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_MEMINFO);
+
+ if (global || zone->zone_phys_mem_ctl == UINT64_MAX) {
+ total_mem = physmem * PAGESIZE;
+ free_mem = freemem * PAGESIZE;
+ } else {
+ total_mem = zone->zone_phys_mem_ctl;
+ free_mem = zone->zone_phys_mem_ctl - zone->zone_phys_mem;
+ }
+
+ if (global || zone->zone_max_swap_ctl == UINT64_MAX) {
+ total_swap = k_anoninfo.ani_max * PAGESIZE;
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ total_swap = zone->zone_max_swap_ctl;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+
+ /*
+ * SwapFree
+ * On illumos we reserve swap up front, whereas on Linux they just
+ * wing it and kill a random process if they run out of backing store
+ * for virtual memory. Our swap reservation doesn't translate to that
+ * model, so just inform the caller that no swap is being used.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemShared: %8u kB\n"
+ "Buffers: %8u kB\n"
+ "Cached: %8u kB\n"
+ "SwapCached:%8u kB\n"
+ "Active: %8u kB\n"
+ "Inactive: %8u kB\n"
+ "HighTotal: %8u kB\n"
+ "HighFree: %8u kB\n"
+ "LowTotal: %8u kB\n"
+ "LowFree: %8u kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ btok(total_mem), /* MemTotal */
+ btok(free_mem), /* MemFree */
+ 0, /* MemShared */
+ 0, /* Buffers */
+ 0, /* Cached */
+ 0, /* SwapCached */
+ 0, /* Active */
+ 0, /* Inactive */
+ 0, /* HighTotal */
+ 0, /* HighFree */
+ btok(total_mem), /* LowTotal */
+ btok(free_mem), /* LowFree */
+ btok(total_swap), /* SwapTotal */
+ btok(total_swap)); /* SwapFree */
+}
+
+/*
+ * lxpr_read_mounts():
+ *
+ * Note: we currently also use this for /proc/{pid}/mounts since we don't
+ * yet support mount namespaces.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_mounts(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ list_t *mounts;
+ lxpr_mount_entry_t *lme;
+
+ mounts = lxpr_enumerate_mounts(zone);
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ lme = list_remove_head(mounts);
+ while (lme != NULL) {
+ char *resource, *mntpt, *fstype, *rwflag;
+ vnode_t *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(lme->lme_mntpt);
+ resource = (char *)refstr_value(lme->lme_resource);
+
+ if (mntpt == NULL || mntpt[0] == '\0') {
+ goto nextp;
+ }
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ goto nextp;
+ } else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+ }
+ } else {
+ resource = "none";
+ }
+
+ /* Make things look more like Linux. */
+ fstype = vfssw[lme->lme_fstype].vsw_name;
+ if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+ !lme->lme_force) {
+ goto nextp;
+ }
+ rwflag = ((lme->lme_flag & VFS_RDONLY) == 0) ? "rw" : "ro";
+
+ lxpr_uiobuf_printf(uiobuf, "%s %s %s %s 0 0\n",
+ resource, mntpt, fstype, rwflag);
+
+nextp:
+ refstr_rele(lme->lme_mntpt);
+ refstr_rele(lme->lme_resource);
+ kmem_free(lme, sizeof (lxpr_mount_entry_t));
+ lme = list_remove_head(mounts);
+ }
+
+ list_destroy(mounts);
+ kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_partitions():
+ *
+ * Over the years, /proc/partitions has been made considerably smaller -- to
+ * the point that it really is only major number, minor number, number of
+ * blocks (which we report as 0), and partition name.
+ *
+ * We support this because some things want to see it to make sense of
+ * /proc/diskstats, and also because "fdisk -l" and a few other things look
+ * here to find all disks on the system.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_partitions(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *lxzd;
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PARTITIONS);
+
+ lxpr_uiobuf_printf(uiobuf, "major minor #blocks name\n\n");
+
+ lxzd = ztolxzd(curproc->p_zone);
+ if (lxzd == NULL)
+ return;
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ lxpr_uiobuf_printf(uiobuf, "%4d %7d %10d %s\n",
+ getmajor(vd->lxvd_emul_dev), getminor(vd->lxvd_emul_dev),
+ 0, vd->lxvd_name);
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+}
+
+/*
+ * There aren't many actual devices inside a zone but we want to provide the
+ * major numbers for the pseudo devices that do exist, including our pts/ptm
+ * device, as well as the zvol virtual disk device. We simply hardcode the
+ * emulated major numbers that are used elsewhere in the code and that match
+ * the expected Linux major numbers. See lx devfs where some of the major
+ * numbers have no defined constants.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_devices(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_DEVICES);
+
+ lxpr_uiobuf_printf(uiobuf, "Character devices:\n");
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/tty\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/console\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/ptmx\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d ptm\n", LX_PTM_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d pts\n", LX_PTS_MAJOR_MIN);
+
+ lxpr_uiobuf_printf(uiobuf, "\nBlock devices:\n");
+ lxpr_uiobuf_printf(uiobuf, "%3d zvol\n", LX_MAJOR_DISK);
+}
+
+/*
+ * lxpr_read_diskstats():
+ *
+ * See the block comment above the per-device output-generating line for the
+ * details of the format.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_diskstats(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd;
+ kstat_t kn;
+ int num;
+ zone_vfs_kstat_t *kip;
+ size_t size;
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_DISKSTATS);
+
+ lxzd = ztolxzd(zone);
+ if (lxzd == NULL)
+ return;
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ /*
+ * Use the zone_vfs kstat, which is a superset of a kstat_io_t, since
+ * it tracks IO at the zone level.
+ */
+ (void) strlcpy(kn.ks_module, "zone_vfs", sizeof (kn.ks_module));
+ (void) strlcpy(kn.ks_name, zone->zone_name, sizeof (kn.ks_name));
+ kn.ks_instance = getzoneid();
+
+ kip = (zone_vfs_kstat_t *)lxpr_kstat_read(&kn, B_TRUE, &size, &num);
+ if (kip == NULL)
+ return;
+
+ if (size < sizeof (kstat_io_t)) {
+ kmem_free(kip, size);
+ return;
+ }
+
+ /*
+ * Because the zone vfs stats are tracked at the zone level we use
+ * the same kstat for the zone's virtual disk (the zpool) and any
+ * zvols that might also visible within the zone.
+ */
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ /*
+ * /proc/diskstats is defined to have one line of output for
+ * each block device, with each line containing the following
+ * 14 fields:
+ *
+ * 1 - major number
+ * 2 - minor mumber
+ * 3 - device name
+ * 4 - reads completed successfully
+ * 5 - reads merged
+ * 6 - sectors read
+ * 7 - time spent reading (ms)
+ * 8 - writes completed
+ * 9 - writes merged
+ * 10 - sectors written
+ * 11 - time spent writing (ms)
+ * 12 - I/Os currently in progress
+ * 13 - time spent doing I/Os (ms)
+ * 14 - weighted time spent doing I/Os (ms)
+ *
+ * One small hiccup: we don't actually keep track of time
+ * spent reading vs. time spent writing -- we keep track of
+ * time waiting vs. time actually performing I/O. While we
+ * could divide the total time by the I/O mix (making the
+ * obviously wrong assumption that I/O operations all take the
+ * same amount of time), this has the undesirable side-effect
+ * of moving backwards. Instead, we report the total time
+ * (read + write) for all three stats (read, write, total).
+ * This is also a lie of sorts, but it should be more
+ * immediately clear to the user that reads and writes are
+ * each being double-counted as the other.
+ *
+ * Since certain consumers interpret the major/minor numbers to
+ * infer device names, some translation is required to avoid
+ * output which results in totally unexpected results.
+ */
+
+ lxpr_uiobuf_printf(uiobuf, "%4d %7d %s ",
+ getmajor(vd->lxvd_emul_dev),
+ getminor(vd->lxvd_emul_dev),
+ vd->lxvd_name);
+
+ if (vd->lxvd_type == LXVD_ZFS_DS) {
+ /*
+ * Use the zone-wide vfs stats for any zfs datasets
+ * represented via virtual devices.
+ */
+#define KV(N) kip->zv_ ## N.value.ui64
+#define NS_PER_MS (uint64_t)(NANOSEC / MILLISEC)
+ lxpr_uiobuf_printf(uiobuf,
+ "%llu %llu %llu %llu "
+ "%llu %llu %llu %llu "
+ "%llu %llu %llu\n",
+ (uint64_t)KV(reads), 0LL,
+ KV(nread) / (uint64_t)LXPR_SECTOR_SIZE,
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (uint64_t)KV(writes), 0LL,
+ KV(nwritten) / (uint64_t)LXPR_SECTOR_SIZE,
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (uint64_t)(KV(rcnt) + KV(wcnt)),
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (KV(rlentime) + KV(wlentime)) / NS_PER_MS);
+#undef KV
+#undef NS_PER_MS
+ } else {
+ /*
+ * Report nearly-zeroed statistics for other devices.
+ *
+ * Since iostat will ignore devices which report no
+ * succesful reads or writes, a single read of one
+ * sector, taking 1ms, is reported.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "1 0 1 1 0 0 0 0 0 0 0\n");
+ }
+
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ kmem_free(kip, size);
+}
+
+/*
+ * lxpr_read_version(): read the contents of the "version" file.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_version(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *lxzd = ztolxzd(LXPTOZ(lxpnp));
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ char release[LX_KERN_RELEASE_MAX];
+ char version[LX_KERN_VERSION_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(release, lxzd->lxzd_kernel_release, sizeof (release));
+ (void) strlcpy(version, lxzd->lxzd_kernel_version, sizeof (version));
+ mutex_exit(&lxzd->lxzd_lock);
+
+ /* Use per-process overrides, if specified */
+ if (lxpd != NULL && lxpd->l_uname_release[0] != '\0') {
+ (void) strlcpy(release, lxpd->l_uname_release,
+ sizeof (release));
+ }
+ if (lxpd != NULL && lxpd->l_uname_version[0] != '\0') {
+ (void) strlcpy(version, lxpd->l_uname_version,
+ sizeof (version));
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%s version %s (%s version %d.%d.%d) %s\n",
+ LX_UNAME_SYSNAME, release,
+#if defined(__GNUC__)
+ "gcc", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__,
+#else
+ "cc", 1, 0, 0,
+#endif
+ version);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_vmstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /* Only count CPUs which are present and active. */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /*
+ * Needless to say, the metrics presented by vmstat are very specific
+ * to the internals of the Linux kernel. There is little per-zone
+ * information which can be translated in a meaningful way to fit the
+ * expected fields. For the time being, the output is kept sparse.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "pgpgin %lu\n"
+ "pgpgout %lu\n"
+ "pswpin %lu\n"
+ "pswpout %lu\n",
+ pgpgin_cum,
+ pgpgout_cum,
+ pgswapin_cum,
+ pgswapout_cum);
+}
+
+/*
+ * lxpr_read_stat(): read the contents of the "stat" file.
+ *
+ */
+/* ARGSUSED */
+static void
+lxpr_read_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t sys_cum = 0;
+ ulong_t user_cum = 0;
+ ulong_t irq_cum = 0;
+ ulong_t cpu_nrunnable_cum = 0;
+ ulong_t w_io_cum = 0;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+ ulong_t intr_cum = 0;
+ ulong_t pswitch_cum = 0;
+ ulong_t forks_cum = 0;
+ hrtime_t msnsecs[NCMSTATES];
+ /* is the emulated release > 2.4 */
+ boolean_t newer_than24 = lx_kern_release_cmp(LXPTOZ(lxpnp), "2.4") > 0;
+ zone_t *zone = LXPTOZ(lxpnp);
+ const char *fmtstr0, *fmtstr1;
+ /* temporary variable since scalehrtime modifies data in place */
+ hrtime_t tmptime;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_STAT);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ int i;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_cum += NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_cum += NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_cum += NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+
+ if (newer_than24) {
+ cpu_nrunnable_cum += cp->cpu_disp->disp_nrunnable;
+ w_io_cum += CPU_STATS(cp, sys.iowait);
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_cum += NSEC_TO_TICK(tmptime);
+ }
+ }
+
+ for (i = 0; i < PIL_MAX; i++)
+ intr_cum += CPU_STATS(cp, sys.intr[i]);
+
+ pswitch_cum += CPU_STATS(cp, sys.pswitch);
+ forks_cum += CPU_STATS(cp, sys.sysfork);
+ forks_cum += CPU_STATS(cp, sys.sysvfork);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ if (lx_kern_release_cmp(zone, "2.6.33") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.6.24") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.6.11") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.5.41") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0\n";
+ } else {
+ /* Note: we pass an unused param to these fmt strings */
+ fmtstr0 = "cpu %lu 0 %lu %lu\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu\n";
+ }
+
+ lxpr_uiobuf_printf(uiobuf, fmtstr0,
+ user_cum, sys_cum, idle_cum, irq_cum);
+
+ /* Do per processor stats */
+ do {
+ int i;
+
+ ulong_t idle_ticks;
+ ulong_t sys_ticks;
+ ulong_t user_ticks;
+ ulong_t irq_ticks = 0;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_ticks = NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_ticks = NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_ticks = NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_ticks += NSEC_TO_TICK(tmptime);
+ }
+
+ lxpr_uiobuf_printf(uiobuf, fmtstr1,
+ cp->cpu_id, user_ticks, sys_ticks, idle_ticks, irq_ticks);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ if (newer_than24) {
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n"
+ "procs_running %lu\n"
+ "procs_blocked %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ boot_time,
+ forks_cum,
+ cpu_nrunnable_cum,
+ w_io_cum);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ boot_time,
+ forks_cum);
+ }
+}
+
+/*
+ * lxpr_read_swaps():
+ *
+ * We don't support swap files or partitions, but some programs like to look
+ * here just to check we have some swap on the system, so we lie and show
+ * our entire swap cap as one swap partition. See lxpr_read_meminfo for an
+ * explanation on why we report 0 used swap.
+ *
+ * It is important to use formatting identical to the Linux implementation
+ * so that consumers do not break. See swap_show() in mm/swapfile.c.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_swaps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = curzone;
+ uint64_t totswap, usedswap;
+
+ if (zone == global_zone || zone->zone_max_swap_ctl == UINT64_MAX) {
+ totswap = (k_anoninfo.ani_max * PAGESIZE) >> 10;
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ /* Uses units of 1 kb (2^10). */
+ totswap = zone->zone_max_swap_ctl >> 10;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+ usedswap = 0;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
+ lxpr_uiobuf_printf(uiobuf, "%-40s%s\t%llu\t%llu\t%d\n",
+ "/dev/swap", "partition", totswap, usedswap, -1);
+}
+
+/*
+ * inotify tunables exported via /proc.
+ */
+extern int inotify_maxevents;
+extern int inotify_maxinstances;
+extern int inotify_maxwatches;
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxevents);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxinstances);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxwatches);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_caplcap(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_CAPLCAP);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", LX_CAP_MAX_VALID);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_corepatt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = curproc->p_zone;
+ struct core_globals *cg;
+ refstr_t *rp;
+ corectl_path_t *ccp;
+ char tr[MAXPATHLEN];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+ cg = zone_getspecific(core_zone_key, zone);
+ ASSERT(cg != NULL);
+
+ /* If core dumps are disabled, return an empty string. */
+ if ((cg->core_options & CC_PROCESS_PATH) == 0) {
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ ccp = cg->core_default_path;
+ mutex_enter(&ccp->ccp_mtx);
+ if ((rp = ccp->ccp_path) != NULL)
+ refstr_hold(rp);
+ mutex_exit(&ccp->ccp_mtx);
+
+ if (rp == NULL) {
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ bzero(tr, sizeof (tr));
+ if (lxpr_core_path_s2l(refstr_value(rp), tr, sizeof (tr)) != 0) {
+ refstr_rele(rp);
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ refstr_rele(rp);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", tr);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_hostname(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_HOSTNAME);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", uts_nodename());
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_msgmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_MSGMNI);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_msgmni,
+ curproc->p_zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_NGROUPS_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", ngroups_max);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_osrel(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *br_data;
+ char version[LX_KERN_VERSION_MAX];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_OSREL);
+ br_data = ztolxzd(curproc->p_zone);
+ if (curproc->p_zone->zone_brand == &lx_brand) {
+ mutex_enter(&br_data->lxzd_lock);
+ (void) strlcpy(version, br_data->lxzd_kernel_version,
+ sizeof (version));
+ mutex_exit(&br_data->lxzd_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%s\n", version);
+ } else {
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ }
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_pid_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_PID_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", maxpid);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ /*
+ * This file isn't documented on the Linux proc(5) man page but
+ * according to the blog of the author of systemd/journald (the
+ * consumer), he says:
+ * boot_id: A random ID that is regenerated on each boot. As such it
+ * can be used to identify the local machine's current boot. It's
+ * universally available on any recent Linux kernel. It's a good and
+ * safe choice if you need to identify a specific boot on a specific
+ * booted kernel.
+ *
+ * We'll just generate a random ID if necessary. On Linux the format
+ * appears to resemble a uuid but since it is not documented to be a
+ * uuid, we don't worry about that.
+ */
+ lx_zone_data_t *br_data;
+ char bootid[LX_BOOTID_LEN];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_BOOTID);
+
+ if (curproc->p_zone->zone_brand != &lx_brand) {
+ lxpr_uiobuf_printf(uiobuf, "0\n");
+ return;
+ }
+
+ br_data = ztolxzd(curproc->p_zone);
+ mutex_enter(&br_data->lxzd_lock);
+ if (br_data->lxzd_bootid[0] == '\0') {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ u_longlong_t n;
+ char s[32];
+
+ (void) random_get_bytes((uint8_t *)&n, sizeof (n));
+ switch (i) {
+ case 0: (void) snprintf(s, sizeof (s), "%08llx", n);
+ s[8] = '\0';
+ break;
+ case 4: (void) snprintf(s, sizeof (s), "%012llx", n);
+ s[12] = '\0';
+ break;
+ default: (void) snprintf(s, sizeof (s), "%04llx", n);
+ s[4] = '\0';
+ break;
+ }
+ if (i > 0)
+ (void) strlcat(br_data->lxzd_bootid, "-",
+ sizeof (br_data->lxzd_bootid));
+ (void) strlcat(br_data->lxzd_bootid, s,
+ sizeof (br_data->lxzd_bootid));
+ }
+ }
+ (void) strlcpy(bootid, br_data->lxzd_bootid, sizeof (bootid));
+ mutex_exit(&br_data->lxzd_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%s\n", bootid);
+
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_sem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *pp = curproc;
+ rctl_qty_t vmsl, vopm, vmni, vmns;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SEM);
+
+ mutex_enter(&pp->p_lock);
+ vmsl = rctl_enforced_value(rc_process_semmsl, pp->p_rctls, pp);
+ vopm = rctl_enforced_value(rc_process_semopm, pp->p_rctls, pp);
+ vmni = rctl_enforced_value(rc_zone_semmni, pp->p_zone->zone_rctls, pp);
+ mutex_exit(&pp->p_lock);
+ vmns = vmsl * vmni;
+ if (vmns < vmsl || vmns < vmni) {
+ vmns = ULLONG_MAX;
+ }
+ /*
+ * Format: semmsl semmns semopm semmni
+ * - semmsl: Limit semaphores in a sempahore set.
+ * - semmns: Limit semaphores in all semaphore sets
+ * - semopm: Limit operations in a single semop call
+ * - semmni: Limit number of semaphore sets
+ */
+ lxpr_uiobuf_printf(uiobuf, "%llu\t%llu\t%llu\t%llu\n",
+ vmsl, vmns, vopm, vmni);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmall(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMALL);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmax,
+ curproc->p_zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ /* value is in pages */
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)btop(val));
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmmax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMAX);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmax,
+ curproc->p_zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ if (val > FOURGB)
+ val = FOURGB;
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMNI);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmni,
+ curproc->p_zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ if (val > FOURGB)
+ val = FOURGB;
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_threads_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_THREADS_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", curproc->p_zone->zone_nlwps_ctl);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_core_somaxc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_printf(uiobuf, "%d\n", SOMAXCONN);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_conn_req_max_q);
+ netstack_rele(ns);
+}
+
+/*
+ * ip_local_port_range
+ *
+ * The low & high port number range.
+ * integers; default: 32768 61000
+ *
+ * illumos: tcp_smallest_anon_port & tcp_largest_anon_port
+ * Not in tcp(7p) man page.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\t%d\n",
+ tcps->tcps_smallest_anon_port, tcps->tcps_largest_anon_port);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_fin_timeout
+ *
+ * This specifies how many seconds to wait for a final FIN packet before the
+ * socket is forcibly closed. This is strictly a violation of the TCP
+ * specification, but required to prevent denial-of-service attacks.
+ * integer; default: 60;
+ *
+ * illumos: tcp_fin_wait_2_flush_interval
+ * Not in tcp(7p) man page but see comment in uts/common/inet/tcp/tcp_input.c
+ * in the tcp_input_data() function on the use of tcp_fin_wait_2_flush_interval.
+ * The value is in milliseconds.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ tcps->tcps_fin_wait_2_flush_interval / 1000);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_intvl
+ *
+ * The number of seconds between TCP keep-alive probes. default: 75
+ * Linux retries tcp_keepalive_probes (9) times before timing out.
+ *
+ * illumos:
+ * We have tcp_ka_rinterval but there is no corresponding tcps_* tunable for
+ * this. The closest is tcps_keepalive_abort_interval which specifies the
+ * time threshold for aborting a TCP connection in milliseconds. Linux retries
+ * 9 times (giving a total of 11.25 minutes) so we emulate this by dividing out
+ * tcps_keepalive_abort_interval by 9.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_keepalive_abort_interval / 1000) / 9);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_time
+ *
+ * The number of seconds a connection needs to be idle before TCP begins
+ * sending out keep-alive probes. The default value is 7200 seconds (2 hours).
+ *
+ * illumos: tcp_keepalive_interval
+ * The interval for sending out the first probe in milliseconds. The default is
+ * two hours.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_keepalive_interval / 1000));
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_sack
+ *
+ * Enable RFC 2018 TCP Selective Acknowledgements. Boolean, default: enabled
+ *
+ * illumos: tcp_sack_permitted
+ * tcp_sack_permitted 0 == disabled, 1 == no initiate but accept,
+ * 2 == initiate and accept. default is 2.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_sack_permitted == 0 ? 0 : 1));
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_window_scaling
+ *
+ * RFC 1323 TCP window scaling. This feature allows the use of a large window
+ * (> 64K) on a TCP connection. Boolean; default: enabled
+ *
+ * illumos: tcp_wscale_always
+ * tcp_wscale_always is set to 1, the window scale option will always be
+ * set when connecting to a remote system. If tcp_wscale_always is 0, the
+ * window scale option will be set only if the user has requested a send or
+ * receive window larger than 64K. The default value of is 1.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_wscale_always);
+ netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MAX_MAP_CNT);
+ /* We don't limit mappings, just say we have a large limit. */
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 16777215);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_minfr_kb(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MINFR_KB);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_nhpages(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_NHUGEP);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_OVERCOMMIT_MEM);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_swappiness(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_SWAPPINESS);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/*
+ * lxpr_read_uptime(): read the contents of the "uptime" file.
+ *
+ * format is: "%.2lf, %.2lf",uptime_secs, idle_secs
+ * Use fixed point arithmetic to get 2 decimal places
+ */
+/* ARGSUSED */
+static void
+lxpr_read_uptime(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t cpu_count = 0;
+ ulong_t idle_s;
+ ulong_t idle_cs;
+ ulong_t up_s;
+ ulong_t up_cs;
+ hrtime_t birthtime;
+ hrtime_t centi_sec = 10000000; /* 10^7 */
+
+ ASSERT(lxpnp->lxpr_type == LXPR_UPTIME);
+
+ /* Calculate cumulative stats */
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_idle);
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_wait);
+ cpu_count += 1;
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /* Getting the Zone zsched process startup time */
+ birthtime = LXPTOZ(lxpnp)->zone_zsched->p_mstart;
+ up_cs = (gethrtime() - birthtime) / centi_sec;
+ up_s = up_cs / 100;
+ up_cs %= 100;
+
+ ASSERT(cpu_count > 0);
+ idle_cum /= cpu_count;
+ idle_s = idle_cum / hz;
+ idle_cs = idle_cum % hz;
+ idle_cs *= 100;
+ idle_cs /= hz;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d\n", up_s, up_cs, idle_s, idle_cs);
+}
+
+/*
+ * Report a list of each cgroup subsystem supported by our emulated cgroup fs.
+ * This needs to exist for systemd to run but for now we don't report any
+ * cgroup subsystems as being installed. The commented example below shows
+ * how to print a subsystem entry.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_cgroups(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+ "#subsys_name", "hierarchy", "num_cgroups", "enabled");
+
+ /*
+ * lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+ * "cpu,cpuacct", "2", "1", "1");
+ */
+}
+
+/*
+ * Report the zone boot arguments.
+ */
+static void
+lxpr_read_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", zone->zone_bootargs);
+}
+
+
+typedef enum {
+ LXCS_ALWAYS = 0,
+ LXCS_CPUID1_ECX,
+ LXCS_CPUID1_EDX,
+ LXCS_CPUID7_EBX,
+ LXCS_CPUIDD1_EAX,
+ LXCS_CPUIDX1_ECX,
+ LXCS_CPUIDX1_EDX,
+ LXCS_REG_MAX
+} lx_cpuinfo_source_t;
+
+typedef struct {
+ lx_cpuinfo_source_t lxcm_source;
+ uint32_t lxcm_flag;
+ const char *lxcm_name;
+} lx_cpuinfo_mapping_t;
+
+/*
+ * This listing is derived from the X86_FEATURE flags data in the Linux kernel.
+ * Some entries are missing detectino routines. They remain in the list,
+ * although commented out, to preserve proper order should they be fixed later.
+ */
+lx_cpuinfo_mapping_t lx_cpuinfo_mappings[] = {
+ /* CPUID EDX: */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_FPU, "fpu" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_VME, "vme" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_DE, "de" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE, "pse" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_TSC, "tsc" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MSR, "msr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAE, "pae" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCE, "mce" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CX8, "cx8" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_APIC, "apic" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SEP, "sep" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MTRR, "mtrr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PGE, "pge" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCA, "mca" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CMOV, "cmov" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAT, "pat" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE36, "pse36" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSN, "pn" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CLFSH, "clflush" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_DS, "dts" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_ACPI, "acpi" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MMX, "mmx" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_FXSR, "fxsr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE, "sse" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE2, "sse2" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SS, "ss" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_HTT, "ht" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_TM, "tm" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PBE, "pbe" },
+
+ /* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+#if defined(__amd64)
+ { LXCS_ALWAYS, 1, "syscall" },
+#endif
+ /* Present in the Linux listing but not in recent AMD docs: "mp" */
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_NX, "nx" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_MMXamd, "mmxext" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_FFXSR, "fxsr_opt" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_1GPG, "pdpe1gb" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_TSCP, "rdtscp" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_LM, "lm" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNowx, "3dnowext" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNow, "3dnow" },
+
+ /* CPUID ECX: */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE3, "pni" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCLMULQDQ, "pclmulqdq" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DTES64, "dtes64" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_MON, "monitor" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DSCPL, "ds_cpl" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_VMX, "vmx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SMX, "smx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_EST, "est" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_TM2, "tm2" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSSE3, "ssse3" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_CID, "cid" },
+ { LXCS_CPUID1_ECX, 0x00000800, "sdbg" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_FMA, "fma" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_CX16, "cx16" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_ETPRD, "xtpr" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PDCM, "pdcm" },
+ /* reserved */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCID, "pcid" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DCA, "dca" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_1, "sse4_1" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_2, "sse4_2" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_X2APIC, "x2apic" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_MOVBE, "movbe" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_POPCNT, "popcnt" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_TSCDL, "tsc_deadline_timer" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_AES, "aes" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_XSAVE, "xsave" },
+ /* osxsave */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_AVX, "avx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_F16C, "f16c" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_RDRAND, "rdrand" },
+ /* not used */
+
+ /*
+ * Other features, Linux-defined mapping
+ * This range is used for feature bits which conflict or are synthesized
+ * Skipped:
+ * "recovery",
+ * "longrun",
+ * "lrti",
+ * "cxmmx",
+ * "k6_mtrr",
+ * "cyrix_arr",
+ * "centaur_mcr",
+ * "constant_tsc",
+ * "up",
+ * "arch_perfmon",
+ * "pebs",
+ * "bts",
+ * "rep_good",
+ * "nopl",
+ * "xtopology",
+ * "tsc_reliable",
+ * "nonstop_tsc",
+ * "extd_apicid",
+ * "amd_dcm",
+ * "aperfmperf",
+ * "eagerfpu",
+ * "nonstop_tsc_s3",
+ *
+ * "hypervisor",
+ * "rng",
+ * "rng_en",
+ * "ace",
+ * "ace_en",
+ * "ace2",
+ * "ace2_en",
+ * "phe",
+ * "phe_en",
+ * "pmm",
+ * "pmm_en",
+ */
+
+ /*
+ * More extended AMD flags: CPUID level 0x80000001, ecx, word 6
+ */
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_AHF64, "lahf_lm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CMP_LGCY, "cmp_legacy" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SVM, "svm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_EAS, "extapic" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CR8D, "cr8_legacy" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LZCNT, "abm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SSE4A, "sse4a" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_MAS, "misalignsse" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_3DNP, "3dnowprefetch" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_OSVW, "osvw" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_IBS, "ibs" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SSE5, "xop" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SKINIT, "skinit" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_WDT, "wdt" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LWP, "lwp" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_FMA4, "fma4" },
+ { LXCS_CPUIDX1_ECX, 0x00020000, "tce" },
+
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_NIDMSR, "nodeid_msr" },
+
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TBM, "tbm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TOPOEXT, "topoext" },
+ { LXCS_CPUIDX1_ECX, 0x00800000, "perfctr_core" },
+ { LXCS_CPUIDX1_ECX, 0x01000000, "perfctr_nb" },
+ { LXCS_CPUIDX1_ECX, 0x02000000, "bpext" },
+ { LXCS_CPUIDX1_ECX, 0x04000000, "perfctr_l2" },
+ { LXCS_CPUIDX1_ECX, 0x08000000, "mwaitx" },
+
+ /*
+ * Aux flags and virt bits.
+ * Skipped:
+ * "cpb",
+ * "epb",
+ * "hw_pstate",
+ * "proc_feedback",
+ * "intel_pt",
+ * "tpr_shadow",
+ * "vnmi",
+ * "flexpriority",
+ * "ept",
+ * "vpid",
+ * "vmmcall",
+ */
+
+ /*
+ * Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9
+ */
+ { LXCS_CPUID7_EBX, 0x00000001, "fsgsbase" },
+ { LXCS_CPUID7_EBX, 0x00000002, "tsc_adjust" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI1, "bmi1" },
+ { LXCS_CPUID7_EBX, 0x00000010, "hle" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX2, "avx2" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMEP, "smep" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI2, "bmi2" },
+ { LXCS_CPUID7_EBX, 0x00000200, "erms" },
+ { LXCS_CPUID7_EBX, 0x00000400, "invpcid" },
+ { LXCS_CPUID7_EBX, 0x00000800, "rtm" },
+ { LXCS_CPUID7_EBX, 0x00000000, "cqm" },
+ { LXCS_CPUID7_EBX, 0x00004000, "mpx" },
+ { LXCS_CPUID7_EBX, 0x00010000, "avx512f" },
+
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_RDSEED, "rdseed" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_ADX, "adx" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMAP, "smap" },
+
+ { LXCS_CPUID7_EBX, 0x00400000, "pcommit" },
+ { LXCS_CPUID7_EBX, 0x00800000, "clflushopt" },
+ { LXCS_CPUID7_EBX, 0x01000000, "clwb" },
+
+ { LXCS_CPUID7_EBX, 0x04000000, "avx512pf" },
+ { LXCS_CPUID7_EBX, 0x08000000, "avx512er" },
+ { LXCS_CPUID7_EBX, 0x10000000, "avx512cd" },
+ { LXCS_CPUID7_EBX, 0x20000000, "sha_ni" },
+
+ /*
+ * Extended state features, CPUID level 0x0000000d:1 (eax)
+ */
+ { LXCS_CPUIDD1_EAX, 0x00000001, "xsaveopt" },
+ { LXCS_CPUIDD1_EAX, 0x00000002, "xsavec" },
+ { LXCS_CPUIDD1_EAX, 0x00000004, "xgetbv1" },
+ { LXCS_CPUIDD1_EAX, 0x00000008, "xsaves" },
+
+ /*
+ * Skipped:
+ * "cqm_llc",
+ * "cqm_occup_llc",
+ * "clzero",
+ */
+
+ /*
+ * Thermal and Power Management Leaf, CPUID level 0x00000006 (eax)
+ * Skipped:
+ * "dtherm",
+ * "ida",
+ * "arat",
+ * "pln",
+ * "pts",
+ * "hwp",
+ * "hwp_notify",
+ * "hwp_act_window",
+ * "hwp_epp",
+ * "hwp_pkg_req",
+ */
+
+ /*
+ * AMD SVM Feature Identification, CPUID level 0x8000000a (edx)
+ * Skipped:
+ * "npt",
+ * "lbrv",
+ * "svm_lock",
+ * "nrip_save",
+ * "tsc_scale",
+ * "vmcb_clean",
+ * "flushbyasid",
+ * "decodeassists",
+ * "pausefilter",
+ * "pfthreshold",
+ */
+};
+
+#define LX_CPUINFO_MAPPING_MAX \
+ (sizeof (lx_cpuinfo_mappings) / sizeof (lx_cpuinfo_mappings[0]))
+
+/* ARGSUSED */
+static void
+lxpr_read_cpuinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ int i;
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ char brandstr[CPU_IDSTRLEN];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_CPUINFO);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ struct cpuid_regs cpr;
+ uint32_t maxeax, xmaxeax, cpuid_res[LXCS_REG_MAX] = { 0 };
+
+ cpr.cp_eax = 0;
+ maxeax = cpuid_insn(cp, &cpr);
+ cpr.cp_eax = 0x80000000;
+ xmaxeax = cpuid_insn(cp, &cpr);
+
+ cpuid_res[LXCS_ALWAYS] = 1;
+ if (maxeax >= 1) {
+ cpr.cp_eax = 1;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUID1_ECX] = cpr.cp_ecx;
+ cpuid_res[LXCS_CPUID1_EDX] = cpr.cp_edx;
+ }
+ if (maxeax >= 7) {
+ cpr.cp_eax = 7;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUID7_EBX] = cpr.cp_ebx;
+ }
+ if (maxeax >= 0xd) {
+ cpr.cp_eax = 0xd;
+ cpr.cp_ecx = 1;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUIDD1_EAX] = cpr.cp_eax;
+ }
+ if (xmaxeax >= 0x80000001) {
+ cpr.cp_eax = 0x80000001;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUIDX1_ECX] = cpr.cp_ecx;
+ cpuid_res[LXCS_CPUIDX1_EDX] = cpr.cp_edx;
+ }
+
+ (void) cpuid_getbrandstr(cp, brandstr, CPU_IDSTRLEN);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "processor\t: %d\n"
+ "vendor_id\t: %s\n"
+ "cpu family\t: %d\n"
+ "model\t\t: %d\n"
+ "model name\t: %s\n"
+ "stepping\t: %d\n"
+ "cpu MHz\t\t: %u.%03u\n",
+ cp->cpu_id, cpuid_getvendorstr(cp), cpuid_getfamily(cp),
+ cpuid_getmodel(cp), brandstr, cpuid_getstep(cp),
+ (uint32_t)(cpu_freq_hz / 1000000),
+ ((uint32_t)(cpu_freq_hz / 1000)) % 1000);
+
+ lxpr_uiobuf_printf(uiobuf, "cache size\t: %u KB\n",
+ getl2cacheinfo(cp, NULL, NULL, NULL) / 1024);
+
+ if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
+ /*
+ * 'siblings' is used for HT-style threads
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "physical id\t: %lu\n"
+ "siblings\t: %u\n",
+ pg_plat_hw_instance_id(cp, PGHW_CHIP),
+ cpuid_get_ncpu_per_chip(cp));
+ }
+
+ /*
+ * Since we're relatively picky about running on older hardware,
+ * we can be somewhat cavalier about the answers to these ones.
+ *
+ * In fact, given the hardware we support, we just say:
+ *
+ * fdiv_bug : no (if we're on a 64-bit kernel)
+ * hlt_bug : no
+ * f00f_bug : no
+ * coma_bug : no
+ * wp : yes (write protect in supervsr mode)
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "fdiv_bug\t: %s\n"
+ "hlt_bug \t: no\n"
+ "f00f_bug\t: no\n"
+ "coma_bug\t: no\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
+ "flags\t\t:",
+#if defined(__i386)
+ fpu_pentium_fdivbug ? "yes" : "no",
+#else
+ "no",
+#endif /* __i386 */
+ fpu_exists ? "yes" : "no", fpu_exists ? "yes" : "no",
+ maxeax);
+
+ /* Print CPUID feature flags */
+ for (i = 0; i < LX_CPUINFO_MAPPING_MAX; i++) {
+ lx_cpuinfo_mapping_t *lxm = &lx_cpuinfo_mappings[i];
+
+ ASSERT(lxm->lxcm_source < LXCS_REG_MAX);
+ if (cpuid_res[lxm->lxcm_source] & lxm->lxcm_flag) {
+ lxpr_uiobuf_printf(uiobuf, " %s",
+ lxm->lxcm_name);
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "\n\n");
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FD_FD);
+ lxpr_uiobuf_seterr(uiobuf, EFAULT);
+}
+
+/*
+ * Report a list of file systems loaded in the kernel. We only report the ones
+ * which we support and which may be checked by various components to see if
+ * they are loaded.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_filesystems(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "autofs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "cgroup");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "nfs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "proc");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "sysfs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "tmpfs");
+}
+
+/*
+ * lxpr_getattr(): Vnode operation for VOP_GETATTR()
+ */
+static int
+lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ extern uint_t nproc;
+ int error;
+
+ /*
+ * Return attributes of underlying vnode if ATTR_REAL
+ *
+ * but keep fd files with the symlink permissions
+ */
+ if (lxpnp->lxpr_realvp != NULL && (flags & ATTR_REAL)) {
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+
+ /*
+ * withold attribute information to owner or root
+ */
+ if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * now its attributes
+ */
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * if it's a file in lx /proc/pid/fd/xx then set its
+ * mode and keep it looking like a symlink, fifo or socket
+ */
+ if (type == LXPR_PID_FD_FD) {
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_type = lxpnp->lxpr_realvp->v_type;
+ vap->va_size = 0;
+ vap->va_nlink = 1;
+ }
+ return (0);
+ }
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxpnp->lxpr_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxpnp->lxpr_uid;
+ vap->va_gid = lxpnp->lxpr_gid;
+ vap->va_nodeid = lxpnp->lxpr_ino;
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vap->va_nlink = nproc + 2 + PROCDIRFILES;
+ vap->va_size = (nproc + 2 + PROCDIRFILES) * LXPR_SDSIZE;
+ break;
+ case LXPR_PIDDIR:
+ vap->va_nlink = PIDDIRFILES;
+ vap->va_size = PIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_PID_TASK_IDDIR:
+ vap->va_nlink = TIDDIRFILES;
+ vap->va_size = TIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_SELF:
+ vap->va_uid = crgetruid(curproc->p_cred);
+ vap->va_gid = crgetrgid(curproc->p_cred);
+ break;
+ case LXPR_PID_FD_FD:
+ case LXPR_PID_TID_FD_FD:
+ /*
+ * Restore VLNK type for lstat-type activity.
+ * See lxpr_readlink for more details.
+ */
+ if ((flags & FOLLOW) == 0)
+ vap->va_type = VLNK;
+ default:
+ break;
+ }
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxpr_access(): Vnode operation for VOP_ACCESS()
+ */
+static int
+lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int shift = 0;
+ proc_t *tp;
+
+ /* lx /proc is primarily a read only file system */
+ if ((mode & VWRITE) && !lxpr_is_writable(type)) {
+ return (EROFS);
+ }
+
+ /*
+ * If this is a restricted file, check access permissions.
+ */
+ switch (type) {
+ case LXPR_PIDDIR:
+ return (0);
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ENV:
+ case LXPR_PID_EXE:
+ case LXPR_PID_LIMITS:
+ case LXPR_PID_MAPS:
+ case LXPR_PID_MEM:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_FDDIR:
+ case LXPR_PID_FD_FD:
+ case LXPR_PID_TID_FDDIR:
+ case LXPR_PID_TID_FD_FD:
+ if ((tp = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK)) == NULL)
+ return (ENOENT);
+ if (tp != curproc && secpolicy_proc_access(cr) != 0 &&
+ priv_proc_cred_perm(cr, tp, NULL, mode) != 0) {
+ lxpr_unlock(tp);
+ return (EACCES);
+ }
+ lxpr_unlock(tp);
+ default:
+ break;
+ }
+
+ if (lxpnp->lxpr_realvp != NULL) {
+ /*
+ * For these we use the underlying vnode's accessibility.
+ */
+ return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct));
+ }
+
+ /* If user is root allow access regardless of permission bits */
+ if (secpolicy_proc_access(cr) == 0)
+ return (0);
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxpnp->lxpr_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxpnp->lxpr_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxpnp->lxpr_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxpr_lookup_not_a_dir(vnode_t *dp, char *comp)
+{
+ return (NULL);
+}
+
+/*
+ * lxpr_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the lookup
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxpnp->lxpr_parent);
+ *vpp = lxpnp->lxpr_parent;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxpr_lookup_function[type](dp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+/*
+ * Do a sequential search on the given directory table
+ */
+static vnode_t *
+lxpr_lookup_common(vnode_t *dp, char *comp, proc_t *p,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ lxpr_node_t *lxpnp;
+ int count;
+
+ for (count = 0; count < dirtablen; count++) {
+ if (strcmp(dirtab[count].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, dirtab[count].d_type, p, 0);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ return (dp);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxpr_lookup_piddir(vnode_t *dp, char *comp)
+{
+ proc_t *p;
+
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PIDDIR);
+
+ p = lxpr_lock(VTOLXP(dp)->lxpr_pid, ZOMB_OK);
+ if (p == NULL)
+ return (NULL);
+
+ dp = lxpr_lookup_common(dp, comp, p, piddir, PIDDIRFILES);
+
+ lxpr_unlock(p);
+
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_taskdir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ proc_t *p;
+ pid_t real_pid;
+ uint_t tid;
+ int c;
+ kthread_t *t;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+ /*
+ * convert the string rendition of the filename to a thread ID
+ */
+ tid = 0;
+ while ((c = *comp++) != '\0') {
+ int otid;
+ if (c < '0' || c > '9')
+ return (NULL);
+
+ otid = tid;
+ tid = 10 * tid + c - '0';
+ /* integer overflow */
+ if (tid / 10 != otid)
+ return (NULL);
+ }
+
+ /*
+ * get the proc to work with and lock it
+ */
+ real_pid = get_real_pid(dlxpnp->lxpr_pid);
+ p = lxpr_lock(real_pid, NO_ZOMB);
+ if ((p == NULL))
+ return (NULL);
+
+ /*
+ * Bail if this is a system process.
+ */
+ if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ if (p->p_brand == &lx_brand) {
+ t = lxpr_get_thread(p, tid);
+ } else {
+ /*
+ * Only the main thread is visible for non-branded processes.
+ */
+ t = p->p_tlist;
+ if (tid != p->p_pid || t == NULL) {
+ t = NULL;
+ } else {
+ thread_lock(t);
+ }
+ }
+ if (t == NULL) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+ thread_unlock(t);
+
+ /*
+ * Allocate and fill in a new lx /proc taskid node.
+ * Instead of the last arg being a fd, it is a tid.
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PID_TASK_IDDIR, p, tid);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ lxpr_unlock(p);
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_task_tid_dir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ proc_t *p;
+ pid_t real_pid;
+ kthread_t *t;
+ int i;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+ /*
+ * get the proc to work with and lock it
+ */
+ real_pid = get_real_pid(dlxpnp->lxpr_pid);
+ p = lxpr_lock(real_pid, NO_ZOMB);
+ if ((p == NULL))
+ return (NULL);
+
+ /*
+ * Bail if this is a system process.
+ */
+ if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /* need to confirm tid is still there */
+ t = lxpr_get_thread(p, dlxpnp->lxpr_desc);
+ if (t == NULL) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+ thread_unlock(t);
+
+ /*
+ * allocate and fill in the new lx /proc taskid dir node
+ */
+ for (i = 0; i < TIDDIRFILES; i++) {
+ if (strcmp(tiddir[i].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, tiddir[i].d_type, p,
+ dlxpnp->lxpr_desc);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ lxpr_unlock(p);
+ return (dp);
+ }
+ }
+
+ lxpr_unlock(p);
+ return (NULL);
+}
+
+/*
+ * Lookup one of the process's open files.
+ */
+static vnode_t *
+lxpr_lookup_fddir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PID_FDDIR ||
+ VTOLXP(dp)->lxpr_type == LXPR_PID_TID_FDDIR);
+
+ return (lxpr_lookup_fdnode(dp, comp));
+}
+
+static vnode_t *
+lxpr_lookup_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_NETDIR);
+
+ dp = lxpr_lookup_common(dp, comp, NULL, netdir, NETDIRFILES);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_procdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PROCDIR);
+
+ /*
+ * We know all the names of files & dirs in our file system structure
+ * except those that are pid names. These change as pids are created/
+ * deleted etc., so we just look for a number as the first char to see
+ * if we are we doing pid lookups.
+ *
+ * Don't need to check for "self" as it is implemented as a symlink
+ */
+ if (*comp >= '0' && *comp <= '9') {
+ pid_t pid = 0;
+ lxpr_node_t *lxpnp = NULL;
+ proc_t *p;
+ int c;
+
+ while ((c = *comp++) != '\0')
+ pid = 10 * pid + c - '0';
+
+ /*
+ * Can't continue if the process is still loading or it doesn't
+ * really exist yet (or maybe it just died!)
+ */
+ p = lxpr_lock(pid, ZOMB_OK);
+ if (p == NULL)
+ return (NULL);
+
+ if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * allocate and fill in a new lx /proc node
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PIDDIR, p, 0);
+
+ lxpr_unlock(p);
+
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+
+ return (dp);
+ }
+
+ /* Lookup fixed names */
+ return (lxpr_lookup_common(dp, comp, NULL, lx_procdir, PROCDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sysdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYSDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sysdir, SYSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kerneldir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNELDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_kerneldir,
+ SYS_KERNELDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kdir_randdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_randdir,
+ SYS_RANDDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NETDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_netdir,
+ SYS_NETDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_coredir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_COREDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_net_coredir,
+ SYS_NET_COREDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_ipv4dir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_net_ipv4dir,
+ SYS_NET_IPV4DIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_vmdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_VMDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_vmdir,
+ SYS_VMDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fsdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FSDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_fsdir,
+ SYS_FSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fs_inotifydir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_fs_inotifydir,
+ SYS_FS_INOTIFYDIRFILES));
+}
+
+/*
+ * lxpr_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the readdir
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXPR_SDSIZE)
+ return (ENOENT);
+
+ return (lxpr_readdir_function[type](lxpnp, uiop, eofp));
+}
+
+/* ARGSUSED */
+static int
+lxpr_readdir_not_a_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (ENOTDIR);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxpr_readdir_common(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Satisfy user request
+ */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXPR_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxpnp->lxpr_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXPR_SDSIZE) {
+
+ dirent->d_ino = lxpr_parentinode(lxpnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxpr_inode(dirtab[dirindex].d_type,
+ lxpnp->lxpr_pid, 0);
+
+ VERIFY(slen < LXPNSIZ);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ if (eofp) {
+ *eofp =
+ (uiop->uio_offset >= ((dirtablen+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+ return (0);
+}
+
+
+static int
+lxpr_readdir_procdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ zoneid_t zoneid;
+ pid_t pid;
+ int error;
+ int ceof;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PROCDIR);
+
+ oresid = uiop->uio_resid;
+ zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ /*
+ * We return directory entries in the order: "." and ".." then the
+ * unique lxproc files, then the directories corresponding to the
+ * running processes. We have defined this as the ordering because
+ * it allows us to more easily keep track of where we are betwen calls
+ * to getdents(). If the number of processes changes between calls
+ * then we can't lose track of where we are in the lxproc files.
+ */
+
+ /* Do the fixed entries */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, lx_procdir,
+ PROCDIRFILES);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ return (error);
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Do the process entries */
+ while ((uresid = uiop->uio_resid) > 0) {
+ proc_t *p;
+ int len;
+ int reclen;
+ int i;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop when entire proc table has been examined.
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2 - PROCDIRFILES;
+ if (i < 0 || i >= v.v_proc) {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+ mutex_enter(&pidlock);
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, a PID of 0,
+ * and anything the security policy doesn't allow
+ * us to look at.
+ */
+ if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+ p->p_pid == 0 ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ mutex_exit(&pidlock);
+ goto next;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's
+ * init process, or 0 if zsched, otherwise use the value from
+ * the proc structure
+ */
+ if (p->p_pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1;
+ } else if (p->p_pid == curproc->p_zone->zone_zsched->p_pid) {
+ pid = 0;
+ } else {
+ pid = p->p_pid;
+ }
+
+ /*
+ * If this /proc was mounted in the global zone, view
+ * all procs; otherwise, only view zone member procs.
+ */
+ if (zoneid != GLOBAL_ZONEID && p->p_zone->zone_id != zoneid) {
+ goto next;
+ }
+
+ ASSERT(p->p_stat != 0);
+
+ dirent->d_ino = lxpr_inode(LXPR_PIDDIR, pid, 0);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", pid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ return (EINVAL);
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ if (eofp != NULL) {
+ *eofp = (uiop->uio_offset >=
+ ((v.v_proc + PROCDIRFILES + 2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+ return (0);
+}
+
+static int
+lxpr_readdir_piddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+ pid_t find_pid;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PIDDIR);
+
+ /* can't read its contents if it died */
+ mutex_enter(&pidlock);
+
+ if (lxpnp->lxpr_pid == 1) {
+ find_pid = curproc->p_zone->zone_proc_initpid;
+ } else if (lxpnp->lxpr_pid == 0) {
+ find_pid = curproc->p_zone->zone_zsched->p_pid;
+ } else {
+ find_pid = lxpnp->lxpr_pid;
+ }
+ p = prfind(find_pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, piddir, PIDDIRFILES));
+}
+
+static int
+lxpr_readdir_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, netdir, NETDIRFILES));
+}
+
+static int
+lxpr_readdir_taskdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error, ceof, tiddirsize, tasknum;
+ proc_t *p;
+ pid_t real_pid;
+ kthread_t *t;
+ boolean_t branded;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+ oresid = uiop->uio_resid;
+
+ real_pid = get_real_pid(lxpnp->lxpr_pid);
+ p = lxpr_lock(real_pid, ZOMB_OK);
+ if (p == NULL) {
+ return (ENOENT);
+ }
+ if (p->p_stat == SIDL) {
+ lxpr_unlock(p);
+ return (ENOENT);
+ }
+
+ /*
+ * Just emit static entries for system processes and zombies.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+ }
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its threads.
+ */
+ tiddirsize = p->p_lwpcnt;
+ branded = (p->p_brand == &lx_brand);
+ mutex_exit(&p->p_lock);
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ if ((t = p->p_tlist) == NULL) {
+ if (eofp != NULL)
+ *eofp = 1;
+ goto out;
+ }
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until all thread's have
+ * been returned.
+ */
+ for (tasknum = 0; (uresid = uiop->uio_resid) > 0; tasknum++) {
+ int i, reclen, len;
+ uint_t emul_tid;
+ lx_lwp_data_t *lwpd;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the thread list
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2;
+ if (i < 0 || i >= tiddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (i != tasknum)
+ goto next;
+
+ if (!branded) {
+ /*
+ * Emulating the goofy linux task model is impossible
+ * to do for native processes. We can compromise by
+ * presenting only the main thread to the consumer.
+ */
+ emul_tid = p->p_pid;
+ } else {
+ if ((lwpd = ttolxlwp(t)) == NULL) {
+ goto next;
+ }
+ emul_tid = lwpd->br_pid;
+ /*
+ * Convert pid to Linux default of 1 if we're the
+ * zone's init.
+ */
+ if (emul_tid == curproc->p_zone->zone_proc_initpid)
+ emul_tid = 1;
+ }
+
+ dirent->d_ino = lxpr_inode(LXPR_PID_TASK_IDDIR, lxpnp->lxpr_pid,
+ emul_tid);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", emul_tid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+
+ if ((t = t->t_forw) == p->p_tlist || !branded) {
+ if (eofp != NULL)
+ *eofp = 1;
+ goto out;
+ }
+ }
+
+ if (eofp != NULL)
+ *eofp = 0;
+
+out:
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+static int
+lxpr_readdir_task_tid_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+ pid_t real_pid;
+ kthread_t *t;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+ mutex_enter(&pidlock);
+
+ real_pid = get_real_pid(lxpnp->lxpr_pid);
+ p = prfind(real_pid);
+
+ /* can't read its contents if it died */
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+
+ mutex_exit(&pidlock);
+
+ /* need to confirm tid is still there */
+ t = lxpr_get_thread(p, lxpnp->lxpr_desc);
+ if (t == NULL) {
+ /* we can't find this specific thread */
+ return (NULL);
+ }
+ thread_unlock(t);
+
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, tiddir, TIDDIRFILES));
+}
+
+static int
+lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error, ceof, fddirsize;
+ proc_t *p;
+ uf_info_t *fip;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FDDIR ||
+ lxpnp->lxpr_type == LXPR_PID_TID_FDDIR);
+
+ oresid = uiop->uio_resid;
+
+ /* can't read its contents if it died */
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL)
+ return (ENOENT);
+
+ /*
+ * For exiting/exited processes or those belonging to the system, only
+ * emit the fixed entries.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+ }
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /* Get open file info */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+ fddirsize = fip->fi_nfiles;
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until
+ * all file descriptors have been examined.
+ */
+ for (; (uresid = uiop->uio_resid) > 0;
+ uiop->uio_offset = uoffset + LXPR_SDSIZE) {
+ int reclen;
+ int fd;
+ int len;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the fd list
+ */
+ fd = (uoffset / LXPR_SDSIZE) - 2;
+ if (fd < 0 || fd >= fddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (fip->fi_list[fd].uf_file == NULL)
+ continue;
+
+ dirent->d_ino = lxpr_inode(LXPR_PID_FD_FD, lxpnp->lxpr_pid, fd);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", fd);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+ }
+
+ if (eofp != NULL) {
+ *eofp =
+ (uiop->uio_offset >= ((fddirsize+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+out:
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+static int
+lxpr_readdir_sysdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYSDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sysdir, SYSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fsdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FSDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fsdir,
+ SYS_FSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fs_inotifydir,
+ SYS_FS_INOTIFYDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kerneldir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNELDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_kerneldir,
+ SYS_KERNELDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kdir_randdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_randdir,
+ SYS_RANDDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_netdir,
+ SYS_NETDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_coredir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_COREDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_coredir,
+ SYS_NET_COREDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_ipv4dir,
+ SYS_NET_IPV4DIRFILES));
+}
+
+static int
+lxpr_readdir_sys_vmdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VMDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_vmdir,
+ SYS_VMDIRFILES));
+}
+
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+/*
+ * Obtain a numeric value from the null-terminated input string.
+ * We don't have strtok in the kernel, so tokenize this ourselves and
+ * validate the input.
+ */
+static int
+lxpr_tokenize_num(char *str, long *pv, char **ep)
+{
+ char *pstart, *pc, c, *endptr;
+ long v;
+
+ for (pc = str; isspace(*pc); pc++)
+ ;
+
+ for (pstart = pc; isdigit(*pc); pc++)
+ ;
+ if (pc == pstart || (!isspace(*pc) && *pc != '\0'))
+ return (EINVAL);
+ c = *pc;
+ *pc = '\0';
+
+ if (ddi_strtol(pstart, &endptr, 10, &v) != 0) {
+ *pc = c;
+ return (EINVAL);
+ }
+ if (*endptr != '\0') {
+ *pc = c;
+ return (EINVAL);
+ }
+
+ if (pv != NULL)
+ *pv = v;
+ if (ep != NULL)
+ *ep = ++pc;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_tcp_property(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct, char *prop,
+ int (*xlate)(char *, int))
+{
+ int error;
+ int res = 0;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ netstack_t *ns;
+ mod_prop_info_t *ptbl = NULL;
+ mod_prop_info_t *pinfo = NULL;
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (val[olen - 1] == '\n')
+ val[olen - 1] = '\0';
+
+ if (val[0] == '\0') /* no input */
+ return (EINVAL);
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return (EINVAL);
+
+ if (xlate != NULL && xlate(val, sizeof (val)) != 0) {
+ netstack_rele(ns);
+ return (EINVAL);
+ }
+
+ ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+ pinfo = mod_prop_lookup(ptbl, prop, MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, val, 0) != 0)
+ res = EINVAL;
+
+ netstack_rele(ns);
+ return (res);
+}
+
+static int
+lxpr_write_sys_net_core_somaxc(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_conn_req_max_q", NULL));
+}
+
+static int
+lxpr_xlate_sec2ms(char *val, int size)
+{
+ long sec;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (snprintf(val, size, "%ld", sec * 1000) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+lxpr_xlate_ka_intvl(char *val, int size)
+{
+ long sec;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (snprintf(val, size, "%ld", sec * 1000 * 9) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+lxpr_xlate_sack(char *val, int size)
+{
+ long flag;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &flag, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (flag != 0 && flag != 1)
+ return (EINVAL);
+ /* see comment on lxpr_read_sys_net_ipv4_tcp_sack */
+ if (snprintf(val, size, "%d", (flag == 0 ? 0 : 2)) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+/*
+ * We expect two port numbers on a line as input for the range, and we have to
+ * set two properties on the netstack_tcp, so we can't reuse
+ * lxpr_write_tcp_property.
+ */
+/* ARGSUSED */
+static int
+lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ int res;
+ size_t olen;
+ char vals[32]; /* big enough for a line w/ 2 16-bit numeric strings */
+ char *ep;
+ long low, high;
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ mod_prop_info_t *ptbl = NULL;
+ mod_prop_info_t *pinfo = NULL;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (vals) - 1)
+ return (EINVAL);
+
+ bzero(vals, sizeof (vals));
+ res = uiomove(vals, olen, UIO_WRITE, uio);
+ if (res != 0)
+ return (res);
+
+ if (lxpr_tokenize_num(vals, &low, &ep) != 0)
+ return (EINVAL);
+
+ if (lxpr_tokenize_num(ep, &high, &ep) != 0)
+ return (EINVAL);
+
+ if (*ep != '\0') {
+ /* make sure no other tokens on the line */
+ *ep++ = '\0';
+ for (; isspace(*ep); ep++)
+ ;
+ if (*ep != '\0')
+ return (EINVAL);
+ }
+
+ if (low > high || high > 65535)
+ return (EINVAL);
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return (EINVAL);
+
+ tcps = ns->netstack_tcp;
+ if (low < tcps->tcps_smallest_nonpriv_port) {
+ netstack_rele(ns);
+ return (EINVAL);
+ }
+
+ ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+
+ (void) snprintf(vals, sizeof (vals), "%ld", low);
+ pinfo = mod_prop_lookup(ptbl, "smallest_anon_port", MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+
+ (void) snprintf(vals, sizeof (vals), "%ld", high);
+ pinfo = mod_prop_lookup(ptbl, "largest_anon_port", MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+
+ netstack_rele(ns);
+ return (res);
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_fin_wait_2_flush_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_keepalive_abort_interval", lxpr_xlate_ka_intvl));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_keepalive_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "sack",
+ lxpr_xlate_sack));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "_wscale_always",
+ NULL));
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_sys_kernel_corepatt(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ zone_t *zone = curproc->p_zone;
+ struct core_globals *cg;
+ refstr_t *rp, *nrp;
+ corectl_path_t *ccp;
+ char val[MAXPATHLEN];
+ char valtr[MAXPATHLEN];
+ size_t olen;
+ int error;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+ cg = zone_getspecific(core_zone_key, zone);
+ ASSERT(cg != NULL);
+
+ if (secpolicy_coreadm(cr) != 0)
+ return (EPERM);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (val[olen - 1] == '\n')
+ val[olen - 1] = '\0';
+
+ if (val[0] == '|')
+ return (EINVAL);
+
+ if ((error = lxpr_core_path_l2s(val, valtr, sizeof (valtr))) != 0)
+ return (error);
+
+ nrp = refstr_alloc(valtr);
+
+ ccp = cg->core_default_path;
+ mutex_enter(&ccp->ccp_mtx);
+ rp = ccp->ccp_path;
+ refstr_hold((ccp->ccp_path = nrp));
+ cg->core_options |= CC_PROCESS_PATH;
+ mutex_exit(&ccp->ccp_mtx);
+
+ if (rp != NULL)
+ refstr_rele(rp);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_pid_loginuid(lxpr_node_t *lxpnp, struct uio *uio, struct cred *cr,
+ caller_context_t *ct)
+{
+ int error;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ char *ep;
+ long u;
+ proc_t *p;
+ lx_proc_data_t *pd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (lxpr_tokenize_num(val, &u, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+
+ if ((p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB)) == NULL)
+ return (ENXIO);
+
+ if ((pd = ptolxproc(p)) != NULL) {
+ pd->l_loginuid = (uid_t)u;
+ }
+ lxpr_unlock(p);
+
+ return (0);
+}
+
+/*
+ * lxpr_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char bp[MAXPATHLEN + 1];
+ size_t buflen = sizeof (bp);
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ pid_t pid;
+ int error = 0;
+
+ /*
+ * Linux does something very "clever" for /proc/<pid>/fd/<num> entries.
+ * Open FDs are represented as symlinks, the link contents
+ * corresponding to the open resource. For plain files or devices,
+ * this isn't absurd since one can dereference the symlink to query
+ * the underlying resource. For sockets or pipes, it becomes ugly in a
+ * hurry. To maintain this human-readable output, those FD symlinks
+ * point to bogus targets such as "socket:[<inodenum>]". This requires
+ * circumventing vfs since the stat/lstat behavior on those FD entries
+ * will be unusual. (A stat must retrieve information about the open
+ * socket or pipe. It cannot fail because the link contents point to
+ * an absent file.)
+ *
+ * To accomplish this, lxpr_getnode returns an vnode typed VNON for FD
+ * entries. This bypasses code paths which would normally
+ * short-circuit on symlinks and allows us to emulate the vfs behavior
+ * expected by /proc consumers.
+ */
+ if (vp->v_type != VLNK && lxpnp->lxpr_type != LXPR_PID_FD_FD)
+ return (EINVAL);
+
+ /* Try to produce a symlink name for anything that has a realvp */
+ if (rvp != NULL) {
+ if ((error = lxpr_access(vp, VREAD, 0, CRED(), ct)) != 0)
+ return (error);
+ if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 0) {
+ /*
+ * Special handling possible for /proc/<pid>/fd/<num>
+ * Generate <type>:[<inode>] links, if allowed.
+ */
+ if (lxpnp->lxpr_type != LXPR_PID_FD_FD ||
+ lxpr_readlink_fdnode(lxpnp, bp, buflen) != 0) {
+ return (error);
+ }
+ }
+ } else {
+ switch (lxpnp->lxpr_type) {
+ case LXPR_SELF:
+ /*
+ * Convert pid to the Linux default of 1 if we're the
+ * zone's init process or 0 if zsched.
+ */
+ if (curproc->p_pid ==
+ curproc->p_zone->zone_proc_initpid) {
+ pid = 1;
+ } else if (curproc->p_pid ==
+ curproc->p_zone->zone_zsched->p_pid) {
+ pid = 0;
+ } else {
+ pid = curproc->p_pid;
+ }
+
+ /*
+ * Don't need to check result as every possible int
+ * will fit within MAXPATHLEN bytes.
+ */
+ (void) snprintf(bp, buflen, "%d", pid);
+ break;
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_EXE:
+ return (EACCES);
+ default:
+ /*
+ * Need to return error so that nothing thinks
+ * that the symlink is empty and hence "."
+ */
+ return (EINVAL);
+ }
+ }
+
+ /* copy the link data to user space */
+ return (uiomove(bp, strlen(bp), UIO_READ, uiop));
+}
+
+
+/*
+ * lxpr_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_freenode(VTOLXP(vp));
+}
+
+/*
+ * lxpr_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxpr_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxpr_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ while (vn_matchops(vp1, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp1)->lxpr_realvp) != NULL) {
+ vp1 = rvp;
+ }
+
+ while (vn_matchops(vp2, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp2)->lxpr_realvp) != NULL) {
+ vp2 = rvp;
+ }
+
+ if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops))
+ return (vp1 == vp2);
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+/*
+ * lxpr_realvp(): Vnode operation for VOP_REALVP()
+ */
+static int
+lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) {
+ vp = rvp;
+ if (VOP_REALVP(vp, &rvp, ct) == 0)
+ vp = rvp;
+ }
+
+ *vpp = vp;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int i;
+
+ for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+ if (wr_tab[i].wft_type == type) {
+ if (wr_tab[i].wft_wrf != NULL) {
+ return (wr_tab[i].wft_wrf(lxpnp, uiop, cr, ct));
+ }
+ break;
+ }
+ }
+
+ /* pretend we wrote the whole thing */
+ uiop->uio_offset += uiop->uio_resid;
+ uiop->uio_resid = 0;
+ return (0);
+}
+
+/* Needed for writable files which are first "truncated" */
+/* ARGSUSED */
+static int
+lxpr_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
+ cred_t *cred, caller_context_t *ct)
+{
+ int error;
+
+ if (cmd != F_FREESP)
+ return (EINVAL);
+ if ((error = lxpr_access(vp, VWRITE, 0, cred, ct)) != 0)
+ return (error);
+
+ return (0);
+}
+
+/*
+ * Needed for writable files which are first "truncated". We only support
+ * truncation.
+ */
+/* ARGSUSED */
+static int
+lxpr_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ if (vap->va_mask != AT_SIZE)
+ return (EINVAL);
+ if ((error = lxpr_access(vp, VWRITE, 0, cr, ct)) != 0)
+ return (error);
+
+ return (0);
+}
+
+/*
+ * We need to allow open with O_CREAT for the writable files.
+ */
+/* ARGSUSED */
+static int
+lxpr_create(vnode_t *dvp, char *nm, vattr_t *vap, enum vcexcl exclusive,
+ int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dvp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *vp = NULL;
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * restrict create permission to owner or root
+ */
+ if ((error = lxpr_access(dvp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ if (*nm == '\0')
+ return (EPERM);
+
+ if (dvp->v_type != VDIR)
+ return (EPERM);
+
+ if (exclusive == EXCL)
+ return (EEXIST);
+
+ /*
+ * No writable files in top-level proc dir. We check this to avoid
+ * getting a non-proc node via "..".
+ */
+ if (type != LXPR_PROCDIR &&
+ lxpr_lookup(dvp, nm, &vp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ lxpr_nodetype_t ftype = VTOLXP(vp)->lxpr_type;
+ if (!lxpr_is_writable(ftype)) {
+ VN_RELE(vp);
+ vp = NULL;
+ }
+ }
+
+ if (vp != NULL) {
+ ASSERT(vp->v_type != VDIR);
+
+ /* confirm permissions against existing file */
+ if ((error = lxpr_access(vp, mode, 0, cr, ct)) != 0) {
+ VN_RELE(vp);
+ return (error);
+ }
+
+ *vpp = vp;
+ return (0);
+ }
+
+ /*
+ * Linux proc does not allow creation of addition, non-subsystem
+ * specific files inside the hierarchy. ENOENT is tossed when such
+ * actions are attempted.
+ */
+ return (ENOENT);
+}
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs.h b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
new file mode 100644
index 0000000000..17b19895f4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
@@ -0,0 +1,511 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_AUTOFS_H
+#define _LX_AUTOFS_H
+
+/*
+ * The lxautofs filesystem and driver exist to emulate the Linux autofs
+ * filesystem and /dev/autofs device (this code emulates both). The
+ * purpose is to provide support for the Linux "automount" automounter.
+ *
+ * The device ioctls map fairly closely to the filesystem ioctls. The device
+ * ioctls have superseded the filesystem ioctls and the automounter will
+ * use the device ioctls if the device exists.
+ *
+ * The device ioctls are used by the automounter to perform recovery
+ * in cases where the automounter is restarted while mounts are present. It
+ * also allows for better management operations when a filesystem is mounted
+ * on top of an autofs mountpoint, as in the case of an NFS direct mount on
+ * top of an autofs mount.
+ *
+ *
+ * +++ Linux automounter background.
+ *
+ * Linux has two automounters: "amd" (not used in any popular, modern distro)
+ * and "automount".
+ *
+ * "automount" is the normal Linux automounter. It utilizes a kernel
+ * filesystem (autofs) and device (/dev/autofs) to provide its functionality.
+ * Basically, it mounts the autofs filesystem at any automounter controlled
+ * mountpoint. This filesystem then intercepts and redirects lookup operations
+ * to the userland automounter process via a pipe. The pipe to the automounter
+ * is established via a mount option when the autofs filesystem is mounted or
+ * via the setpipefd ioctl if the automounter restarts. When the automounter
+ * receives a request via this pipe, it does lookups (or unmounts) to whatever
+ * backing store it's configured to use, does mkdir operations on the autofs
+ * filesystem, mounts remote NFS filesystems on any directories it manages or
+ * just created, and signals the autofs device via an ioctl to let it know
+ * that the lookup (or expire) can continue. Other management operations (such
+ * as querying expiration for unmounting) are performed using the autofs device.
+ *
+ *
+ * +++ Linux autofs documentation.
+ *
+ * Within the Linux src tree, see the file:
+ * Documentation/filesystems/autofs4-mount-control.txt
+ * This documents some of the autofs behavior and the device driver ioctls.
+ *
+ * The following URL (https://lwn.net/Articles/606960/) documents autofs in
+ * general. This patch was targeted for Documentation/filesystems/autofs4.txt,
+ * but seems to have never integrated into the Linux src tree.
+ *
+ *
+ * +++ Linux autofs (and automount daemon) notes
+ *
+ * Since we're mimicking the behavior of the Linux autofs filesystem and
+ * device, we document some of the observed behavior here.
+ *
+ * There are multiple versions of the autofs filesystem kernel API protocol
+ * and modern implementations of the user-land automount daemon would depend
+ * on v5, although the filesystem API has been superseded by the driver ioctl
+ * API, which is roughly similar.
+ *
+ * We'll describe the filesystem ioctls first, since support for those was
+ * implemented first. The device ioctls roughly correspond to the filesystem
+ * ioctls and were implemented last, but the automounter will use those
+ * ioctls, instead of the filesystem ioctls, when the device is present.
+ *
+ * Our original autofs implementation was developed in the mid-2000s around the
+ * v2 protocol, but that is currently obsolete. Our current implementation is
+ * based around the v5 protocol API. There was no autofs device support at that
+ * time.
+ *
+ * The autoumounter supports 3 different, mutually exclusive, mount options for
+ * each mountpoint:
+ * - indirect (this was all you got with the v2 support)
+ * - direct
+ * - offset
+ *
+ * An 'indirect' mountpoint is managed with dynamic mounts below that
+ * mountpoint. For example, if '/home' were an indirect autofs mount, then
+ * accessing a username under /home would traverse the 'lookup' code described
+ * below, cause a local subdirectory to be created, and a mount, usually NFS,
+ * onto that username subdirectory.
+ *
+ * A 'direct' mountpoint is an autofs mountpoint which will trigger the
+ * mounting of another filesystem overtop that mountpoint when accessed.
+ *
+ * An 'offset' mountpoint behaves like a 'direct' mountpoint but it is
+ * created dynamically by the automounter underneath an 'indirect' mountpoint.
+ * For example, if '/net' were an indirect autosfs mountpoint and the host
+ * 'jurassic' exported two NFS filesystems; '/var/crash' and '/var/core', then
+ * accessing '/net/jurassic' would trigger the automounter to create two
+ * subdirectories; '/net/jurassic/var/crash' and '/net/jurassic/var/core'. The
+ * automounter would then mount an autofs offset mount onto each one of these
+ * directories. Accessing either of those directories would then trigger
+ * automounter to perform another mount on top, as is done with a 'direct'
+ * mount.
+ *
+ * General behavior
+ *
+ * A) Autofs allows root owned, non-automounter processes to create
+ * directories in the autofs filesystem. The autofs filesystem treats the
+ * automounter's process group as special, but it doesn't prevent root
+ * processes outside of the automounter's process group from creating new
+ * directories in the autofs filesystem.
+ *
+ * B) Autofs doesn't allow creation of any non-directory entries in the
+ * autofs filesystem. No entity can create files (e.g. /bin/touch or
+ * VOP_CREATE/VOP_SYMLINK/etc.) The only entries that can exist within
+ * the autofs filesystem are directories.
+ *
+ * C) Autofs only intercepts vop lookup operations. Notably, it does _not_
+ * intercept and re-direct vop readdir operations. This means that the
+ * observed behavior of the Linux automounter can be considerably different
+ * from that of the illumos automounter. Specifically, on illumos if an autofs
+ * mountpoint is mounted _without_ the -nobrowse option then if a user does
+ * an ls operation (which translates into a vop readdir operation) then the
+ * automounter will intercept that operation and list all the possible
+ * directories and mountpoints without actually mounting any filesystems.
+ * Essentially, all automounter managed mountpoints on Linux will behave
+ * like "-nobrowse" mountpoints on illumos. Here's an example to illustrate
+ * this. If /ws was mounted on illumos without the -nobrowse option and an
+ * auto_ws yp map was setup as the backing store for this mountpoint, then an
+ * "ls /ws" would list all the keys in the map as valid directories, but an
+ * "ls /ws" on Linux would list an emptry directory.
+ *
+ * D) NFS mounts are performed by the automount process. When the automount
+ * process gets a redirected lookup request, it determines _all_ the
+ * possible remote mountpoints for that request, creates directory paths
+ * via mkdir, and mounts the remote filesystems on the newly created paths.
+ * This is described in the offset mount example above. Once the automounter
+ * completed the mounts it would signal the autofs filesystem (via an ioctl)
+ * that the lookup could continue.
+ *
+ * E.1) Autofs only redirects vop lookup operations for path entries that
+ * don't already exist in the autofs filesystem. So for the example above,
+ * an initial (after the start of the automounter) "ls /net/jurassic" would
+ * result in a request to the automounter. A subsequest "ls /net/jurassic"
+ * would not result in a request to the automounter. Even if
+ * /net/jurassic/var/crash and /net/jurassic/var/core were manually unmounted
+ * after the initial "ls /net/jurassic", a subsequest "ls /net/jurassic"
+ * would not result in a new request to the automounter.
+ *
+ * E.2) Autofs lookup requests that are sent to the automounter only include
+ * the root directory path component. So for example, after starting up
+ * the automounter if a user were to do a "ls /net/jurassic/var/crash", the
+ * initial lookup request actually sent to the automounter would just be for
+ * "jurassic" (the same request as if the user had done "ls /net/jurassic").
+ * After the initial mounting of the two offset mounts onto crash and core the
+ * lookup would continue and a final lookup request would be sent to the
+ * automounter for "crash" (but this would be on a different vfs from the
+ * /net vfs).
+ *
+ * E.3) The two statements above aren't entirely entirely true. The Linux
+ * autofs filesystem will also redirect lookup operations for leaf
+ * directories that don't have a filesystem mounted on them. Using the
+ * example above, if a user did a "ls /net/jurassic", then manually
+ * unmounted /net/jurassic/var/crash, and then did an "ls
+ * /net/jurassic/var/crash", this would result in a request for
+ * "jurassic/var/crash" being sent to the automounter. The strange thing
+ * (a Linux bug perhaps) is that the automounter won't do anything with this
+ * request and the lookup will fail.
+ *
+ * F) The autofs filesystem communication protocol (what ioctls it supports
+ * and what data it passes to the automount process) is versioned. The
+ * userland automount daemon (as of version v5.0.7) expects v5 of the protocol
+ * (by running the AUTOFS_IOC_PROTOSUBVER ioctl), and exits if that is not
+ * supported. For v2-v5 the structure passed through the pipe always begins
+ * with a common header followed by different fields depending on the packet
+ * type. In addition the different versions support additional ioctls.
+ *
+ * v2 - basic lookup request
+ * v3 - adds expiring (umounting)
+ * v4 - adds expire multi
+ * v5 - adds missing indirect, expire indirect, missing direct & expire direct.
+ * Defines a new protocol structure layout.
+ * The v5 'missing indirect' and 'missing direct' ioctls are analogous to
+ * the v2 'missing' ioctl. These ioctls are used to initiate a mount via
+ * a lookup. The 'expire' ioctls are used by the automounter to query if
+ * it is possible to unmount the filesystem. 'direct' and 'indirect'
+ * refer to the mount option type that the automounter performed and
+ * correlate to an automounter direct or indirect map mointpoint.
+ *
+ * G) The automounter periodically issues an 'expire' ioctl to autofs to
+ * obtain the name of a mountpoint which the automounter can unmount.
+ * Unmounting is dicussed in more detail below.
+ *
+ * H) The device ioctls roughly correspond to the filesystem ioctls, but
+ * instead of being tied to an auotfs mountpoint vnode, they can be called any
+ * time. The argument structure uses either a path or an autofs pipe file
+ * descriptor to indicate what is being operated on.
+ *
+ * +++ lxautofs notes
+ *
+ * 1) In general, the lxautofs filesystem tries to mimic the behavior of the
+ * Linux autofs filesystem with the following exceptions:
+ *
+ * 1.1) We don't bother to implement the E.3 functionality listed above
+ * since it doesn't appear to be of any use.
+ *
+ * 1.2) We only fully implement v2 and v5 of the autofs protocol.
+ *
+ * 2) In general, the approach taken for lxautofs is to keep it as simple
+ * as possible and to minimize it's memory usage. To do this all information
+ * about the contents of the lxautofs filesystem are mirrored in the
+ * underlying filesystem that lxautofs is mounted on and most vop operations
+ * are simply passed onto this underlying filesystem. This means we don't
+ * have to implement most of the complex operations that a full filesystem
+ * normally has to implement. It also means that most of our filesystem state
+ * (wrt the contents of the filesystem) doesn't actually have to be stored
+ * in memory, we can simply go to the underlying filesystem to get it when
+ * it's requested. For the purposes of discussion, we'll call the underlying
+ * filesystem the "backing store."
+ *
+ * The backing store is actually a directory called ".lxautofs" which is created
+ * in the directory where the lxautofs filesystem is mounted. When the
+ * lxautofs filesystem is unmounted this backing store directory is deleted.
+ * If this directory exists at mount time (perhaps the system crashed while a
+ * previous lxautofs instance was mounted at the same location) it will be
+ * deleted. There are a few implications of using a backing store worth
+ * mentioning.
+ *
+ * 2.1) lxautofs can't be mounted on a read only filesystem. If this
+ * proves to be a problem we can probably move the location of the
+ * backing store.
+ *
+ * 2.2) If the backing store filesystem runs out of space then the
+ * automounter process won't be able to create more directories and mount
+ * new filesystems. Of course, strange failures usually happen when
+ * filesystems run out of space.
+ *
+ * 3) Why aren't we using gfs? gfs has two different usage models.
+ *
+ * 3.1) I'm my own filesystem but i'm using gfs to help with managing
+ * readdir operations.
+ *
+ * 3.2) I'm a gfs filesystem and gfs is managing all my vnodes
+ *
+ * We're not using the 3.1 interfaces because we don't implement readdir
+ * ourselves. We pass all readdir operations onto the backing store
+ * filesystem and utilize its readdir implementation.
+ *
+ * We're not using the 3.2 interfaces because they are really designed for
+ * in memory filesystems where all of the filesystem state is stored in
+ * memory. They don't lend themselves to filesystems where part of the
+ * state is in memory and part of the state is on disk.
+ *
+ * For more information on gfs take a look at the block comments in the
+ * top of gfs.c
+ *
+ * 4) Unmounting
+ *
+ * The automounter has a timeout associated with each mount. It informs autofs
+ * of this timeout using the LX_AUTOFS_DEV_IOC_TIMEOUT_CMD ioctl after autofs
+ * has been mounted on the mountpoint.
+ *
+ * After the automounter has mounted something associated with the mountpoint
+ * then periodically (<timeout>/4 seconds) the automounter will issue the
+ * LX_AUTOFS_DEV_IOC_EXPIRE_CMD ioctl on the autofs mount. autofs is expected
+ * to respond with an underlying mountpoint entry which is a candidate for
+ * unmounting. The automounter will attempt to unmount the filesystem
+ * (which may fail if it is busy, since this is obviously racy) and then
+ * acknowledge the expire ioctl. The successful acknowledgement is independent
+ * of the success of unmounting the underlying filesystem.
+ *
+ * Unmount handling varies based on which type of mount the autofs was mounted
+ * with (indirect, direct or offset).
+ *
+ * To support 'indirect' mount expiration, the autofs vfs keeps track of the
+ * filesystems mounted immediately under the autofs mountpoint (in
+ * lav_mnt_list) after a lookup has completed successfully. Upon receipt of the
+ * LX_AUTOFS_IOC_DEV_EXPIRE_CMD ioctl, autofs removes the first element from
+ * the list, attempts to check if it is busy and if not, returns that mountpoint
+ * over the fifo (if busy the entry is added to the end of the list). When the
+ * ioctl is acknowledged, if the mountpoint still exists, that means the unmount
+ * failed and the entry is added at the back of the list. If there are no
+ * elements or the first one is busy, EAGAIN is returned for the 'expire' ioctl
+ * and the autoumounter will check again in <timeout>/4 seconds.
+ *
+ * For example, if /home is an autofs indirect mount, then there are typically
+ * many different {username}-specific NFS mounts under that /home autofs mount.
+ * autofs uses the lav_mnt_list to respond to 'expire' ioctls in a round-robin
+ * fashion so that the automounter can unmount user file systems that aren't in
+ * use.
+ *
+ * Expiring 'direct' mounts is similar, but since there is only a single mount,
+ * the lav_mnt_list only will have at most one entry if there is a filesystem
+ * mounted overtop of the autofs mount.
+ *
+ * Expiring 'offset' mounts is more complicated because there are at least
+ * two different autofs VFSs involved (the top-level and one for each offset
+ * mount underneath). The actual offset mount is handled exactly like a 'direct'
+ * mount. The top-level is an indirect mount and is handled in a similar way
+ * as described above for indirect mounts, but special handling is needed for
+ * each offset mount below.
+ *
+ * This can be explained using the same 'jurassic' example described earlier
+ * (/net is an autofs 'indirect' mount and the host 'jurassic' has two exported
+ * file systems; /var/crash and /var/core). If the user accesses
+ * /net/jurassic/var/crash then the automounter would setup the system so that
+ * the following mounts exist:
+ * - /net (the original autofs indirect mount which triggers everything)
+ * - /net/jurassic/var/crash (autofs offset mount)
+ * - /net/jurassic/var/crash (NFS mount on top of the autofs offset mount)
+ * - /net/jurassic/var/core (autofs offset mount)
+ *
+ * For expiration the automounter will issue the LX_AUTOFS_IOC_EXPIRE_MULTI
+ * ioctl on each autofs vfs for which something is mounted, so we would receive
+ * an expire ioctl on /net and another on /net/jusrassic/var/crash. The vfs for
+ * /net will be tracking "jurassic", but we detect it is busy and won't do
+ * anything at first. The vfs for "crash" will work like a direct mount and
+ * acknowledge the expire ioctl to the automounter once that filesystem times
+ * out and is no longer busy. The automounter will then unmount the "crash"
+ * NFS mount.
+ *
+ * Once the "crash" NFS mount has been unmounted by the automounter, we're left
+ * with the two autofs offset mounts under jurassic. The automounter will not
+ * try to unmount either of those, so we have to do that. Once we get another
+ * expire ioctl on /net and check "jurassic", we'll see there are only autofs
+ * mounts under /net/jurassic. We umount those using the lx_autofs_umount_offset
+ * function and respond to the automounter expire ioctl with "jurassic", in the
+ * same way as we would for any other indirect mount.
+ *
+ * 5) Recovery
+ *
+ * If the automounter is restarted for any reason, it needs to cope with
+ * pre-existing autofs mounts, as well as other automount-initiated mounts (e.g.
+ * a direct mount on top of an autofs mountpoint). The automounter uses the
+ * /proc/mounts file to correlate mounts to the managed mountpoints. It then
+ * uses the /dev/autofs device to openmount each of the autofs devices and
+ * reinitialize them using the various dev ioctls (timeout, requester, etc.).
+ *
+ * In general, the autoumounter will closemount the mountpoint once it's done,
+ * but it doesn't in the case of an offset mountpoint with nothing mounted
+ * on top. In this case the automounter expects autofs to expire that mountpoint
+ * before it will closemount (so things can subsequently cleanup). We handle
+ * this special case in the expire code path.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Note that the name of the actual file system is lxautofs, not lx_autofs, but
+ * the code uses lx_autofs to prefix the various names. This is because file
+ * system names are limited to 8 characters.
+ */
+#define LX_AUTOFS_NAME "lxautofs"
+
+#define LX_AUTOFS_MINORNAME "autofs"
+
+/*
+ * Mount options supported.
+ */
+#define LX_MNTOPT_FD "fd"
+#define LX_MNTOPT_PGRP "pgrp"
+#define LX_MNTOPT_MINPROTO "minproto"
+#define LX_MNTOPT_MAXPROTO "maxproto"
+#define LX_MNTOPT_INDIRECT "indirect"
+#define LX_MNTOPT_DIRECT "direct"
+#define LX_MNTOPT_OFFSET "offset"
+
+/*
+ * Version/subversion of the Linux kernel automount protocol we support.
+ *
+ * We fully support v2 and v5. We'll return ENOTSUP for all of the ioctls we
+ * don't yet handle.
+ */
+#define LX_AUTOFS_PROTO_VERS5 5
+#define LX_AUTOFS_PROTO_SUBVERSION 2
+#define LX_AUTOFS_PROTO_VERS2 2
+
+/* packet types */
+typedef enum laph_ptype {
+ LX_AUTOFS_PTYPE_MISSING, /* 0 */
+ LX_AUTOFS_PTYPE_EXPIRE, /* 1 */
+ LX_AUTOFS_PTYPE_EXPIRE_MULTI, /* 2 */
+ LX_AUTOFS_PTYPE_MISSING_INDIR, /* 3 */
+ LX_AUTOFS_PTYPE_EXPIRE_INDIR, /* 4 */
+ LX_AUTOFS_PTYPE_MISSING_DIRECT, /* 5 */
+ LX_AUTOFS_PTYPE_EXPIRE_DIRECT /* 6 */
+} laph_ptype_t;
+
+/*
+ * Common header for all versions of the protocol.
+ */
+typedef struct lx_autofs_pkt_hdr {
+ int laph_protover; /* protocol version number */
+ laph_ptype_t laph_type;
+ int laph_id; /* every pkt must have a unique id */
+} lx_autofs_pkt_hdr_t;
+
+/*
+ * Command structure sent to automount process from lxautofs via a pipe.
+ * This structure is the same for v2-v4 of the automount protocol
+ * (the communication pipe is established at mount time).
+ */
+typedef struct lx_autofs_v2_pkt {
+ lx_autofs_pkt_hdr_t lap_hdr;
+ int lap_name_len; /* don't include newline or NULL */
+ char lap_name[256]; /* path component to lookup */
+} lx_autofs_v2_pkt_t;
+
+/* v4 multi-expire */
+typedef struct lx_autofs_v4_exp_pkt {
+ lx_autofs_pkt_hdr_t lape_hdr;
+ int lape_len;
+ char lape_name[MAXNAMELEN];
+} lx_autofs_v4_exp_pkt_t;
+
+/* v5 */
+typedef struct lx_autofs_v5_pkt {
+ lx_autofs_pkt_hdr_t lap_hdr;
+ uint32_t lap_dev;
+ uint64_t lap_ino;
+ uint32_t lap_uid;
+ uint32_t lap_gid;
+ uint32_t lap_pid;
+ uint32_t lap_tgid;
+ uint32_t lap_name_len;
+ char lap_name[256];
+} lx_autofs_v5_pkt_t;
+
+union lx_autofs_pkt {
+ lx_autofs_v2_pkt_t lap_v2;
+ lx_autofs_v5_pkt_t lap_v5;
+};
+
+#define lap_protover lap_v2.lap_hdr.laph_protover
+#define lap_type lap_v2.lap_hdr.laph_type
+#define lap_id lap_v2.lap_hdr.laph_id
+
+/*
+ * Ioctls fully supported (v2 protocol).
+ */
+#define LX_AUTOFS_IOC_READY 0x00009360 /* arg: int */
+#define LX_AUTOFS_IOC_FAIL 0x00009361 /* arg: int */
+#define LX_AUTOFS_IOC_CATATONIC 0x00009362 /* arg: <none> */
+
+/*
+ * Ioctls supported (v3/v4 protocol).
+ */
+#define LX_AUTOFS_IOC_PROTOVER 0x80049363 /* arg: int */
+#define LX_AUTOFS_IOC_SETTIMEOUT 0xc0089364 /* arg: ulong_t */
+
+/*
+ * Ioctls not supported (v3/v4 protocol).
+ */
+ /* arg: lx_autofs_v3_exp_pkt_t * */
+#define LX_AUTOFS_IOC_EXPIRE 0x81109365
+
+/*
+ * Ioctls supported (v5 protocol).
+ */
+#define LX_AUTOFS_IOC_PROTOSUBVER 0x80049367 /* arg: int */
+#define LX_AUTOFS_IOC_ASKUMOUNT 0x80049370 /* arg: int */
+#define LX_AUTOFS_IOC_EXPIRE_MULTI 0x40049366 /* arg: int */
+#define LX_AUTOFS_IOC_EXPIRE_INDIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
+#define LX_AUTOFS_IOC_EXPIRE_DIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
+
+/*
+ * autofs device ioctls
+ */
+#define LX_AUTOFS_DEV_IOC_VERSION_CMD 0xc0189371
+#define LX_AUTOFS_DEV_IOC_PROTOVER_CMD 0xc0189372
+#define LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD 0xc0189373
+#define LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD 0xc0189374
+#define LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD 0xc0189375
+#define LX_AUTOFS_DEV_IOC_READY_CMD 0xc0189376
+#define LX_AUTOFS_DEV_IOC_FAIL_CMD 0xc0189377
+#define LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD 0xc0189378
+#define LX_AUTOFS_DEV_IOC_CATATONIC_CMD 0xc0189379
+#define LX_AUTOFS_DEV_IOC_TIMEOUT_CMD 0xc018937a
+#define LX_AUTOFS_DEV_IOC_REQUESTER_CMD 0xc018937b
+#define LX_AUTOFS_DEV_IOC_EXPIRE_CMD 0xc018937c
+#define LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD 0xc018937d
+#define LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD 0xc018937e
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUTOFS_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
new file mode 100644
index 0000000000..39ea96d1fe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
@@ -0,0 +1,162 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_AUTOFS_IMPL_H
+#define _LX_AUTOFS_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/file.h>
+#include <sys/id_space.h>
+#include <sys/modhash.h>
+#include <sys/vnode.h>
+
+#include <sys/lx_autofs.h>
+
+/*
+ * Space key.
+ * Used to persist data across lx_autofs filesystem module unloads.
+ */
+#define LX_AUTOFS_SPACE_KEY_UDEV LX_AUTOFS_NAME "_udev"
+
+/*
+ * Name of the backing store directory.
+ */
+#define LX_AUTOFS_BS_DIR "." LX_AUTOFS_NAME
+
+#define LX_AUTOFS_VFS_ID_HASH_SIZE 15
+#define LX_AUTOFS_VFS_PATH_HASH_SIZE 15
+#define LX_AUTOFS_VFS_VN_HASH_SIZE 15
+
+enum lx_autofs_mnttype { LXAMT_NONE, LXAMT_INDIR, LXAMT_DIRECT, LXAMT_OFFSET };
+
+typedef struct lx_autofs_mntent {
+ list_node_t lxafme_lst;
+ uint64_t lxafme_ts; /* time stamp */
+ uint_t lxafme_len;
+ char *lxafme_path;
+} lx_autofs_mntent_t;
+
+/*
+ * VFS data object.
+ */
+typedef struct lx_autofs_vfs {
+ /* Info about the underlying filesystem and backing store. */
+ vnode_t *lav_mvp;
+ char *lav_bs_name;
+ vnode_t *lav_bs_vp;
+
+ /* Info about the automounter process managing this filesystem. */
+ int lav_fd;
+ pid_t lav_pgrp;
+ file_t *lav_fifo_wr;
+ file_t *lav_fifo_rd;
+
+ /* The mount's dev and ino values for v5 protocol msg */
+ uint64_t lav_dev;
+ u_longlong_t lav_ino;
+
+ /* options from the mount */
+ enum lx_autofs_mnttype lav_mnttype;
+ int lav_min_proto;
+
+ /*
+ * ioctl-set timeout value. The automounter will perform an expire
+ * ioctl every timeout/4 seconds. We use this to expire a mount once
+ * it is inactive for the full timeout.
+ */
+ ulong_t lav_timeout;
+
+ /* ioctl-set catatonic value (prevents future mounts). */
+ boolean_t lav_catatonic;
+
+ /* Mount initiator's uid/gid for recovery handling. */
+ uid_t lav_uid;
+ gid_t lav_gid;
+
+ /* Each automount requests needs a unique id. */
+ id_space_t *lav_ids;
+
+ /* All remaining structure members are protected by lav_lock. */
+ kmutex_t lav_lock;
+ /* openmount counter */
+ int lav_openmnt_cnt;
+
+
+ /* Hashes to keep track of outstanding automounter requests. */
+ mod_hash_t *lav_path_hash;
+ mod_hash_t *lav_id_hash;
+
+ /* We need to keep track of all our vnodes. */
+ vnode_t *lav_root;
+ mod_hash_t *lav_vn_hash;
+
+ /* list of current mounts */
+ list_t lav_mnt_list;
+} lx_autofs_vfs_t;
+
+enum lx_autofs_callres { LXACR_NONE, LXACR_READY, LXACR_FAIL };
+
+/*
+ * Structure to keep track of automounter requests sent to user-land.
+ */
+typedef struct lx_autofs_automnt_req {
+ /* Packet that gets sent to the automounter. */
+ union lx_autofs_pkt laar_pkt;
+ int laar_pkt_size;
+
+ /* Reference count. Always updated atomically. */
+ uint_t laar_ref;
+
+ /*
+ * Fields to keep track and sync threads waiting on a lookup.
+ * Fields are protected by lalr_lock.
+ */
+ kmutex_t laar_lock;
+ kcondvar_t laar_cv;
+ int laar_complete;
+
+ enum lx_autofs_callres laar_result;
+} lx_autofs_automnt_req_t;
+
+/*
+ * Generic stack structure.
+ */
+typedef struct stack_elem {
+ list_node_t se_list;
+ caddr_t se_ptr1;
+ caddr_t se_ptr2;
+ caddr_t se_ptr3;
+} stack_elem_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUTOFS_IMPL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
new file mode 100644
index 0000000000..44ea80070d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -0,0 +1,680 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_BRAND_H
+#define _LX_BRAND_H
+
+#ifndef _ASM
+#include <sys/types.h>
+#include <sys/cpuvar.h>
+#include <sys/zone.h>
+#include <sys/ksocket.h>
+#include <sys/vfs.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LX_BRANDNAME "lx"
+
+/*
+ * Brand uname info
+ */
+#define LX_UNAME_SYSNAME "Linux"
+#define LX_UNAME_RELEASE_2_6 "2.6.18"
+#define LX_UNAME_RELEASE_2_4 "2.4.21"
+#define LX_UNAME_VERSION "BrandZ virtual linux"
+#define LX_UNAME_MACHINE32 "i686"
+#define LX_UNAME_MACHINE64 "x86_64"
+
+#define LX_LIB_PATH32 "/native/usr/lib/lx_brand.so.1"
+#define LX_LIB_PATH64 "/native/usr/lib/amd64/lx_brand.so.1"
+
+#define LX_VDSO_PATH32 "/native/usr/lib/brand/lx/lx_vdso.so.1"
+#define LX_VDSO_PATH64 "/native/usr/lib/brand/lx/amd64/lx_vdso.so.1"
+
+#if defined(_LP64)
+#define LX_LIB_PATH LX_LIB_PATH64
+#define LX_UNAME_MACHINE LX_UNAME_MACHINE64
+#define LX_VDSO_PATH LX_VDSO_PATH64
+#else
+#define LX_LIB_PATH LX_LIB_PATH32
+#define LX_UNAME_MACHINE LX_UNAME_MACHINE32
+#define LX_VDSO_PATH LX_VDSO_PATH32
+#endif
+
+/*
+ * This must be large enough for both the 32-bit table and 64-bit table.
+ */
+#define LX_NSYSCALLS 358
+
+/* Highest capability we know about */
+#define LX_CAP_MAX_VALID 36
+
+/*
+ * brand(2) subcommands
+ *
+ * Everything >= 128 is a brand-specific subcommand.
+ * > 192 is reserved for in-kernel emulated system calls.
+ */
+#define B_LPID_TO_SPAIR 128
+#define B_GET_CURRENT_CONTEXT 129
+#define B_EMULATION_DONE 130
+/* formerly B_PTRACE_KERNEL 131 */
+#define B_SET_AFFINITY_MASK 132
+#define B_GET_AFFINITY_MASK 133
+#define B_PTRACE_CLONE_BEGIN 134
+#define B_PTRACE_STOP_FOR_OPT 135
+#define B_UNSUPPORTED 136
+#define B_STORE_ARGS 137
+#define B_GETPID 138
+#define B_JUMP_TO_LINUX 139
+#define B_SET_THUNK_PID 140
+#define B_EXIT_AS_SIG 141
+/* formerly B_HELPER_WAITID 142 */
+#define B_HELPER_CLONE 143
+#define B_HELPER_SETGROUPS 144
+#define B_HELPER_SIGQUEUE 145
+#define B_HELPER_TGSIGQUEUE 146
+#define B_SET_NATIVE_STACK 147
+#define B_SIGEV_THREAD_ID 148
+#define B_OVERRIDE_KERN_VER 149
+#define B_PTRACE_SIG_RETURN 150
+#define B_GET_PERSONALITY 151
+
+#ifndef _ASM
+/*
+ * Support for Linux PTRACE_SETOPTIONS handling.
+ */
+typedef enum lx_ptrace_options {
+ LX_PTRACE_O_TRACESYSGOOD = 0x0001,
+ LX_PTRACE_O_TRACEFORK = 0x0002,
+ LX_PTRACE_O_TRACEVFORK = 0x0004,
+ LX_PTRACE_O_TRACECLONE = 0x0008,
+ LX_PTRACE_O_TRACEEXEC = 0x0010,
+ LX_PTRACE_O_TRACEVFORKDONE = 0x0020,
+ LX_PTRACE_O_TRACEEXIT = 0x0040,
+ LX_PTRACE_O_TRACESECCOMP = 0x0080
+} lx_ptrace_options_t;
+
+#define LX_PTRACE_O_ALL \
+ (LX_PTRACE_O_TRACESYSGOOD | LX_PTRACE_O_TRACEFORK | \
+ LX_PTRACE_O_TRACEVFORK | LX_PTRACE_O_TRACECLONE | \
+ LX_PTRACE_O_TRACEEXEC | LX_PTRACE_O_TRACEVFORKDONE | \
+ LX_PTRACE_O_TRACEEXIT | LX_PTRACE_O_TRACESECCOMP)
+#endif /* !_ASM */
+
+/* siginfo si_status for traced events */
+#define LX_PTRACE_EVENT_FORK 0x100
+#define LX_PTRACE_EVENT_VFORK 0x200
+#define LX_PTRACE_EVENT_CLONE 0x300
+#define LX_PTRACE_EVENT_EXEC 0x400
+#define LX_PTRACE_EVENT_VFORK_DONE 0x500
+#define LX_PTRACE_EVENT_EXIT 0x600
+#define LX_PTRACE_EVENT_SECCOMP 0x700
+
+/*
+ * Brand-private values for the "pr_what" member of lwpstatus, for use with the
+ * PR_BRAND stop reason. These reasons are validated in lx_stop_notify();
+ * update it if you add new reasons here.
+ */
+#define LX_PR_SYSENTRY 1
+#define LX_PR_SYSEXIT 2
+#define LX_PR_SIGNALLED 3
+#define LX_PR_EVENT 4
+
+
+#define LX_VERSION_1 1
+#define LX_VERSION LX_VERSION_1
+
+#define LX_ATTR_KERN_RELEASE ZONE_ATTR_BRAND_ATTRS
+#define LX_ATTR_KERN_VERSION (ZONE_ATTR_BRAND_ATTRS + 1)
+
+/*
+ * Aux vector containing phdr of Linux executable and ehdr of interpreter
+ * (if any), both of which are used by lx_librtld_db to ascertain r_debug.
+ * We repurpose the 3rd brand-specific aux vector slot for the Linux
+ * AT_SYSINFO_EHDR entry (we modify the a_type in the brand library).
+ */
+#define AT_SUN_BRAND_LX_PHDR AT_SUN_BRAND_AUX1
+#define AT_SUN_BRAND_LX_INTERP AT_SUN_BRAND_AUX2
+#define AT_SUN_BRAND_LX_CLKTCK AT_SUN_BRAND_AUX3
+#define AT_SUN_BRAND_LX_SYSINFO_EHDR AT_SUN_BRAND_AUX4
+
+/* Aux vectors containing real/effective user/group IDs */
+#define AT_LX_UID 11
+#define AT_LX_EUID 12
+#define AT_LX_GID 13
+#define AT_LX_EGID 14
+/* Aux vector containing hz value */
+#define AT_CLKTCK 17
+/* Aux vector containing secure boolean */
+#define AT_SECURE 23
+/* Aux vector containing vDSO addr */
+#define AT_SYSINFO_EHDR 33
+
+/*
+ * Usermode emulation routines are run on an alternate stack allocated by
+ * the brand library. Every LWP in a process will incur this overhead beyond
+ * the regular thread stack:
+ */
+#define LX_NATIVE_STACK_PAGE_COUNT 64
+
+/*
+ * When returning in a new child process created with vfork(2) (or CLONE_VFORK)
+ * we discard some of the native stack to prevent corruption of the parent
+ * emulation state.
+ */
+#define LX_NATIVE_STACK_VFORK_GAP 0x3000
+
+#ifndef _ASM
+
+extern struct brand lx_brand;
+
+typedef struct lx_brand_registration {
+ uint_t lxbr_version; /* version number */
+ void *lxbr_handler; /* base address of handler */
+ uint32_t lxbr_flags; /* LX_PROC_* registration flags */
+} lx_brand_registration_t;
+
+typedef struct lx_brand_registration32 {
+ uint_t lxbr_version; /* version number */
+ uint32_t lxbr_handler; /* base address of handler */
+ uint32_t lxbr_flags; /* LX_PROC_* registration flags */
+} lx_brand_registration32_t;
+
+#endif /* _ASM */
+
+/*
+ * GDT usage
+ */
+#define GDT_TLSMIN (GDT_BRANDMIN)
+#define GDT_TLSMAX (GDT_TLSMIN + 2)
+#define LX_TLSNUM (GDT_TLSMAX - GDT_TLSMIN)
+
+#ifndef _ASM
+
+/*
+ * Stores information needed by the lx linker to launch the main
+ * lx executable.
+ */
+typedef struct lx_elf_data64 {
+ uintptr_t ed_phdr;
+ uintptr_t ed_phent;
+ uintptr_t ed_phnum;
+ uintptr_t ed_entry;
+ uintptr_t ed_base;
+ uintptr_t ed_ldentry;
+} lx_elf_data64_t;
+
+typedef struct lx_elf_data32 {
+ uint32_t ed_phdr;
+ uint32_t ed_phent;
+ uint32_t ed_phnum;
+ uint32_t ed_entry;
+ uint32_t ed_base;
+ uint32_t ed_ldentry;
+} lx_elf_data32_t;
+
+#if defined(_LP64)
+typedef lx_elf_data64_t lx_elf_data_t;
+#else
+typedef lx_elf_data32_t lx_elf_data_t;
+#endif
+
+typedef enum lx_proc_flags {
+ /* flags configurable via brandsys() and members of LX_PROC_ALL */
+ LX_PROC_INSTALL_MODE = 0x01,
+ LX_PROC_STRICT_MODE = 0x02,
+ /* internal flags */
+ LX_PROC_CHILD_DEATHSIG = 0x04,
+ LX_PROC_AIO_USED = 0x08
+} lx_proc_flags_t;
+
+#define LX_PROC_ALL (LX_PROC_INSTALL_MODE | LX_PROC_STRICT_MODE)
+
+/* Maximum length for fields of LX uname */
+#define LX_SYS_UTS_LN 65
+
+/* Max. length of kernel release string */
+#define LX_KERN_RELEASE_MAX LX_SYS_UTS_LN
+#define LX_KERN_VERSION_MAX LX_SYS_UTS_LN
+
+#ifdef _KERNEL
+
+/*
+ * Entry points for cgroup integration.
+ */
+extern void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+extern void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+#define LX_RLFAKE_LOCKS 0
+#define LX_RLFAKE_NICE 1
+#define LX_RLFAKE_RTPRIO 2
+#define LX_RLFAKE_RTTIME 3
+
+#define LX_RLFAKE_NLIMITS 4
+
+#define LX_RLIM64_INFINITY (~0ULL)
+
+typedef struct {
+ uint64_t rlim_cur;
+ uint64_t rlim_max;
+} lx_rlimit64_t;
+
+typedef struct lx_proc_data {
+ uintptr_t l_handler; /* address of user-space handler */
+ pid_t l_ppid; /* pid of originating parent proc */
+ uid_t l_loginuid; /* /proc/{pid}/loginuid */
+ int64_t l_ptrace; /* count of process lwps observed by ptrace */
+ lx_elf_data_t l_elf_data; /* ELF data for linux executable */
+ /* signal to deliver to parent when this thread group dies */
+ int l_signal;
+ /* native signal to deliver to process when parent dies */
+ int l_parent_deathsig;
+ lx_proc_flags_t l_flags;
+
+ lx_rlimit64_t l_fake_limits[LX_RLFAKE_NLIMITS];
+
+ /* original start/end bounds of arg/env string data */
+ uintptr_t l_args_start;
+ uintptr_t l_envs_start;
+ uintptr_t l_envs_end;
+
+ /* Override zone-wide settings for uname release and version */
+ char l_uname_release[LX_KERN_RELEASE_MAX];
+ char l_uname_version[LX_KERN_VERSION_MAX];
+
+ /* Linux process personality */
+ unsigned int l_personality;
+
+ /* VDSO location */
+ uintptr_t l_vdso;
+} lx_proc_data_t;
+
+#endif /* _KERNEL */
+
+/*
+ * Linux process personality(2) flags stored in l_personality
+ */
+#define LX_PER_UNAME26 0x0020000
+#define LX_PER_ADDR_NO_RANDOMIZE 0x0040000
+#define LX_PER_FDPIC_FUNCPTRS 0x0080000
+#define LX_PER_MMAP_PAGE_ZERO 0x0100000
+#define LX_PER_ADDR_COMPAT_LAYOUT 0x0200000
+#define LX_PER_READ_IMPLIES_EXEC 0x0400000
+#define LX_PER_ADDR_LIMIT_32BIT 0x0800000
+#define LX_PER_SHORT_INODE 0x1000000
+#define LX_PER_WHOLE_SECONDS 0x2000000
+#define LX_PER_STICKY_TIMEOUTS 0x4000000
+#define LX_PER_ADDR_LIMIT_3GB 0x8000000
+
+#define LX_PER_LINUX 0x00
+#define LX_PER_SUNOS (0x06 | LX_PER_STICKY_TIMEOUTS)
+#define LX_PER_MASK 0xff
+
+/*
+ * A data type big enough to bitmap all Linux possible cpus.
+ * The bitmap size is defined as 1024 cpus in the Linux 2.4 and 2.6 man pages
+ * for sched_getaffinity() and sched_getaffinity().
+ */
+#define LX_NCPU (1024)
+#define LX_AFF_ULONGS (LX_NCPU / (8 * sizeof (ulong_t)))
+typedef ulong_t lx_affmask_t[LX_AFF_ULONGS];
+
+/* Length of proc boot_id string */
+#define LX_BOOTID_LEN 37
+
+/*
+ * Flag values for uc_brand_data[0] in the ucontext_t:
+ */
+#define LX_UC_STACK_NATIVE 0x00001
+#define LX_UC_STACK_BRAND 0x00002
+#define LX_UC_RESTORE_NATIVE_SP 0x00010
+#define LX_UC_FRAME_IS_SYSCALL 0x00100
+#define LX_UC_RESTART_SYSCALL 0x01000
+#define LX_UC_IGNORE_LINK 0x10000
+
+#ifdef _KERNEL
+
+typedef struct lx_lwp_data lx_lwp_data_t;
+
+/*
+ * Flag values for "lxpa_flags" on a ptrace(2) accord.
+ */
+typedef enum lx_accord_flags {
+ LX_ACC_TOMBSTONE = 0x01
+} lx_accord_flags_t;
+
+/*
+ * Flags values for "br_ptrace_flags" in the LWP-specific data.
+ */
+typedef enum lx_ptrace_flags {
+ LX_PTF_SYSCALL = 0x01, /* handling syscall or a trap */
+ LX_PTF_EXITING = 0x02,
+ LX_PTF_STOPPING = 0x04,
+ LX_PTF_INHERIT = 0x08,
+ LX_PTF_STOPPED = 0x10,
+ LX_PTF_PARENT_WAIT = 0x20,
+ LX_PTF_CLDPEND = 0x40,
+ LX_PTF_CLONING = 0x80,
+ LX_PTF_WAITPEND = 0x100,
+ LX_PTF_NOSTOP = 0x200, /* disable syscall stop event */
+ LX_PTF_INSYSCALL = 0x400 /* between syscall enter & exit */
+} lx_ptrace_flags_t;
+
+/*
+ * A ptrace(2) accord represents the relationship between a tracer LWP and the
+ * set of LWPs that it is tracing: the tracees. This data structure belongs
+ * primarily to the tracer, but is reference counted so that it may be freed by
+ * whoever references it last.
+ */
+typedef struct lx_ptrace_accord {
+ kmutex_t lxpa_lock;
+ uint_t lxpa_refcnt;
+ lx_accord_flags_t lxpa_flags;
+
+ /*
+ * The tracer must hold "pidlock" while clearing these fields for
+ * exclusion of waitid(), etc.
+ */
+ lx_lwp_data_t *lxpa_tracer;
+ kcondvar_t *lxpa_cvp;
+
+ /*
+ * The "lxpa_tracees_lock" mutex protects the tracee list.
+ */
+ kmutex_t lxpa_tracees_lock;
+ list_t lxpa_tracees;
+} lx_ptrace_accord_t;
+
+/*
+ * These values are stored in the per-LWP data for a tracee when it is attached
+ * to a tracer. They record the method that was used to attach.
+ */
+typedef enum lx_ptrace_attach {
+ LX_PTA_NONE = 0x00, /* not attached */
+ LX_PTA_ATTACH = 0x01, /* due to tracer using PTRACE_ATTACH */
+ LX_PTA_TRACEME = 0x02, /* due to child using PTRACE_TRACEME */
+ LX_PTA_INHERIT_CLONE = 0x04, /* due to PTRACE_CLONE clone(2) flag */
+ LX_PTA_INHERIT_OPTIONS = 0x08 /* due to PTRACE_SETOPTIONS options */
+} lx_ptrace_attach_t;
+
+typedef enum lx_stack_mode {
+ LX_STACK_MODE_PREINIT = 0,
+ LX_STACK_MODE_INIT,
+ LX_STACK_MODE_NATIVE,
+ LX_STACK_MODE_BRAND
+} lx_stack_mode_t;
+
+struct lx_pid {
+ pid_t s_pid; /* the SunOS pid and ... */
+ id_t s_tid; /* ... tid pair */
+ pid_t l_pid; /* the corresponding linux pid */
+ time_t l_start; /* birthday of this pid */
+ struct pid *l_pidp;
+ struct lx_pid *stol_next; /* link in stol hash table */
+ struct lx_pid *ltos_next; /* link in ltos hash table */
+};
+
+/*
+ * lx-specific data in the klwp_t
+ */
+struct lx_lwp_data {
+ uint_t br_lwp_flags; /* misc. flags */
+ klwp_t *br_lwp; /* back pointer to container lwp */
+ int br_signal; /* signal to send to parent when */
+ /* clone()'ed child terminates */
+ int br_exitwhy; /* reason for thread (process) exit */
+ int br_exitwhat; /* exit code / killing signal */
+ lx_affmask_t br_affinitymask; /* bitmask of CPU sched affinities */
+ struct user_desc br_tls[LX_TLSNUM];
+ /* descriptors used by libc for TLS */
+ ulong_t br_lx_fsbase; /* lx fsbase for 64-bit thread ptr */
+ ulong_t br_ntv_fsbase; /* native fsbase 64-bit thread ptr */
+ ulong_t br_lx_gsbase; /* lx user-land gsbase */
+ ulong_t br_ntv_gsbase; /* native user-land gsbase */
+ pid_t br_pid; /* converted pid for this thread */
+ pid_t br_tgid; /* thread group ID for this thread */
+ pid_t br_ppid; /* parent pid for this thread */
+ id_t br_ptid; /* parent tid for this thread */
+ void *br_clear_ctidp; /* clone thread id ptr */
+ void *br_set_ctidp; /* clone thread id ptr */
+ void *br_robust_list; /* robust lock list, if any */
+
+ /*
+ * The following struct is used by some system calls to pass extra
+ * flags into the kernel without impinging on the namespace for
+ * illumos.
+ */
+ void *br_scall_args;
+ int br_args_size; /* size in bytes of br_scall_args */
+
+ boolean_t br_waitid_emulate;
+ int br_waitid_flags;
+
+ lx_ptrace_flags_t br_ptrace_flags; /* ptrace flags for this LWP */
+ lx_ptrace_options_t br_ptrace_options; /* PTRACE_SETOPTIONS options */
+ lx_ptrace_options_t br_ptrace_clone_option; /* current clone(2) type */
+
+ lx_ptrace_attach_t br_ptrace_attach; /* how did we get attached */
+ lx_ptrace_accord_t *br_ptrace_accord; /* accord for this tracer LWP */
+ lx_ptrace_accord_t *br_ptrace_tracer; /* accord tracing this LWP */
+ list_node_t br_ptrace_linkage; /* linkage for lxpa_tracees list */
+
+ ushort_t br_ptrace_whystop; /* stop reason, 0 for no stop */
+ ushort_t br_ptrace_whatstop; /* stop sub-reason */
+
+ int32_t br_ptrace_stopsig; /* stop signal, 0 for no signal */
+ /*
+ * Track the last (native) signal number processed by a ptrace.
+ * This allows the tracee to properly handle ignored signals after
+ * the tracer has been notified and the tracee restarted.
+ */
+ int32_t br_ptrace_donesig;
+ uintptr_t br_ptrace_stopucp; /* usermode ucontext_t pointer */
+
+ uint_t br_ptrace_event;
+ ulong_t br_ptrace_eventmsg;
+
+ int br_syscall_num; /* current system call number */
+ boolean_t br_syscall_restart; /* should restart on EINTR */
+
+ /*
+ * Store the LX_STACK_MODE for this LWP, and the current extent of the
+ * native (emulation) stack. This is similar, in principle, to the
+ * sigaltstack mechanism for signal handling. We also use this mode
+ * flag to determine how to process system calls from this LWP.
+ */
+ lx_stack_mode_t br_stack_mode;
+ uintptr_t br_ntv_stack;
+ uintptr_t br_ntv_stack_current;
+
+ /*
+ * If this pid is set, we return it with getpid(). This allows the
+ * thunking server to interpose on the pid returned to the Linux
+ * syslog software.
+ */
+ pid_t br_lx_thunk_pid;
+
+ /*
+ * If strict mode is enabled (via LX_STRICT in the environment), any
+ * call to lx_unsupported() will set this boolean to B_TRUE. This will
+ * cause us to drop SIGSYS on the LWP as it attempts to return to
+ * usermode.
+ */
+ boolean_t br_strict_failure;
+
+ /*
+ * Some syscalls emulated in-kernel still call back out to the
+ * userspace emulation for certain functions. When that is the case,
+ * the syscall_return logic must be bypassed at the end of the
+ * in-kernel syscall code. The NORMALRETURN and JUSTRETURN constants
+ * are used to choose the behavior.
+ */
+ char br_eosys;
+
+ /*
+ * Hold a pre-allocated lx_pid structure to be used during lx_initlwp.
+ */
+ struct lx_pid *br_lpid;
+
+ /*
+ * ID of the cgroup this thread belongs to.
+ */
+ uint_t br_cgroupid;
+};
+
+/*
+ * Upper limit on br_args_size, low because this value can persist until
+ * overridden with another value, and the size is given from userland.
+ */
+#define LX_BR_ARGS_SIZE_MAX (1024)
+
+/*
+ * brand specific data
+ *
+ * We currently only support a single cgroup mount in an lx zone so we only have
+ * one ptr (lxzd_cgroup) but this could be changed to a list if cgroups is ever
+ * enhanced to support different mounts with different subsystem controllers.
+ */
+typedef struct lx_zone_data {
+ kmutex_t lxzd_lock; /* protects all members */
+ char lxzd_kernel_release[LX_KERN_RELEASE_MAX];
+ char lxzd_kernel_version[LX_KERN_VERSION_MAX];
+ ksocket_t lxzd_ioctl_sock;
+ char lxzd_bootid[LX_BOOTID_LEN]; /* procfs boot_id */
+ vfs_t *lxzd_cgroup; /* cgroup for this zone */
+ list_t *lxzd_vdisks; /* virtual disks (zvols) */
+ dev_t lxzd_zfs_dev; /* major num for zfs */
+} lx_zone_data_t;
+
+#define BR_CPU_BOUND 0x0001
+
+#define ttolxlwp(t) ((struct lx_lwp_data *)ttolwpbrand(t))
+#define lwptolxlwp(l) ((struct lx_lwp_data *)lwptolwpbrand(l))
+#define ttolxproc(t) \
+ (((t)->t_procp->p_brand == &lx_brand) ? \
+ (struct lx_proc_data *)(t)->t_procp->p_brand_data : NULL)
+#define ptolxproc(p) \
+ (((p)->p_brand == &lx_brand) ? \
+ (struct lx_proc_data *)(p)->p_brand_data : NULL)
+#define ztolxzd(z) \
+ (((z)->zone_brand == &lx_brand) ? \
+ (lx_zone_data_t *)(z)->zone_brand_data : NULL)
+
+/* Macro for converting to system call arguments. */
+#define LX_ARGS(scall) ((struct lx_##scall##_args *)\
+ (ttolxlwp(curthread)->br_scall_args))
+
+typedef enum lx_virt_disk_type {
+ LXVD_NONE,
+ LXVD_ZFS_DS,
+ LXVD_ZVOL
+} lx_virt_disk_type_t;
+
+typedef struct lx_virt_disk {
+ list_node_t lxvd_link;
+ char lxvd_name[MAXNAMELEN];
+ lx_virt_disk_type_t lxvd_type;
+ dev_t lxvd_emul_dev;
+ dev_t lxvd_real_dev;
+ uint64_t lxvd_volsize;
+ uint64_t lxvd_blksize;
+ char lxvd_real_name[MAXPATHLEN];
+} lx_virt_disk_t;
+
+/*
+ * Determine the upper bound on the system call number:
+ */
+#if defined(_LP64)
+#define LX_MAX_SYSCALL(lwp) \
+ ((lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) ? \
+ lx_nsysent64 : lx_nsysent32)
+#else
+#define LX_MAX_SYSCALL(lwp) lx_nsysent32
+#endif
+
+extern int lx_kern_release_cmp(zone_t *, const char *);
+
+extern void lx_lwp_set_native_stack_current(lx_lwp_data_t *, uintptr_t);
+extern void lx_divert(klwp_t *, uintptr_t);
+extern int lx_runexe(klwp_t *, void *);
+extern void lx_switch_to_native(klwp_t *);
+
+extern int lx_syscall_enter(void);
+extern void lx_syscall_return(klwp_t *, int, long);
+
+extern void lx_trace_sysenter(int, uintptr_t *);
+extern void lx_trace_sysreturn(int, long);
+
+extern void lx_emulate_user(klwp_t *, int, uintptr_t *);
+#if defined(_SYSCALL32_IMPL)
+extern void lx_emulate_user32(klwp_t *, int, uintptr_t *);
+#endif
+
+extern int lx_debug;
+#define lx_print if (lx_debug) printf
+
+extern void lx_pid_assign(kthread_t *, struct lx_pid *);
+extern void lx_pid_reassign(kthread_t *);
+extern void lx_pid_rele(pid_t, id_t);
+extern pid_t lx_lpid_to_spair(pid_t, pid_t *, id_t *);
+extern pid_t lx_lwp_ppid(klwp_t *, pid_t *, id_t *);
+extern void lx_pid_init(void);
+extern void lx_pid_fini(void);
+
+/*
+ * In-Kernel Linux System Call Description.
+ */
+typedef struct lx_sysent {
+ char *sy_name;
+ long (*sy_callc)();
+ char sy_flags;
+ char sy_narg;
+} lx_sysent_t;
+
+#if defined(_LP64)
+extern lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+extern int lx_nsysent64;
+#endif
+extern lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+extern int lx_nsysent32;
+
+#endif /* _KERNEL */
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_BRAND_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_fcntl.h b/usr/src/uts/common/brand/lx/sys/lx_fcntl.h
new file mode 100644
index 0000000000..f82c6b867d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_fcntl.h
@@ -0,0 +1,161 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_FCNTL_H
+#define _SYS_LX_FCNTL_H
+
+#include <sys/vnode.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Lx open/fcntl flags
+ */
+#define LX_O_RDONLY 00
+#define LX_O_WRONLY 01
+#define LX_O_RDWR 02
+#define LX_O_ACCMODE (LX_O_RDONLY | LX_O_WRONLY | LX_O_RDWR)
+#define LX_O_CREAT 0100
+#define LX_O_EXCL 0200
+#define LX_O_NOCTTY 0400
+#define LX_O_TRUNC 01000
+#define LX_O_APPEND 02000
+#define LX_O_NONBLOCK 04000
+#define LX_O_NDELAY LX_O_NONBLOCK
+#define LX_O_SYNC 010000
+#define LX_O_FSYNC LX_O_SYNC
+#define LX_O_ASYNC 020000
+#define LX_O_DIRECT 040000
+#define LX_O_LARGEFILE 0100000
+#define LX_O_DIRECTORY 0200000
+#define LX_O_NOFOLLOW 0400000
+#define LX_O_CLOEXEC 02000000
+#define LX_O_PATH 010000000
+
+#define LX_F_DUPFD 0
+#define LX_F_GETFD 1
+#define LX_F_SETFD 2
+#define LX_F_GETFL 3
+#define LX_F_SETFL 4
+#define LX_F_GETLK 5
+#define LX_F_SETLK 6
+#define LX_F_SETLKW 7
+#define LX_F_SETOWN 8
+#define LX_F_GETOWN 9
+#define LX_F_SETSIG 10
+#define LX_F_GETSIG 11
+
+#define LX_F_GETLK64 12
+#define LX_F_SETLK64 13
+#define LX_F_SETLKW64 14
+
+#define LX_F_SETLEASE 1024
+#define LX_F_GETLEASE 1025
+#define LX_F_NOTIFY 1026
+#define LX_F_CANCELLK 1029
+#define LX_F_DUPFD_CLOEXEC 1030
+#define LX_F_SETPIPE_SZ 1031
+#define LX_F_GETPIPE_SZ 1032
+
+#define LX_F_RDLCK 0
+#define LX_F_WRLCK 1
+#define LX_F_UNLCK 2
+
+/* Test for emulated O_PATH setting in file_t flags */
+#define LX_IS_O_PATH(f) (((f)->f_flag & (FREAD|FWRITE)) == 0)
+
+extern int lx_vp_at(int, char *, vnode_t **, int);
+
+/*
+ * Lx flock codes.
+ */
+#define LX_NAME_MAX 255
+#define LX_LOCK_SH 1 /* shared */
+#define LX_LOCK_EX 2 /* exclusive */
+#define LX_LOCK_NB 4 /* non-blocking */
+#define LX_LOCK_UN 8 /* unlock */
+
+/*
+ * On Linux the constants AT_REMOVEDIR and AT_EACCESS have the same value.
+ * AT_REMOVEDIR is used only by unlinkat and AT_EACCESS is used only by
+ * faccessat.
+ */
+#define LX_AT_FDCWD (-100)
+#define LX_AT_SYMLINK_NOFOLLOW 0x100
+#define LX_AT_REMOVEDIR 0x200
+#define LX_AT_EACCESS 0x200
+#define LX_AT_SYMLINK_FOLLOW 0x400
+#define LX_AT_NO_AUTOMOUNT 0x800
+#define LX_AT_EMPTY_PATH 0x1000
+
+typedef struct lx_flock {
+ short l_type;
+ short l_whence;
+ long l_start;
+ long l_len;
+ int l_pid;
+} lx_flock_t;
+
+typedef struct lx_flock64 {
+ short l_type;
+ short l_whence;
+ long long l_start;
+ long long l_len;
+ int l_pid;
+} lx_flock64_t;
+
+#if defined(_KERNEL)
+
+/*
+ * 64-bit kernel view of 32-bit usermode structs.
+ */
+#pragma pack(4)
+typedef struct lx_flock32 {
+ int16_t l_type;
+ int16_t l_whence;
+ int32_t l_start;
+ int32_t l_len;
+ int32_t l_pid;
+} lx_flock32_t;
+
+typedef struct lx_flock64_32 {
+ int16_t l_type;
+ int16_t l_whence;
+ int64_t l_start;
+ int64_t l_len;
+ int32_t l_pid;
+} lx_flock64_32_t;
+#pragma pack()
+
+#endif /* _KERNEL && _SYSCALL32_IMPL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_FCNTL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_futex.h b/usr/src/uts/common/brand/lx/sys/lx_futex.h
new file mode 100644
index 0000000000..a400b3bd83
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_futex.h
@@ -0,0 +1,121 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LX_FUTEX_H
+#define _SYS_LX_FUTEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+#define FUTEX_FD 2
+#define FUTEX_REQUEUE 3
+#define FUTEX_CMP_REQUEUE 4
+#define FUTEX_WAKE_OP 5
+#define FUTEX_LOCK_PI 6
+#define FUTEX_UNLOCK_PI 7
+#define FUTEX_TRYLOCK_PI 8
+#define FUTEX_WAIT_BITSET 9
+#define FUTEX_WAKE_BITSET 10
+#define FUTEX_WAIT_REQUEUE_PI 11
+#define FUTEX_CMP_REQUEUE_PI 12
+#define FUTEX_MAX_CMD FUTEX_CMP_REQUEUE_PI
+
+/*
+ * Flags that can be OR'd into a futex operation.
+ */
+#define FUTEX_CMD_MASK 0x007f
+#define FUTEX_PRIVATE_FLAG 0x0080
+#define FUTEX_CLOCK_REALTIME 0x0100
+
+#define FUTEX_BITSET_MATCH_ANY 0xffffffff
+/*
+ * FUTEX_WAKE_OP operations
+ */
+#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */
+#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */
+#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */
+#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */
+#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */
+
+/*
+ * FUTEX_WAKE_OP comparison operations
+ */
+#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */
+#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */
+#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */
+#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */
+#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */
+#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */
+
+/*
+ * The encoding of the FUTEX_WAKE_OP operation in 32 bits:
+ *
+ * +--+-- - --+-- - --+-- - --+-- - --+
+ * |S |OP |CMP |OPARG |CMPARG |
+ * +--+-- - --+-- - --+-- - --+-- - --+
+ * |31|30 - 28|27 - 24|23 - 12|11 - 0|
+ *
+ * The S bit denotes that the OPARG should be (1 << OPARG) instead of OPARG.
+ * (Yes, this whole thing is entirely absurd -- see the block comment in
+ * lx_futex.c for an explanation of this nonsense.) Macros to extract the
+ * various components from the operation, given the above encoding:
+ */
+#define FUTEX_OP_OP(x) (((x) >> 28) & 7)
+#define FUTEX_OP_CMP(x) (((x) >> 24) & 15)
+#define FUTEX_OP_OPARG(x) (((x) >> 31) ? (1 << (((x) << 8) >> 20)) : \
+ ((((x) << 8) >> 20)))
+#define FUTEX_OP_CMPARG(x) (((x) << 20) >> 20)
+
+#ifdef _KERNEL
+
+#define FUTEX_WAITERS 0x80000000
+#define FUTEX_OWNER_DIED 0x40000000
+#define FUTEX_TID_MASK 0x3fffffff
+
+#define FUTEX_ROBUST_LOCK_PI 1
+#define FUTEX_ROBUST_LIST_LIMIT 2048
+
+extern long lx_futex(uintptr_t addr, int cmd, int val, uintptr_t lx_timeout,
+ uintptr_t addr2, int val2);
+extern void lx_futex_init(void);
+extern int lx_futex_fini(void);
+extern long lx_set_robust_list(void *listp, size_t len);
+extern long lx_get_robust_list(pid_t pid, void **listp, size_t *lenp);
+extern void lx_futex_robust_exit(uintptr_t addr, uint32_t tid);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_FUTEX_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_impl.h b/usr/src/uts/common/brand/lx/sys/lx_impl.h
new file mode 100644
index 0000000000..03b9d43038
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_impl.h
@@ -0,0 +1,52 @@
+/*
+ * 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 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _LX_IMPL_H
+#define _LX_IMPL_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (lx_systrace_f)(ulong_t, ulong_t, ulong_t, ulong_t, ulong_t,
+ ulong_t, ulong_t);
+
+
+extern lx_systrace_f *lx_systrace_entry_ptr;
+extern lx_systrace_f *lx_systrace_return_ptr;
+
+extern void lx_brand_systrace_enable(void);
+extern void lx_brand_systrace_disable(void);
+
+extern void lx_unsupported(char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_IMPL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_ldt.h b/usr/src/uts/common/brand/lx/sys/lx_ldt.h
new file mode 100644
index 0000000000..825933e86c
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_ldt.h
@@ -0,0 +1,91 @@
+/*
+ * 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 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LINUX_LDT_H
+#define _SYS_LINUX_LDT_H
+
+#include <sys/segments.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ldt_info {
+ uint_t entry_number;
+ uint_t base_addr;
+ uint_t limit;
+ uint_t seg_32bit:1,
+ contents:2,
+ read_exec_only:1,
+ limit_in_pages:1,
+ seg_not_present:1,
+ useable:1;
+};
+
+#define LDT_INFO_EMPTY(info) \
+ ((info)->base_addr == 0 && (info)->limit == 0 && \
+ (info)->contents == 0 && (info)->read_exec_only == 1 && \
+ (info)->seg_32bit == 0 && (info)->limit_in_pages == 0 && \
+ (info)->seg_not_present == 1 && (info)->useable == 0)
+
+#if defined(__amd64)
+#define SETMODE(desc) (desc)->usd_long = SDP_SHORT;
+#else
+#define SETMODE(desc)
+#endif
+
+#define LDT_INFO_TO_DESC(info, desc) { \
+ USEGD_SETBASE(desc, (info)->base_addr); \
+ USEGD_SETLIMIT(desc, (info)->limit); \
+ (desc)->usd_type = ((info)->contents << 2) | \
+ ((info)->read_exec_only ^ 1) << 1 | 0x10; \
+ (desc)->usd_dpl = SEL_UPL; \
+ (desc)->usd_p = (info)->seg_not_present ^ 1; \
+ (desc)->usd_def32 = (info)->seg_32bit; \
+ (desc)->usd_gran = (info)->limit_in_pages; \
+ (desc)->usd_avl = (info)->useable; \
+ SETMODE(desc); \
+}
+
+#define DESC_TO_LDT_INFO(desc, info) { \
+ bzero((info), sizeof (*(info))); \
+ (info)->base_addr = USEGD_GETBASE(desc); \
+ (info)->limit = USEGD_GETLIMIT(desc); \
+ (info)->seg_not_present = (desc)->usd_p ^ 1; \
+ (info)->contents = ((desc)->usd_type >> 2) & 3; \
+ (info)->read_exec_only = (((desc)->usd_type >> 1) & 1) ^ 1; \
+ (info)->seg_32bit = (desc)->usd_def32; \
+ (info)->limit_in_pages = (desc)->usd_gran; \
+ (info)->useable = (desc)->usd_avl; \
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LINUX_LDT_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_misc.h b/usr/src/uts/common/brand/lx/sys/lx_misc.h
new file mode 100644
index 0000000000..3c6c45b0fc
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_misc.h
@@ -0,0 +1,119 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS__LX_MISC_H
+#define _SYS__LX_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <sys/siginfo.h>
+#include <sys/lx_brand.h>
+
+#ifdef _KERNEL
+
+extern void lx_setrval(klwp_t *, int, int);
+extern void lx_exec();
+extern void lx_exitlwp(klwp_t *);
+extern void lx_freelwp(klwp_t *);
+extern void *lx_lwpdata_alloc(proc_t *);
+extern void lx_lwpdata_free(void *);
+extern void lx_initlwp(klwp_t *, void *);
+extern void lx_initlwp_post(klwp_t *);
+extern void lx_forklwp(klwp_t *, klwp_t *);
+
+extern void lx_set_gdt(int, user_desc_t *);
+extern void lx_clear_gdt(int);
+
+extern longlong_t lx_nosys();
+
+extern greg_t lx_fixsegreg(greg_t, model_t);
+extern uintptr_t lx_fsbase(klwp_t *, uintptr_t);
+extern void lx_exit_with_sig(proc_t *, sigqueue_t *);
+extern boolean_t lx_wait_filter(proc_t *, proc_t *);
+extern void lx_sigfd_translate(k_siginfo_t *);
+extern int stol_ksiginfo_copyout(k_siginfo_t *, void *);
+
+extern int ltos_at_flag(int, int, boolean_t);
+#if defined(_SYSCALL32_IMPL)
+extern int stol_ksiginfo32_copyout(k_siginfo_t *, void *);
+#endif
+extern void lx_read_argv_bounds(proc_t *p);
+
+typedef enum lx_regs_location {
+ LX_REG_LOC_UNAVAIL,
+ LX_REG_LOC_LWP,
+ LX_REG_LOC_UCP
+} lx_regs_location_t;
+
+extern lx_regs_location_t lx_regs_location(lx_lwp_data_t *, void **, boolean_t);
+
+
+typedef enum lx_if_action {
+ LX_IF_FROMNATIVE,
+ LX_IF_TONATIVE
+} lx_if_action_t;
+
+/* Linux ARP protocol hardware identifiers */
+#define LX_ARPHRD_ETHER 1 /* Ethernet */
+#define LX_ARPHRD_LOOPBACK 772 /* Loopback */
+#define LX_ARPHRD_VOID 0xffff /* Unknown */
+
+/* IPv6 address scope values used in /proc/net/if_inet6 */
+#define LX_IPV6_ADDR_LOOPBACK 0x0010U
+#define LX_IPV6_ADDR_LINKLOCAL 0x0020U
+#define LX_IPV6_ADDR_SITELOCAL 0x0040U
+#define LX_IPV6_ADDR_COMPATv4 0x0080U
+
+extern void lx_ifname_convert(char *, lx_if_action_t);
+extern void lx_ifflags_convert(uint64_t *, lx_if_action_t);
+extern unsigned int lx_ipv6_scope_convert(const in6_addr_t *);
+extern void lx_stol_hwaddr(const struct sockaddr_dl *, struct sockaddr *,
+ int *);
+
+extern boolean_t lx_ptrace_stop(ushort_t);
+extern void lx_stop_notify(proc_t *, klwp_t *, ushort_t, ushort_t);
+extern void lx_ptrace_init(void);
+extern void lx_ptrace_fini(void);
+extern int lx_waitid_helper(idtype_t, id_t, k_siginfo_t *, int, boolean_t *,
+ int *);
+extern void lx_ptrace_exit(proc_t *, klwp_t *);
+extern void lx_ptrace_inherit_tracer(lx_lwp_data_t *, lx_lwp_data_t *);
+extern int lx_ptrace_stop_for_option(int, boolean_t, ulong_t, uintptr_t);
+extern int lx_ptrace_set_clone_inherit(int, boolean_t);
+extern int lx_sigcld_repost(proc_t *, sigqueue_t *);
+extern int lx_ptrace_issig_stop(proc_t *, klwp_t *);
+extern boolean_t lx_ptrace_sig_ignorable(proc_t *, klwp_t *, int);
+
+extern int lx_helper_clone(int64_t *, int, void *, void *, void *);
+extern int lx_helper_setgroups(int, gid_t *);
+extern int lx_helper_rt_sigqueueinfo(pid_t, int, siginfo_t *);
+extern int lx_helper_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t *);
+
+extern boolean_t lx_vsyscall_iscall(klwp_t *, uintptr_t, int *);
+extern void lx_vsyscall_enter(proc_t *, klwp_t *, int);
+
+extern void lx_check_strict_failure(lx_lwp_data_t *);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS__LX_MISC_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_ptm.h b/usr/src/uts/common/brand/lx/sys/lx_ptm.h
new file mode 100644
index 0000000000..74bbc939a3
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_ptm.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_PTM_LINUX_H
+#define _SYS_PTM_LINUX_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LX_PTM_DRV "lx_ptm"
+#define LX_PTM_MINOR_NODE "lx_ptmajor"
+
+#define LX_PTM_DEV_TO_PTS(dev) (getminor(dev) - 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_PTM_LINUX_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_sched.h b/usr/src/uts/common/brand/lx/sys/lx_sched.h
new file mode 100644
index 0000000000..b0ae748f3c
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_sched.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_LINUX_SCHED_H
+#define _SYS_LINUX_SCHED_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/procset.h>
+#include <sys/priocntl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux scheduler policies.
+ */
+#define LX_SCHED_OTHER 0
+#define LX_SCHED_FIFO 1
+#define LX_SCHED_RR 2
+
+#define LX_PRI_MAX 99
+
+typedef int l_pid_t;
+
+struct lx_sched_param {
+ int lx_sched_prio;
+};
+
+extern int sched_setprocset(procset_t *, l_pid_t);
+extern long do_priocntlsys(int, procset_t *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LINUX_SCHED_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_siginfo.h b/usr/src/uts/common/brand/lx/sys/lx_siginfo.h
new file mode 100644
index 0000000000..9f606b614f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_siginfo.h
@@ -0,0 +1,190 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGINFO_H
+#define _LX_SIGINFO_H
+
+#include <sys/lx_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_siginfo_t lsi_code values
+ *
+ * LX_SI_ASYNCNL: Sent by asynch name lookup completion
+ * LX_SI_DETHREAD: Sent by execve() killing subsidiary threads
+ * LX_SI_SIGIO: Sent by queued SIGIO
+ * LX_SI_ASYNCIO: Sent by asynchronous I/O completion
+ * LX_SI_MESGQ: Sent by real time message queue state change
+ * LX_SI_TIMER: Sent by timer expiration
+ * LX_SI_QUEUE: Sent by sigqueue
+ * LX_SI_USER: Sent by kill, sigsend, raise, etc.
+ * LX_SI_KERNEL: Sent by kernel
+ * LX_SI_CODE_NOT_EXIST: Error code. When translating from Linux to
+ * illumos errors, if there is no translation available, this value
+ * should be used. This value should have no meaning as an si_code in
+ * illumos or Linux.
+ *
+ * At present, LX_SI_ASYNCNL, LX_SI_DETHREAD, and LX_SI_SIGIO are unused by
+ * BrandZ.
+ */
+#define LX_SI_CODE_NOT_EXIST (-61)
+#define LX_SI_ASYNCNL (-60)
+#define LX_SI_DETHREAD (-7)
+#define LX_SI_TKILL (-6)
+#define LX_SI_SIGIO (-5)
+#define LX_SI_ASYNCIO (-4)
+#define LX_SI_MESGQ (-3)
+#define LX_SI_TIMER (-2)
+#define LX_SI_QUEUE (-1)
+#define LX_SI_USER (0)
+#define LX_SI_KERNEL (0x80)
+
+#define LX_SI_MAX_SIZE 128
+#define LX_SI_PAD_SIZE_32 ((LX_SI_MAX_SIZE / sizeof (int)) - 3)
+#define LX_SI_PAD_SIZE_64 ((LX_SI_MAX_SIZE / sizeof (int)) - 4)
+
+#if defined(_LP64)
+/*
+ * Because of the odd number (3) of ints before the union, we need to account
+ * for the smaller padding needed on x64 due to the union being offset to an 8
+ * byte boundary.
+ */
+#define LX_SI_PAD_SIZE LX_SI_PAD_SIZE_64
+#else
+#define LX_SI_PAD_SIZE LX_SI_PAD_SIZE_32
+#endif
+
+typedef struct lx_siginfo {
+ int lsi_signo;
+ int lsi_errno;
+ int lsi_code;
+ union {
+ int _pad[LX_SI_PAD_SIZE];
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ } _kill;
+
+ struct {
+ uint_t _timer1;
+ uint_t _timer2;
+ } _timer;
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ union sigval _sigval;
+ } _rt;
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ int _status;
+ clock_t _utime;
+ clock_t _stime;
+ } _sigchld;
+
+ struct {
+ void *_addr;
+ } _sigfault;
+
+ struct {
+ int _band;
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} lx_siginfo_t;
+
+#if defined(_KERNEL) && defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit "lx_siginfo_t" object.
+ */
+#pragma pack(4)
+typedef struct lx_siginfo32 {
+ int lsi_signo;
+ int lsi_errno;
+ int lsi_code;
+ union {
+ int _pad[LX_SI_PAD_SIZE_32];
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ } _kill;
+
+ struct {
+ uint_t _timer1;
+ uint_t _timer2;
+ } _timer;
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ union sigval32 _sigval;
+ } _rt;
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ int _status;
+ clock32_t _utime;
+ clock32_t _stime;
+ } _sigchld;
+
+ struct {
+ caddr32_t _addr;
+ } _sigfault;
+
+ struct {
+ int _band;
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} lx_siginfo32_t;
+#pragma pack()
+#endif /* defined(_KERNEL) && defined(_SYSCALL32_IMPL) */
+
+#define lsi_pid _sifields._kill._pid
+#define lsi_uid _sifields._kill._uid
+#define lsi_status _sifields._sigchld._status
+#define lsi_utime _sifields._sigchld._utime
+#define lsi_stime _sifields._sigchld._stime
+#define lsi_value _sifields._rt._sigval
+#define lsi_int _sifields._rt._sigval.sivalx_int
+#define lsi_ptr _sifields._rt._sigval.sivalx_ptr
+#define lsi_addr _sifields._sigfault._addr
+#define lsi_band _sifields._sigpoll._band
+#define lsi_fd _sifields._sigpoll._fd
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SIGINFO_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_signal.h b/usr/src/uts/common/brand/lx/sys/lx_signal.h
new file mode 100644
index 0000000000..552c36238b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_signal.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGNAL_H
+#define _LX_SIGNAL_H
+
+#include <lx_signum.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lx_ltos_sigset(lx_sigset_t *, k_sigset_t *);
+extern void lx_stol_sigset(k_sigset_t *, lx_sigset_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SIGNAL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_socket.h b/usr/src/uts/common/brand/lx/sys/lx_socket.h
new file mode 100644
index 0000000000..eb9826eebe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_socket.h
@@ -0,0 +1,434 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SOCKET_H
+#define _SYS_LX_SOCKET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux address family definitions
+ * Some of these are not supported
+ */
+#define LX_AF_UNSPEC 0 /* Unspecified */
+#define LX_AF_UNIX 1 /* local file/pipe name */
+#define LX_AF_INET 2 /* IP protocol family */
+#define LX_AF_AX25 3 /* Amateur Radio AX.25 */
+#define LX_AF_IPX 4 /* Novell Internet Protocol */
+#define LX_AF_APPLETALK 5 /* Appletalk */
+#define LX_AF_NETROM 6 /* Amateur radio */
+#define LX_AF_BRIDGE 7 /* Multiprotocol bridge */
+#define LX_AF_ATMPVC 8 /* ATM PVCs */
+#define LX_AF_X25 9 /* X.25 */
+#define LX_AF_INET6 10 /* IPV 6 */
+#define LX_AF_ROSE 11 /* Amateur Radio X.25 */
+#define LX_AF_DECNET 12 /* DECnet */
+#define LX_AF_NETBEUI 13 /* 802.2LLC */
+#define LX_AF_SECURITY 14 /* Security callback */
+#define LX_AF_KEY 15 /* key management */
+#define LX_AF_ROUTE 16 /* Alias to emulate 4.4BSD */
+#define LX_AF_NETLINK LX_AF_ROUTE
+#define LX_AF_PACKET 17 /* Packet family */
+#define LX_AF_ASH 18 /* Ash ? */
+#define LX_AF_ECONET 19 /* Acorn Econet */
+#define LX_AF_ATMSVC 20 /* ATM SVCs */
+#define LX_AF_SNA 22 /* Linux SNA */
+#define LX_AF_IRDA 23 /* IRDA sockets */
+#define LX_AF_PPPOX 24 /* PPPoX sockets */
+#define LX_AF_WANPIPE 25 /* Wanpipe API sockets */
+#define LX_AF_LLC 26
+/* gap in Linux defines for 27 and 28 */
+#define LX_AF_CAN 29
+#define LX_AF_TIPC 30
+#define LX_AF_BLUETOOTH 31 /* Bluetooth sockets */
+#define LX_AF_IUCV 32
+#define LX_AF_RXRPC 33
+
+/* limit of AF mappings */
+#define LX_AF_MAX LX_AF_RXRPC
+
+#define AF_NOTSUPPORTED -1
+#define AF_INVAL -2
+
+/*
+ * Options for use with [gs]etsockopt at the SOL_SOCKET level.
+ */
+#define LX_SOL_SOCKET 1
+
+#define LX_SCM_RIGHTS 1
+#define LX_SCM_CRED 2
+
+#define LX_SO_DEBUG 1
+#define LX_SO_REUSEADDR 2
+#define LX_SO_TYPE 3
+#define LX_SO_ERROR 4
+#define LX_SO_DONTROUTE 5
+#define LX_SO_BROADCAST 6
+#define LX_SO_SNDBUF 7
+#define LX_SO_RCVBUF 8
+#define LX_SO_KEEPALIVE 9
+#define LX_SO_OOBINLINE 10
+#define LX_SO_NO_CHECK 11
+#define LX_SO_PRIORITY 12
+#define LX_SO_LINGER 13
+#define LX_SO_BSDCOMPAT 14
+#define LX_SO_REUSEPORT 15
+/*
+ * For Linux see unix(7) man page SO_PASSCRED description. For Illumos see
+ * socket.h(3HEAD) man page SO_RECVUCRED description.
+ */
+#define LX_SO_PASSCRED 16
+#define LX_SO_PEERCRED 17
+#define LX_SO_RCVLOWAT 18
+#define LX_SO_SNDLOWAT 19
+#define LX_SO_RCVTIMEO 20
+#define LX_SO_SNDTIMEO 21
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define LX_SO_SECURITY_AUTHENTICATION 22
+#define LX_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+#define LX_SO_SECURITY_ENCRYPTION_NETWORK 24
+#define LX_SO_BINDTODEVICE 25
+/* Socket filtering */
+#define LX_SO_ATTACH_FILTER 26
+#define LX_SO_DETACH_FILTER 27
+#define LX_SO_PEERNAME 28
+#define LX_SO_TIMESTAMP 29
+#define LX_SCM_TIMESTAMP LX_SO_TIMESTAMP
+#define LX_SO_ACCEPTCONN 30
+
+#define LX_SO_PEERSEC 31
+#define LX_SO_SNDBUFFORCE 32
+#define LX_SO_RCVBUFFORCE 33
+#define LX_SO_PASSSEC 34
+#define LX_SO_TIMESTAMPNS 35
+#define LX_SCM_TIMESTAMPNS LX_SO_TIMESTAMPNS
+#define LX_SO_MARK 36
+#define LX_SO_TIMESTAMPING 37
+#define LX_SCM_TIMESTAMPING LX_SO_TIMESTAMPING
+#define LX_SO_PROTOCOL 38
+#define LX_SO_DOMAIN 39
+#define LX_SO_RXQ_OVFL 40
+#define LX_SO_WIFI_STATUS 41
+#define LX_SCM_WIFI_STATUS LX_SO_WIFI_STATUS
+#define LX_SO_PEEK_OFF 42
+#define LX_SO_NOFCS 43
+#define LX_SO_LOCK_FILTER 44
+#define LX_SO_SELECT_ERR_QUEUE 45
+#define LX_SO_BUSY_POLL 46
+#define LX_SO_MAX_PACING_RATE 47
+#define LX_SO_BPF_EXTENSIONS 48
+
+/*
+ * Options for use with [gs]etsockopt at the RAW level.
+ * IPPROTO_RAW
+ */
+#define LX_ICMP_FILTER 1
+
+/*
+ * Options for use with [gs]etsockopt at the PACKET level.
+ * SOL_PACKET
+ */
+#define LX_SOL_PACKET 263
+
+#define LX_PACKET_ADD_MEMBERSHIP 1
+#define LX_PACKET_DROP_MEMBERSHIP 2
+#define LX_PACKET_RECV_OUTPUT 3
+#define LX_PACKET_RX_RING 5
+#define LX_PACKET_STATISTICS 6
+
+/*
+ * Options for use with [gs]etsockopt at the NETLINK level.
+ * SOL_NETLINK
+ */
+#define LX_SOL_NETLINK 270
+
+/*
+ * Linux socket type definitions
+ */
+#define LX_SOCK_STREAM 1 /* Connection-based byte streams */
+#define LX_SOCK_DGRAM 2 /* Connectionless, datagram */
+#define LX_SOCK_RAW 3 /* Raw protocol interface */
+#define LX_SOCK_RDM 4 /* Reliably-delivered message */
+#define LX_SOCK_SEQPACKET 5 /* Sequenced packet stream */
+#define LX_SOCK_PACKET 10 /* Linux specific */
+#define LX_SOCK_MAX 11
+
+/*
+ * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC).
+ */
+#define LX_SOCK_TYPE_MASK 0xf
+
+/*
+ * Linux flags for socket, socketpair and accept4. These are or-ed into the
+ * socket type value. In the Linux net.h header these come from fcntl.h (note
+ * that they are in octal in the Linux header).
+ */
+#define LX_SOCK_CLOEXEC 0x80000
+#define LX_SOCK_NONBLOCK 0x800
+
+#define SOCK_NOTSUPPORTED -1
+#define SOCK_INVAL -2
+
+/*
+ * PF_PACKET protocol definitions.
+ */
+#define LX_ETH_P_802_3 0x0001
+#define LX_ETH_P_ALL 0x0003
+#define LX_ETH_P_802_2 0x0004
+#define LX_ETH_P_IP 0x0800
+#define LX_ETH_P_ARP 0x0806
+#define LX_ETH_P_IPV6 0x86DD
+
+/*
+ * IP Protocol levels. Some of these match the Illumos IPPROTO_* values.
+ */
+#define LX_IPPROTO_IP 0
+#define LX_IPPROTO_ICMP 1
+#define LX_IPPROTO_IGMP 2
+#define LX_IPPROTO_TCP 6
+#define LX_IPPROTO_UDP 17
+#define LX_IPPROTO_IPV6 41
+#define LX_IPPROTO_ICMPV6 58
+#define LX_IPPROTO_RAW 255
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IP
+ */
+#define LX_IP_TOS 1
+#define LX_IP_TTL 2
+#define LX_IP_HDRINCL 3
+#define LX_IP_OPTIONS 4
+#define LX_IP_ROUTER_ALERT 5
+#define LX_IP_RECVOPTS 6
+#define LX_IP_RETOPTS 7
+#define LX_IP_PKTINFO 8
+#define LX_IP_PKTOPTIONS 9
+#define LX_IP_MTU_DISCOVER 10
+#define LX_IP_RECVERR 11
+#define LX_IP_RECVTTL 12
+#define LX_IP_RECVTOS 13
+#define LX_IP_MTU 14
+#define LX_IP_FREEBIND 15
+#define LX_IP_IPSEC_POLICY 16
+#define LX_IP_XFRM_POLICY 17
+#define LX_IP_PASSSEC 18
+#define LX_IP_TRANSPARENT 19
+#define LX_IP_ORIGDSTADDR 20
+#define LX_IP_MINTTL 21
+#define LX_IP_NODEFRAG 22
+/* Linux apparently leaves a gap here */
+#define LX_IP_MULTICAST_IF 32
+#define LX_IP_MULTICAST_TTL 33
+#define LX_IP_MULTICAST_LOOP 34
+#define LX_IP_ADD_MEMBERSHIP 35
+#define LX_IP_DROP_MEMBERSHIP 36
+#define LX_IP_UNBLOCK_SOURC 37
+#define LX_IP_BLOCK_SOURCE 38
+#define LX_IP_ADD_SOURCE_MEMBERSHIP 39
+#define LX_IP_DROP_SOURCE_MEMBERSHIP 40
+#define LX_IP_MSFILTER 41
+#define LX_MCAST_JOIN_GROUP 42
+#define LX_MCAST_BLOCK_SOURCE 43
+#define LX_MCAST_UNBLOCK_SOURCE 44
+#define LX_MCAST_LEAVE_GROUP 45
+#define LX_MCAST_JOIN_SOURCE_GROUP 46
+#define LX_MCAST_LEAVE_SOURCE_GROUP 47
+#define LX_MCAST_MSFILTER 48
+#define LX_IP_MULTICAST_ALL 49
+#define LX_IP_UNICAST_IF 50
+
+/*
+ * LX_IP_MTU_DISCOVER values
+ */
+#define LX_IP_PMTUDISC_DONT 0
+#define LX_IP_PMTUDISC_WANT 1
+#define LX_IP_PMTUDISC_DO 2
+#define LX_IP_PMTUDISC_PROBE 3
+#define LX_IP_PMTUDISC_INTERFACE 4
+#define LX_IP_PMTUDISC_OMIT 5
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IPV6
+ */
+
+#define LX_IPV6_ADDRFORM 1
+#define LX_IPV6_2292PKTINFO 2
+#define LX_IPV6_2292HOPOPTS 3
+#define LX_IPV6_2292DSTOPTS 4
+#define LX_IPV6_2292RTHDR 5
+#define LX_IPV6_2292PKTOPTIONS 6
+#define LX_IPV6_CHECKSUM 7
+#define LX_IPV6_2292HOPLIMIT 8
+#define LX_IPV6_NEXTHOP 9
+#define LX_IPV6_AUTHHDR 10
+#define LX_IPV6_UNICAST_HOPS 16
+#define LX_IPV6_MULTICAST_IF 17
+#define LX_IPV6_MULTICAST_HOPS 18
+#define LX_IPV6_MULTICAST_LOOP 19
+#define LX_IPV6_JOIN_GROUP 20
+#define LX_IPV6_LEAVE_GROUP 21
+#define LX_IPV6_ROUTER_ALERT 22
+#define LX_IPV6_MTU_DISCOVER 23
+#define LX_IPV6_MTU 24
+#define LX_IPV6_RECVERR 25
+#define LX_IPV6_V6ONLY 26
+#define LX_IPV6_JOIN_ANYCAST 27
+#define LX_IPV6_LEAVE_ANYCAST 28
+#define LX_IPV6_IPSEC_POLICY 34
+#define LX_IPV6_XFRM_POLICY 35
+
+#define LX_IPV6_RECVPKTINFO 49
+#define LX_IPV6_PKTINFO 50
+#define LX_IPV6_RECVHOPLIMIT 51
+#define LX_IPV6_HOPLIMIT 52
+#define LX_IPV6_RECVHOPOPTS 53
+#define LX_IPV6_HOPOPTS 54
+#define LX_IPV6_RTHDRDSTOPTS 55
+#define LX_IPV6_RECVRTHDR 56
+#define LX_IPV6_RTHDR 57
+#define LX_IPV6_RECVDSTOPTS 58
+#define LX_IPV6_DSTOPTS 59
+#define LX_IPV6_RECVTCLASS 66
+#define LX_IPV6_TCLASS 67
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_ICMPV6
+ */
+
+#define LX_ICMP6_FILTER 1
+
+/*
+ * Options for use with [gs]etsockopt at the TCP level.
+ * IPPROTO_TCP
+ */
+#define LX_TCP_NODELAY 1 /* Don't delay send to coalesce packets */
+#define LX_TCP_MAXSEG 2 /* Set maximum segment size */
+#define LX_TCP_CORK 3 /* Control sending of partial frames */
+#define LX_TCP_KEEPIDLE 4 /* Start keeplives after this period */
+#define LX_TCP_KEEPINTVL 5 /* Interval between keepalives */
+#define LX_TCP_KEEPCNT 6 /* Number of keepalives before death */
+#define LX_TCP_SYNCNT 7 /* Number of SYN retransmits */
+#define LX_TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */
+#define LX_TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */
+#define LX_TCP_WINDOW_CLAMP 10 /* Bound advertised window */
+#define LX_TCP_INFO 11 /* Information about this connection. */
+#define LX_TCP_QUICKACK 12 /* Bock/reenable quick ACKs. */
+#define LX_TCP_CONGESTION 13 /* Congestion control algorithm */
+#define LX_TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
+#define LX_TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts on thin streams */
+#define LX_TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
+#define LX_TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */
+#define LX_TCP_REPAIR 19 /* TCP socket under repair */
+#define LX_TCP_REPAIR_QUEUE 20
+#define LX_TCP_QUEUE_SEQ 21
+#define LX_TCP_REPAIR_OPTIONS 22
+#define LX_TCP_FASTOPEN 23 /* Enable FastOpen on listeners */
+#define LX_TCP_TIMESTAMP 24
+#define LX_TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes */
+
+/*
+ * Options for use with [gs]etsockopt at the IGMP level.
+ * IPPROTO_IGMP
+ */
+#define LX_IGMP_MINLEN 8
+#define LX_IGMP_MAX_HOST_REPORT_DELAY 10
+#define LX_IGMP_HOST_MEMBERSHIP_QUERY 0x11
+#define LX_IGMP_HOST_MEMBERSHIP_REPORT 0x12
+#define LX_IGMP_DVMRP 0x13
+#define LX_IGMP_PIM 0x14
+#define LX_IGMP_TRACE 0x15
+#define LX_IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16
+#define LX_IGMP_HOST_LEAVE_MESSAGE 0x17
+#define LX_IGMP_MTRACE_RESP 0x1e
+#define LX_IGMP_MTRACE 0x1f
+
+/*
+ * Linux socket flags for use with recv(2)/send(2)/recvmsg(2)/sendmsg(2)
+ */
+#define LX_MSG_OOB 0x1
+#define LX_MSG_PEEK 0x2
+#define LX_MSG_DONTROUTE 0x4
+#define LX_MSG_CTRUNC 0x8
+#define LX_MSG_PROXY 0x10
+#define LX_MSG_TRUNC 0x20
+#define LX_MSG_DONTWAIT 0x40
+#define LX_MSG_EOR 0x80
+#define LX_MSG_WAITALL 0x100
+#define LX_MSG_FIN 0x200
+#define LX_MSG_SYN 0x400
+#define LX_MSG_CONFIRM 0x800
+#define LX_MSG_RST 0x1000
+#define LX_MSG_ERRQUEUE 0x2000
+#define LX_MSG_NOSIGNAL 0x4000
+#define LX_MSG_MORE 0x8000
+#define LX_MSG_WAITFORONE 0x10000
+#define LX_MSG_FASTOPEN 0x20000000
+#define LX_MSG_CMSG_CLOEXEC 0x40000000
+
+typedef struct lx_msghdr {
+ void *msg_name; /* optional address */
+ socklen_t msg_namelen; /* size of address */
+ struct iovec *msg_iov; /* scatter/gather array */
+ size_t msg_iovlen; /* # elements in msg_iov */
+ void *msg_control; /* ancillary data */
+ size_t msg_controllen; /* ancillary data buffer len */
+ int msg_flags; /* flags on received message */
+} lx_msghdr_t;
+
+
+#if defined(_LP64)
+
+typedef struct lx_msghdr32 {
+ caddr32_t msg_name; /* optional address */
+ uint32_t msg_namelen; /* size of address */
+ caddr32_t msg_iov; /* scatter/gather array */
+ int32_t msg_iovlen; /* # elements in msg_iov */
+ caddr32_t msg_control; /* ancillary data */
+ uint32_t msg_controllen; /* ancillary data buffer len */
+ int32_t msg_flags; /* flags on received message */
+} lx_msghdr32_t;
+
+#endif
+
+typedef struct lx_sockaddr_in6 {
+ sa_family_t sin6_family;
+ in_port_t sin6_port;
+ uint32_t sin6_flowinfo;
+ struct in6_addr sin6_addr;
+ uint32_t sin6_scope_id; /* Depends on scope of sin6_addr */
+ /* one 32-bit field shorter than illumos */
+} lx_sockaddr_in6_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SOCKET_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
new file mode 100644
index 0000000000..0d4a74c900
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -0,0 +1,306 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_LINUX_SYSCALLS_H
+#define _SYS_LINUX_SYSCALLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+extern long lx_accept();
+extern long lx_accept4();
+extern long lx_access();
+extern long lx_alarm();
+extern long lx_arch_prctl();
+extern long lx_bind();
+extern long lx_brk();
+extern long lx_chdir();
+extern long lx_chmod();
+extern long lx_chown();
+extern long lx_chown16();
+extern long lx_chroot();
+extern long lx_clock_getres();
+extern long lx_clock_gettime();
+extern long lx_clock_settime();
+extern long lx_close();
+extern long lx_connect();
+extern long lx_creat();
+extern long lx_dup();
+extern long lx_dup2();
+extern long lx_dup3();
+extern long lx_epoll_create();
+extern long lx_epoll_create1();
+extern long lx_epoll_ctl();
+extern long lx_epoll_pwait();
+extern long lx_epoll_wait();
+extern long lx_faccessat();
+extern long lx_fadvise64();
+extern long lx_fadvise64_32();
+extern long lx_fadvise64_64();
+extern long lx_fallocate();
+extern long lx_fallocate32();
+extern long lx_fchdir();
+extern long lx_fchmod();
+extern long lx_fchmodat();
+extern long lx_fchown();
+extern long lx_fchown16();
+extern long lx_fchownat();
+extern long lx_fcntl();
+extern long lx_fcntl64();
+extern long lx_fgetxattr();
+extern long lx_flistxattr();
+extern long lx_fremovexattr();
+extern long lx_fsetxattr();
+extern long lx_fstat32();
+extern long lx_fstat64();
+extern long lx_fstatat64();
+extern long lx_futex();
+extern long lx_get_robust_list();
+extern long lx_get_thread_area();
+extern long lx_getcpu();
+extern long lx_getcwd();
+extern long lx_getdents_32();
+extern long lx_getdents_64();
+extern long lx_getdents64();
+extern long lx_getegid();
+extern long lx_getegid16();
+extern long lx_geteuid();
+extern long lx_geteuid16();
+extern long lx_getgid();
+extern long lx_getgid16();
+extern long lx_getitimer();
+extern long lx_getpeername();
+extern long lx_getpgid();
+extern long lx_getpgrp();
+extern long lx_getsockname();
+extern long lx_getpid();
+extern long lx_getppid();
+extern long lx_getrandom();
+extern long lx_getresgid();
+extern long lx_getresgid16();
+extern long lx_getresuid();
+extern long lx_getresuid16();
+extern long lx_getrlimit();
+extern long lx_getrusage();
+extern long lx_getsid();
+extern long lx_getsockopt();
+extern long lx_gettid();
+extern long lx_gettimeofday();
+extern long lx_getuid();
+extern long lx_getuid16();
+extern long lx_getxattr();
+extern long lx_io_setup();
+extern long lx_ioctl();
+extern long lx_ioprio_get();
+extern long lx_ioprio_set();
+extern long lx_kill();
+extern long lx_lchown();
+extern long lx_lchown16();
+extern long lx_lgetxattr();
+extern long lx_link();
+extern long lx_linkat();
+extern long lx_listen();
+extern long lx_llistxattr();
+extern long lx_llseek();
+extern long lx_lremovexattr();
+extern long lx_lseek32();
+extern long lx_lseek64();
+extern long lx_lsetxattr();
+extern long lx_lstat32();
+extern long lx_lstat64();
+extern long lx_listxattr();
+extern long lx_mincore();
+extern long lx_mkdir();
+extern long lx_mkdirat();
+extern long lx_modify_ldt();
+extern long lx_mount();
+extern long lx_munmap();
+extern long lx_nanosleep();
+extern long lx_nice();
+extern long lx_oldgetrlimit();
+extern long lx_open();
+extern long lx_openat();
+extern long lx_pause();
+extern long lx_personality();
+extern long lx_pipe();
+extern long lx_pipe2();
+extern long lx_poll();
+extern long lx_ppoll();
+extern long lx_pread();
+extern long lx_pread32();
+extern long lx_preadv();
+extern long lx_preadv32();
+extern long lx_prctl();
+extern long lx_prlimit64();
+extern long lx_pselect();
+extern long lx_ptrace();
+extern long lx_pwrite();
+extern long lx_pwrite32();
+extern long lx_pwritev();
+extern long lx_pwritev32();
+extern long lx_read();
+extern long lx_readlink();
+extern long lx_readlinkat();
+extern long lx_readv();
+extern long lx_reboot();
+extern long lx_recv();
+extern long lx_recvmsg();
+extern long lx_recvfrom();
+extern long lx_rename();
+extern long lx_renameat();
+extern long lx_sched_getparam();
+extern long lx_sched_getscheduler();
+extern long lx_sched_rr_get_interval();
+extern long lx_sched_setparam();
+extern long lx_sched_setscheduler();
+extern long lx_sched_yield();
+extern long lx_select();
+extern long lx_send();
+extern long lx_sendmsg();
+extern long lx_sendto();
+extern long lx_set_robust_list();
+extern long lx_set_thread_area();
+extern long lx_set_tid_address();
+extern long lx_setdomainname();
+extern long lx_setfsuid();
+extern long lx_setfsuid16();
+extern long lx_setfsgid();
+extern long lx_setfsgid16();
+extern long lx_setgid();
+extern long lx_setgid16();
+extern long lx_sethostname();
+extern long lx_setpgid();
+extern long lx_setregid();
+extern long lx_setregid16();
+extern long lx_setresgid();
+extern long lx_setresgid16();
+extern long lx_setresuid();
+extern long lx_setresuid16();
+extern long lx_setreuid();
+extern long lx_setreuid16();
+extern long lx_setrlimit();
+extern long lx_setsid();
+extern long lx_setuid();
+extern long lx_setuid16();
+extern long lx_setxattr();
+extern long lx_setsockopt();
+extern long lx_symlink();
+extern long lx_symlinkat();
+extern long lx_shutdown();
+extern long lx_socket();
+extern long lx_socketcall();
+extern long lx_socketpair();
+extern long lx_stat32();
+extern long lx_stat64();
+extern long lx_stime();
+extern long lx_sync();
+extern long lx_sync_file_range();
+extern long lx_syncfs();
+extern long lx_sysinfo32();
+extern long lx_sysinfo64();
+extern long lx_syslog();
+extern long lx_removexattr();
+extern long lx_tgkill();
+extern long lx_time();
+extern long lx_tkill();
+extern long lx_umask();
+extern long lx_umount();
+extern long lx_umount2();
+extern long lx_uname();
+extern long lx_unlink();
+extern long lx_unlinkat();
+extern long lx_vhangup();
+extern long lx_wait4();
+extern long lx_waitid();
+extern long lx_waitpid();
+extern long lx_write();
+extern long lx_writev();
+
+#if defined(_LP64)
+/*
+ * Linux vsyscall addresses:
+ */
+#define LX_VSYS_gettimeofday (uintptr_t)0xffffffffff600000
+#define LX_VSYS_time (uintptr_t)0xffffffffff600400
+#define LX_VSYS_getcpu (uintptr_t)0xffffffffff600800
+
+#define LX_VSYSCALL_ADDR (uintptr_t)0xffffffffff600000
+#define LX_VSYSCALL_SIZE (uintptr_t)0x1000
+#endif
+
+#endif /* _KERNEL */
+
+/*
+ * System call numbers for revectoring:
+ */
+
+#if defined(__amd64)
+#define LX_SYS_close 3
+#define LX_SYS_gettimeofday 96
+#define LX_SYS_mount 165
+#define LX_SYS_time 201
+#define LX_SYS_io_setup 206
+#define LX_SYS_clock_gettime 228
+#define LX_SYS_getcpu 309
+
+#define LX_SYS32_close 6
+#define LX_SYS32_gettimeofday 78
+#define LX_SYS32_time 13
+#define LX_SYS32_mount 21
+#define LX_SYS32_clock_gettime 265
+#define LX_SYS32_io_setup 245
+#define LX_SYS32_getcpu 318
+#elif defined(__i386)
+#define LX_SYS_close 6
+#define LX_SYS_mount 21
+#define LX_SYS_gettimeofday 78
+#define LX_SYS_time 13
+#define LX_SYS_clock_gettime 265
+#define LX_SYS_io_setup 245
+#define LX_SYS_getcpu 318
+#else
+#error "Architecture not supported"
+#endif /* defined(__amd64) */
+
+/*
+ * The current code in the VDSO operates under the expectation that it will be
+ * mapped at a fixed offset from the comm page. This simplifies the act of
+ * locating said page without any other reference. The VDSO must fit within
+ * this offset, matching the same value as COMM_PAGE_ALIGN.
+ * See: uts/i86pc/sys/comm_page.h
+ */
+#define LX_VDSO_SIZE 0x4000
+#define LX_VDSO_ADDR_MASK ~(LX_VDSO_SIZE - 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LINUX_SYSCALLS_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_types.h b/usr/src/uts/common/brand/lx/sys/lx_types.h
new file mode 100644
index 0000000000..90363c8939
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_types.h
@@ -0,0 +1,144 @@
+/*
+ * 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 2016 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LX_TYPES_H
+#define _SYS_LX_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _KERNEL
+
+#define SHRT_MIN (-32768) /* min value of a "short int" */
+#define SHRT_MAX 32767 /* max value of a "short int" */
+#define USHRT_MAX 65535 /* max of "unsigned short int" */
+#define INT_MIN (-2147483647-1) /* min value of an "int" */
+#define INT_MAX 2147483647 /* max value of an "int" */
+#define UINT_MAX 4294967295U /* max value of an "unsigned int" */
+
+#ifndef LLONG_MAX
+#define LLONG_MAX 9223372036854775807LL
+#endif
+
+#if defined(_LP64)
+#define LONG_MAX 9223372036854775807L
+#define ULONG_MAX 18446744073709551615UL
+#else
+#define LONG_MAX 2147483647L /* max value of a 32-bit "long int" */
+#define ULONG_MAX 4294967295UL /* max value of a 32-bit "ulong int" */
+#endif
+
+#endif /* !_KERNEL */
+
+
+typedef uint64_t lx_dev_t;
+typedef uint16_t lx_dev16_t;
+typedef uint32_t lx_ino_t;
+typedef uint64_t lx_ino64_t;
+typedef uint32_t lx_uid_t;
+typedef uint16_t lx_uid16_t;
+typedef uint32_t lx_gid_t;
+typedef uint16_t lx_gid16_t;
+typedef uint32_t lx_off_t;
+typedef uint64_t lx_off64_t;
+typedef uint32_t lx_blksize_t;
+typedef uint32_t lx_blkcnt_t;
+typedef uint64_t lx_blkcnt64_t;
+typedef uint32_t lx_mode_t;
+typedef uint16_t lx_mode16_t;
+
+/*
+ * Linux mangles major/minor numbers into dev_t differently than SunOS.
+ */
+#ifdef _LP64
+#define LX_MAKEDEVICE(maj, min) \
+ (((min) & 0xff) | (((maj) & 0xfff) << 8) | \
+ ((uint64_t)((min) & ~0xff) << 12) | ((uint64_t)((maj) & ~0xfff) << 32))
+
+#define LX_GETMAJOR(lx_dev) ((((lx_dev) >> 8) & 0xfff) | \
+ ((((uint64_t)(lx_dev)) >> 32) & ~0xfff))
+
+#else
+#define LX_MAKEDEVICE(maj, min) \
+ (((min) & 0xff) | (((maj) & 0xfff) << 8) | (((min) & ~0xff) << 12))
+
+#define LX_GETMAJOR(lx_dev) (((lx_dev) >> 8) & 0xfff)
+#endif
+
+#define LX_GETMINOR(lx_dev) (((lx_dev) & 0xff) | (((lx_dev) >> 12) & ~0xff))
+/* Linux supports 20 bits for the minor, and 12 bits for the major number */
+#define LX_MAXMIN 0xfffff
+#define LX_MAXMAJ 0xfff
+
+/*
+ * Certain Linux tools care deeply about major/minor number mapping.
+ * Map virtual disks (zfs datasets, zvols, etc) into a safe reserved range.
+ */
+#define LX_MAJOR_DISK 203
+
+/* LX ptm driver major/minor number */
+#define LX_PTM_MAJOR 5
+#define LX_PTM_MINOR 2
+
+/* LX pts driver major number range */
+#define LX_PTS_MAJOR_MIN 136
+#define LX_PTS_MAJOR_MAX 143
+
+/* LX tty/cons driver major number */
+#define LX_TTY_MAJOR 5
+
+#define LX_UID16_TO_UID32(uid16) \
+ (((uid16) == (lx_uid16_t)-1) ? ((lx_uid_t)-1) : (lx_uid_t)(uid16))
+
+#define LX_GID16_TO_GID32(gid16) \
+ (((gid16) == (lx_gid16_t)-1) ? ((lx_gid_t)-1) : (lx_gid_t)(gid16))
+
+/* Overflow values default to NFS nobody. */
+
+#define UID16_OVERFLOW ((lx_uid16_t)65534)
+#define GID16_OVERFLOW ((lx_gid16_t)65534)
+
+/*
+ * All IDs with high word non-zero are converted to default overflow values to
+ * avoid inadvertent truncation to zero (root) (!).
+ */
+#define LX_UID32_TO_UID16(uid32) \
+ ((((uid32) & 0xffff0000) == 0) ? ((lx_uid16_t)(uid32)) : \
+ (((uid32) == ((lx_uid_t)-1)) ? ((lx_uid16_t)-1) : UID16_OVERFLOW))
+
+#define LX_GID32_TO_GID16(gid32) \
+ ((((gid32) & 0xffff0000) == 0) ? ((lx_gid16_t)(gid32)) : \
+ (((gid32) == ((lx_gid_t)-1)) ? ((lx_gid16_t)-1) : GID16_OVERFLOW))
+
+#define LX_32TO64(lo, hi) \
+ ((uint64_t)((uint64_t)(lo) | ((uint64_t)(hi) << 32)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_TYPES_H */
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_access.c b/usr/src/uts/common/brand/lx/syscall/lx_access.c
new file mode 100644
index 0000000000..8cf836cd7a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_access.c
@@ -0,0 +1,223 @@
+/*
+ * 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) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ *
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/isa_defs.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/cred_impl.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/pathname.h>
+#include <sys/vnode.h>
+#include <sys/uio.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/file.h>
+#include <fs/fs_subr.h>
+#include <c2/audit.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * Determine accessibility of file.
+ */
+
+#define E_OK 010 /* use effective ids */
+#define R_OK 004
+#define W_OK 002
+#define X_OK 001
+
+/*
+ * Convert Linux LX_AT_* flags to SunOS AT_* flags but skip verifying allowed
+ * flags have been passed. This also allows EACCESS/REMOVEDIR to be translated
+ * correctly since on linux they have the same value.
+ *
+ * Some code can actually pass in other bits in the flag. We may have to simply
+ * ignore these, as indicated by the enforce parameter.
+ */
+int
+ltos_at_flag(int lflag, int allow, boolean_t enforce)
+{
+ int sflag = 0;
+
+ if ((lflag & LX_AT_EACCESS) && (allow & AT_EACCESS)) {
+ lflag &= ~LX_AT_EACCESS;
+ sflag |= AT_EACCESS;
+ }
+
+ if ((lflag & LX_AT_REMOVEDIR) && (allow & AT_REMOVEDIR)) {
+ lflag &= ~LX_AT_REMOVEDIR;
+ sflag |= AT_REMOVEDIR;
+ }
+
+ if ((lflag & LX_AT_SYMLINK_NOFOLLOW) && (allow & AT_SYMLINK_NOFOLLOW)) {
+ lflag &= ~LX_AT_SYMLINK_NOFOLLOW;
+ sflag |= AT_SYMLINK_NOFOLLOW;
+ }
+
+ /* right now SunOS doesn't have a _FOLLOW flag, so use a fake one */
+ if ((lflag & LX_AT_SYMLINK_FOLLOW) && (allow & LX_AT_SYMLINK_FOLLOW)) {
+ lflag &= ~LX_AT_SYMLINK_FOLLOW;
+ sflag |= LX_AT_SYMLINK_FOLLOW;
+ }
+
+ /* If lflag is not zero than some flags did not hit the above code. */
+ if (enforce && lflag)
+ return (-1);
+
+ return (sflag);
+}
+
+/*
+ * For illumos, access() does this:
+ * If the process has appropriate privileges, an implementation may indicate
+ * success for X_OK even if none of the execute file permission bits are set.
+ *
+ * But for Linux, access() does this:
+ * If the calling process is privileged (i.e., its real UID is zero), then
+ * an X_OK check is successful for a regular file if execute permission is
+ * enabled for any of the file owner, group, or other.
+ *
+ * Linux used to behave more like illumos on older kernels:
+ * In kernel 2.4 (and earlier) there is some strangeness in the handling
+ * of X_OK tests for superuser. If all categories of execute permission
+ * are disabled for a nondirectory file, then the only access() test that
+ * returns -1 is when mode is specified as just X_OK; if R_OK or W_OK is
+ * also specified in mode, then access() returns 0 for such files.
+ *
+ * So we need to handle the case where a privileged process is checking for
+ * X_OK but none of the execute bits are set on the file. We'll keep the old
+ * 2.4 behavior for 2.4 emulation but use the new behavior for any other
+ * kernel rev.
+ */
+static int
+lx_common_access(char *fname, int fmode, vnode_t *startvp)
+{
+ vnode_t *vp;
+ cred_t *tmpcr;
+ int error;
+ int mode;
+ cred_t *cr;
+ int estale_retry = 0;
+
+ if (fmode & ~(E_OK|R_OK|W_OK|X_OK))
+ return (EINVAL);
+
+ mode = ((fmode & (R_OK|W_OK|X_OK)) << 6);
+
+ cr = CRED();
+
+ /* OK to use effective uid/gid, i.e., no need to crdup(CRED())? */
+ if ((fmode & E_OK) != 0 ||
+ (cr->cr_uid == cr->cr_ruid && cr->cr_gid == cr->cr_rgid)) {
+ tmpcr = cr;
+ crhold(tmpcr);
+ } else {
+ tmpcr = crdup(cr);
+ tmpcr->cr_uid = cr->cr_ruid;
+ tmpcr->cr_gid = cr->cr_rgid;
+ tmpcr->cr_ruid = cr->cr_uid;
+ tmpcr->cr_rgid = cr->cr_gid;
+ }
+
+lookup:
+ if ((error = lookupnameatcred(fname, UIO_USERSPACE, FOLLOW, NULLVPP,
+ &vp, startvp, tmpcr)) != 0) {
+ if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
+ goto lookup;
+ crfree(tmpcr);
+ return (error);
+ }
+
+ if (mode != 0) {
+ error = VOP_ACCESS(vp, mode, 0, tmpcr, NULL);
+ if (error != 0) {
+ if ((error == ESTALE) &&
+ fs_need_estale_retry(estale_retry++)) {
+ VN_RELE(vp);
+ goto lookup;
+ }
+
+ } else if ((fmode & X_OK) != 0 && cr->cr_ruid == 0 &&
+ lx_kern_release_cmp(curproc->p_zone, "2.4.0") > 0) {
+ /* check for incorrect execute success */
+ vattr_t va;
+
+ va.va_mask = AT_MODE;
+ if ((error = VOP_GETATTR(vp, &va, 0, cr, NULL)) == 0) {
+ mode_t m = VTTOIF(va.va_type) | va.va_mode;
+
+ if ((m & S_IFMT) == S_IFREG &&
+ !(m & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+ /* no execute bits set in the mode */
+ error = EACCES;
+ }
+ }
+ }
+ }
+
+ crfree(tmpcr);
+ VN_RELE(vp);
+ return (error);
+}
+
+int
+lx_faccessat(int atfd, char *fname, int fmode, int flag)
+{
+ vnode_t *startvp;
+ int error;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ if ((flag = ltos_at_flag(flag, AT_EACCESS, B_FALSE)) < 0)
+ return (set_errno(EINVAL));
+
+ if (fname == NULL)
+ return (set_errno(EFAULT));
+ if ((error = fgetstartvp(atfd, fname, &startvp)) != 0)
+ return (set_errno(error));
+ if (AU_AUDITING() && startvp != NULL)
+ audit_setfsat_path(1);
+
+ /* Do not allow E_OK unless AT_EACCESS flag is set */
+ if ((flag & AT_EACCESS) == 0)
+ fmode &= ~E_OK;
+
+ error = lx_common_access(fname, fmode, startvp);
+ if (startvp != NULL)
+ VN_RELE(startvp);
+ if (error)
+ return (set_errno(error));
+ return (0);
+}
+
+int
+lx_access(char *fname, int fmode)
+{
+ return (lx_faccessat(LX_AT_FDCWD, fname, fmode, 0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_aio.c b/usr/src/uts/common/brand/lx/syscall/lx_aio.c
new file mode 100644
index 0000000000..12f37ea4c7
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_aio.c
@@ -0,0 +1,45 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+
+
+long
+lx_io_setup(unsigned int nr_events, void **ctxp)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ uintptr_t uargs[2] = {(uintptr_t)nr_events, (uintptr_t)ctxp};
+
+ mutex_enter(&curproc->p_lock);
+ lxpd->l_flags |= LX_PROC_AIO_USED;
+ mutex_exit(&curproc->p_lock);
+
+ ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(ttolwp(curthread), LX_SYS32_io_setup, uargs);
+ } else
+#endif
+ {
+ lx_emulate_user(ttolwp(curthread), LX_SYS_io_setup, uargs);
+ }
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_brk.c b/usr/src/uts/common/brand/lx/syscall/lx_brk.c
new file mode 100644
index 0000000000..19a7577ac0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_brk.c
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+
+/*
+ * The brk() system call needs to be in-kernel because Linux expects a call to
+ * brk(0) to return the current breakpoint. In Solaris, the process breakpoint
+ * is setup and managed by libc. Due to the way we link our libraries and the
+ * need for Linux to manage its own breakpoint, this has to remain in the
+ * kernel.
+ */
+extern int brk(caddr_t);
+
+long
+lx_brk(caddr_t nva)
+{
+ proc_t *p = curproc;
+ klwp_t *lwp = ttolwp(curthread);
+
+ if (nva != 0) {
+ (void) brk(nva);
+
+ /*
+ * Despite claims to the contrary in the manpage, when Linux
+ * brk() fails, errno is left unchanged.
+ */
+ lwp->lwp_errno = 0;
+ }
+ return ((long)(p->p_brkbase + p->p_brksize));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_chmod.c b/usr/src/uts/common/brand/lx/syscall/lx_chmod.c
new file mode 100644
index 0000000000..7783b97cb0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_chmod.c
@@ -0,0 +1,107 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/thread.h>
+#include <sys/klwp.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+long
+lx_vn_chmod(vnode_t *vp, int mode)
+{
+ vattr_t vattr;
+
+ vattr.va_mode = mode & MODEMASK;
+ vattr.va_mask = AT_MODE;
+
+ if (vn_is_readonly(vp)) {
+ return (EROFS);
+ }
+ return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+static long
+lx_fchmodat_wrapper(int fd, char *path, int mode)
+{
+ long error;
+ vnode_t *vp;
+
+ if ((error = lx_vp_at(fd, path, &vp, 0)) != 0) {
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ /*
+ * If the process is in "install mode", return success
+ * if the operation failed due to an absent file.
+ */
+ if (error == ENOENT &&
+ (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+ return (0);
+ }
+ return (set_errno(error));
+ }
+
+ error = lx_vn_chmod(vp, mode);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchmodat(int fd, char *path, int mode)
+{
+ return (lx_fchmodat_wrapper(fd, path, mode));
+}
+
+long
+lx_fchmod(int fd, int mode)
+{
+ file_t *fp;
+ vnode_t *vp;
+ long error;
+
+ /*
+ * In order to do proper O_PATH handling, lx_fchmod cannot leverage
+ * lx_fchmodat with a NULL path since the desired behavior differs.
+ */
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ if (LX_IS_O_PATH(fp)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ releasef(fd);
+
+ error = lx_vn_chmod(vp, mode);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_chmod(char *path, int mode)
+{
+ return (lx_fchmodat_wrapper(LX_AT_FDCWD, path, mode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_chown.c b/usr/src/uts/common/brand/lx/syscall/lx_chown.c
new file mode 100644
index 0000000000..830fba0a73
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_chown.c
@@ -0,0 +1,180 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+
+long
+lx_vn_chown(vnode_t *vp, uid_t uid, gid_t gid)
+{
+ vattr_t vattr;
+ zone_t *zone = crgetzone(CRED());
+
+ if ((uid != (uid_t)-1 && !VALID_UID(uid, zone)) ||
+ (gid != (gid_t)-1 && !VALID_GID(gid, zone))) {
+ return (EINVAL);
+ }
+ vattr.va_uid = uid;
+ vattr.va_gid = gid;
+ vattr.va_mask = 0;
+ if (vattr.va_uid != -1)
+ vattr.va_mask |= AT_UID;
+ if (vattr.va_gid != -1)
+ vattr.va_mask |= AT_GID;
+
+ if (vn_is_readonly(vp)) {
+ return (EROFS);
+ }
+ return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+long
+lx_fchownat_wrapper(int fd, char *path, uid_t uid, gid_t gid, int native_flag)
+{
+ long error;
+ vnode_t *vp;
+
+ if ((error = lx_vp_at(fd, path, &vp, native_flag)) != 0) {
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ /*
+ * If the process is in "install mode", return success
+ * if the operation failed due to an absent file.
+ */
+ if (error == ENOENT &&
+ (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+ return (0);
+ }
+ return (set_errno(error));
+ }
+
+ error = lx_vn_chown(vp, uid, gid);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchown_wrapper(int fd, uid_t uid, gid_t gid)
+{
+ file_t *fp;
+ vnode_t *vp;
+ long error;
+
+ /*
+ * In order to do proper O_PATH handling, lx_fchown cannot leverage
+ * lx_fchownat with a NULL path since the desired behavior differs.
+ */
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ if (LX_IS_O_PATH(fp)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ releasef(fd);
+
+ error = lx_vn_chown(vp, uid, gid);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag)
+{
+ int native_flag = 0;
+
+ if (flag & LX_AT_EMPTY_PATH) {
+ char c;
+
+ /*
+ * According to fchownat(2), when AT_EMPTY_PATH is set: "if
+ * path is an empty string, operate on the file referred to by
+ * fd". We pass NULL in place of the empty string, which
+ * causes fchownat() to operate on the fd we passed without an
+ * additional lookup.
+ */
+ if (copyin(path, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (c == '\0') {
+ path = NULL;
+ }
+
+ flag &= ~LX_AT_EMPTY_PATH;
+ }
+ if (flag & LX_AT_SYMLINK_NOFOLLOW) {
+ flag &= ~LX_AT_SYMLINK_NOFOLLOW;
+ native_flag |= AT_SYMLINK_NOFOLLOW;
+ }
+ if (flag != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ return (lx_fchownat_wrapper(fd, path, uid, gid, native_flag));
+}
+
+long
+lx_fchown(int fd, uid_t uid, gid_t gid)
+{
+ return (lx_fchown_wrapper(fd, uid, gid));
+}
+
+long
+lx_lchown(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid,
+ AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid, 0));
+}
+
+long
+lx_fchown16(int fd, lx_uid16_t uid, lx_gid16_t gid)
+{
+ return (lx_fchown_wrapper(fd, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_lchown16(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid), AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown16(char *path, lx_uid16_t uid, lx_gid16_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid), 0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_clone.c b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
new file mode 100644
index 0000000000..50cdeaeab9
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
@@ -0,0 +1,143 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+
+/*
+ * Our lwp has already been created at this point, so this routine is
+ * responsible for setting up all the state needed to track this as a
+ * linux cloned thread.
+ */
+/* ARGSUSED */
+int
+lx_helper_clone(int64_t *rval, int flags, void *ptidp, void *tls, void *ctidp)
+{
+ struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+ struct lx_proc_data *lproc = ttolxproc(curthread);
+ struct ldt_info info;
+ struct user_desc descr;
+ int tls_index;
+ int entry = -1;
+ int signo;
+
+ signo = flags & LX_CSIGNAL;
+ if (signo < 0 || signo > LX_NSIG)
+ return (set_errno(EINVAL));
+
+ if (!(flags & LX_CLONE_THREAD)) {
+ lproc->l_signal = signo;
+ } else {
+ if (flags & LX_CLONE_SETTLS) {
+ if (get_udatamodel() == DATAMODEL_ILP32) {
+ if (copyin((caddr_t)tls, &info, sizeof (info)))
+ return (set_errno(EFAULT));
+
+ if (LDT_INFO_EMPTY(&info))
+ return (set_errno(EINVAL));
+
+ entry = info.entry_number;
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ tls_index = entry - GDT_TLSMIN;
+
+ /*
+ * Convert the user-space structure into a real
+ * x86 descriptor and copy it into this LWP's
+ * TLS array. We also load it into the GDT.
+ */
+ LDT_INFO_TO_DESC(&info, &descr);
+ bcopy(&descr, &lwpd->br_tls[tls_index],
+ sizeof (descr));
+ lx_set_gdt(entry, &lwpd->br_tls[tls_index]);
+ } else {
+ /*
+ * Set the Linux %fsbase for this LWP. We will
+ * restore it the next time we return to Linux
+ * via setcontext()/lx_restorecontext().
+ */
+ lwpd->br_lx_fsbase = (uintptr_t)tls;
+ }
+ }
+
+ lwpd->br_clear_ctidp =
+ (flags & LX_CLONE_CHILD_CLEARTID) ? ctidp : NULL;
+
+ if (signo && ! (flags & LX_CLONE_DETACH))
+ lwpd->br_signal = signo;
+ else
+ lwpd->br_signal = 0;
+
+ if (flags & LX_CLONE_THREAD)
+ lwpd->br_tgid = curthread->t_procp->p_pid;
+
+ if (flags & LX_CLONE_PARENT)
+ lwpd->br_ppid = 0;
+
+ if ((flags & LX_CLONE_CHILD_SETTID) && (ctidp != NULL) &&
+ (suword32(ctidp, lwpd->br_pid) != 0)) {
+ if (entry >= 0)
+ lx_clear_gdt(entry);
+ return (set_errno(EFAULT));
+ }
+ if ((flags & LX_CLONE_PARENT_SETTID) && (ptidp != NULL) &&
+ (suword32(ptidp, lwpd->br_pid) != 0)) {
+ if (entry >= 0)
+ lx_clear_gdt(entry);
+ return (set_errno(EFAULT));
+ }
+ }
+
+ *rval = lwpd->br_pid;
+ return (0);
+}
+
+long
+lx_set_tid_address(int *tidp)
+{
+ struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+ long rv;
+
+ lwpd->br_clear_ctidp = tidp;
+
+ if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+ rv = 1;
+ } else {
+ rv = lwpd->br_pid;
+ }
+
+ return (rv);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_close.c b/usr/src/uts/common/brand/lx/syscall/lx_close.c
new file mode 100644
index 0000000000..8df0cbbe2f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_close.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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+
+
+extern int close(int);
+
+long
+lx_close(int fdes)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ boolean_t aio_used;
+ uintptr_t uargs[1] = {(uintptr_t)fdes};
+
+ mutex_enter(&curproc->p_lock);
+ aio_used = ((lxpd->l_flags & LX_PROC_AIO_USED) != 0);
+ mutex_exit(&curproc->p_lock);
+
+ if (!aio_used) {
+ return (close(fdes));
+ }
+
+ /*
+ * If the process potentially has any AIO contexts open, the userspace
+ * emulation must be used so that libc can properly maintain its state.
+ */
+
+ ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(ttolwp(curthread), LX_SYS32_close, uargs);
+ } else
+#endif
+ {
+ lx_emulate_user(ttolwp(curthread), LX_SYS_close, uargs);
+ }
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_cpu.c b/usr/src/uts/common/brand/lx/syscall/lx_cpu.c
new file mode 100644
index 0000000000..b0a92394dc
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_cpu.c
@@ -0,0 +1,36 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We support neither the second argument (NUMA node), nor the third (obsolete
+ * pre-2.6.24 caching functionality which was ultimately broken).
+ */
+/* ARGSUSED1 */
+long
+lx_getcpu(unsigned int *cpu, uintptr_t p2, uintptr_t p3)
+{
+ unsigned int curcpu = curthread->t_cpu->cpu_id;
+
+ if (copyout(&curcpu, cpu, sizeof (curcpu)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_dup.c b/usr/src/uts/common/brand/lx/syscall/lx_dup.c
new file mode 100644
index 0000000000..d0f513753c
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_dup.c
@@ -0,0 +1,53 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+/* From usr/src/uts/common/syscall/fcntl.c */
+extern int fcntl(int, int, intptr_t);
+
+long
+lx_dup(int fd)
+{
+ return (fcntl(fd, F_DUPFD, 0));
+}
+
+long
+lx_dup2(int oldfd, int newfd)
+{
+ return (fcntl(oldfd, F_DUP2FD, newfd));
+}
+
+long
+lx_dup3(int oldfd, int newfd, int flags)
+{
+ int rc;
+
+ /* The only valid flag is O_CLOEXEC. */
+ if (flags & ~LX_O_CLOEXEC)
+ return (set_errno(EINVAL));
+
+ /* Only DUP2FD_CLOEXEC returns EINVAL on the same fd's */
+ if (oldfd == newfd)
+ return (set_errno(EINVAL));
+
+ rc = fcntl(oldfd, (flags == 0) ? F_DUP2FD : F_DUP2FD_CLOEXEC, newfd);
+ return (rc);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_epoll.c b/usr/src/uts/common/brand/lx/syscall/lx_epoll.c
new file mode 100644
index 0000000000..8eead951f4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_epoll.c
@@ -0,0 +1,279 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/epoll.h>
+#include <sys/devpoll.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/vnode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_signal.h>
+
+static major_t devpoll_major = 0;
+
+static boolean_t
+lx_epoll_isvalid(file_t *fp)
+{
+ vnode_t *vp = fp->f_vnode;
+
+ if (vp->v_type == VCHR && getmajor(vp->v_rdev) == devpoll_major)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+long
+lx_epoll_create1(int flags)
+{
+ int err, fd, rv;
+ int fmode = FREAD | FWRITE;
+ boolean_t cloexec = B_FALSE;
+ vnode_t *vp = NULL;
+ file_t *fp = NULL;
+
+ if (flags & EPOLL_CLOEXEC) {
+ cloexec = B_TRUE;
+ flags &= ~EPOLL_CLOEXEC;
+ }
+ if (flags != 0) {
+ /* No other flags accepted at this time */
+ return (set_errno(EINVAL));
+ }
+
+ if (falloc((vnode_t *)NULL, fmode, &fp, &fd) != 0) {
+ err = EMFILE;
+ goto error;
+ }
+ if (ldi_vp_from_name("/devices/pseudo/poll@0:poll", &vp) != 0) {
+ err = ENOENT;
+ goto error;
+ }
+ if ((err = VOP_OPEN(&vp, fmode | FKLYR, CRED(), NULL)) != 0) {
+ goto error;
+ }
+ err = VOP_IOCTL(vp, DP_EPOLLCOMPAT, 0, fmode, CRED(), &rv, NULL);
+ if (err != 0) {
+ (void) VOP_CLOSE(vp, fmode, 0, 0, CRED(), NULL);
+ goto error;
+ }
+
+ devpoll_major = getmajor(vp->v_rdev);
+
+ fp->f_vnode = vp;
+ mutex_exit(&fp->f_tlock);
+ setf(fd, fp);
+ if (cloexec) {
+ f_setfd(fd, FD_CLOEXEC);
+ }
+ return (fd);
+
+error:
+ if (fp != NULL) {
+ setf(fd, NULL);
+ unfalloc(fp);
+ }
+ if (vp != NULL) {
+ VN_RELE(vp);
+ }
+ return (set_errno(err));
+}
+
+long
+lx_epoll_create(int size)
+{
+ if (size <= 0) {
+ return (set_errno(EINVAL));
+ }
+
+ return (lx_epoll_create1(0));
+}
+
+
+/* Match values from libc implementation */
+#define EPOLLIGNORED (EPOLLMSG | EPOLLWAKEUP)
+#define EPOLLSWIZZLED \
+ (EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM)
+
+long
+lx_epoll_ctl(int fd, int op, int pfd, void *event)
+{
+ epoll_event_t epevent;
+ dvpoll_epollfd_t dpevent[2];
+ file_t *fp;
+ iovec_t aiov;
+ uio_t auio;
+ uint32_t events, ev = 0;
+ int error = 0, i = 0;
+
+ dpevent[i].dpep_pollfd.fd = pfd;
+ switch (op) {
+ case EPOLL_CTL_DEL:
+ dpevent[i].dpep_pollfd.events = POLLREMOVE;
+ break;
+
+ case EPOLL_CTL_MOD:
+ /*
+ * In the modify case, we pass down two events: one to
+ * remove the event and another to add it back.
+ */
+ dpevent[i++].dpep_pollfd.events = POLLREMOVE;
+ dpevent[i].dpep_pollfd.fd = pfd;
+ /* FALLTHROUGH */
+
+ case EPOLL_CTL_ADD:
+ if (copyin(event, &epevent, sizeof (epevent)) != 0)
+ return (set_errno(EFAULT));
+
+ /*
+ * Mask off the events that we ignore, and then swizzle the
+ * events for which our values differ from their epoll(7)
+ * equivalents.
+ */
+ events = epevent.events;
+ ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
+
+ if (events & EPOLLRDHUP)
+ ev |= POLLRDHUP;
+ if (events & EPOLLET)
+ ev |= POLLET;
+ if (events & EPOLLONESHOT)
+ ev |= POLLONESHOT;
+ if (events & EPOLLWRNORM)
+ ev |= POLLWRNORM;
+ if (events & EPOLLWRBAND)
+ ev |= POLLWRBAND;
+
+ dpevent[i].dpep_data = epevent.data.u64;
+ dpevent[i].dpep_pollfd.events = ev;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+
+ aiov.iov_base = (void *)dpevent;
+ aiov.iov_len = sizeof (dvpoll_epollfd_t) * (i + 1);
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = aiov.iov_len;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_loffset = 0;
+ auio.uio_fmode = fp->f_flag;
+
+ error = VOP_WRITE(fp->f_vnode, &auio, 1, fp->f_cred, NULL);
+
+ releasef(fd);
+ if (error == ELOOP) {
+ /*
+ * In the case of descriptor loops, /dev/poll emits a more
+ * descriptive error than Linux epoll consumers would expect.
+ */
+ return (set_errno(EINVAL));
+ } else if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_epoll_wait(int fd, void *events, int maxevents, int timeout)
+{
+ struct dvpoll arg;
+ file_t *fp;
+ int rv = 0, error, flag;
+
+ if (maxevents <= 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+
+ arg.dp_nfds = maxevents;
+ arg.dp_timeout = timeout;
+ arg.dp_fds = (pollfd_t *)events;
+ flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, DP_POLL, (uintptr_t)&arg, flag,
+ fp->f_cred, &rv, NULL);
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (rv);
+}
+
+long
+lx_epoll_pwait(int fd, void *events, int maxevents, int timeout, void *sigmask)
+{
+ struct dvpoll arg;
+ file_t *fp;
+ int rv = 0, error, flag;
+ k_sigset_t ksig;
+
+ if (maxevents <= 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+ if (sigmask != NULL) {
+ lx_sigset_t lsig;
+
+ if (copyin(sigmask, &lsig, sizeof (lsig)) != 0) {
+ releasef(fd);
+ return (set_errno(EFAULT));
+ }
+ lx_ltos_sigset(&lsig, &ksig);
+ arg.dp_setp = (sigset_t *)&ksig;
+ } else {
+ arg.dp_setp = NULL;
+ }
+
+ arg.dp_nfds = maxevents;
+ arg.dp_timeout = timeout;
+ arg.dp_fds = (pollfd_t *)events;
+ flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, DP_PPOLL, (uintptr_t)&arg, flag,
+ fp->f_cred, &rv, NULL);
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (rv);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c b/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c
new file mode 100644
index 0000000000..61f9b936f2
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c
@@ -0,0 +1,103 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_misc.h>
+
+/*
+ * Based on illumos posix_fadvise which does nothing. The only difference is
+ * that on Linux an fd refering to a pipe or FIFO returns EINVAL. The Linux
+ * POSIX_FADV_* values are the same as the illumos values. See how the 32-bit
+ * glibc calls fadvise64; the offeset is a 64-bit value, but the length is not.
+ * fadvise64_64 passes both the offset and length as 64-bit values. The 64-bit
+ * fadvise64 caller always passes 64-bit values for the offset and length.
+ */
+
+/*
+ * This is the fadvise64 function used by 64-bit callers, and by 32-bit callers
+ * after they have adjusted their arguments.
+ */
+/* ARGSUSED */
+int
+lx_fadvise64(int fd, off64_t offset, off64_t len, int advice)
+{
+ file_t *fp;
+ boolean_t is_fifo;
+
+ switch (advice) {
+ case POSIX_FADV_NORMAL:
+ case POSIX_FADV_RANDOM:
+ case POSIX_FADV_SEQUENTIAL:
+ case POSIX_FADV_WILLNEED:
+ case POSIX_FADV_DONTNEED:
+ case POSIX_FADV_NOREUSE:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if (len < 0)
+ return (set_errno(EINVAL));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+ is_fifo = (fp->f_vnode->v_type == VFIFO);
+ releasef(fd);
+
+ if (is_fifo)
+ return (set_errno(ESPIPE));
+
+ return (0);
+}
+
+/*
+ * This is the fadvise64 function used by 32-bit callers. Linux passes the
+ * 64-bit offset by concatenating consecutive arguments. We must perform the
+ * same conversion here.
+ */
+long
+lx_fadvise64_32(int fd, uint32_t off_lo, uint32_t off_hi, int32_t len,
+ int advice)
+{
+ off64_t offset;
+
+ offset = off_hi;
+ offset = offset << 32;
+ offset |= off_lo;
+
+ return (lx_fadvise64(fd, offset, (off64_t)len, advice));
+}
+
+/*
+ * This function is only used by 32-bit callers. Linux passes the 64-bit offset
+ * and length by concatenating consecutive arguments. We must perform the same
+ * conversion here.
+ */
+long
+lx_fadvise64_64(int fd, uint32_t off_lo, uint32_t off_hi, uint32_t len_lo,
+ uint32_t len_hi, int advice)
+{
+ off64_t offset;
+ off64_t len;
+
+ offset = off_hi;
+ offset = offset << 32;
+ offset |= off_lo;
+ len = len_hi;
+ len = len << 32;
+ len |= len_lo;
+
+ return (lx_fadvise64(fd, offset, len, advice));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c b/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c
new file mode 100644
index 0000000000..338e4399fe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c
@@ -0,0 +1,251 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/nbmlock.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/sdt.h>
+
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+
+#define LX_FALLOC_FL_KEEP_SIZE 0x01
+#define LX_FALLOC_FL_PUNCH_HOLE 0x02
+#define LX_FALLOC_FL_NO_HIDE_STALE 0x04
+#define LX_FALLOC_FL_COLLAPSE_RANGE 0x08
+#define LX_FALLOC_FL_ZERO_RANGE 0x10
+
+#define LX_FALLOC_VALID (LX_FALLOC_FL_KEEP_SIZE | LX_FALLOC_FL_PUNCH_HOLE | \
+ LX_FALLOC_FL_NO_HIDE_STALE | LX_FALLOC_FL_COLLAPSE_RANGE | \
+ LX_FALLOC_FL_ZERO_RANGE)
+
+#define LX_FALLOC_UNSUPP (LX_FALLOC_FL_NO_HIDE_STALE | \
+ LX_FALLOC_FL_COLLAPSE_RANGE)
+
+long
+lx_fallocate(int fd, int mode, off_t offset, off_t len)
+{
+ int error = 0;
+ file_t *fp;
+ vnode_t *vp;
+ int64_t tot;
+ struct flock64 bf;
+ vattr_t vattr;
+ u_offset_t f_offset;
+ boolean_t in_crit = B_FALSE;
+
+ /*
+ * Error checking is in a specific order to make LTP happy.
+ */
+
+ tot = offset + len;
+ if (tot > (LLONG_MAX / (int64_t)1024))
+ return (set_errno(EFBIG));
+
+ if (mode & LX_FALLOC_UNSUPP)
+ return (set_errno(EOPNOTSUPP));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ if ((fp->f_flag & FWRITE) == 0) {
+ error = EBADF;
+ goto done;
+ }
+
+ vp = fp->f_vnode;
+ if (vp->v_type != VREG) {
+ error = EINVAL;
+ goto done;
+ }
+
+ if (offset < 0 || len <= 0) {
+ error = EINVAL;
+ goto done;
+ }
+
+ if (tot < 0LL) {
+ error = EFBIG;
+ goto done;
+ }
+
+ if ((mode & ~LX_FALLOC_VALID) != 0) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * If this is the only flag then we don't actually do any work.
+ */
+ if (mode == LX_FALLOC_FL_KEEP_SIZE)
+ goto done;
+
+ bzero(&bf, sizeof (bf));
+
+ vattr.va_mask = AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+ goto done;
+
+ if (mode == 0) {
+ /* Nothing to do if not extending the file */
+ if (vattr.va_size >= tot)
+ goto done;
+
+ /* Extend the file. */
+ bf.l_start = (off64_t)tot;
+ bf.l_len = (off64_t)0;
+
+ } else if (mode & LX_FALLOC_FL_PUNCH_HOLE) {
+ /*
+ * Deallocate space in the file.
+ */
+ if ((mode & LX_FALLOC_FL_KEEP_SIZE) == 0) {
+ /* this flag is required with punch hole */
+ error = EINVAL;
+ goto done;
+ }
+
+ if (mode &
+ ~(LX_FALLOC_FL_PUNCH_HOLE | LX_FALLOC_FL_KEEP_SIZE)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Make sure we don't extend since keep_size is set. */
+ if (vattr.va_size < tot) {
+ if (offset > vattr.va_size)
+ goto done;
+ len = (off_t)vattr.va_size - offset;
+ }
+
+ bf.l_start = (off64_t)offset;
+ bf.l_len = (off64_t)len;
+
+ } else if (mode & LX_FALLOC_FL_ZERO_RANGE) {
+ /*
+ * Zero out the space in the file.
+ */
+ if (mode &
+ ~(LX_FALLOC_FL_ZERO_RANGE | LX_FALLOC_FL_KEEP_SIZE)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Make sure we don't extend when keep_size is set. */
+ if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+ if (offset > vattr.va_size)
+ goto done;
+ len = vattr.va_size - offset;
+ }
+
+ bf.l_start = (off64_t)offset;
+ bf.l_len = (off64_t)len;
+ } else {
+ /* We should have already handled all flags */
+ VERIFY(0);
+ }
+
+ /*
+ * Check for locks in the range.
+ */
+ f_offset = fp->f_offset;
+ error = flock_check(vp, &bf, f_offset, MAXOFF_T);
+ if (error != 0)
+ goto done;
+
+ /*
+ * Check for conflicting non-blocking mandatory locks.
+ * We need to get the size again under nbl_start_crit.
+ */
+ if (nbl_need_check(vp)) {
+ u_offset_t begin;
+ ssize_t length;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ vattr.va_mask = AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+ goto done;
+
+ /*
+ * Make sure we don't extend when keep_size is set.
+ */
+ if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+ ASSERT(mode & (LX_FALLOC_FL_PUNCH_HOLE |
+ LX_FALLOC_FL_ZERO_RANGE));
+
+ /*
+ * If the size grew we can short-circuit the rest of
+ * the work, otherwise adjust bf for the vop_space
+ * call.
+ */
+ if (offset >= vattr.va_size)
+ goto done;
+ len = vattr.va_size - offset;
+ bf.l_len = (off64_t)len;
+ }
+
+ if (offset > vattr.va_size) {
+ begin = vattr.va_size;
+ length = offset - vattr.va_size;
+ } else {
+ begin = offset;
+ length = vattr.va_size - offset;
+ }
+
+ if (nbl_conflict(vp, NBL_WRITE, begin, length, 0, NULL)) {
+ error = EACCES;
+ goto done;
+ }
+ }
+
+ error = VOP_SPACE(vp, F_FREESP, &bf, 0, f_offset, fp->f_cred, NULL);
+
+done:
+ if (in_crit)
+ nbl_end_crit(vp);
+
+ releasef(fd);
+ if (error != 0)
+ return (set_errno(error));
+
+ return (0);
+}
+
+long
+lx_fallocate32(int fd, int mode, uint32_t offl, uint32_t offh, uint32_t lenl,
+ uint32_t lenh)
+{
+ int64_t offset = 0, len = 0;
+
+ /*
+ * From 32-bit callers, Linux passes the 64-bit offset and len by
+ * concatenating consecutive arguments. We must perform the same
+ * conversion here.
+ */
+ offset = offh;
+ offset = offset << 32;
+ offset |= offl;
+ len = lenh;
+ len = len << 32;
+ len |= lenl;
+
+ return (lx_fallocate(fd, mode, (off_t)offset, (off_t)len));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c b/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c
new file mode 100644
index 0000000000..c812d94fc7
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c
@@ -0,0 +1,660 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/pathname.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/fs/fifonode.h>
+#include <sys/strsubr.h>
+#include <sys/stream.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+
+
+int
+lx_vp_at(int fd, char *upath, vnode_t **vpp, int flag)
+{
+ vnode_t *startvp;
+ int error;
+
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+
+ if ((error = fgetstartvp(fd, upath, &startvp)) != 0) {
+ return (error);
+ }
+
+ if (upath != NULL) {
+ uio_seg_t seg = UIO_USERSPACE;
+
+ error = lookupnameat(upath, seg,
+ (flag == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW,
+ NULLVPP, vpp, startvp);
+ if (startvp != NULL) {
+ VN_RELE(startvp);
+ }
+ return (error);
+ } else {
+ /* VN_HOLD was established in fgetstartvp */
+ *vpp = startvp;
+ VERIFY(*vpp);
+ return (0);
+ }
+}
+
+#define LTOS_FLOCK(l, s) \
+{ \
+ s->l_type = ltos_type(l->l_type); \
+ s->l_whence = l->l_whence; \
+ s->l_start = l->l_start; \
+ s->l_len = l->l_len; \
+ s->l_sysid = 0; /* not defined in linux */ \
+ s->l_pid = (pid_t)l->l_pid; \
+}
+
+#define STOL_FLOCK(s, l) \
+{ \
+ l->l_type = stol_type(s->l_type); \
+ l->l_whence = s->l_whence; \
+ l->l_start = s->l_start; \
+ l->l_len = s->l_len; \
+ l->l_pid = (int)s->l_pid; \
+}
+
+static short
+ltos_type(short l_type)
+{
+ switch (l_type) {
+ case LX_F_RDLCK:
+ return (F_RDLCK);
+ case LX_F_WRLCK:
+ return (F_WRLCK);
+ case LX_F_UNLCK:
+ return (F_UNLCK);
+ default:
+ return (-1);
+ }
+}
+
+static short
+stol_type(short l_type)
+{
+ switch (l_type) {
+ case F_RDLCK:
+ return (LX_F_RDLCK);
+ case F_WRLCK:
+ return (LX_F_WRLCK);
+ case F_UNLCK:
+ return (LX_F_UNLCK);
+ default:
+ /* can't ever happen */
+ return (0);
+ }
+}
+
+static void
+ltos_flock(struct lx_flock *l, struct flock64 *s)
+{
+ LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock(struct flock64 *s, struct lx_flock *l)
+{
+ STOL_FLOCK(s, l)
+}
+
+static void
+ltos_flock64(struct lx_flock64_32 *l, struct flock64 *s)
+{
+ LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock64(struct flock64 *s, struct lx_flock64_32 *l)
+{
+ STOL_FLOCK(s, l)
+}
+
+static int
+lx_fcntl_getfl(int fd)
+{
+ int retval;
+ int rc;
+
+ retval = fcntl(fd, F_GETFL, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ if ((retval & O_ACCMODE) == O_RDONLY)
+ rc = LX_O_RDONLY;
+ else if ((retval & O_ACCMODE) == O_WRONLY)
+ rc = LX_O_WRONLY;
+ else
+ rc = LX_O_RDWR;
+ /* O_NDELAY != O_NONBLOCK, so we need to check for both */
+ if (retval & O_NDELAY)
+ rc |= LX_O_NDELAY;
+ if (retval & O_NONBLOCK)
+ rc |= LX_O_NONBLOCK;
+ if (retval & O_APPEND)
+ rc |= LX_O_APPEND;
+ if (retval & O_SYNC)
+ rc |= LX_O_SYNC;
+ if (retval & O_LARGEFILE)
+ rc |= LX_O_LARGEFILE;
+ if (retval & FASYNC)
+ rc |= LX_O_ASYNC;
+
+ return (rc);
+}
+
+#define LX_SETFL_MASK (O_NONBLOCK | O_APPEND | O_SYNC | FASYNC);
+
+static int
+lx_fcntl_setfl(int fd, ulong_t arg)
+{
+ int flags;
+
+ /*
+ * When performing fcntl(F_SETFL), only certain flags are
+ * allowed to be manipulated. A mask is used to preserve
+ * other flags, such as those which are specified during
+ * open(2). The mask on Linux excludes O_LARGEFILE from
+ * being manipulated, whereas illumos expects the flag to
+ * be set. In order to properly preserve the O_LARGEFILE
+ * (FOFFMAX) state, we must first query for it via
+ * fcntl(F_GETFL) so that the value can be carried
+ * through.
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ flags &= ~LX_SETFL_MASK;
+
+ /* LX_O_NDELAY == LX_O_NONBLOCK, so we only check for one */
+ if (arg & LX_O_NDELAY)
+ flags |= O_NONBLOCK;
+ if (arg & LX_O_APPEND)
+ flags |= O_APPEND;
+ if (arg & LX_O_SYNC)
+ flags |= O_SYNC;
+ if (arg & LX_O_ASYNC)
+ flags |= FASYNC;
+
+ return (fcntl(fd, F_SETFL, flags));
+}
+
+/* The default unprivileged limit in Linux is 1MB */
+static int lx_pipe_max_size = 1048576;
+
+static int
+lx_fcntl_pipesz(int fd, int cmd, ulong_t arg)
+{
+ file_t *fp;
+ vnode_t *vp;
+ stdata_t *str;
+ int err = 0, res = 0;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ if (vp->v_type != VFIFO || vp->v_op != fifo_vnodeops) {
+ err = EBADF;
+ goto out;
+ }
+ VERIFY((str = vp->v_stream) != NULL);
+
+ if (cmd == LX_F_SETPIPE_SZ) {
+ stdata_t *mate;
+ intptr_t val = arg;
+
+ if (val < PAGESIZE || val > lx_pipe_max_size) {
+ err = EINVAL;
+ goto out;
+ }
+ if (!STRMATED(str)) {
+ err = strqset(RD(str->sd_wrq), QHIWAT, 0, val);
+ goto out;
+ }
+
+ /*
+ * Ensure consistent order so the set operation is always
+ * attempted on the "higher" stream first.
+ */
+ if (str > str->sd_mate) {
+ VERIFY((mate = str->sd_mate) != NULL);
+ } else {
+ mate = str;
+ VERIFY((str = mate->sd_mate) != NULL);
+ }
+
+ /*
+ * While it is unfortunate that an error could occur for the
+ * latter half of the stream pair, there is little to be done
+ * about it aside from reporting the failure.
+ */
+ if ((err = strqset(RD(str->sd_wrq), QHIWAT, 0, val)) != 0) {
+ goto out;
+ }
+ err = strqset(RD(mate->sd_wrq), QHIWAT, 0, val);
+ } else if (cmd == LX_F_GETPIPE_SZ) {
+ size_t val;
+
+ err = strqget(RD(str->sd_wrq), QHIWAT, 0, &val);
+ res = val;
+ } else {
+ /* NOTREACHED */
+ ASSERT(0);
+ }
+
+out:
+ releasef(fd);
+ if (err != 0) {
+ return (set_errno(err));
+ }
+ return (res);
+}
+
+static int
+lx_fcntl_common(int fd, int cmd, ulong_t arg)
+{
+ int rc = 0;
+ pid_t pid;
+ int error;
+ int rv;
+ int32_t flag;
+ file_t *fp;
+
+ /*
+ * We depend on the call to fcntl to set the errno if necessary.
+ */
+ ttolwp(curthread)->lwp_errno = 0;
+
+ switch (cmd) {
+ case LX_F_SETSIG:
+ case LX_F_GETSIG:
+ case LX_F_SETLEASE:
+ case LX_F_GETLEASE:
+ case LX_F_NOTIFY:
+ case LX_F_CANCELLK:
+ {
+ char buf[80];
+
+ (void) snprintf(buf, sizeof (buf),
+ "unsupported fcntl command: %d", cmd);
+ lx_unsupported(buf);
+ }
+ return (set_errno(ENOTSUP));
+
+ case LX_F_DUPFD:
+ rc = fcntl(fd, F_DUPFD, arg);
+ break;
+
+ case LX_F_DUPFD_CLOEXEC:
+ rc = fcntl(fd, F_DUPFD_CLOEXEC, arg);
+ break;
+
+ case LX_F_GETFD:
+ rc = fcntl(fd, F_GETFD, 0);
+ break;
+
+ case LX_F_SETFD:
+ rc = fcntl(fd, F_SETFD, arg);
+ break;
+
+ case LX_F_GETFL:
+ rc = lx_fcntl_getfl(fd);
+ break;
+
+ case LX_F_SETFL:
+ rc = lx_fcntl_setfl(fd, arg);
+ break;
+
+ case LX_F_SETOWN:
+ pid = (pid_t)arg;
+ if (pid == 1) {
+ /* Setown for the init process uses the real pid. */
+ pid = curzone->zone_proc_initpid;
+ }
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ rv = 0;
+
+ flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, FIOSETOWN, (intptr_t)&pid,
+ flag, CRED(), &rv, NULL);
+ releasef(fd);
+ if (error != 0) {
+ /*
+ * On illumos F_SETOWN is only defined for sockets, but
+ * some apps hardcode to do this fcntl on other devices
+ * (e.g. /dev/tty) to setup signal handling. If the
+ * app is only setting itself to be the signal
+ * handler, we pretend to succeed.
+ */
+ if (error != EINVAL ||
+ curthread->t_procp->p_pid != pid) {
+ return (set_errno(error));
+ }
+ }
+
+ rc = 0;
+ break;
+
+ case LX_F_GETOWN:
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ rv = 0;
+
+ flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, FIOGETOWN, (intptr_t)&pid,
+ flag, CRED(), &rv, NULL);
+ releasef(fd);
+ if (error != 0)
+ return (set_errno(error));
+
+ if (pid == curzone->zone_proc_initpid) {
+ /* Getown for the init process returns 1. */
+ pid = 1;
+ }
+
+ rc = pid;
+ break;
+
+ case LX_F_SETPIPE_SZ:
+ case LX_F_GETPIPE_SZ:
+ rc = lx_fcntl_pipesz(fd, cmd, arg);
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ return (rc);
+}
+
+static int
+lx_fcntl_lock_cmd_to_s(int lx_cmd)
+{
+ switch (lx_cmd) {
+ case LX_F_GETLK:
+ return (F_GETLK);
+ case LX_F_SETLK:
+ return (F_SETLK);
+ case LX_F_SETLKW:
+ return (F_SETLKW);
+ case LX_F_GETLK64:
+ return (F_GETLK64);
+ case LX_F_SETLK64:
+ return (F_SETLK64);
+ case LX_F_SETLKW64:
+ return (F_SETLKW64);
+ default:
+ VERIFY(0);
+ /*NOTREACHED*/
+ return (0);
+ }
+}
+
+/*
+ * This is a pain but we can't re-use the fcntl code for locking since it does
+ * its own copyin/copyout for the flock struct. Since we have to convert the
+ * struct we have to do our own copyin/out. Thus we replicate the fcntl code for
+ * these 3 cmds. Luckily it's not much.
+ */
+static int
+lx_fcntl_lock(int fd, int lx_cmd, void *arg)
+{
+ int cmd;
+ int error = 0;
+ file_t *fp;
+ vnode_t *vp;
+ int flag;
+ offset_t maxoffset;
+ u_offset_t offset;
+ model_t datamodel;
+ lx_flock_t lxflk;
+ lx_flock64_32_t lxflk64;
+ struct flock64 bf;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ maxoffset = MAXOFF_T;
+ datamodel = DATAMODEL_NATIVE;
+#if defined(_SYSCALL32_IMPL)
+ if ((datamodel = get_udatamodel()) == DATAMODEL_ILP32)
+ maxoffset = MAXOFF32_T;
+#endif
+ vp = fp->f_vnode;
+ flag = fp->f_flag;
+ offset = fp->f_offset;
+
+ cmd = lx_fcntl_lock_cmd_to_s(lx_cmd);
+
+ switch (cmd) {
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ if (datamodel == DATAMODEL_NATIVE) {
+ if (copyin(arg, &lxflk, sizeof (lx_flock_t)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ lx_flock32_t lxflk32;
+
+ if (copyin(arg, &lxflk32, sizeof (lxflk32)) != 0) {
+ error = EFAULT;
+ break;
+ }
+
+ lxflk.l_type = lxflk32.l_type;
+ lxflk.l_whence = lxflk32.l_whence;
+ lxflk.l_start = (off64_t)lxflk32.l_start;
+ lxflk.l_len = (off64_t)lxflk32.l_len;
+ lxflk.l_pid = lxflk32.l_pid;
+ }
+#endif /* _SYSCALL32_IMPL */
+
+ ltos_flock(&lxflk, &bf);
+
+ if ((error = flock_check(vp, &bf, offset, maxoffset)) != 0)
+ break;
+
+ if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+ fp->f_cred, NULL)) != 0)
+ break;
+
+ if (cmd != F_GETLK)
+ break;
+
+ /*
+ * The command is GETLK, return result.
+ */
+ stol_flock(&bf, &lxflk);
+
+ /*
+ * If no lock is found, only the type field is changed.
+ */
+ if (lxflk.l_type == LX_F_UNLCK) {
+ /* l_type always first entry, always a short */
+ if (copyout(&lxflk.l_type, &((lx_flock_t *)arg)->l_type,
+ sizeof (lxflk.l_type)))
+ error = EFAULT;
+ break;
+ }
+
+ if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ if (datamodel == DATAMODEL_NATIVE) {
+ if (copyout(&lxflk, arg, sizeof (lxflk)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ lx_flock32_t lxflk32;
+
+ if (bf.l_start > MAXOFF32_T || bf.l_len > MAXOFF32_T) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ lxflk32.l_type = lxflk.l_type;
+ lxflk32.l_whence = lxflk.l_whence;
+ lxflk32.l_start = lxflk.l_start;
+ lxflk32.l_len = lxflk.l_len;
+ lxflk32.l_pid = lxflk.l_pid;
+
+ if (copyout(&lxflk32, arg, sizeof (lxflk32)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#endif /* _SYSCALL32_IMPL */
+ break;
+
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ /*
+ * Large File support is only used for ILP32 apps.
+ */
+ if (datamodel != DATAMODEL_ILP32) {
+ error = EINVAL;
+ break;
+ }
+
+ if (cmd == F_GETLK64)
+ cmd = F_GETLK;
+ else if (cmd == F_SETLK64)
+ cmd = F_SETLK;
+ else if (cmd == F_SETLKW64)
+ cmd = F_SETLKW;
+
+ if (copyin(arg, &lxflk64, sizeof (lxflk64)) != 0) {
+ error = EFAULT;
+ break;
+ }
+
+ ltos_flock64(&lxflk64, &bf);
+
+ if ((error = flock_check(vp, &bf, offset, MAXOFFSET_T)) != 0)
+ break;
+
+ if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+ fp->f_cred, NULL)) != 0)
+ break;
+
+ if (cmd != F_GETLK)
+ break;
+
+ /*
+ * The command is GETLK, return result.
+ */
+ stol_flock64(&bf, &lxflk64);
+
+ /*
+ * If no lock is found, only the type field is changed.
+ */
+ if (lxflk64.l_type == LX_F_UNLCK) {
+ /* l_type always first entry, always a short */
+ if (copyout(&lxflk64.l_type,
+ &((lx_flock64_t *)arg)->l_type,
+ sizeof (lxflk64.l_type)))
+ error = EFAULT;
+ break;
+ }
+
+ if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ if (copyout(&lxflk64, arg, sizeof (lxflk64)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ break;
+ }
+
+ releasef(fd);
+ if (error)
+ return (set_errno(error));
+
+ return (0);
+}
+
+long
+lx_fcntl(int fd, int cmd, intptr_t arg)
+{
+ switch (cmd) {
+ case LX_F_GETLK64:
+ case LX_F_SETLK64:
+ case LX_F_SETLKW64:
+ /* The 64-bit fcntl commands must go through fcntl64(). */
+ return (set_errno(EINVAL));
+
+ case LX_F_GETLK:
+ case LX_F_SETLK:
+ case LX_F_SETLKW:
+ return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+ default:
+ return (lx_fcntl_common(fd, cmd, arg));
+ }
+}
+
+long
+lx_fcntl64(int fd, int cmd, intptr_t arg)
+{
+ switch (cmd) {
+ case LX_F_GETLK:
+ case LX_F_SETLK:
+ case LX_F_SETLKW:
+ case LX_F_GETLK64:
+ case LX_F_SETLKW64:
+ case LX_F_SETLK64:
+ return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+ default:
+ return (lx_fcntl_common(fd, cmd, (ulong_t)arg));
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_futex.c b/usr/src/uts/common/brand/lx/syscall/lx_futex.c
new file mode 100644
index 0000000000..e7648e1fc3
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_futex.c
@@ -0,0 +1,1104 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <vm/page.h>
+#include <sys/priv.h>
+#include <sys/mman.h>
+#include <sys/timer.h>
+#include <sys/condvar.h>
+#include <sys/inttypes.h>
+#include <sys/cmn_err.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_impl.h>
+
+/*
+ * Futexes are a Linux-specific implementation of inter-process mutexes.
+ * They are designed to use shared memory for simple, uncontested
+ * operations, and rely on the kernel to resolve any contention issues.
+ *
+ * Most of the information in this section comes from the paper "Futexes
+ * Are Tricky", by Ulrich Drepper. This paper is currently available at:
+ * http://people.redhat.com/~drepper/futex.pdf.
+ *
+ * A futex itself a 4-byte integer, which must be 4-byte aligned. The
+ * value of this integer is expected to be modified using user-level atomic
+ * operations. The futex(4) design itself does not impose any semantic
+ * constraints on the value stored in the futex; it is up to the
+ * application to define its own protocol.
+ *
+ * When the application decides that kernel intervention is required, it
+ * will use the futex(2) system call. There are 5 different operations
+ * that can be performed on a futex, using this system call. Since this
+ * interface has evolved over time, there are several different prototypes
+ * available to the user. Fortunately, there is only a single kernel-level
+ * interface:
+ *
+ * long sys_futex(void *futex1, int cmd, int val1,
+ * struct timespec *timeout, void *futex2, int val2)
+ *
+ * The kernel-level operations that may be performed on a futex are:
+ *
+ * FUTEX_WAIT
+ *
+ * Atomically verify that futex1 contains the value val1. If it
+ * doesn't, return EWOULDBLOCK. If it does contain the expected
+ * value, the thread will sleep until somebody performs a FUTEX_WAKE
+ * on the futex. The caller may also specify a timeout, indicating
+ * the maximum time the thread should sleep. If the timer expires,
+ * the call returns ETIMEDOUT. If the thread is awoken with a signal,
+ * the call returns EINTR. Otherwise, the call returns 0.
+ *
+ * FUTEX_WAKE
+ *
+ * Wake up val1 processes that are waiting on futex1. The call
+ * returns the number of blocked threads that were woken up.
+ *
+ * FUTEX_WAIT_BITSET/FUTEX_WAKE_BITSET
+ *
+ * Similar to FUTEX_WAIT/FUTEX_WAKE, but each takes an additional argument
+ * denoting a bit vector, with wakers will only waking waiters that match
+ * in one or more bits. These semantics are dubious enough, but the
+ * interface has an inconsistency that is glaring even by the
+ * embarrassingly low standards that Linux sets for itself: the timeout
+ * argument to FUTEX_WAIT_BITSET is absolute, not relative as it is for
+ * FUTEX_WAIT. And as if that weren't enough unnecessary complexity,
+ * the caller may specify this absolute timeout to be against either
+ * CLOCK_MONOTONIC or CLOCK_REALTIME -- but only for FUTEX_WAIT_BITSET,
+ * of course!
+ *
+ * FUTEX_WAKE_OP
+ *
+ * The implementation of a conditional variable in terms of futexes
+ * actually uses two futexes: one to assure sequential access and one to
+ * represent the condition variable. This implementation gives rise to a
+ * particular performance problem whereby a thread is awoken on the futex
+ * that represents the condition variable only to have to (potentially)
+ * immediately wait on the futex that protects the condition variable.
+ * (Do not confuse the futex that serves to protect the condition variable
+ * with the pthread_mutex_t associated with pthread_cond_t -- which
+ * represents a third futex.) To (over)solve this problem, FUTEX_WAKE_OP
+ * was invented, which performs an atomic compare-and-exchange on a
+ * second address in a specified fashion (that is, with a specified
+ * operation). Here are the possible operations (OPARG is defined
+ * to be 12 bit value embedded in the operation):
+ *
+ * - FUTEX_OP_SET: Sets the value at the second address to OPARG
+ * - FUTEX_OP_ADD: Adds the value to OPARG
+ * - FUTEX_OP_OR: OR's the value with OPARG
+ * - FUTEX_OP_ANDN: Performs a negated AND of the value with OPARG
+ * - FUTEX_OP_XOR: XOR's the value with OPARG
+ *
+ * After this compare-and-exchange on the second address, a FUTEX_WAKE is
+ * performed on the first address and -- if the compare-and-exchange
+ * matches a specified result based on a specified comparison operation --
+ * a FUTEX_WAKE is performed on the second address. Here are the possible
+ * comparison operations:
+ *
+ * - FUTEX_OP_CMP_EQ: If old value is CMPARG, wake
+ * - FUTEX_OP_CMP_NE: If old value is not equal to CMPARG, wake
+ * - FUTEX_OP_CMP_LT: If old value is less than CMPARG, wake
+ * - FUTEX_OP_CMP_LE: If old value is less than or equal to CMPARG, wake
+ * - FUTEX_OP_CMP_GT: If old value is greater than CMPARG, wake
+ * - FUTEX_OP_CMP_GE: If old value is greater than or equal to CMPARG, wake
+ *
+ * As a practical matter, the only way that this is used (or, some might
+ * argue, is usable) is by the implementation of pthread_cond_signal(),
+ * which uses FUTEX_WAKE_OP to -- in a single system call -- unlock the
+ * futex that protects the condition variable and wake the futex that
+ * represents the condition variable. The second wake-up is conditional
+ * because the futex that protects the condition variable (rather than the
+ * one that represents it) may or may not have waiters. Given that this
+ * is the use case, FUTEX_WAKE_OP is falsely generic: despite allowing for
+ * five different kinds of operations and six different kinds of
+ * comparision operations, in practice only one is used. (Namely, setting
+ * to 0 and waking if the old value is greater than 1 -- which denotes
+ * that waiters are present and the wakeup should be performed.) Moreover,
+ * because FUTEX_WAKE_OP does not (and cannot) optimize anything in the
+ * case that the pthread_mutex_t associated with the pthread_cond_t is
+ * held at the time of a pthread_cond_signal(), this entire mechanism is
+ * essentially for naught in this case. As one can imagine (and can
+ * verify on just about any source base that uses pthread_cond_signal()),
+ * it is overwhelmingly the common case that the lock associated with the
+ * pthread_cond_t is held at the time of pthread_cond_signal(), assuring
+ * that the problem that all of this complexity was designed to solve
+ * isn't, in fact, solved because the signalled thread simply wakes up
+ * only to block again on the held mutex. Cue a slow clap!
+ *
+ * FUTEX_CMP_REQUEUE
+ *
+ * If the value stored in futex1 matches that passed in in val2, wake
+ * up val1 processes that are waiting on futex1. Otherwise, return
+ * EAGAIN.
+ *
+ * If there are more than val1 threads waiting on the futex, remove
+ * the remaining threads from this futex, and requeue them on futex2.
+ * The caller can limit the number of threads being requeued by
+ * encoding an integral numerical value in the position usually used
+ * for the timeout pointer.
+ *
+ * The call returns the number of blocked threads that were woken up
+ * or requeued.
+ *
+ * FUTEX_REQUEUE
+ *
+ * Identical to FUTEX_CMP_REQUEUE except that it does not use val2.
+ * This command has been declared broken and obsolete, but we still
+ * need to support it.
+ *
+ * FUTEX_FD
+ *
+ * Return a file descriptor, which can be used to refer to the futex.
+ * This operation was broken by design, and was blessedly removed in
+ * Linux 2.6.26 ("because it was inherently racy"); it should go without
+ * saying that we don't support this operation.
+ */
+
+/*
+ * This structure is used to track all the threads currently waiting on a
+ * futex. There is one fwaiter_t for each blocked thread. We store all
+ * fwaiter_t's in a hash structure, indexed by the memid_t of the integer
+ * containing the futex's value.
+ *
+ * At the moment, all fwaiter_t's for a single futex are simply dumped into
+ * the hash bucket. If futex contention ever becomes a hot path, we can
+ * chain a single futex's waiters together.
+ */
+typedef struct fwaiter {
+ memid_t fw_memid; /* memid of the user-space futex */
+ kcondvar_t fw_cv; /* cond var */
+ struct fwaiter *fw_next; /* hash queue */
+ struct fwaiter *fw_prev; /* hash queue */
+ uint32_t fw_bits; /* bits waiting on */
+ volatile int fw_woken;
+} fwaiter_t;
+
+/*
+ * The structure of the robust_list, as set with the set_robust_list() system
+ * call. See lx_futex_robust_exit(), below, for details.
+ */
+typedef struct futex_robust_list {
+ uintptr_t frl_head; /* list of robust locks held */
+ uint64_t frl_offset; /* offset of lock word within a lock */
+ uintptr_t frl_pending; /* pending operation */
+} futex_robust_list_t;
+
+#if defined(_SYSCALL32_IMPL)
+
+#pragma pack(4)
+typedef struct futex_robust_list32 {
+ uint32_t frl_head; /* list of robust locks held */
+ uint32_t frl_offset; /* offset of lock word within a lock */
+ uint32_t frl_pending; /* pending operation */
+} futex_robust_list32_t;
+#pragma pack()
+
+#endif
+
+#define MEMID_COPY(s, d) \
+ { (d)->val[0] = (s)->val[0]; (d)->val[1] = (s)->val[1]; }
+#define MEMID_EQUAL(s, d) \
+ ((d)->val[0] == (s)->val[0] && (d)->val[1] == (s)->val[1])
+
+/*
+ * Because collisions on this hash table can be a source of negative
+ * scalability, we make it pretty large: 4,096 entries -- 64K. If this
+ * size is found to be insufficient, the size should be made dynamic.
+ * (Making it dynamic will be delicate because the per-chain locking will
+ * necessitate memory retiring or similar; see the 2008 ACM Queue article
+ * "Real-world concurrency" for details on this technique.)
+ */
+#define HASH_SHIFT_SZ 12
+#define HASH_SIZE (1 << HASH_SHIFT_SZ)
+#define HASH_FUNC(id) \
+ ((((uintptr_t)((id)->val[1]) >> 3) + \
+ ((uintptr_t)((id)->val[1]) >> (3 + HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[1]) >> (3 + 2 * HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[0]) >> 3) + \
+ ((uintptr_t)((id)->val[0]) >> (3 + HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[0]) >> (3 + 2 * HASH_SHIFT_SZ))) & \
+ (HASH_SIZE - 1))
+
+/*
+ * We place the per-chain lock next to the pointer to the chain itself.
+ * When compared to an array of orthogonal locks, this reduces false sharing
+ * (though adjacent entries can still be falsely shared -- just not as many),
+ * while having the additional bonus of increasing locality.
+ */
+typedef struct futex_hash {
+ kmutex_t fh_lock;
+ fwaiter_t *fh_waiters;
+} futex_hash_t;
+
+static futex_hash_t futex_hash[HASH_SIZE];
+
+static void
+futex_hashin(fwaiter_t *fwp)
+{
+ int index;
+
+ index = HASH_FUNC(&fwp->fw_memid);
+ ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+ fwp->fw_prev = NULL;
+ fwp->fw_next = futex_hash[index].fh_waiters;
+ if (fwp->fw_next)
+ fwp->fw_next->fw_prev = fwp;
+ futex_hash[index].fh_waiters = fwp;
+}
+
+static void
+futex_hashout(fwaiter_t *fwp)
+{
+ int index;
+
+ index = HASH_FUNC(&fwp->fw_memid);
+ ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+ if (fwp->fw_prev)
+ fwp->fw_prev->fw_next = fwp->fw_next;
+ if (fwp->fw_next)
+ fwp->fw_next->fw_prev = fwp->fw_prev;
+ if (futex_hash[index].fh_waiters == fwp)
+ futex_hash[index].fh_waiters = fwp->fw_next;
+
+ fwp->fw_prev = NULL;
+ fwp->fw_next = NULL;
+}
+
+/*
+ * Go to sleep until somebody does a WAKE operation on this futex, we get a
+ * signal, or the timeout expires.
+ */
+static int
+futex_wait(memid_t *memid, caddr_t addr,
+ int val, timespec_t *timeout, uint32_t bits)
+{
+ kthread_t *t = curthread;
+ int err, ret;
+ int32_t curval;
+ fwaiter_t fw;
+ int index;
+
+ /*
+ * The LMS_USER_LOCK micro state becomes valid if we sleep; otherwise
+ * our time will accrue against LMS_SYSTEM. Use of this micro state
+ * is modelled on lwp_mutex_timedlock(), a native analogue of
+ * futex_wait().
+ */
+ (void) new_mstate(t, LMS_USER_LOCK);
+
+ fw.fw_woken = 0;
+ fw.fw_bits = bits;
+
+ MEMID_COPY(memid, &fw.fw_memid);
+ cv_init(&fw.fw_cv, NULL, CV_DEFAULT, NULL);
+
+ index = HASH_FUNC(&fw.fw_memid);
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ if (fuword32(addr, (uint32_t *)&curval)) {
+ err = set_errno(EFAULT);
+ goto out;
+ }
+ if (curval != val) {
+ err = set_errno(EWOULDBLOCK);
+ goto out;
+ }
+
+ futex_hashin(&fw);
+
+ err = 0;
+ while ((fw.fw_woken == 0) && (err == 0)) {
+ ret = cv_waituntil_sig(&fw.fw_cv, &futex_hash[index].fh_lock,
+ timeout, timechanged);
+ if (ret < 0) {
+ err = set_errno(ETIMEDOUT);
+ } else if (ret == 0) {
+ /*
+ * According to signal(7), a futex(2) call with the
+ * FUTEX_WAIT operation is restartable.
+ */
+ ttolxlwp(t)->br_syscall_restart = B_TRUE;
+ err = set_errno(EINTR);
+ }
+ }
+
+ /*
+ * The futex is normally hashed out in wakeup. If we timed out or
+ * got a signal, we need to hash it out here instead.
+ */
+ if (fw.fw_woken == 0)
+ futex_hashout(&fw);
+
+out:
+ mutex_exit(&futex_hash[index].fh_lock);
+
+ return (err);
+}
+
+/*
+ * Wake up to wake_threads threads that are blocked on the futex at memid.
+ */
+static int
+futex_wake(memid_t *memid, int wake_threads, uint32_t mask)
+{
+ fwaiter_t *fwp, *next;
+ int index;
+ int ret = 0;
+
+ index = HASH_FUNC(memid);
+
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ for (fwp = futex_hash[index].fh_waiters;
+ fwp != NULL && ret < wake_threads; fwp = next) {
+ next = fwp->fw_next;
+ if (MEMID_EQUAL(&fwp->fw_memid, memid) &&
+ (fwp->fw_bits & mask)) {
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ ret++;
+ }
+ }
+
+ mutex_exit(&futex_hash[index].fh_lock);
+
+ return (ret);
+}
+
+static int
+futex_wake_op_execute(int32_t *addr, int32_t val3)
+{
+ int32_t op = FUTEX_OP_OP(val3);
+ int32_t cmp = FUTEX_OP_CMP(val3);
+ int32_t cmparg = FUTEX_OP_CMPARG(val3);
+ int32_t oparg, oldval, newval;
+ label_t ljb;
+ int rval;
+
+ if ((uintptr_t)addr >= KERNELBASE)
+ return (set_errno(EFAULT));
+
+ if (on_fault(&ljb))
+ return (set_errno(EFAULT));
+
+ oparg = FUTEX_OP_OPARG(val3);
+
+ do {
+ oldval = *addr;
+ newval = oparg;
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ break;
+
+ case FUTEX_OP_ADD:
+ newval += oparg;
+ break;
+
+ case FUTEX_OP_OR:
+ newval |= oparg;
+ break;
+
+ case FUTEX_OP_ANDN:
+ newval &= ~oparg;
+ break;
+
+ case FUTEX_OP_XOR:
+ newval ^= oparg;
+ break;
+
+ default:
+ no_fault();
+ return (set_errno(EINVAL));
+ }
+ } while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+ no_fault();
+
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ:
+ rval = (oldval == cmparg);
+ break;
+
+ case FUTEX_OP_CMP_NE:
+ rval = (oldval != cmparg);
+ break;
+
+ case FUTEX_OP_CMP_LT:
+ rval = (oldval < cmparg);
+ break;
+
+ case FUTEX_OP_CMP_LE:
+ rval = (oldval <= cmparg);
+ break;
+
+ case FUTEX_OP_CMP_GT:
+ rval = (oldval > cmparg);
+ break;
+
+ case FUTEX_OP_CMP_GE:
+ rval = (oldval >= cmparg);
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ return (rval);
+}
+
+static int
+futex_wake_op(memid_t *memid, caddr_t addr2, memid_t *memid2,
+ int wake_threads, int wake_threads2, int val3)
+{
+ kmutex_t *l1, *l2;
+ int ret = 0, ret2 = 0, wake;
+ fwaiter_t *fwp, *next;
+ int index1, index2;
+
+ index1 = HASH_FUNC(memid);
+ index2 = HASH_FUNC(memid2);
+
+ if (index1 == index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = NULL;
+ } else if (index1 < index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = &futex_hash[index2].fh_lock;
+ } else {
+ l1 = &futex_hash[index2].fh_lock;
+ l2 = &futex_hash[index1].fh_lock;
+ }
+
+ mutex_enter(l1);
+ if (l2 != NULL)
+ mutex_enter(l2);
+
+ /* LINTED: alignment */
+ if ((wake = futex_wake_op_execute((int32_t *)addr2, val3)) < 0)
+ goto out;
+
+ for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+ continue;
+
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ if (++ret >= wake_threads) {
+ break;
+ }
+ }
+
+ if (!wake)
+ goto out;
+
+ for (fwp = futex_hash[index2].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid2))
+ continue;
+
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ if (++ret2 >= wake_threads2) {
+ break;
+ }
+ }
+
+ ret += ret2;
+out:
+ if (l2 != NULL)
+ mutex_exit(l2);
+ mutex_exit(l1);
+
+ return (ret);
+}
+
+/*
+ * Wake up to wake_threads waiting on the futex at memid. If there are
+ * more than that many threads waiting, requeue the remaining threads on
+ * the futex at requeue_memid.
+ */
+static int
+futex_requeue(memid_t *memid, memid_t *requeue_memid, int wake_threads,
+ ulong_t requeue_threads, caddr_t addr, int *cmpval)
+{
+ fwaiter_t *fwp, *next;
+ int index1, index2;
+ int ret = 0;
+ int32_t curval;
+ kmutex_t *l1, *l2;
+
+ /*
+ * To ensure that we don't miss a wakeup if the value of cmpval
+ * changes, we need to grab locks on both the original and new hash
+ * buckets. To avoid deadlock, we always grab the lower-indexed
+ * lock first.
+ */
+ index1 = HASH_FUNC(memid);
+ index2 = HASH_FUNC(requeue_memid);
+
+ if (index1 == index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = NULL;
+ } else if (index1 < index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = &futex_hash[index2].fh_lock;
+ } else {
+ l1 = &futex_hash[index2].fh_lock;
+ l2 = &futex_hash[index1].fh_lock;
+ }
+
+ mutex_enter(l1);
+ if (l2 != NULL)
+ mutex_enter(l2);
+
+ if (cmpval != NULL) {
+ if (fuword32(addr, (uint32_t *)&curval)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (curval != *cmpval) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ }
+
+ for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+ continue;
+
+ futex_hashout(fwp);
+ if (ret++ < wake_threads) {
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ } else {
+ MEMID_COPY(requeue_memid, &fwp->fw_memid);
+ futex_hashin(fwp);
+
+ if ((ret - wake_threads) >= requeue_threads)
+ break;
+ }
+ }
+
+out:
+ if (l2 != NULL)
+ mutex_exit(l2);
+ mutex_exit(l1);
+
+ if (ret < 0)
+ return (set_errno(-ret));
+ return (ret);
+}
+
+/*
+ * Copy in the relative timeout provided by the application and convert it
+ * to an absolute timeout. Sadly, this is complicated by the different
+ * timeout of semantics of FUTEX_WAIT vs. FUTEX_WAIT_BITSET. (Yes, you read
+ * that correctly; FUTEX_WAIT and FUTEX_WAIT_BITSET have different timeout
+ * semantics; see the block comment at the top of the file for commentary
+ * on this inanity.)
+ */
+static int
+get_timeout(void *lx_timeout, timestruc_t *timeout, int cmd, int clock)
+{
+ timestruc_t now;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(lx_timeout, timeout, sizeof (timestruc_t)))
+ return (EFAULT);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ timestruc32_t timeout32;
+ if (copyin(lx_timeout, &timeout32, sizeof (timestruc32_t)))
+ return (EFAULT);
+ timeout->tv_sec = (time_t)timeout32.tv_sec;
+ timeout->tv_nsec = timeout32.tv_nsec;
+ }
+#endif
+ if (itimerspecfix(timeout))
+ return (EINVAL);
+
+ if (cmd == FUTEX_WAIT) {
+ /*
+ * We've been given a relative time; add it to the current
+ * time to derive an absolute time.
+ */
+ gethrestime(&now);
+ timespecadd(timeout, &now);
+ } else {
+ /*
+ * This is a FUTEX_WAIT_BITSET operation, which (1) specifies
+ * the timeout as an absolute rather than a relative timeout
+ * and (2) allows for different clock types to be specified.
+ * If the clock is CLOCK_REALTIME, we actually have nothing
+ * to do -- but if this is CLOCK_MONOTONIC, we need to convert
+ * our absolute time back into a relative time and then add
+ * it to our current hrestime to get an absolute CLOCK_REALTIME
+ * timeout.
+ */
+ if (clock == CLOCK_MONOTONIC) {
+ /*
+ * Get our current time, and subtract it from our
+ * timeout to get the relative value.
+ */
+ hrt2ts(gethrtime(), &now);
+ timespecsub(timeout, &now);
+
+ /*
+ * If our timeout is in the past, set it to be 0.
+ */
+ if (timeout->tv_sec < 0) {
+ timeout->tv_sec = 0;
+ timeout->tv_nsec = 0;
+ }
+
+ /*
+ * Add the relative time back into the current time.
+ */
+ gethrestime(&now);
+ timespecadd(timeout, &now);
+ }
+ }
+
+ return (0);
+}
+
+long
+lx_futex(uintptr_t addr, int op, int val, uintptr_t lx_timeout,
+ uintptr_t addr2, int val3)
+{
+ struct as *as = curproc->p_as;
+ memid_t memid, memid2;
+ timestruc_t timeout;
+ timestruc_t *tptr = NULL;
+ int val2 = NULL;
+ int rval = 0;
+ int cmd = op & FUTEX_CMD_MASK;
+ int private = op & FUTEX_PRIVATE_FLAG;
+ char dmsg[32];
+
+ /* must be aligned on int boundary */
+ if (addr & 0x3)
+ return (set_errno(EINVAL));
+
+ /* Sanity check the futex command */
+ if (cmd < 0 || cmd > FUTEX_MAX_CMD)
+ return (set_errno(EINVAL));
+
+ if (cmd == FUTEX_FD) {
+ /*
+ * FUTEX_FD was sentenced to death for grievous crimes of
+ * semantics against humanity; it has been ripped out of Linux
+ * and will never be supported by us.
+ */
+ (void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+ lx_unsupported(dmsg);
+ return (set_errno(ENOSYS));
+ }
+
+ switch (cmd) {
+ case FUTEX_LOCK_PI:
+ case FUTEX_UNLOCK_PI:
+ case FUTEX_TRYLOCK_PI:
+ case FUTEX_WAIT_REQUEUE_PI:
+ case FUTEX_CMP_REQUEUE_PI:
+ /*
+ * These are operations that we don't currently support, but
+ * may well need to in the future. For now, callers need to
+ * deal with these being missing -- but if and as that changes,
+ * they may well need to be implemented.
+ */
+ (void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+ lx_unsupported(dmsg);
+ return (set_errno(ENOSYS));
+ }
+
+ if ((op & FUTEX_CLOCK_REALTIME) && cmd != FUTEX_WAIT_BITSET) {
+ /*
+ * Linux only allows FUTEX_CLOCK_REALTIME to be set on the
+ * FUTEX_WAIT_BITSET and FUTEX_WAIT_REQUEUE_PI commands.
+ */
+ return (set_errno(ENOSYS));
+ }
+
+ /* Copy in the timeout structure from userspace. */
+ if ((cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET) &&
+ lx_timeout != NULL) {
+ rval = get_timeout((timespec_t *)lx_timeout, &timeout, cmd,
+ op & FUTEX_CLOCK_REALTIME ? CLOCK_REALTIME :
+ CLOCK_MONOTONIC);
+
+ if (rval != 0)
+ return (set_errno(rval));
+ tptr = &timeout;
+ }
+
+ switch (cmd) {
+ case FUTEX_REQUEUE:
+ case FUTEX_CMP_REQUEUE:
+ case FUTEX_WAKE_OP:
+ /*
+ * lx_timeout is nominally a pointer to a userspace address.
+ * For several commands, however, it actually contains
+ * an additional integer parameter. This is horrible, and
+ * the people who did this to us should be sorry.
+ */
+ val2 = (int)lx_timeout;
+ }
+
+ /*
+ * Translate the process-specific, user-space futex virtual
+ * address(es) to a universal memid. If the private bit is set, we
+ * can just use our as plus the virtual address, saving quite a bit
+ * of effort.
+ */
+ if (private) {
+ memid.val[0] = (uintptr_t)as;
+ memid.val[1] = (uintptr_t)addr;
+ } else {
+ rval = as_getmemid(as, (void *)addr, &memid);
+ if (rval != 0)
+ return (set_errno(rval));
+ }
+
+ if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+ cmd == FUTEX_WAKE_OP) {
+ if (addr2 & 0x3)
+ return (set_errno(EINVAL));
+
+ if (private) {
+ memid2.val[0] = (uintptr_t)as;
+ memid2.val[1] = (uintptr_t)addr2;
+ } else {
+ rval = as_getmemid(as, (void *)addr2, &memid2);
+ if (rval)
+ return (set_errno(rval));
+ }
+ }
+
+ switch (cmd) {
+ case FUTEX_WAIT:
+ rval = futex_wait(&memid, (void *)addr, val,
+ tptr, FUTEX_BITSET_MATCH_ANY);
+ break;
+
+ case FUTEX_WAIT_BITSET:
+ rval = futex_wait(&memid, (void *)addr, val, tptr, val3);
+ break;
+
+ case FUTEX_WAKE:
+ rval = futex_wake(&memid, val, FUTEX_BITSET_MATCH_ANY);
+ break;
+
+ case FUTEX_WAKE_BITSET:
+ rval = futex_wake(&memid, val, val3);
+ break;
+
+ case FUTEX_WAKE_OP:
+ rval = futex_wake_op(&memid, (void *)addr2, &memid2,
+ val, val2, val3);
+ break;
+
+ case FUTEX_CMP_REQUEUE:
+ case FUTEX_REQUEUE:
+ rval = futex_requeue(&memid, &memid2, val,
+ val2, (void *)addr2, &val3);
+
+ break;
+ }
+
+ return (rval);
+}
+
+/*
+ * Does the dirty work of actually dropping a held robust lock in the event
+ * of the untimely death of the owner; see lx_futex_robust_exit(), below.
+ */
+static void
+lx_futex_robust_drop(uintptr_t addr, uint32_t tid)
+{
+ memid_t memid;
+ uint32_t oldval, newval;
+
+ VERIFY(addr + sizeof (uint32_t) < KERNELBASE);
+
+ do {
+ fuword32_noerr((void *)addr, &oldval);
+
+ if ((oldval & FUTEX_TID_MASK) != tid)
+ return;
+
+ newval = (oldval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
+ } while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+ /*
+ * We have now denoted that this lock's owner is dead; we need to
+ * wake any waiters.
+ */
+ if (as_getmemid(curproc->p_as, (void *)addr, &memid) != 0)
+ return;
+
+ (void) futex_wake(&memid, 1, FUTEX_BITSET_MATCH_ANY);
+}
+
+/*
+ * Called when a thread is exiting. The role of the kernel is very clearly
+ * spelled out in the Linux design document entitled robust-futex-ABI.txt:
+ * we must (carefully!) iterate over the list of held locks pointed to by
+ * the robust list head; for each lock, we'll check to see if the calling
+ * (exiting) thread is the owner, and if so, denote that the lock is dead
+ * and wake any waiters. (The "pending" field of the head points to a lock
+ * that is in transition; it should be dropped if held.) If there are any
+ * errors through here at all (including memory operations), we abort the
+ * entire operation.
+ */
+void
+lx_futex_robust_exit(uintptr_t addr, uint32_t tid)
+{
+ futex_robust_list_t list;
+ uintptr_t entry, next;
+ model_t model = get_udatamodel();
+ int length = 0;
+ label_t ljb;
+
+ if (on_fault(&ljb))
+ return;
+
+ if (addr + sizeof (futex_robust_list_t) >= KERNELBASE)
+ goto out;
+
+ if (model == DATAMODEL_NATIVE) {
+ copyin_noerr((void *)addr, &list, sizeof (list));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ futex_robust_list32_t list32;
+
+ copyin_noerr((void *)addr, &list32, sizeof (list32));
+ list.frl_head = list32.frl_head;
+ list.frl_offset = list32.frl_offset;
+ list.frl_pending = list32.frl_pending;
+ }
+#endif
+
+ /*
+ * Strip off the PI bit, if any.
+ */
+ entry = list.frl_head & ~FUTEX_ROBUST_LOCK_PI;
+
+ while (entry != addr && length++ < FUTEX_ROBUST_LIST_LIMIT) {
+ if (entry + list.frl_offset + sizeof (uint32_t) >= KERNELBASE)
+ goto out;
+
+ if (model == DATAMODEL_NATIVE) {
+ fulword_noerr((void *)entry, &next);
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ uint32_t next32;
+ fuword32_noerr((void *)entry, &next32);
+ next = next32;
+ }
+#endif
+
+ /*
+ * Drop the robust mutex -- but only if our pending lock didn't
+ * somehow sneak on there.
+ */
+ if (entry != list.frl_pending)
+ lx_futex_robust_drop(entry + list.frl_offset, tid);
+
+ entry = next & ~FUTEX_LOCK_PI;
+ }
+
+ /*
+ * Finally, drop the pending lock if there is one.
+ */
+ if (list.frl_pending != NULL && list.frl_pending +
+ list.frl_offset + sizeof (uint32_t) < KERNELBASE)
+ lx_futex_robust_drop(list.frl_pending + list.frl_offset, tid);
+
+out:
+ no_fault();
+}
+
+long
+lx_set_robust_list(void *listp, size_t len)
+{
+ proc_t *p = curproc;
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (len != sizeof (futex_robust_list_t))
+ return (set_errno(EINVAL));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ if (len != sizeof (futex_robust_list32_t))
+ return (set_errno(EINVAL));
+ }
+#endif
+
+ /*
+ * To assure that we are serialized with respect to any racing call
+ * to lx_get_robust_list(), we lock ourselves to set the value. (Note
+ * that sprunlock() drops p_lock.)
+ */
+ mutex_enter(&p->p_lock);
+ sprlock_proc(p);
+ lwpd->br_robust_list = listp;
+ sprunlock(p);
+
+ return (0);
+}
+
+long
+lx_get_robust_list(pid_t pid, void **listp, size_t *lenp)
+{
+ model_t model = get_udatamodel();
+ pid_t rpid;
+ id_t rtid;
+ proc_t *rproc;
+ klwp_t *rlwp;
+ lx_lwp_data_t *rlwpd;
+ kthread_t *rthr;
+ void *list;
+ int err = 0;
+
+ if (pid == 0) {
+ /*
+ * A pid of 0 denotes the current thread; we lock the current
+ * process even though it isn't strictly necessary (we can't
+ * race with set_robust_list() because a thread may only set
+ * its robust list on itself).
+ */
+ rproc = curproc;
+ rlwpd = lwptolxlwp(ttolwp(curthread));
+ mutex_enter(&curproc->p_lock);
+ sprlock_proc(rproc);
+ } else {
+ if (lx_lpid_to_spair(pid, &rpid, &rtid) != 0 ||
+ (rproc = sprlock(rpid)) == NULL) {
+ /*
+ * We couldn't find the specified process.
+ */
+ return (set_errno(ESRCH));
+ }
+
+ if (rproc->p_model != model ||
+ (rthr = idtot(rproc, rtid)) == NULL ||
+ (rlwp = ttolwp(rthr)) == NULL ||
+ (rlwpd = lwptolxlwp(rlwp)) == NULL) {
+ /*
+ * The target process does not match our data model, or
+ * we couldn't find the LWP, or the target process is
+ * not branded.
+ */
+ err = ESRCH;
+ goto out;
+ }
+ }
+
+ if (curproc != rproc &&
+ priv_proc_cred_perm(curproc->p_cred, rproc, NULL, VREAD) != 0) {
+ /*
+ * We don't have the permission to examine the target.
+ */
+ err = EPERM;
+ goto out;
+ }
+
+ list = rlwpd->br_robust_list;
+
+out:
+ sprunlock(rproc);
+
+ if (err != 0)
+ return (set_errno(err));
+
+ if (model == DATAMODEL_NATIVE) {
+ if (sulword(listp, (uintptr_t)list) != 0)
+ return (set_errno(EFAULT));
+
+ if (sulword(lenp, sizeof (futex_robust_list_t)) != 0)
+ return (set_errno(EFAULT));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ if (suword32(listp, (uint32_t)(uintptr_t)list) != 0)
+ return (set_errno(EFAULT));
+
+ if (suword32(lenp, sizeof (futex_robust_list32_t)) != 0)
+ return (set_errno(EFAULT));
+ }
+#endif
+
+ return (0);
+}
+
+void
+lx_futex_init(void)
+{
+ int i;
+
+ for (i = 0; i < HASH_SIZE; i++)
+ mutex_init(&futex_hash[i].fh_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+int
+lx_futex_fini(void)
+{
+ int i, err;
+
+ err = 0;
+ for (i = 0; (err == 0) && (i < HASH_SIZE); i++) {
+ mutex_enter(&futex_hash[i].fh_lock);
+ if (futex_hash[i].fh_waiters != NULL)
+ err = EBUSY;
+ mutex_exit(&futex_hash[i].fh_lock);
+ }
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c b/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c
new file mode 100644
index 0000000000..7fcc594d81
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c
@@ -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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+
+/*
+ * getcwd() - Linux syscall semantics are slightly different; we need to return
+ * the length of the pathname copied (+ 1 for the terminating NULL byte.)
+ */
+long
+lx_getcwd(char *buf, int size)
+{
+ int len;
+ int error;
+ vnode_t *vp;
+ char path[MAXPATHLEN + 1];
+
+ vp = PTOU(curproc)->u_cdir;
+ VN_HOLD(vp);
+ if ((error = vnodetopath(NULL, vp, path, sizeof (path), CRED())) != 0) {
+ VN_RELE(vp);
+ return (set_errno(error));
+ }
+ VN_RELE(vp);
+
+ len = strlen(path) + 1;
+ if (len > size)
+ return (set_errno(ERANGE));
+
+ if (copyout(path, buf, len) != 0)
+ return (set_errno(EFAULT));
+
+ return (len);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getdents.c b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c
new file mode 100644
index 0000000000..101d8b2911
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c
@@ -0,0 +1,354 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/sunddi.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+
+#define LX_NAMEMAX 256
+
+#define LX_GETDENTS_MAX_BUFSZ 65536
+
+/*
+ * Because the Linux dirent has an extra field (d_type), it's possible that
+ * each entry will be 8 bytes larger (and aligned to 8 bytes) due to padding.
+ * To prevent overrun during translation, the illumos-native buffer is sized
+ * pessimistically.
+ */
+#define LTOS_GETDENTS_BUFSZ(bufsz, datasz) \
+ (((bufsz) / (((datasz) + 15) & ~7)) * sizeof (struct dirent))
+
+/*
+ * Record must be long enough to house d_name string, null terminator and
+ * d_type field. It's then padded to nearest 8-byte boundary
+ */
+#define LX_RECLEN(l, t) \
+ ((offsetof(t, d_name) + 2 + (l) + 7) & ~7)
+
+/*
+ * Bytes after d_name string until d_reclen should be zeroed.
+ * Includes zero-terminating d_name
+ */
+#define LX_ZEROLEN(l, t) \
+ (LX_RECLEN(l, t) - \
+ ((offsetof(t, d_name) + (l))))
+
+/* The output format of getdents differs if the caller is 32 or 64 bit. */
+struct lx_dirent_32 {
+ uint32_t d_ino;
+ int32_t d_off;
+ ushort_t d_reclen;
+ char d_name[1];
+ uchar_t d_type;
+};
+
+struct lx_dirent_64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ ushort_t d_reclen;
+ char d_name[1];
+ uchar_t d_type;
+};
+
+static long
+lx_getdents_common(int fd, caddr_t uptr, size_t count,
+ unsigned int lx_size, int (*outcb)(caddr_t, caddr_t, int))
+{
+ vnode_t *vp;
+ file_t *fp;
+ struct uio auio;
+ struct iovec aiov;
+ int error, at_eof;
+ int sbufsz, lbufsz, bufsz;
+ void *lbuf, *sbuf;
+ size_t outb = 0;
+
+ if (count < lx_size) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ if (vp->v_type != VDIR) {
+ releasef(fd);
+ return (set_errno(ENOTDIR));
+ }
+ if (!(fp->f_flag & FREAD)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+
+ if (count > LX_GETDENTS_MAX_BUFSZ) {
+ /*
+ * If the target buffer passed to us is huge, keep the
+ * translation buffers moderate in size. Iteration will be
+ * used to fill the request.
+ */
+ lbufsz = LX_GETDENTS_MAX_BUFSZ;
+ sbufsz = LTOS_GETDENTS_BUFSZ(LX_GETDENTS_MAX_BUFSZ, lx_size);
+ } else if (count < (lx_size + MAXPATHLEN)) {
+ /*
+ * If the target buffer is tiny, allocate a Linux-format buffer
+ * big enough to hold at least one max-length row while keeping
+ * the illumos-format buffer pesimistic in size.
+ *
+ * Assuming the buffer is truely tiny, it's likely that the
+ * result will not fit and an EINVAL will be tossed.
+ */
+ lbufsz = (lx_size + MAXPATHLEN);
+ sbufsz = MAX((LTOS_GETDENTS_BUFSZ(count, lx_size)),
+ sizeof (struct dirent));
+ } else {
+ lbufsz = count;
+ sbufsz = LTOS_GETDENTS_BUFSZ(count, lx_size);
+ }
+ bufsz = sbufsz;
+ lbuf = kmem_alloc(lbufsz, KM_SLEEP);
+ sbuf = kmem_alloc(sbufsz, KM_SLEEP);
+
+ aiov.iov_base = sbuf;
+ aiov.iov_len = sbufsz;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = sbufsz;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Since we use a conservative buffer allocation for the differing
+ * struct sizing and Linux places fewer limits on getdents buffers in
+ * general, there's a chance we'll undershoot on the record count.
+ * When this happens, we can simply repeat the READDIR operation until
+ * the available records are exhausted or we've filled the user buffer.
+ */
+ do {
+ int res;
+
+ (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
+ error = VOP_READDIR(vp, &auio, fp->f_cred, &at_eof, NULL, 0);
+ VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
+ if (error != 0 || auio.uio_resid == sbufsz) {
+ break;
+ }
+ res = outcb(sbuf, lbuf, bufsz - auio.uio_resid);
+ VERIFY(res <= lbufsz);
+ if (res == 0) {
+ /* no records to copyout from this batch */
+ break;
+ } else if (res > count) {
+ /*
+ * For very small buffer sizes, it's possible that a
+ * single record is too large due to a long filename.
+ */
+ error = EINVAL;
+ break;
+ }
+
+ VERIFY(outb + res <= count);
+ if (copyout(lbuf, (void *)(uptr + outb), res) != 0) {
+ error = EFAULT;
+ break;
+ }
+ outb += res;
+
+ /*
+ * We undershot the request buffer.
+ * Reset for another READDIR, taking care not to overshoot.
+ */
+ bufsz = MIN(sbufsz, LTOS_GETDENTS_BUFSZ(count - outb, lx_size));
+ auio.uio_resid = bufsz;
+ aiov.iov_len = bufsz;
+ aiov.iov_base = sbuf;
+
+ /*
+ * Continued progress is allowed only if EOF has not been
+ * reached and there is enough remaining buffer space to hold
+ * an entry with a max-length filename.
+ */
+ } while (at_eof == 0 && (count - outb) >= (lx_size + MAXPATHLEN));
+
+ kmem_free(lbuf, lbufsz);
+ kmem_free(sbuf, sbufsz);
+
+ if (error) {
+ releasef(fd);
+ return (set_errno(error));
+ }
+
+ fp->f_offset = auio.uio_loffset;
+ releasef(fd);
+ return (outb);
+}
+
+
+static int
+lx_getdents_format32(caddr_t sbuf, caddr_t lbuf, int len)
+{
+ struct dirent *sd;
+ struct lx_dirent_32 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent_32 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+ struct lx_dirent_32);
+ /* Zero out any alignment padding and d_type */
+ bzero(ld->d_name + namelen,
+ LX_ZEROLEN(namelen, struct lx_dirent_32));
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+static int
+lx_getdents_format64(caddr_t sbuf, caddr_t lbuf, int len)
+{
+ struct dirent *sd;
+ struct lx_dirent_64 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent_64 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+ struct lx_dirent_64);
+ /* Zero out any alignment padding and d_type */
+ bzero(ld->d_name + namelen,
+ LX_ZEROLEN(namelen, struct lx_dirent_64));
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+long
+lx_getdents_32(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent_32), lx_getdents_format32));
+}
+
+long
+lx_getdents_64(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent_64), lx_getdents_format64));
+}
+
+struct lx_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ ushort_t d_reclen;
+ uchar_t d_type;
+ char d_name[1];
+};
+
+#define LX_RECLEN64(namelen) \
+ ((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7)
+
+#define LX_ZEROLEN64(namelen) \
+ (LX_RECLEN64(namelen) - \
+ ((offsetof(struct lx_dirent64, d_name) + (namelen))))
+
+static int
+lx_getdents64_format(caddr_t sbuf, caddr_t lbuf, int len)
+{
+ struct dirent *sd;
+ struct lx_dirent64 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent64 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ ld->d_type = 0;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN64(namelen);
+ /* Zero out any alignment padding */
+ bzero(ld->d_name + namelen, LX_ZEROLEN64(namelen));
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+
+long
+lx_getdents64(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent64), lx_getdents64_format));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getpid.c b/usr/src/uts/common/brand/lx/syscall/lx_getpid.c
new file mode 100644
index 0000000000..c2506f52c5
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getpid.c
@@ -0,0 +1,79 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * return the pid
+ */
+long
+lx_getpid(void)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ long rv;
+
+ if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+ rv = 1;
+ } else {
+ VERIFY(lwpd != NULL);
+
+ if (lwpd->br_lx_thunk_pid != 0) {
+ rv = lwpd->br_lx_thunk_pid;
+ } else {
+ rv = lwpd->br_tgid;
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * return the parent pid
+ */
+long
+lx_getppid(void)
+{
+ return (lx_lwp_ppid(ttolwp(curthread), NULL, NULL));
+}
+
+/*
+ * return the thread id
+ */
+long
+lx_gettid(void)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ return (lwpd->br_pid);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c b/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c
new file mode 100644
index 0000000000..acc4073483
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c
@@ -0,0 +1,33 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * From "uts/common/syscall/getrandom.c":
+ */
+extern int getrandom(void *, size_t, int);
+
+long
+lx_getrandom(void *bufp, size_t buflen, int flags)
+{
+ /*
+ * According to signal(7), calls to getrandom(2) are restartable.
+ */
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+
+ return (getrandom(bufp, buflen, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_id.c b/usr/src/uts/common/brand/lx/syscall/lx_id.c
new file mode 100644
index 0000000000..67f0fc9e5e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_id.c
@@ -0,0 +1,509 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/zone.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/policy.h>
+#include <sys/lx_types.h>
+
+#define LX_NGROUPS_MAX 32
+
+/* From usr/src/uts/common/syscall/gid.c & uid.c */
+extern int setgid(gid_t);
+extern int setregid(gid_t, gid_t);
+extern int setreuid(uid_t, uid_t);
+extern int setuid(uid_t);
+
+/* From usr/src/uts/common/syscall/groups.c */
+extern int setgroups(int, gid_t *);
+
+long
+lx_getegid(void)
+{
+ return (crgetgid(CRED()));
+}
+
+long
+lx_getegid16(void)
+{
+ return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+long
+lx_geteuid(void)
+{
+ return (crgetuid(CRED()));
+}
+
+long
+lx_geteuid16(void)
+{
+ return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+long
+lx_getgid(void)
+{
+ return (crgetrgid(CRED()));
+}
+
+long
+lx_getgid16(void)
+{
+ return ((int)LX_GID32_TO_GID16(crgetrgid(CRED())));
+}
+
+long
+lx_getuid(void)
+{
+ return (crgetruid(CRED()));
+}
+
+long
+lx_getuid16(void)
+{
+ return ((int)LX_UID32_TO_UID16(crgetruid(CRED())));
+}
+
+long
+lx_setgid(gid_t gid)
+{
+ return (setgid(gid));
+}
+
+long
+lx_setgid16(lx_gid16_t gid)
+{
+ return (setgid(LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_setregid(gid_t rgid, gid_t egid)
+{
+ return (setregid(rgid, egid));
+}
+
+long
+lx_setregid16(lx_gid16_t rgid, lx_gid16_t egid)
+{
+ return (setregid(LX_UID16_TO_UID32(rgid), LX_UID16_TO_UID32(egid)));
+}
+
+long
+lx_setreuid(uid_t ruid, uid_t euid)
+{
+ return (setreuid(ruid, euid));
+}
+
+long
+lx_setreuid16(lx_uid16_t ruid, lx_uid16_t euid)
+{
+ return (setreuid(LX_UID16_TO_UID32(ruid), LX_UID16_TO_UID32(euid)));
+}
+
+long
+lx_setuid(uid_t uid)
+{
+ return (setuid(uid));
+}
+
+long
+lx_setuid16(lx_uid16_t uid)
+{
+ return (setuid(LX_UID16_TO_UID32(uid)));
+}
+
+/*
+ * This function is based on setreuid in common/syscall/uid.c and exists
+ * because illumos does not have a way to explicitly set the saved uid (suid)
+ * from any other system call.
+ */
+long
+lx_setresuid(lx_uid_t ruid, lx_uid_t euid, lx_uid_t suid)
+{
+ proc_t *p;
+ int error = 0;
+ int do_nocd = 0;
+ int uidchge = 0;
+ uid_t oldruid = ruid;
+ cred_t *cr, *newcr;
+ zoneid_t zoneid = getzoneid();
+
+ if ((ruid != -1 && (ruid > MAXUID)) ||
+ (euid != -1 && (euid > MAXUID)) ||
+ (suid != -1 && (suid > MAXUID))) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Need to pre-allocate the new cred structure before grabbing
+ * the p_crlock mutex.
+ */
+ newcr = cralloc();
+
+ p = ttoproc(curthread);
+
+retry:
+ mutex_enter(&p->p_crlock);
+ cr = p->p_cred;
+
+ if (ruid != -1 &&
+ ruid != cr->cr_ruid && ruid != cr->cr_uid &&
+ ruid != cr->cr_suid && secpolicy_allow_setid(cr, ruid, B_FALSE)) {
+ error = EPERM;
+ } else if (euid != -1 &&
+ euid != cr->cr_ruid && euid != cr->cr_uid &&
+ euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) {
+ error = EPERM;
+ } else if (suid != -1 &&
+ suid != cr->cr_ruid && suid != cr->cr_uid &&
+ suid != cr->cr_suid && secpolicy_allow_setid(cr, suid, B_FALSE)) {
+ error = EPERM;
+ } else {
+ if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) {
+ /*
+ * The ruid of the process is going to change. In order
+ * to avoid a race condition involving the
+ * process count associated with the newly given ruid,
+ * we increment the count before assigning the
+ * credential to the process.
+ * To do that, we'll have to take pidlock, so we first
+ * release p_crlock.
+ */
+ mutex_exit(&p->p_crlock);
+ uidchge = 1;
+ mutex_enter(&pidlock);
+ upcount_inc(ruid, zoneid);
+ mutex_exit(&pidlock);
+ /*
+ * As we released p_crlock we can't rely on the cr
+ * we read. So retry the whole thing.
+ */
+ goto retry;
+ }
+ crhold(cr);
+ crcopy_to(cr, newcr);
+ p->p_cred = newcr;
+
+ if (euid != -1)
+ newcr->cr_uid = euid;
+ if (suid != -1)
+ newcr->cr_suid = suid;
+ if (ruid != -1) {
+ oldruid = newcr->cr_ruid;
+ newcr->cr_ruid = ruid;
+ ASSERT(ruid != oldruid ? uidchge : 1);
+ }
+
+ /*
+ * A process that gives up its privilege
+ * must be marked to produce no core dump.
+ */
+ if ((cr->cr_uid != newcr->cr_uid ||
+ cr->cr_ruid != newcr->cr_ruid ||
+ cr->cr_suid != newcr->cr_suid))
+ do_nocd = 1;
+
+ crfree(cr);
+ }
+ mutex_exit(&p->p_crlock);
+
+ /*
+ * We decrement the number of processes associated with the oldruid
+ * to match the increment above, even if the ruid of the process
+ * did not change or an error occurred (oldruid == uid).
+ */
+ if (uidchge) {
+ ASSERT(oldruid != -1 && ruid != -1);
+ mutex_enter(&pidlock);
+ upcount_dec(oldruid, zoneid);
+ mutex_exit(&pidlock);
+ }
+
+ if (error == 0) {
+ if (do_nocd) {
+ mutex_enter(&p->p_lock);
+ p->p_flag |= SNOCD;
+ mutex_exit(&p->p_lock);
+ }
+ crset(p, newcr); /* broadcast to process threads */
+ goto done;
+ }
+ crfree(newcr);
+done:
+ if (error)
+ return (set_errno(error));
+ else
+ return (0);
+}
+
+long
+lx_setresuid16(lx_uid16_t ruid16, lx_uid16_t euid16, lx_uid16_t suid16)
+{
+ long rval;
+
+ rval = lx_setresuid(
+ LX_UID16_TO_UID32(ruid16),
+ LX_UID16_TO_UID32(euid16),
+ LX_UID16_TO_UID32(suid16));
+
+ return (rval);
+}
+
+/*
+ * This function is based on setregid in common/syscall/gid.c
+ */
+long
+lx_setresgid(lx_gid_t rgid, lx_gid_t egid, lx_gid_t sgid)
+{
+ proc_t *p;
+ int error = 0;
+ int do_nocd = 0;
+ cred_t *cr, *newcr;
+
+ if ((rgid != -1 && (rgid > MAXUID)) ||
+ (egid != -1 && (egid > MAXUID)) ||
+ (sgid != -1 && (sgid > MAXUID))) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Need to pre-allocate the new cred structure before grabbing
+ * the p_crlock mutex.
+ */
+ newcr = cralloc();
+
+ p = ttoproc(curthread);
+ mutex_enter(&p->p_crlock);
+ cr = p->p_cred;
+
+ if (rgid != -1 &&
+ rgid != cr->cr_rgid && rgid != cr->cr_gid &&
+ rgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else if (egid != -1 &&
+ egid != cr->cr_rgid && egid != cr->cr_gid &&
+ egid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else if (sgid != -1 &&
+ sgid != cr->cr_rgid && sgid != cr->cr_gid &&
+ sgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else {
+ crhold(cr);
+ crcopy_to(cr, newcr);
+ p->p_cred = newcr;
+
+ if (egid != -1)
+ newcr->cr_gid = egid;
+ if (sgid != -1)
+ newcr->cr_sgid = sgid;
+ if (rgid != -1)
+ newcr->cr_rgid = rgid;
+
+ /*
+ * A process that gives up its privilege
+ * must be marked to produce no core dump.
+ */
+ if ((cr->cr_gid != newcr->cr_gid ||
+ cr->cr_rgid != newcr->cr_rgid ||
+ cr->cr_sgid != newcr->cr_sgid))
+ do_nocd = 1;
+
+ crfree(cr);
+ }
+ mutex_exit(&p->p_crlock);
+
+ if (error == 0) {
+ if (do_nocd) {
+ mutex_enter(&p->p_lock);
+ p->p_flag |= SNOCD;
+ mutex_exit(&p->p_lock);
+ }
+ crset(p, newcr); /* broadcast to process threads */
+ goto done;
+ }
+ crfree(newcr);
+done:
+ if (error)
+ return (set_errno(error));
+ else
+ return (0);
+}
+
+long
+lx_setresgid16(lx_gid16_t rgid16, lx_gid16_t egid16, lx_gid16_t sgid16)
+{
+ long rval;
+
+ rval = lx_setresgid(
+ LX_GID16_TO_GID32(rgid16),
+ LX_GID16_TO_GID32(egid16),
+ LX_GID16_TO_GID32(sgid16));
+
+ return (rval);
+}
+
+/*
+ * Linux defines NGROUPS_MAX to be 32, but on illumos it is only 16. We employ
+ * the terrible hack below so that tests may proceed, if only on DEBUG kernels.
+ */
+int
+lx_helper_setgroups(int ngroups, gid_t *grouplist)
+{
+#ifdef DEBUG
+ if (ngroups > ngroups_max && ngroups <= LX_NGROUPS_MAX)
+ ngroups = ngroups_max;
+#endif /* DEBUG */
+
+ return (setgroups(ngroups, grouplist));
+}
+
+long
+lx_getresuid(lx_uid_t *ruid, lx_uid_t *euid, lx_uid_t *suid)
+{
+ lx_uid_t lx_ruid, lx_euid, lx_suid;
+ cred_t *cr = CRED();
+
+ lx_ruid = (lx_uid_t)crgetruid(cr);
+ lx_euid = (lx_uid_t)crgetuid(cr);
+ lx_suid = (lx_uid_t)crgetsuid(cr);
+
+ if (copyout(&lx_ruid, (void *)ruid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_euid, (void *)euid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_suid, (void *)suid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresuid16(lx_uid16_t *ruid16, lx_uid16_t *euid16, lx_uid16_t *suid16)
+{
+ lx_uid16_t lx_ruid16, lx_euid16, lx_suid16;
+ cred_t *cr = CRED();
+
+ lx_ruid16 = LX_UID32_TO_UID16((lx_uid_t)crgetruid(cr));
+ lx_euid16 = LX_UID32_TO_UID16((lx_uid_t)crgetuid(cr));
+ lx_suid16 = LX_UID32_TO_UID16((lx_uid_t)crgetsuid(cr));
+
+ if (copyout(&lx_ruid16, (void *)ruid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_euid16, (void *)euid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_suid16, (void *)suid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresgid(lx_gid_t *rgid, lx_gid_t *egid, lx_gid_t *sgid)
+{
+ lx_gid_t lx_rgid, lx_egid, lx_sgid;
+ cred_t *cr = CRED();
+
+ lx_rgid = (lx_gid_t)crgetrgid(cr);
+ lx_egid = (lx_gid_t)crgetgid(cr);
+ lx_sgid = (lx_gid_t)crgetsgid(cr);
+
+ if (copyout(&lx_rgid, (void *)rgid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_egid, (void *)egid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_sgid, (void *)sgid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresgid16(lx_gid16_t *rgid16, lx_gid16_t *egid16, lx_gid16_t *sgid16)
+{
+ lx_gid16_t lx_rgid16, lx_egid16, lx_sgid16;
+ cred_t *cr = CRED();
+
+ lx_rgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetrgid(cr));
+ lx_egid16 = LX_GID32_TO_GID16((lx_gid_t)crgetgid(cr));
+ lx_sgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetsgid(cr));
+
+ if (copyout(&lx_rgid16, (void *)rgid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_egid16, (void *)egid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_sgid16, (void *)sgid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/*
+ * The lx brand cannot support the setfs[ug]id16/setfs[ug]id calls as that
+ * would require significant rework of the illumos privilege mechanisms, so
+ * instead return the current effective [ug]id.
+ *
+ * In Linux, fsids track effective IDs, so returning the effective IDs works
+ * as a substitute; returning the current value also denotes failure of the
+ * call if the caller had specified something different. We don't need to
+ * worry about setting error codes because the Linux calls don't set any.
+ */
+/*ARGSUSED*/
+long
+lx_setfsuid16(uid_t fsuid16)
+{
+ return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid16(gid_t fsgid16)
+{
+ return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsuid(uid_t fsuid)
+{
+ return (crgetuid(CRED()));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid(gid_t fsgid)
+{
+ return (crgetgid(CRED()));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
new file mode 100644
index 0000000000..c1966731ff
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
@@ -0,0 +1,1769 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <sys/termio.h>
+#include <sys/termios.h>
+#include <sys/ptyvar.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/ptms.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_ptm.h>
+#include <sys/sunddi.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/session.h>
+#include <sys/kmem.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <net/if_arp.h>
+#include <sys/ioccom.h>
+#include <sys/dtrace.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/lx_autofs.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+#include <sys/dkio.h>
+#include <sys/sdt.h>
+
+/*
+ * Linux ioctl types
+ */
+#define LX_IOC_TYPE_HD 0x03
+#define LX_IOC_TYPE_BLK 0x12
+#define LX_IOC_TYPE_FD 0x54
+#define LX_IOC_TYPE_DTRACE 0x68
+#define LX_IOC_TYPE_SOCK 0x89
+#define LX_IOC_TYPE_AUTOFS 0x93
+
+/*
+ * Supported ioctls
+ */
+#define LX_HDIO_GETGEO 0x0301
+#define LX_BLKGETSIZE 0x1260
+#define LX_BLKSSZGET 0x1268
+#define LX_BLKGETSIZE64 0x80081272
+#define LX_TCGETS 0x5401
+#define LX_TCSETS 0x5402
+#define LX_TCSETSW 0x5403
+#define LX_TCSETSF 0x5404
+#define LX_TCGETA 0x5405
+#define LX_TCSETA 0x5406
+#define LX_TCSETAW 0x5407
+#define LX_TCSETAF 0x5408
+#define LX_TCSBRK 0x5409
+#define LX_TCXONC 0x540a
+#define LX_TCFLSH 0x540b
+#define LX_TIOCEXCL 0x540c
+#define LX_TIOCNXCL 0x540d
+#define LX_TIOCSCTTY 0x540e
+#define LX_TIOCGPGRP 0x540f
+#define LX_TIOCSPGRP 0x5410
+#define LX_TIOCOUTQ 0x5411
+#define LX_TIOCSTI 0x5412
+#define LX_TIOCGWINSZ 0x5413
+#define LX_TIOCSWINSZ 0x5414
+#define LX_TIOCMGET 0x5415
+#define LX_TIOCMBIS 0x5416
+#define LX_TIOCMBIC 0x5417
+#define LX_TIOCMSET 0x5418
+#define LX_TIOCGSOFTCAR 0x5419
+#define LX_TIOCSSOFTCAR 0x541a
+#define LX_FIONREAD 0x541b
+#define LX_TIOCPKT 0x5420
+#define LX_FIONBIO 0x5421
+#define LX_TIOCNOTTY 0x5422
+#define LX_TIOCSETD 0x5423
+#define LX_TIOCGETD 0x5424
+#define LX_TCSBRKP 0x5425
+#define LX_TIOCGSID 0x5429
+#define LX_TIOCGPTN 0x80045430
+#define LX_TIOCSPTLCK 0x40045431
+#define LX_FIONCLEX 0x5450
+#define LX_FIOCLEX 0x5451
+#define LX_FIOASYNC 0x5452
+#define LX_FIOSETOWN 0x8901
+#define LX_SIOCSPGRP 0x8902
+#define LX_FIOGETOWN 0x8903
+#define LX_SIOCGPGRP 0x8904
+#define LX_SIOCATMARK 0x8905
+#define LX_SIOCGSTAMP 0x8906
+#define LX_SIOCADDRT 0x890b
+#define LX_SIOCDELRT 0x890c
+#define LX_SIOCRTMSG 0x890d
+#define LX_SIOCGIFNAME 0x8910
+#define LX_SIOCSIFLINK 0x8911
+#define LX_SIOCGIFCONF 0x8912
+#define LX_SIOCGIFFLAGS 0x8913
+#define LX_SIOCSIFFLAGS 0x8914
+#define LX_SIOCGIFADDR 0x8915
+#define LX_SIOCSIFADDR 0x8916
+#define LX_SIOCGIFDSTADDR 0x8917
+#define LX_SIOCSIFDSTADDR 0x8918
+#define LX_SIOCGIFBRDADDR 0x8919
+#define LX_SIOCSIFBRDADDR 0x891a
+#define LX_SIOCGIFNETMASK 0x891b
+#define LX_SIOCSIFNETMASK 0x891c
+#define LX_SIOCGIFMETRIC 0x891d
+#define LX_SIOCSIFMETRIC 0x891e
+#define LX_SIOCGIFMEM 0x891f
+#define LX_SIOCSIFMEM 0x8920
+#define LX_SIOCGIFMTU 0x8921
+#define LX_SIOCSIFMTU 0x8922
+#define LX_SIOCSIFNAME 0x8923
+#define LX_SIOCSIFHWADDR 0x8924
+#define LX_SIOCGIFENCAP 0x8925
+#define LX_SIOCSIFENCAP 0x8926
+#define LX_SIOCGIFHWADDR 0x8927
+#define LX_SIOCGIFSLAVE 0x8929
+#define LX_SIOCSIFSLAVE 0x8930
+#define LX_SIOCADDMULTI 0x8931
+#define LX_SIOCDELMULTI 0x8932
+#define LX_SIOCGIFINDEX 0x8933
+#define LX_SIOCSIFPFLAGS 0x8934
+#define LX_SIOCGIFPFLAGS 0x8935
+#define LX_SIOCDIFADDR 0x8936
+#define LX_SIOCSIFHWBROADCAST 0x8937
+#define LX_SIOCGIFCOUNT 0x8938
+#define LX_SIOCGIFBR 0x8940
+#define LX_SIOCSIFBR 0x8941
+#define LX_SIOCGIFTXQLEN 0x8942
+#define LX_SIOCSIFTXQLEN 0x8943
+#define LX_SIOCETHTOOL 0x8946
+#define LX_SIOCGMIIPHY 0x8947
+#define LX_SIOCGMIIREG 0x8948
+#define LX_SIOCSMIIREG 0x8949
+#define LX_SIOCWANDEV 0x894a
+#define LX_SIOCOUTQNSD 0x894b
+#define LX_SIOCDARP 0x8953
+#define LX_SIOCGARP 0x8954
+#define LX_SIOCSARP 0x8955
+#define LX_SIOCDRARP 0x8960
+#define LX_SIOCGRARP 0x8961
+#define LX_SIOCSRARP 0x8962
+#define LX_SIOCGIFMAP 0x8970
+#define LX_SIOCSIFMAP 0x8971
+#define LX_SIOCADDDLCI 0x8980
+#define LX_SIOCDELDLCI 0x8981
+#define LX_SIOCGIFVLAN 0x8982
+#define LX_SIOCSIFVLAN 0x8983
+#define LX_SIOCBONDENSLAVE 0x8990
+#define LX_SIOCBONDRELEASE 0x8991
+#define LX_SIOCBONDSETHWADDR 0x8992
+#define LX_SIOCBONDSLAVEINFOQUERY 0x8993
+#define LX_SIOCBONDINFOQUERY 0x8994
+#define LX_SIOCBONDCHANGEACTIVE 0x8995
+#define LX_SIOCBRADDBR 0x89a0
+#define LX_SIOCBRDELBR 0x89a1
+#define LX_SIOCBRADDIF 0x89a2
+#define LX_SIOCBRDELIF 0x89a3
+#define LX_SIOCSHWTSTAMP 0x89b0
+#define LX_SIOCGHWTSTAMP 0x89b1
+#define LX_SIOCDEVPRIVATE 0x89f0
+#define LX_SIOCPROTOPRIVATE 0x89e0
+
+#define FLUSER(fp) fp->f_flag | get_udatamodel()
+#define FLFAKE(fp) fp->f_flag | FKIOCTL
+
+/*
+ * LX_NCC must be different from LX_NCCS since while the termio and termios
+ * structures may look similar they are fundamentally different sizes and
+ * have different members.
+ */
+#define LX_NCC 8
+#define LX_NCCS 19
+
+struct lx_termio {
+ unsigned short c_iflag; /* input mode flags */
+ unsigned short c_oflag; /* output mode flags */
+ unsigned short c_cflag; /* control mode flags */
+ unsigned short c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[LX_NCC]; /* control characters */
+};
+
+struct lx_termios {
+ uint32_t c_iflag; /* input mode flags */
+ uint32_t c_oflag; /* output mode flags */
+ uint32_t c_cflag; /* control mode flags */
+ uint32_t c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[LX_NCCS]; /* control characters */
+};
+
+/*
+ * c_cc characters which are valid for lx_termio and lx_termios
+ */
+#define LX_VINTR 0
+#define LX_VQUIT 1
+#define LX_VERASE 2
+#define LX_VKILL 3
+#define LX_VEOF 4
+#define LX_VTIME 5
+#define LX_VMIN 6
+#define LX_VSWTC 7
+
+/*
+ * c_cc characters which are valid for lx_termios
+ */
+#define LX_VSTART 8
+#define LX_VSTOP 9
+#define LX_VSUSP 10
+#define LX_VEOL 11
+#define LX_VREPRINT 12
+#define LX_VDISCARD 13
+#define LX_VWERASE 14
+#define LX_VLNEXT 15
+#define LX_VEOL2 16
+
+/*
+ * Defaults needed for SunOS to Linux format conversion.
+ * See INIT_C_CC in linux-stable/include/asm-generic/termios.h
+ */
+#define LX_DEF_VTIME 0
+#define LX_DEF_VMIN 1
+#define LX_DEF_VEOF '\004'
+#define LX_DEF_VEOL 0
+
+/* VSD key for lx_cc information */
+static uint_t lx_ioctl_vsd = 0;
+
+extern int lx_lpid_to_spair(pid_t l_pid, pid_t *s_pid, id_t *s_tid);
+
+/* Terminal helpers */
+
+static void
+l2s_termios(struct lx_termios *l_tios, struct termios *s_tios)
+{
+ ASSERT((l_tios != NULL) && (s_tios != NULL));
+
+ bzero(s_tios, sizeof (*s_tios));
+
+ s_tios->c_iflag = l_tios->c_iflag;
+ s_tios->c_oflag = l_tios->c_oflag;
+ s_tios->c_cflag = l_tios->c_cflag;
+ s_tios->c_lflag = l_tios->c_lflag;
+
+ if (s_tios->c_lflag & ICANON) {
+ s_tios->c_cc[VEOF] = l_tios->c_cc[LX_VEOF];
+ s_tios->c_cc[VEOL] = l_tios->c_cc[LX_VEOL];
+ } else {
+ s_tios->c_cc[VMIN] = l_tios->c_cc[LX_VMIN];
+ s_tios->c_cc[VTIME] = l_tios->c_cc[LX_VTIME];
+ }
+
+ s_tios->c_cc[VEOL2] = l_tios->c_cc[LX_VEOL2];
+ s_tios->c_cc[VERASE] = l_tios->c_cc[LX_VERASE];
+ s_tios->c_cc[VKILL] = l_tios->c_cc[LX_VKILL];
+ s_tios->c_cc[VREPRINT] = l_tios->c_cc[LX_VREPRINT];
+ s_tios->c_cc[VLNEXT] = l_tios->c_cc[LX_VLNEXT];
+ s_tios->c_cc[VWERASE] = l_tios->c_cc[LX_VWERASE];
+ s_tios->c_cc[VINTR] = l_tios->c_cc[LX_VINTR];
+ s_tios->c_cc[VQUIT] = l_tios->c_cc[LX_VQUIT];
+ s_tios->c_cc[VSWTCH] = l_tios->c_cc[LX_VSWTC];
+ s_tios->c_cc[VSTART] = l_tios->c_cc[LX_VSTART];
+ s_tios->c_cc[VSTOP] = l_tios->c_cc[LX_VSTOP];
+ s_tios->c_cc[VSUSP] = l_tios->c_cc[LX_VSUSP];
+ s_tios->c_cc[VDISCARD] = l_tios->c_cc[LX_VDISCARD];
+}
+
+static void
+l2s_termio(struct lx_termio *l_tio, struct termio *s_tio)
+{
+ ASSERT((l_tio != NULL) && (s_tio != NULL));
+
+ bzero(s_tio, sizeof (*s_tio));
+
+ s_tio->c_iflag = l_tio->c_iflag;
+ s_tio->c_oflag = l_tio->c_oflag;
+ s_tio->c_cflag = l_tio->c_cflag;
+ s_tio->c_lflag = l_tio->c_lflag;
+
+ if (s_tio->c_lflag & ICANON) {
+ s_tio->c_cc[VEOF] = l_tio->c_cc[LX_VEOF];
+ } else {
+ s_tio->c_cc[VMIN] = l_tio->c_cc[LX_VMIN];
+ s_tio->c_cc[VTIME] = l_tio->c_cc[LX_VTIME];
+ }
+
+ s_tio->c_cc[VINTR] = l_tio->c_cc[LX_VINTR];
+ s_tio->c_cc[VQUIT] = l_tio->c_cc[LX_VQUIT];
+ s_tio->c_cc[VERASE] = l_tio->c_cc[LX_VERASE];
+ s_tio->c_cc[VKILL] = l_tio->c_cc[LX_VKILL];
+ s_tio->c_cc[VSWTCH] = l_tio->c_cc[LX_VSWTC];
+}
+
+static void
+termios2lx_cc(struct lx_termios *l_tios, struct lx_cc *lio)
+{
+ ASSERT((l_tios != NULL) && (lio != NULL));
+
+ bzero(lio, sizeof (*lio));
+
+ lio->veof = l_tios->c_cc[LX_VEOF];
+ lio->veol = l_tios->c_cc[LX_VEOL];
+ lio->vmin = l_tios->c_cc[LX_VMIN];
+ lio->vtime = l_tios->c_cc[LX_VTIME];
+}
+
+static void
+termio2lx_cc(struct lx_termio *l_tio, struct lx_cc *lio)
+{
+ ASSERT((l_tio != NULL) && (lio != NULL));
+
+ bzero(lio, sizeof (*lio));
+
+ lio->veof = l_tio->c_cc[LX_VEOF];
+ lio->veol = 0;
+ lio->vmin = l_tio->c_cc[LX_VMIN];
+ lio->vtime = l_tio->c_cc[LX_VTIME];
+}
+
+static void
+s2l_termios(struct termios *s_tios, struct lx_termios *l_tios)
+{
+ ASSERT((s_tios != NULL) && (l_tios != NULL));
+
+ bzero(l_tios, sizeof (*l_tios));
+
+ l_tios->c_iflag = s_tios->c_iflag;
+ l_tios->c_oflag = s_tios->c_oflag;
+ l_tios->c_cflag = s_tios->c_cflag;
+ l_tios->c_lflag = s_tios->c_lflag;
+
+ /*
+ * Since use of the VMIN/VTIME and VEOF/VEOL control characters is
+ * mutually exclusive (determined by ICANON), SunOS aliases them in the
+ * c_cc field in termio/termios. Linux does not perform this aliasing,
+ * so it expects that the default values are present regardless of
+ * ICANON status.
+ *
+ * These defaults can be overridden later by any values stored via the
+ * lx_cc mechanism.
+ */
+ if (s_tios->c_lflag & ICANON) {
+ l_tios->c_cc[LX_VEOF] = s_tios->c_cc[VEOF];
+ l_tios->c_cc[LX_VEOL] = s_tios->c_cc[VEOL];
+ l_tios->c_cc[LX_VTIME] = LX_DEF_VTIME;
+ l_tios->c_cc[LX_VMIN] = LX_DEF_VMIN;
+
+ } else {
+ l_tios->c_cc[LX_VMIN] = s_tios->c_cc[VMIN];
+ l_tios->c_cc[LX_VTIME] = s_tios->c_cc[VTIME];
+ l_tios->c_cc[LX_VEOF] = LX_DEF_VEOF;
+ l_tios->c_cc[LX_VEOL] = LX_DEF_VEOL;
+ }
+
+ l_tios->c_cc[LX_VEOL2] = s_tios->c_cc[VEOL2];
+ l_tios->c_cc[LX_VERASE] = s_tios->c_cc[VERASE];
+ l_tios->c_cc[LX_VKILL] = s_tios->c_cc[VKILL];
+ l_tios->c_cc[LX_VREPRINT] = s_tios->c_cc[VREPRINT];
+ l_tios->c_cc[LX_VLNEXT] = s_tios->c_cc[VLNEXT];
+ l_tios->c_cc[LX_VWERASE] = s_tios->c_cc[VWERASE];
+ l_tios->c_cc[LX_VINTR] = s_tios->c_cc[VINTR];
+ l_tios->c_cc[LX_VQUIT] = s_tios->c_cc[VQUIT];
+ l_tios->c_cc[LX_VSWTC] = s_tios->c_cc[VSWTCH];
+ l_tios->c_cc[LX_VSTART] = s_tios->c_cc[VSTART];
+ l_tios->c_cc[LX_VSTOP] = s_tios->c_cc[VSTOP];
+ l_tios->c_cc[LX_VSUSP] = s_tios->c_cc[VSUSP];
+ l_tios->c_cc[LX_VDISCARD] = s_tios->c_cc[VDISCARD];
+}
+
+static void
+s2l_termio(struct termio *s_tio, struct lx_termio *l_tio)
+{
+ ASSERT((s_tio != NULL) && (l_tio != NULL));
+
+ bzero(l_tio, sizeof (*l_tio));
+
+ l_tio->c_iflag = s_tio->c_iflag;
+ l_tio->c_oflag = s_tio->c_oflag;
+ l_tio->c_cflag = s_tio->c_cflag;
+ l_tio->c_lflag = s_tio->c_lflag;
+
+ if (s_tio->c_lflag & ICANON) {
+ l_tio->c_cc[LX_VEOF] = s_tio->c_cc[VEOF];
+ l_tio->c_cc[LX_VTIME] = LX_DEF_VTIME;
+ l_tio->c_cc[LX_VMIN] = LX_DEF_VMIN;
+ } else {
+ l_tio->c_cc[LX_VMIN] = s_tio->c_cc[VMIN];
+ l_tio->c_cc[LX_VTIME] = s_tio->c_cc[VTIME];
+ l_tio->c_cc[LX_VEOF] = LX_DEF_VEOF;
+ }
+
+ l_tio->c_cc[LX_VINTR] = s_tio->c_cc[VINTR];
+ l_tio->c_cc[LX_VQUIT] = s_tio->c_cc[VQUIT];
+ l_tio->c_cc[LX_VERASE] = s_tio->c_cc[VERASE];
+ l_tio->c_cc[LX_VKILL] = s_tio->c_cc[VKILL];
+ l_tio->c_cc[LX_VSWTC] = s_tio->c_cc[VSWTCH];
+}
+
+static void
+set_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+ struct lx_cc *cur;
+ /*
+ * Linux expects that the termio/termios control characters are
+ * preserved more strictly than illumos supports. In order to preserve
+ * the illusion that the characters are maintained, they are stored as
+ * vnode-specific data.
+ */
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+ if (cur == NULL) {
+ cur = kmem_alloc(sizeof (struct lx_cc), KM_SLEEP);
+ bcopy(lio, cur, sizeof (struct lx_cc));
+ (void) vsd_set(vp, lx_ioctl_vsd, cur);
+ } else {
+ bcopy(lio, cur, sizeof (struct lx_cc));
+ }
+ mutex_exit(&vp->v_vsd_lock);
+}
+
+static int
+get_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+ struct lx_cc *cur;
+ int rv = 1;
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+ if (cur != NULL) {
+ bcopy(cur, lio, sizeof (*lio));
+ rv = 0;
+ }
+ mutex_exit(&vp->v_vsd_lock);
+ return (rv);
+}
+
+/* Socket helpers */
+
+typedef struct lx_ifreq32 {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr ifru_addr;
+ } ifr_ifrn;
+} lx_ifreq32_t;
+
+typedef struct lx_ifreq64 {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr ifru_addr;
+ /* pad this out to the Linux size */
+ uint64_t ifmap[3];
+ } ifr_ifrn;
+} lx_ifreq64_t;
+
+typedef struct lx_ifconf32 {
+ int32_t if_len;
+ caddr32_t if_buf;
+} lx_ifconf32_t;
+
+typedef struct lx_ifconf64 {
+ int32_t if_len;
+ caddr_t if_buf;
+} lx_ifconf64_t;
+
+
+/* Generic translators */
+
+/* ARGSUSED */
+static int
+ict_pass(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int error = 0;
+ int rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionbio(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp;
+ int32_t iflag, flags;
+ int error;
+
+ if (copyin((caddr_t)arg, &iflag, sizeof (iflag)))
+ return (set_errno(EFAULT));
+
+ mutex_enter(&fp->f_tlock);
+ vp = fp->f_vnode;
+ flags = fp->f_flag;
+ /* Linux sets NONBLOCK instead of FIONBIO */
+ if (iflag)
+ flags |= FNONBLOCK;
+ else
+ flags &= ~FNONBLOCK;
+ /* push the flag down */
+ error = VOP_SETFL(vp, fp->f_flag, flags, fp->f_cred, NULL);
+ fp->f_flag = flags;
+ mutex_exit(&fp->f_tlock);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionread(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp;
+ struct vattr vattr;
+ int error = 0;
+ int rv;
+ /*
+ * offset is int32_t because that is what FIONREAD is defined in terms
+ * of. We cap at INT_MAX as in other cases for this ioctl.
+ */
+ int32_t offset;
+
+ vp = fp->f_vnode;
+
+ if (vp->v_type == VREG || vp->v_type == VDIR) {
+ vattr.va_mask = AT_SIZE;
+ error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
+ if (error != 0)
+ return (set_errno(error));
+ offset = MIN(vattr.va_size - fp->f_offset, INT_MAX);
+ if (copyout(&offset, (caddr_t)arg, sizeof (offset)))
+ return (set_errno(EFAULT));
+ } else {
+ error = VOP_IOCTL(vp, FIONREAD, arg, FLUSER(fp), fp->f_cred,
+ &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+/*
+ * hard disk-related translators
+ *
+ * Note that the normal disk ioctls only work for VCHR devices. See spec_ioctl
+ * which will return ENOTTY for a VBLK device. However, fdisk, etc. expect to
+ * work with block devices.
+ *
+ * We expect a zvol to be the primary block device we're interacting with and
+ * we use the zone's lxzd_vdisks list to handle zvols specifically.
+ */
+
+typedef struct lx_hd_geom {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+} lx_hd_geom_t;
+
+static lx_virt_disk_t *
+lx_lookup_zvol(lx_zone_data_t *lxzd, dev_t dev)
+{
+ lx_virt_disk_t *vd;
+
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZVOL && vd->lxvd_real_dev == dev)
+ return (vd);
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ return (NULL);
+}
+
+/*
+ * See zvol_ioctl() which always fails for DKIOCGGEOM. The geometry for a
+ * zvol (or really any modern disk) is made up, so we do that here as well.
+ */
+/* ARGSUSED */
+static int
+ict_hdgetgeo(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_hd_geom_t lx_geom;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+ if (vd == NULL) {
+ /* should only happen if new zvol */
+ bzero(&lx_geom, sizeof (lx_geom));
+ } else {
+ diskaddr_t tot;
+
+ tot = vd->lxvd_volsize / vd->lxvd_blksize;
+
+ /*
+ * Since the 'sectors' value is only one byte we make
+ * up heads/cylinder values to get things to fit.
+ * We roundup the number of heads to ensure we don't
+ * overflow the sectors due to truncation.
+ */
+ lx_geom.heads = lx_geom.cylinders = (tot / 0xff) + 1;
+ lx_geom.sectors = tot / lx_geom.heads;
+ lx_geom.start = 0;
+ }
+ } else {
+ int res, rv;
+ struct dk_geom geom;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGGEOM, (intptr_t)&geom,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ lx_geom.heads = geom.dkg_nhead;
+ lx_geom.sectors = geom.dkg_nsect;
+ lx_geom.cylinders = geom.dkg_ncyl;
+ lx_geom.start = 0;
+ }
+
+ if (copyout(&lx_geom, (caddr_t)arg, sizeof (lx_geom)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Per the Linux sd(4) man page, get the number of sectors. The linux/fs.h
+ * header says its 512 byte blocks.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ diskaddr_t tot;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+ if (vd == NULL) {
+ /* should only happen if new zvol */
+ tot = 0;
+ } else {
+ tot = vd->lxvd_volsize / 512;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ tot = minfo.dki_capacity;
+ if (minfo.dki_lbsize > 512) {
+ uint_t bsize = minfo.dki_lbsize / 512;
+
+ tot *= bsize;
+ }
+ }
+
+ if (copyout(&tot, (caddr_t)arg, sizeof (long)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Get the sector size (i.e. the logical block size).
+ */
+/* ARGSUSED */
+static int
+ict_blkgetssize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ uint_t bsize;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+ if (vd == NULL) {
+ /* should only happen if new zvol */
+ bsize = 0;
+ } else {
+ bsize = (uint_t)vd->lxvd_blksize;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ bsize = (uint_t)minfo.dki_lbsize;
+ }
+
+ if (copyout(&bsize, (caddr_t)arg, sizeof (bsize)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Get the size. The linux/fs.h header says its in bytes.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ uint64_t tot;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+ if (vd == NULL) {
+ /* should only happen if new zvol */
+ tot = 0;
+ } else {
+ tot = vd->lxvd_volsize;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ tot = minfo.dki_capacity * minfo.dki_lbsize;
+ }
+
+ if (copyout(&tot, (caddr_t)arg, sizeof (uint64_t)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/* ARGSUSED */
+/* Terminal-related translators */
+
+/* ARGSUSED */
+static int
+ict_tcsets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios;
+ struct lx_cc lio;
+ int error, rv;
+
+ ASSERT(cmd == TCSETS || cmd == TCSETSW || cmd == TCSETSF);
+
+ if (copyin((struct lx_termios *)arg, &l_tios, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+ termios2lx_cc(&l_tios, &lio);
+ l2s_termios(&l_tios, &s_tios);
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ /* preserve lx_cc */
+ set_lx_cc(fp->f_vnode, &lio);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcseta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termio l_tio;
+ struct termio s_tio;
+ struct lx_cc lio;
+ int error, rv;
+
+ ASSERT(cmd == TCSETA || cmd == TCSETAW || cmd == TCSETAF);
+
+ if (copyin((struct lx_termio *)arg, &l_tio, sizeof (l_tio)) != 0)
+ return (set_errno(EFAULT));
+ l2s_termio(&l_tio, &s_tio);
+ termio2lx_cc(&l_tio, &lio);
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ /* preserve lx_cc */
+ set_lx_cc(fp->f_vnode, &lio);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_ptm(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios, *s_tiosd;
+ uint_t s_tiosl;
+
+ /* get termios defaults */
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+ DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&s_tiosd,
+ &s_tiosl) != DDI_SUCCESS)
+ return (EIO);
+ ASSERT(s_tiosl == sizeof (*s_tiosd));
+ bcopy(s_tiosd, &s_tios, sizeof (s_tios));
+ ddi_prop_free(s_tiosd);
+
+ /* Now munge the data to how Linux wants it. */
+ s2l_termios(&s_tios, &l_tios);
+ if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_native(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios;
+ struct lx_cc lio;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+
+ /* Now munge the data to how Linux wants it. */
+ s2l_termios(&s_tios, &l_tios);
+
+ /* return preserved lx_cc */
+ if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+ l_tios.c_cc[LX_VEOF] = lio.veof;
+ l_tios.c_cc[LX_VEOL] = lio.veol;
+ l_tios.c_cc[LX_VMIN] = lio.vmin;
+ l_tios.c_cc[LX_VTIME] = lio.vtime;
+ }
+
+ if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ if (getmajor(fp->f_vnode->v_rdev) == ddi_name_to_major(LX_PTM_DRV))
+ return (ict_tcgets_ptm(fp, cmd, arg, lxcmd));
+ else
+ return (ict_tcgets_native(fp, cmd, arg, lxcmd));
+}
+
+/* ARGSUSED */
+static int
+ict_tcgeta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termio l_tio;
+ struct termio s_tio;
+ struct lx_cc lio;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+
+ s2l_termio(&s_tio, &l_tio);
+ /* return preserved lx_cc */
+ if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+ l_tio.c_cc[LX_VEOF] = lio.veof;
+ l_tio.c_cc[LX_VMIN] = lio.vmin;
+ l_tio.c_cc[LX_VTIME] = lio.vtime;
+ }
+
+ if (copyout(&l_tio, (struct lx_termios *)arg, sizeof (l_tio)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocspgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t lpid, spid, tid;
+ int error, rv;
+
+ /* Converting to the illumos pid is necessary */
+ if (copyin((pid_t *)arg, &lpid, sizeof (lpid)) < 0)
+ return (set_errno(EFAULT));
+ if (lx_lpid_to_spair(lpid, &spid, &tid) < 0)
+ return (set_errno(EPERM));
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spid,
+ fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcsbrkp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int rv, error;
+ /* use null duration to emulate TCSBRKP */
+ int dur = 0;
+ error = VOP_IOCTL(fp->f_vnode, TCSBRK, (intptr_t)&dur,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgpgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t spgrp;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spgrp, FLFAKE(fp),
+ fp->f_cred, &rv, NULL);
+ if (error == 0) {
+ if (spgrp == curproc->p_zone->zone_proc_initpid) {
+ spgrp = 1;
+ }
+ if (copyout(&spgrp, (caddr_t)arg, sizeof (spgrp))) {
+ return (set_errno(EFAULT));
+ }
+ }
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_sptlock(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct strioctl istr;
+ int error, rv;
+
+ istr.ic_cmd = UNLKPT;
+ istr.ic_len = 0;
+ istr.ic_timout = 0;
+ istr.ic_dp = NULL;
+ error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+ fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+ /*
+ * The success/fail return values are different between Linux
+ * and illumos. Linux expects 0 or -1. Illumos can return
+ * positive number on success.
+ */
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_gptn(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct strioctl istr;
+ cred_t *cr;
+ pt_own_t pto;
+ int error, rv;
+ int ptyno;
+
+ /* This operation is only valid for the lx_ptm device. */
+ if (getmajor(fp->f_vnode->v_rdev) != ddi_name_to_major(LX_PTM_DRV))
+ return (set_errno(ENOTTY));
+
+ cr = CRED();
+ pto.pto_ruid = cr->cr_uid;
+ pto.pto_rgid = cr->cr_gid;
+
+ istr.ic_cmd = OWNERPT;
+ istr.ic_len = sizeof (pto);
+ istr.ic_timout = 0;
+ istr.ic_dp = (char *)&pto;
+ error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+ if (error)
+ return (set_errno((error == ENOTTY) ? error: EACCES));
+
+ ptyno = getminor(fp->f_vnode->v_rdev) - 1;
+ if (copyout(&ptyno, (caddr_t)arg, sizeof (ptyno)))
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgwinsz(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+
+ /*
+ * A few Linux libc's (e.g. musl) have chosen to implement isatty()
+ * using the TIOCGWINSZ ioctl. Some apps also do the same thing
+ * directly. On Linux that ioctl will return a size of 0x0 for dumb
+ * terminals but on illumos see the handling for TIOCGWINSZ in ptem's
+ * ptioc(). We fail if the winsize is all zeros. To emulate the Linux
+ * behavior use the native ioctl check that we do for isatty and return
+ * a size of 0x0 if that succeeds.
+ */
+ if (error == EINVAL) {
+ int err;
+ struct termio s_tio;
+
+ err = VOP_IOCTL(fp->f_vnode, TCGETA, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+ if (err == 0) {
+ struct winsize w;
+
+ bzero(&w, sizeof (w));
+ if (copyout(&w, (struct winsize *)arg, sizeof (w)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ }
+ }
+
+ if (error != 0)
+ return (set_errno(error));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocsctty(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t ttysid, mysid;
+ int error, rv;
+ proc_t *p = curproc;
+
+ /* getsid */
+ mutex_enter(&p->p_splock);
+ mysid = p->p_sessp->s_sid;
+ mutex_exit(&p->p_splock);
+
+ /*
+ * Report success if we already control the tty.
+ * If no one controls it, TIOCSCTTY will change that later.
+ */
+ error = VOP_IOCTL(fp->f_vnode, TIOCGSID, (intptr_t)&ttysid,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error == 0 && ttysid == mysid)
+ return (0);
+
+ /*
+ * Need to make sure we're a session leader, otherwise the
+ * TIOCSCTTY ioctl will fail.
+ */
+ mutex_enter(&pidlock);
+ if (p->p_sessp->s_sidp != p->p_pidp && !pgmembers(p->p_pid)) {
+ mutex_exit(&pidlock);
+ sess_create();
+ } else {
+ mutex_exit(&pidlock);
+ }
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, 0, FLUSER(fp),
+ fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* Socket-related translators */
+
+/* ARGSUSED */
+static int
+ict_siocatmark(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error, rv;
+ /*
+ * Linux expects a SIOCATMARK of a UDP socket to return ENOTTY, while
+ * Illumos allows it. Linux prior to 2.6.39 returned EINVAL for this.
+ */
+ if (vp->v_type != VSOCK || VTOSO(vp)->so_type != SOCK_STREAM)
+ return (set_errno(ENOTTY));
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ if (error)
+ return (set_errno(error));
+
+ return (0);
+}
+
+static int
+ict_if_ioctl(vnode_t *vn, int cmd, intptr_t arg, int flags, cred_t *cred)
+{
+ int error, rv;
+ lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+ ksocket_t ks;
+
+ ASSERT(lxzd != NULL);
+
+ /*
+ * For ioctls of this type, we are strict about address family
+ * whereas Linux is lenient. This strictness can be avoided by using
+ * an internal AF_INET ksocket, which we use if the family is anything
+ * but AF_PACKET.
+ */
+ if (vn->v_type == VSOCK && VTOSO(vn)->so_family == AF_PACKET)
+ return (VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL));
+
+ mutex_enter(&lxzd->lxzd_lock);
+ ks = lxzd->lxzd_ioctl_sock;
+ if (ks == NULL) {
+ /*
+ * Linux is not at all picky about address family when it comes
+ * to supporting interface-related ioctls. To mimic this
+ * behavior, we'll attempt those ioctls against a ksocket
+ * configured for that purpose.
+ */
+ (void) ksocket_socket(&lxzd->lxzd_ioctl_sock, AF_INET,
+ SOCK_DGRAM, 0, 0, curproc->p_zone->zone_kcred);
+ ks = lxzd->lxzd_ioctl_sock;
+ }
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (ks != NULL) {
+ error = ksocket_ioctl(ks, cmd, arg, &rv, cred);
+ } else {
+ error = VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL);
+ }
+
+ return (error);
+}
+
+static int
+ict_sioghwaddr(file_t *fp, struct lifreq *lreq)
+{
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&lreq->lifr_addr;
+ struct sockaddr hwaddr;
+ int error, size;
+
+ error = ict_if_ioctl(fp->f_vnode, SIOCGLIFHWADDR, (intptr_t)lreq,
+ FLFAKE(fp), fp->f_cred);
+
+ if (error == EADDRNOTAVAIL &&
+ strncmp(lreq->lifr_name, "lo", 2) == 0) {
+ /* Emulate success on suspected loopbacks */
+ sdl->sdl_type = DL_LOOP;
+ sdl->sdl_alen = ETHERADDRL;
+ bzero(LLADDR(sdl), sdl->sdl_alen);
+ error = 0;
+ }
+
+ if (error == 0) {
+ bzero(&hwaddr, sizeof (hwaddr));
+ lx_stol_hwaddr(sdl, &hwaddr, &size);
+ bcopy(&hwaddr, &lreq->lifr_addr,
+ size + sizeof (sdl->sdl_family));
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifname(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct ifreq req;
+ int len;
+ char name[LIFNAMSIZ];
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ phyint_t *phyi;
+
+ if (fp->f_vnode->v_type != VSOCK) {
+ return (set_errno(EINVAL));
+ }
+
+ len = (curproc->p_model == DATAMODEL_LP64) ? sizeof (lx_ifreq64_t) :
+ sizeof (lx_ifreq32_t);
+ if (copyin((struct ifreq *)arg, &req, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * Since Linux calls this ioctl on all sorts of sockets, perform the
+ * interface name lookup manually.
+ */
+ if ((ns = netstack_get_current()) == NULL) {
+ return (set_errno(EINVAL));
+ }
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+ (void *) &req.ifr_index, NULL);
+ if (phyi != NULL) {
+ (void) strncpy(name, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '\0';
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ if (strlen(name) != 0) {
+ /* Truncate for ifreq and copyout */
+ (void) strncpy(req.ifr_name, name, IFNAMSIZ);
+ if (copyout(&req, (struct ifreq *)arg, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+ return (0);
+ }
+
+ return (set_errno(EINVAL));
+}
+
+/* ARGSUSED */
+static int
+ict_siolifreq(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct ifreq req;
+ struct lifreq lreq;
+ int error, len;
+
+ /* Convert from Linux ifreq to illumos lifreq */
+ if (curproc->p_model == DATAMODEL_LP64)
+ len = sizeof (lx_ifreq64_t);
+ else
+ len = sizeof (lx_ifreq32_t);
+ if (copyin((struct ifreq *)arg, &req, len) != 0)
+ return (set_errno(EFAULT));
+ bzero(&lreq, sizeof (lreq));
+ (void) strncpy(lreq.lifr_name, req.ifr_name, IFNAMSIZ);
+ bcopy(&req.ifr_ifru, &lreq.lifr_lifru, len - IFNAMSIZ);
+ lx_ifname_convert(lreq.lifr_name, LX_IF_TONATIVE);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ /*
+ * Convert cmd from SIO*IF* to SIO*LIF*.
+ * This is needed since Linux allows ifreq operations on ipv6
+ * sockets where illumos does not.
+ */
+ cmd = ((cmd & IOC_INOUT) |
+ _IOW('i', ((cmd & 0xff) + 100), struct lifreq));
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFINDEX:
+ cmd = SIOCGLIFINDEX;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFFLAGS:
+ cmd = SIOCGLIFFLAGS;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ if (error == 0)
+ lx_ifflags_convert(&lreq.lifr_flags, LX_IF_FROMNATIVE);
+ break;
+ case SIOCSIFFLAGS:
+ cmd = SIOCSLIFFLAGS;
+ lx_ifflags_convert(&lreq.lifr_flags, LX_IF_TONATIVE);
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFHWADDR:
+ error = ict_sioghwaddr(fp, &lreq);
+ break;
+ case LX_SIOCGIFTXQLEN:
+ /*
+ * Illumos lacks the notion of txqlen. Confirm the provided
+ * interface is valid with SIOCGLIFINDEX and return a fake
+ * txqlen of 1. Loopback devices will report txqlen of 0.
+ */
+ if (strncmp(lreq.lifr_name, "lo", 2) == 0) {
+ lreq.lifr_index = 0;
+ error = 0;
+ break;
+ }
+ cmd = SIOCGLIFINDEX;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ if (error == 0) {
+ /* lifr_index aliases to the qlen field */
+ lreq.lifr_index = 1;
+ }
+ break;
+ case LX_SIOCSIFHWADDR:
+ /*
+ * We're not going to support SIOCSIFHWADDR, but we need to be
+ * able to check the result of the copyin first to see if the
+ * command should have returned EFAULT.
+ */
+ default:
+ error = EINVAL;
+ }
+
+ if (error != 0)
+ return (set_errno(error));
+
+ /* Convert back to a Linux ifreq */
+ lx_ifname_convert(lreq.lifr_name, LX_IF_FROMNATIVE);
+ bzero(&req, sizeof (req));
+ (void) strncpy(req.ifr_name, lreq.lifr_name, IFNAMSIZ);
+ bcopy(&lreq.lifr_lifru, &req.ifr_ifru, len - IFNAMSIZ);
+
+ if (copyout(&req, (struct lifreq *)arg, len) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf32(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_ifconf32_t conf;
+ lx_ifreq32_t *oreq;
+ struct ifconf sconf;
+ int ifcount, error, i, buf_len;
+
+ if (copyin((lx_ifconf32_t *)arg, &conf, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+
+ /* They want to know how many interfaces there are. */
+ if (conf.if_len <= 0 || conf.if_buf == NULL) {
+ error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+ (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
+ if (error != 0)
+ return (set_errno(error));
+
+ conf.if_len = ifcount * sizeof (lx_ifreq32_t);
+
+ if (copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ } else {
+ ifcount = conf.if_len / sizeof (lx_ifreq32_t);
+ }
+
+ /* Get interface configuration list. */
+ sconf.ifc_len = ifcount * sizeof (struct ifreq);
+ sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+ fp->f_cred);
+ if (error != 0) {
+ kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+ return (set_errno(error));
+ }
+
+ /* Convert data to Linux format & rename interfaces */
+ buf_len = ifcount * sizeof (lx_ifreq32_t);
+ oreq = (lx_ifreq32_t *)kmem_alloc(buf_len, KM_SLEEP);
+ for (i = 0; i < sconf.ifc_len / sizeof (struct ifreq); i++) {
+ bcopy(&sconf.ifc_req[i], oreq + i, sizeof (lx_ifreq32_t));
+ lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+ }
+ conf.if_len = i * sizeof (*oreq);
+ kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+
+ error = 0;
+ if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+ copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+ error = set_errno(EFAULT);
+
+ kmem_free(oreq, buf_len);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_ifconf64_t conf;
+ lx_ifreq64_t *oreq;
+ struct ifconf sconf;
+ int ifcount, error, i, buf_len;
+
+ if (copyin((lx_ifconf64_t *)arg, &conf, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+
+ /* They want to know how many interfaces there are. */
+ if (conf.if_len <= 0 || conf.if_buf == NULL) {
+ error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+ (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
+ if (error != 0)
+ return (set_errno(error));
+
+ conf.if_len = ifcount * sizeof (lx_ifreq64_t);
+
+ if (copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ } else {
+ ifcount = conf.if_len / sizeof (lx_ifreq64_t);
+ }
+
+ /* Get interface configuration list. */
+ sconf.ifc_len = ifcount * sizeof (struct ifreq);
+ sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+ fp->f_cred);
+ if (error != 0) {
+ kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+ return (set_errno(error));
+ }
+
+ /* Convert data to Linux format & rename interfaces */
+ buf_len = ifcount * sizeof (lx_ifreq64_t);
+ oreq = (lx_ifreq64_t *)kmem_alloc(buf_len, KM_SLEEP);
+ for (i = 0; i < sconf.ifc_len / sizeof (struct ifreq); i++) {
+ bcopy(&sconf.ifc_req[i], oreq + i, sizeof (lx_ifreq64_t));
+ lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+ }
+ conf.if_len = i * sizeof (*oreq);
+ kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+
+ error = 0;
+ if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+ copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+ error = set_errno(EFAULT);
+
+ kmem_free(oreq, buf_len);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ if (curproc->p_model == DATAMODEL_LP64)
+ return (ict_siocgifconf64(fp, cmd, arg, lxcmd));
+ else
+ return (ict_siocgifconf32(fp, cmd, arg, lxcmd));
+}
+
+/*
+ * Unfortunately some of the autofs ioctls want to return a positive integer
+ * result which does not indicate an error. To minimize disruption in the
+ * rest of the code, we'll treat a positive return as an errno and a negative
+ * return as the non-error return (which we then negate).
+ */
+/* ARGSUSED */
+static int
+ict_autofs(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int res = 0;
+ int rv;
+
+ res = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ if (res > 0)
+ return (set_errno(res));
+ if (res == 0)
+ return (0);
+ return (-res);
+}
+
+/* Structure used to define an ioctl translator. */
+typedef struct lx_ioc_cmd_translator {
+ int lict_lxcmd;
+ int lict_cmd;
+ int (*lict_func)(file_t *fp, int cmd, intptr_t arg, int lxcmd);
+} lx_ioc_cmd_translator_t;
+
+#define LX_IOC_CMD_TRANSLATOR_PASS(ioc_cmd_sym) \
+ { (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define LX_IOC_CMD_TRANSLATOR_FILTER(ioc_cmd_sym, ioct_handler) \
+ { (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define LX_IOC_CMD_TRANSLATOR_CUSTOM(ioc_cmd_sym, ioct_handler) \
+ { (int)ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define LX_IOC_CMD_TRANSLATOR_PTHRU(ioc_cmd_sym) \
+ { (int)ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define LX_IOC_CMD_TRANSLATOR_END \
+ {0, 0, NULL}
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_fd[] = {
+ LX_IOC_CMD_TRANSLATOR_FILTER(FIONBIO, ict_fionbio)
+ LX_IOC_CMD_TRANSLATOR_FILTER(FIONREAD, ict_fionread)
+ LX_IOC_CMD_TRANSLATOR_PASS(FIOASYNC)
+
+ /* streams related */
+ LX_IOC_CMD_TRANSLATOR_PASS(TCXONC)
+ LX_IOC_CMD_TRANSLATOR_PASS(TCFLSH)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCEXCL)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCNXCL)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSTI)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSWINSZ)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIS)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIC)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMSET)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSETD)
+ LX_IOC_CMD_TRANSLATOR_PASS(TCSBRK)
+
+ /* terminal related */
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCGETD)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCGSID)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCNOTTY)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCPKT)
+
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETS, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSW, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSF, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETA, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAW, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAF, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCGETS, ict_tcgets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCGETA, ict_tcgeta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGWINSZ, ict_tiocgwinsz)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TCSBRKP, ict_tcsbrkp)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSPGRP, ict_tiocspgrp)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGPGRP, ict_tiocgpgrp)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSPTLCK, ict_sptlock)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPTN, ict_gptn)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSCTTY, ict_tiocsctty)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_socket[] = {
+ LX_IOC_CMD_TRANSLATOR_PASS(FIOGETOWN)
+
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCSPGRP)
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCGPGRP)
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCGSTAMP)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCATMARK, ict_siocatmark)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFFLAGS, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFFLAGS, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFDSTADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFDSTADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFBRDADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFBRDADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFNETMASK, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFNETMASK, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMETRIC, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMETRIC, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMTU, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMTU, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFHWADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCSIFHWADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFINDEX, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFTXQLEN, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFCONF, ict_siocgifconf)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFNAME, ict_siocgifname)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_dtrace[] = {
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_REMOVE)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADDDOF)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_autofs[] = {
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_READY)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_FAIL)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_CATATONIC)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOVER)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_SETTIMEOUT)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE_MULTI)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOSUBVER)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_ASKUMOUNT)
+
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_VERSION_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_READY_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_FAIL_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CATATONIC_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_TIMEOUT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_REQUESTER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_EXPIRE_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD,
+ ict_autofs)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_hd[] = {
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_HDIO_GETGEO, ict_hdgetgeo)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_blk[] = {
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE, ict_blkgetsize)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKSSZGET, ict_blkgetssize)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE64, ict_blkgetsize64)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static void
+lx_ioctl_vsd_free(void *data)
+{
+ kmem_free(data, sizeof (struct lx_cc));
+}
+
+void
+lx_ioctl_init()
+{
+ vsd_create(&lx_ioctl_vsd, lx_ioctl_vsd_free);
+}
+
+void
+lx_ioctl_fini()
+{
+ vsd_destroy(&lx_ioctl_vsd);
+}
+
+long
+lx_ioctl(int fdes, int cmd, intptr_t arg)
+{
+ file_t *fp;
+ int res = 0, error = ENOTTY;
+ lx_ioc_cmd_translator_t *ict = NULL;
+
+ if (cmd == LX_FIOCLEX || cmd == LX_FIONCLEX) {
+ res = f_setfd_error(fdes, (cmd == LX_FIOCLEX) ? FD_CLOEXEC : 0);
+ return ((res != 0) ? set_errno(res) : 0);
+ }
+
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+
+ switch ((cmd & 0xff00) >> 8) {
+ case LX_IOC_TYPE_FD:
+ ict = lx_ioc_xlate_fd;
+ break;
+
+ case LX_IOC_TYPE_DTRACE:
+ ict = lx_ioc_xlate_dtrace;
+ break;
+
+ case LX_IOC_TYPE_SOCK:
+ ict = lx_ioc_xlate_socket;
+ error = EOPNOTSUPP;
+ break;
+
+ case LX_IOC_TYPE_AUTOFS:
+ ict = lx_ioc_xlate_autofs;
+ break;
+
+ case LX_IOC_TYPE_BLK:
+ ict = lx_ioc_xlate_blk;
+ break;
+
+ case LX_IOC_TYPE_HD:
+ ict = lx_ioc_xlate_hd;
+ break;
+
+ default:
+ releasef(fdes);
+ return (set_errno(ENOTTY));
+ }
+
+ /*
+ * Today, none of the ioctls supported by the emulation possess
+ * overlapping cmd values. Because of that, no type interrogation of
+ * the fd is done before executing specific ioctl emulation. It's
+ * assumed that the vnode-specific logic called by the emulation
+ * function will reject ioctl commands not supported by the fd.
+ */
+ VERIFY(ict != NULL);
+ while (ict->lict_func != NULL) {
+ if (ict->lict_lxcmd == cmd)
+ break;
+ ict++;
+ }
+ if (ict->lict_func == NULL) {
+ releasef(fdes);
+ return (set_errno(error));
+ }
+
+ res = ict->lict_func(fp, ict->lict_cmd, arg, ict->lict_lxcmd);
+ releasef(fdes);
+ return (res);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c b/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c
new file mode 100644
index 0000000000..13397e199e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c
@@ -0,0 +1,66 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/lx_brand.h>
+
+/* 'which' values. */
+#define LX_IOPRIO_WHO_PROCESS 1
+#define LX_IOPRIO_WHO_PGRP 2
+#define LX_IOPRIO_WHO_USER 3
+
+/*
+ * The possible values for the class. We report best effort (BE) as the class
+ * in use.
+ */
+#define LX_IOPRIO_CLASS_RT 1
+#define LX_IOPRIO_CLASS_BE 2
+#define LX_IOPRIO_CLASS_IDLE 3
+
+/* Macro to determine the class from the input mask */
+#define LX_IOPRIO_PRIO_CLASS(m) ((m) >> 13)
+
+/* ARGSUSED */
+long
+lx_ioprio_get(int which, int who)
+{
+ if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+ return (set_errno(EINVAL));
+
+ return (LX_IOPRIO_CLASS_BE);
+}
+
+/*
+ * We allow setting any valid class, even though it's ignored.
+ * We ignore the 'who' parameter which means that we're not searching for
+ * the specified target in order to return a specific errno in the case that
+ * the target does not exist.
+ */
+/* ARGSUSED */
+long
+lx_ioprio_set(int which, int who, int mask)
+{
+ int class;
+
+ if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+ return (set_errno(EINVAL));
+
+ class = LX_IOPRIO_PRIO_CLASS(mask);
+ if (class < LX_IOPRIO_CLASS_RT || class > LX_IOPRIO_CLASS_IDLE)
+ return (set_errno(EINVAL));
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_kill.c b/usr/src/uts/common/brand/lx/syscall/lx_kill.c
new file mode 100644
index 0000000000..eeed914566
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_kill.c
@@ -0,0 +1,402 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/thread.h>
+#include <sys/signal.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <lx_signum.h>
+#include <sys/contract/process_impl.h>
+
+extern int kill(pid_t, int);
+
+/*
+ * Check if it is legal to send this signal to the init process. Linux
+ * kill(2) semantics dictate that no _unhandled_ signal may be sent to pid
+ * 1.
+ */
+static int
+init_sig_check(int sig, pid_t pid)
+{
+ proc_t *p;
+ int rv = 0;
+
+ mutex_enter(&pidlock);
+
+ if (((p = prfind(pid)) == NULL) || (p->p_stat == SIDL))
+ rv = ESRCH;
+ else if (sig && (sigismember(&cantmask, sig) ||
+ (PTOU(p)->u_signal[sig-1] == SIG_DFL) ||
+ (PTOU(p)->u_signal[sig-1] == SIG_IGN)))
+ rv = EPERM;
+
+ mutex_exit(&pidlock);
+
+ return (rv);
+}
+
+static long
+lx_thrkill(pid_t tgid, pid_t pid, int lx_sig, boolean_t tgkill)
+{
+ kthread_t *t;
+ proc_t *pp, *cp = curproc;
+ pid_t initpid;
+ sigqueue_t *sqp;
+ int tid = 1; /* default tid */
+ int sig, rv;
+
+ /*
+ * Unlike kill(2), Linux tkill(2) doesn't allow signals to
+ * be sent to process IDs <= 0 as it doesn't overlay any special
+ * semantics on the pid.
+ */
+ if ((pid <= 0) || ((lx_sig < 0) || (lx_sig > LX_NSIG)) ||
+ ((sig = ltos_signo[lx_sig]) < 0))
+ return (set_errno(EINVAL));
+
+ /*
+ * If the Linux pid is 1, translate the pid to the actual init
+ * pid for the zone. Note that Linux dictates that no unhandled
+ * signals may be sent to init, so check for that, too.
+ *
+ * Otherwise, extract the tid and real pid from the Linux pid.
+ */
+ initpid = cp->p_zone->zone_proc_initpid;
+ if (pid == 1)
+ pid = initpid;
+ if ((pid == initpid) && ((rv = init_sig_check(sig, pid)) != 0))
+ return (set_errno(rv));
+ else if (lx_lpid_to_spair(pid, &pid, &tid) < 0)
+ return (set_errno(ESRCH));
+
+ if (tgkill && tgid != pid)
+ return (set_errno(ESRCH));
+
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+ /*
+ * Find the process for the passed pid...
+ */
+ mutex_enter(&pidlock);
+ if (((pp = prfind(pid)) == NULL) || (pp->p_stat == SIDL)) {
+ mutex_exit(&pidlock);
+ rv = set_errno(ESRCH);
+ goto free_and_exit;
+ }
+ mutex_enter(&pp->p_lock);
+ mutex_exit(&pidlock);
+
+ /*
+ * Deny permission to send the signal if either of the following
+ * is true:
+ *
+ * + The signal is SIGCONT and the target pid is not in the same
+ * session as the sender
+ *
+ * + prochasprocperm() shows the user lacks sufficient permission
+ * to send the signal to the target pid
+ */
+ if (((sig == SIGCONT) && (pp->p_sessp != cp->p_sessp)) ||
+ (!prochasprocperm(pp, cp, CRED()))) {
+ mutex_exit(&pp->p_lock);
+ rv = set_errno(EPERM);
+ goto free_and_exit;
+ }
+
+ /* check for the tid */
+ if ((t = idtot(pp, tid)) == NULL) {
+ mutex_exit(&pp->p_lock);
+ rv = set_errno(ESRCH);
+ goto free_and_exit;
+ }
+
+ /* a signal of 0 means just check for the existence of the thread */
+ if (lx_sig == 0) {
+ mutex_exit(&pp->p_lock);
+ rv = 0;
+ goto free_and_exit;
+ }
+
+ sqp->sq_info.si_signo = sig;
+ sqp->sq_info.si_code = SI_LWP;
+ sqp->sq_info.si_pid = cp->p_pid;
+ sqp->sq_info.si_zoneid = getzoneid();
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(pp, t, sqp);
+
+ mutex_exit(&pp->p_lock);
+
+ return (0);
+
+free_and_exit:
+ kmem_free(sqp, sizeof (sigqueue_t));
+ return (rv);
+}
+
+long
+lx_tgkill(pid_t tgid, pid_t pid, int lx_sig)
+{
+ return (lx_thrkill(tgid, pid, lx_sig, B_TRUE));
+}
+
+long
+lx_tkill(pid_t pid, int lx_sig)
+{
+ return (lx_thrkill(0, pid, lx_sig, B_FALSE));
+}
+
+long
+lx_kill(pid_t lx_pid, int lx_sig)
+{
+ pid_t s_pid, initpid;
+ sigsend_t v;
+ zone_t *zone = curproc->p_zone;
+ struct proc *p;
+ int err, sig, nfound;
+
+ if ((lx_sig < 0) || (lx_sig > LX_NSIG) ||
+ ((sig = ltos_signo[lx_sig]) < 0))
+ return (set_errno(EINVAL));
+
+ /*
+ * Since some linux apps rely on init(1M) having PID 1, we
+ * transparently translate 1 to the real init(1M)'s pid. We then
+ * check to be sure that it is legal for this process to send this
+ * signal to init(1M).
+ */
+ initpid = zone->zone_proc_initpid;
+ if (lx_pid == 1) {
+ s_pid = initpid;
+ } else if (lx_pid == 0 || lx_pid == -1) {
+ s_pid = 0;
+ } else if (lx_pid > 0) {
+ if (lx_lpid_to_spair(lx_pid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid that means it doesn't
+ * exist in this zone.
+ */
+ return (set_errno(ESRCH));
+ }
+ } else {
+ ASSERT(lx_pid < 0);
+ if (lx_lpid_to_spair(-lx_pid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid it means that the
+ * process group leader doesn't exist in this zone.
+ * In this case assuming that the Linux pid is
+ * the same as the Solaris pid will get us the
+ * correct behavior.
+ */
+ s_pid = -lx_pid;
+ }
+ }
+
+ if ((s_pid == initpid) && ((err = init_sig_check(sig, s_pid)) != 0))
+ return (set_errno(err));
+
+ /*
+ * For individual processes, kill() semantics are the same between
+ * Solaris and Linux.
+ */
+ if (lx_pid >= 0)
+ return (kill(s_pid, sig));
+
+ /*
+ * In Solaris, sending a signal to -pid means "send a signal to
+ * everyone in process group pid." In Linux it means "send a
+ * signal to everyone in the group other than init." Sending a
+ * signal to -1 means "send a signal to every process except init
+ * and myself."
+ */
+
+ bzero(&v, sizeof (v));
+ v.sig = sig;
+ v.checkperm = 1;
+ v.sicode = SI_USER;
+ err = 0;
+
+ mutex_enter(&pidlock);
+
+ p = (lx_pid == -1) ? practive : pgfind(s_pid);
+ nfound = 0;
+ while (err == 0 && p != NULL) {
+ if ((p->p_zone == zone) && (p->p_stat != SIDL) &&
+ (p->p_pid != initpid) && (lx_pid < -1 || p != curproc)) {
+ nfound++;
+ err = sigsendproc(p, &v);
+ }
+
+ p = (lx_pid == -1) ? p->p_next : p->p_pglink;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * If we found no processes, we'll return ESRCH -- but unlike our
+ * native kill(2), we do not return EPERM if processes are found but
+ * we did not have permission to send any of them a signal.
+ */
+ if (nfound == 0)
+ err = ESRCH;
+
+ return (err ? set_errno(err) : 0);
+}
+
+/*
+ * This handles the unusual case where the user sends a non-queueable signal
+ * through rt_sigqueueinfo. Signals sent with codes that indicate they are
+ * queuable are sent through the sigqueue syscall via the user level function
+ * lx_rt_sigqueueinfo().
+ */
+int
+lx_helper_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
+{
+ proc_t *target_proc;
+ pid_t s_pid;
+ zone_t *zone = curproc->p_zone;
+ sigsend_t send;
+ int err;
+ siginfo_t kinfo;
+
+ if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0)
+ return (set_errno(EFAULT));
+ /* Unlike in lx_kill, this process id must be exact, no negatives. */
+ if (tgid == 0)
+ return (set_errno(ESRCH));
+ if (tgid < 0)
+ return (set_errno(EINVAL));
+ /*
+ * Translate init directly, otherwise use the convenient utility
+ * function to translate. Since we're sending to the whole group, we
+ * only need the solaris pid, and not the lwp id.
+ */
+ if (tgid == 1) {
+ s_pid = zone->zone_proc_initpid;
+ } else {
+ if (lx_lpid_to_spair(tgid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid that means it doesn't
+ * exist in this zone.
+ */
+ return (set_errno(ESRCH));
+ }
+ }
+ /*
+ * We shouldn't have queuable signals here, those are sent elsewhere by
+ * the usermode handler for this emulated call.
+ */
+ if (!SI_CANQUEUE(kinfo.si_code)) {
+ return (set_errno(EINVAL));
+ }
+ /* Since our signal shouldn't queue, we just call sigsendproc(). */
+ bzero(&send, sizeof (send));
+ send.sig = sig;
+ send.checkperm = 1;
+ send.sicode = kinfo.si_code;
+ send.value = kinfo.si_value;
+
+ mutex_enter(&pidlock);
+ target_proc = prfind(s_pid);
+ err = 0;
+ if (target_proc != NULL) {
+ err = sigsendproc(target_proc, &send);
+ if (err == 0 && send.perm == 0)
+ err = EPERM;
+ } else {
+ err = ESRCH;
+ }
+ mutex_exit(&pidlock);
+
+ return (err ? set_errno(err) : 0);
+}
+
+/*
+ * Unlike the above function, this handles all system calls to rt_tgsigqueue
+ * regardless of si_code.
+ */
+int
+lx_helper_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo)
+{
+ id_t s_tid;
+ pid_t s_pid;
+ proc_t *target_proc;
+ sigqueue_t *sqp;
+ kthread_t *t;
+ siginfo_t kinfo;
+
+ if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0)
+ return (set_errno(EFAULT));
+ if (lx_lpid_to_spair(tid, &s_pid, &s_tid) != 0)
+ return (set_errno(ESRCH));
+ /*
+ * For group leaders, solaris pid == linux pid, so the solaris leader
+ * pid should be the same as the tgid but since the tgid comes in via
+ * the syscall we need to check for an invalid value.
+ */
+ if (s_pid != tgid)
+ return (set_errno(EINVAL));
+
+ mutex_enter(&pidlock);
+ target_proc = prfind(s_pid);
+ if (target_proc != NULL)
+ mutex_enter(&target_proc->p_lock);
+ mutex_exit(&pidlock);
+
+ if (target_proc == NULL) {
+ return (set_errno(ESRCH));
+ }
+ if (sig < 0 || sig >= NSIG)
+ return (set_errno(EINVAL));
+
+ /*
+ * Some code adapted from lwp_kill, duplicated here because we do some
+ * customization to the sq_info field of sqp.
+ */
+ if ((t = idtot(target_proc, s_tid)) == NULL) {
+ mutex_exit(&target_proc->p_lock);
+ return (set_errno(ESRCH));
+ }
+ /* Just checking for existence of the process, not sending a signal. */
+ if (sig == 0) {
+ mutex_exit(&target_proc->p_lock);
+ return (0);
+ }
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+ sqp->sq_info.si_signo = sig;
+ sqp->sq_info.si_code = kinfo.si_code;
+ sqp->sq_info.si_pid = target_proc->p_pid;
+ sqp->sq_info.si_ctid = PRCTID(target_proc);
+ sqp->sq_info.si_zoneid = getzoneid();
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(target_proc, t, sqp);
+ mutex_exit(&target_proc->p_lock);
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_link.c b/usr/src/uts/common/brand/lx/syscall/lx_link.c
new file mode 100644
index 0000000000..e8deaf54f1
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_link.c
@@ -0,0 +1,194 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/systm.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+#define LX_LINK_ALLOWED (LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+/* From uts/common/syscall/unlink.c */
+extern int unlinkat(int, char *, int);
+/* From uts/common/syscall/symlink.c */
+extern int symlinkat(char *, int, char *);
+/* From uts/common/syscall/readlink.c */
+extern int readlinkat(int, char *, char *, size_t);
+
+static long
+lx_link_common(int ffd, char *from, int tfd, char *to, int flags)
+{
+ int error;
+ vnode_t *fsvp = NULL, *tsvp = NULL;
+ enum symfollow follow = NO_FOLLOW;
+
+ if ((flags & ~LX_LINK_ALLOWED) != 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((flags & LX_AT_EMPTY_PATH) == 0) {
+ char c;
+
+ /*
+ * Check that both 'from' and 'to' names are non-empty if
+ * AT_EMPTY_PATH is not set.
+ */
+ if (copyin(from, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ } else if (c == '\0') {
+ return (set_errno(ENOENT));
+ }
+ if (copyin(to, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ } else if (c == '\0') {
+ return (set_errno(ENOENT));
+ }
+
+ /*
+ * XXX: When our support for LX capabilities improves, ENOENT
+ * should be thrown when a process lacking CAP_DAC_READ_SEARCH
+ * attempts to use the AT_EMPTY_PATH flag.
+ */
+ }
+ if ((flags & LX_AT_SYMLINK_FOLLOW) != 0) {
+ follow = FOLLOW;
+ }
+
+ if ((error = fgetstartvp(ffd, from, &fsvp)) != 0) {
+ goto out;
+ }
+ if ((error = fgetstartvp(tfd, to, &tsvp)) != 0) {
+ goto out;
+ }
+ error = vn_linkat(fsvp, from, follow, tsvp, to, UIO_USERSPACE);
+
+out:
+ if (fsvp != NULL) {
+ VN_RELE(fsvp);
+ }
+ if (tsvp != NULL) {
+ VN_RELE(tsvp);
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_link(char *from, char *to)
+{
+ return (lx_link_common(AT_FDCWD, from, AT_FDCWD, to, 0));
+}
+
+long
+lx_linkat(int ffd, char *from, int tfd, char *to, int flags)
+{
+ ffd = (ffd == LX_AT_FDCWD) ? AT_FDCWD : ffd;
+ tfd = (tfd == LX_AT_FDCWD) ? AT_FDCWD : tfd;
+
+ return (lx_link_common(ffd, from, tfd, to, flags));
+}
+
+static boolean_t
+lx_isdir(int atfd, char *path)
+{
+ cred_t *cr = NULL;
+ vnode_t *vp = NULL;
+ boolean_t is_dir;
+
+ if (cstatat_getvp(atfd, path, NO_FOLLOW, &vp, &cr) != 0)
+ return (B_FALSE);
+
+ is_dir = (vp->v_type == VDIR);
+ VN_RELE(vp);
+
+ return (is_dir);
+}
+
+long
+lx_unlink(char *path)
+{
+ int err;
+
+ if ((err = unlinkat(AT_FDCWD, path, 0)) == EPERM) {
+ /* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+ if (lx_isdir(AT_FDCWD, path))
+ return (set_errno(EISDIR));
+ }
+
+ return (err);
+}
+
+long
+lx_unlinkat(int atfd, char *path, int flag)
+{
+ int err;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ if ((flag = ltos_at_flag(flag, AT_REMOVEDIR, B_TRUE)) < 0)
+ return (set_errno(EINVAL));
+
+ err = unlinkat(atfd, path, flag);
+ if (err == EPERM && !(flag & AT_REMOVEDIR)) {
+ /* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+ if (lx_isdir(atfd, path))
+ return (set_errno(EISDIR));
+ }
+
+ return (err);
+}
+
+long
+lx_symlink(char *name1, char *name2)
+{
+ return (symlinkat(name1, AT_FDCWD, name2));
+}
+
+long
+lx_symlinkat(char *name1, int atfd, char *name2)
+{
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ return (symlinkat(name1, atfd, name2));
+}
+
+long
+lx_readlink(char *path, char *buf, size_t bufsize)
+{
+ if (bufsize <= 0)
+ return (set_errno(EINVAL));
+
+ return (readlinkat(AT_FDCWD, path, buf, bufsize));
+}
+
+long
+lx_readlinkat(int atfd, char *path, char *buf, size_t bufsize)
+{
+ if (bufsize <= 0)
+ return (set_errno(EINVAL));
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ return (readlinkat(atfd, path, buf, bufsize));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_lseek.c b/usr/src/uts/common/brand/lx/syscall/lx_lseek.c
new file mode 100644
index 0000000000..222fc03386
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_lseek.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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+
+
+#if defined(_SYSCALL32_IMPL) || defined(_ILP32)
+
+/* from uts/common/syscalls/lseek.c */
+extern offset_t llseek32(int32_t, uint32_t, uint32_t, int);
+extern off32_t lseek32(int32_t, off32_t, int32_t);
+
+long
+lx_llseek(int fd, uint32_t off_high, uint32_t off_low, void *out, int whence)
+{
+ offset_t res;
+
+ ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+ res = llseek32(fd, off_low, off_high, whence);
+ if (ttolwp(curthread)->lwp_errno == 0) {
+ if (copyout(&res, out, sizeof (offset_t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+ return (ttolwp(curthread)->lwp_errno);
+}
+
+
+long
+lx_lseek32(int fd, off32_t offset, int whence)
+{
+ offset_t res;
+
+ /*
+ * When returning EOVERFLOW for an offset which is outside the bounds
+ * of an off32_t, Linux will still perform the actual seek before
+ * yielding EOVERFLOW.
+ *
+ * In order to emulate that behavior, an llseek bound to the 64-bit
+ * boundary is used. The overflow can then be reported after the
+ * successful seek.
+ */
+ ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+ res = llseek32(fd, (uint32_t)offset, 0, whence);
+ if (ttolwp(curthread)->lwp_errno == 0 && res > MAXOFF32_T) {
+ return (set_errno(EOVERFLOW));
+ }
+ return (res);
+
+}
+#endif /* defined(_SYSCALL32_IMPL) || defined(_ILP32) */
+
+#if defined(_LP64)
+
+/* from uts/common/syscalls/lseek.c */
+extern off_t lseek64(int, off_t, int);
+
+long
+lx_lseek64(int fd, off_t offset, int whence)
+{
+ ASSERT(get_udatamodel() == DATAMODEL_LP64);
+ return (lseek64(fd, offset, whence));
+}
+
+#endif /* defined(_LP64) */
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c b/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c
new file mode 100644
index 0000000000..ba986e2742
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c
@@ -0,0 +1,282 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systeminfo.h>
+#include <sys/fcntl.h>
+#include <sys/resource.h>
+#include <sys/uadmin.h>
+#include <sys/lx_misc.h>
+
+#define LINUX_REBOOT_MAGIC1 0xfee1dead
+#define LINUX_REBOOT_MAGIC2 672274793
+#define LINUX_REBOOT_MAGIC2A 85072278
+#define LINUX_REBOOT_MAGIC2B 369367448
+#define LINUX_REBOOT_MAGIC2C 537993216
+
+#define LINUX_REBOOT_CMD_RESTART 0x1234567
+#define LINUX_REBOOT_CMD_HALT 0xcdef0123
+#define LINUX_REBOOT_CMD_CAD_ON 0x89abcdef
+#define LINUX_REBOOT_CMD_CAD_OFF 0
+#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
+#define LINUX_REBOOT_CMD_RESTART2 0xa1b2c3d4
+#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
+#define LINUX_REBOOT_CMD_KEXEC 0x45584543
+
+#define LX_RUSAGE_SELF 0
+#define LX_RUSAGE_CHILDREN (-1)
+#define LX_RUSAGE_BOTH (-2)
+#define LX_RUSAGE_THREAD 1
+
+/* From uts/common/fs/vfs.c */
+extern void vfs_sync(int);
+/* From uts/common/os/grow.c */
+extern int mincore(caddr_t, size_t, char *);
+extern int munmap(caddr_t, size_t);
+/* From uts/common/os/session.c */
+extern int vhangup();
+/* From uts/common/syscall/alarm.c */
+extern int alarm(int);
+/* From uts/common/syscall/chdir.c */
+extern int chdir(char *);
+extern int chroot(char *);
+extern int fchdir(int);
+/* From uts/common/syscall/nice.c */
+extern int nice(int);
+/* From uts/common/syscall/open.c */
+extern int open(char *, int, int);
+/* From uts/common/syscall/pause.c */
+extern int pause();
+/* From uts/common/syscall/rusagesys.c */
+extern int rusagesys(int, void *, void *, void *, void *);
+/* From uts/common/syscall/systeminfo.c */
+extern long systeminfo(int, char *, long);
+/* From uts/common/syscall/timers.c */
+extern int getitimer(uint_t, struct itimerval *);
+/* From uts/common/syscall/time.c */
+extern int stime(time_t);
+/* From uts/common/syscall/uadmin.c */
+extern int uadmin(int, int, uintptr_t);
+
+long
+lx_alarm(int seconds)
+{
+ return (alarm(seconds));
+}
+
+long
+lx_chdir(char *path)
+{
+ return (chdir(path));
+}
+
+long
+lx_chroot(char *path)
+{
+ return (chroot(path));
+}
+
+long
+lx_creat(char *path, mode_t mode)
+{
+ return (open(path, O_WRONLY | O_CREAT | O_TRUNC, mode));
+}
+
+long
+lx_fchdir(int fd)
+{
+ return (fchdir(fd));
+}
+
+long
+lx_getitimer(int which, struct itimerval *value)
+{
+ return (getitimer(which, value));
+}
+
+/* Linux and illumos have the same rusage structures. */
+long
+lx_getrusage(int who, struct rusage *rup)
+{
+ int code;
+
+ switch (who) {
+ case LX_RUSAGE_SELF:
+ code = _RUSAGESYS_GETRUSAGE;
+ break;
+ case LX_RUSAGE_CHILDREN:
+ code = _RUSAGESYS_GETRUSAGE_CHLD;
+ break;
+ case LX_RUSAGE_THREAD:
+ code = _RUSAGESYS_GETRUSAGE_LWP;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ return (rusagesys(code, rup, NULL, NULL, NULL));
+}
+
+long
+lx_mincore(caddr_t addr, size_t len, char *vec)
+{
+ int r;
+
+ r = mincore(addr, len, vec);
+ if (r == EINVAL) {
+ /*
+ * LTP mincore01 expects mincore with a huge len to fail with
+ * ENOMEM on a modern kernel, although on Linux 2.6.11 and
+ * earlier, it will return EINVAL.
+ */
+ if (lx_kern_release_cmp(curzone, "2.6.11") > 0 && (long)len < 0)
+ return (set_errno(ENOMEM));
+ }
+ return (r);
+}
+
+long
+lx_munmap(void *addr, size_t len)
+{
+ return (munmap(addr, len));
+}
+
+long
+lx_nice(int incr)
+{
+ return (nice(incr));
+}
+
+long
+lx_pause(void)
+{
+ return (pause());
+}
+
+/*ARGSUSED*/
+long
+lx_reboot(int magic1, int magic2, uint_t flag, uintptr_t p4)
+{
+ if (magic1 != LINUX_REBOOT_MAGIC1)
+ return (set_errno(EINVAL));
+
+ switch (magic2) {
+ case LINUX_REBOOT_MAGIC2:
+ case LINUX_REBOOT_MAGIC2A:
+ case LINUX_REBOOT_MAGIC2B:
+ case LINUX_REBOOT_MAGIC2C:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Once we have better Linux capabilities(7) support we should check
+ * CAP_SYS_BOOT instead.
+ */
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ switch (flag) {
+ case LINUX_REBOOT_CMD_CAD_ON:
+ case LINUX_REBOOT_CMD_CAD_OFF:
+ /* ignored */
+ return (0);
+
+ case LINUX_REBOOT_CMD_POWER_OFF:
+ case LINUX_REBOOT_CMD_HALT:
+ return (uadmin(A_SHUTDOWN, AD_HALT, NULL));
+
+ case LINUX_REBOOT_CMD_RESTART:
+ case LINUX_REBOOT_CMD_RESTART2:
+ /* RESTART2 may need more work */
+ return (uadmin(A_SHUTDOWN, AD_BOOT, NULL));
+
+ default:
+ return (set_errno(EINVAL));
+ }
+}
+
+long
+lx_setdomainname(char *name, long len)
+{
+ if (len < 0 || len >= LX_SYS_UTS_LN)
+ return (set_errno(EINVAL));
+
+ ttolwp(curthread)->lwp_errno = 0;
+ (void) systeminfo(SI_SET_SRPC_DOMAIN, name, len);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+ return (0);
+}
+
+long
+lx_sethostname(char *name, size_t len)
+{
+ ttolwp(curthread)->lwp_errno = 0;
+ (void) systeminfo(SI_SET_HOSTNAME, name, len);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+ return (0);
+}
+
+long
+lx_stime(time_t *tp)
+{
+ time_t time;
+
+ if (copyin(tp, &time, sizeof (time)) != 0)
+ return (set_errno(EFAULT));
+
+ return (stime(time));
+}
+
+long
+lx_sync(void)
+{
+ vfs_sync(0);
+ return (0);
+}
+
+/*
+ * For syslog, since there is no Linux kernel and nothing to log, we simply
+ * emulate a kernel buffer (LOG_BUF_LEN) of 0 bytes and only handle errors for
+ * bad input. All actions except 3 and 10 require CAP_SYS_ADMIN or CAP_SYSLOG
+ * so without full capabilities support, for now we just perform an euid check.
+ */
+long
+lx_syslog(int type, char *bufp, int len)
+{
+ if (type < 0 || type > 10)
+ return (set_errno(EINVAL));
+
+ if (type != 3 && type != 10 && crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ if (type >= 2 && type <= 4 && (bufp == NULL || len < 0))
+ return (set_errno(EINVAL));
+
+ if (type == 8 && (len < 1 || len > 8))
+ return (set_errno(EINVAL));
+
+ return (0);
+}
+
+long
+lx_vhangup(void)
+{
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+ return (vhangup());
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c b/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c
new file mode 100644
index 0000000000..2f29f56d5f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c
@@ -0,0 +1,38 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * From "uts/common/syscall/mkdir.c":
+ */
+extern int mkdirat(int, char *, int);
+
+long
+lx_mkdirat(int fd, char *dname, int dmode)
+{
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+
+ return (mkdirat(fd, dname, dmode));
+}
+
+long
+lx_mkdir(char *dname, int dmode)
+{
+ return (mkdirat(AT_FDCWD, dname, dmode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c b/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c
new file mode 100644
index 0000000000..aa6e12a7d8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/segments.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/sysi86.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_ldt.h>
+
+/*
+ * Read the ldt_info structure in from the Linux app, convert it to an ssd
+ * structure, and then call setdscr() to do all the heavy lifting.
+ */
+static int
+write_ldt(void *data, ulong_t count)
+{
+ user_desc_t usd;
+ struct ssd ssd;
+ struct ldt_info ldt_inf;
+ proc_t *pp = curthread->t_procp;
+ int err;
+
+ if (count != sizeof (ldt_inf))
+ return (set_errno(EINVAL));
+
+ if (copyin(data, &ldt_inf, sizeof (ldt_inf)))
+ return (set_errno(EFAULT));
+
+ if (ldt_inf.entry_number >= MAXNLDT)
+ return (set_errno(EINVAL));
+
+ LDT_INFO_TO_DESC(&ldt_inf, &usd);
+ usd_to_ssd(&usd, &ssd, SEL_LDT(ldt_inf.entry_number));
+
+ /*
+ * Get everyone into a safe state before changing the LDT.
+ */
+ if (!holdlwps(SHOLDFORK1))
+ return (set_errno(EINTR));
+
+ err = setdscr(&ssd);
+
+ /*
+ * Release the hounds!
+ */
+ mutex_enter(&pp->p_lock);
+ continuelwps(pp);
+ mutex_exit(&pp->p_lock);
+
+ return (err ? set_errno(err) : 0);
+}
+
+static int
+read_ldt(void *uptr, ulong_t count)
+{
+ proc_t *pp = curproc;
+ int bytes;
+
+ if (pp->p_ldt == NULL)
+ return (0);
+
+ bytes = (pp->p_ldtlimit + 1) * sizeof (user_desc_t);
+ if (bytes > count)
+ bytes = count;
+
+ if (copyout(pp->p_ldt, uptr, bytes))
+ return (set_errno(EFAULT));
+
+ return (bytes);
+}
+
+long
+lx_modify_ldt(int op, void *data, ulong_t count)
+{
+ int rval;
+
+ switch (op) {
+ case 0:
+ rval = read_ldt(data, count);
+ break;
+
+ case 1:
+ rval = write_ldt(data, count);
+ break;
+
+ default:
+ rval = set_errno(ENOSYS);
+ break;
+ }
+
+ return (rval);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mount.c b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
new file mode 100644
index 0000000000..254e8ec93a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
@@ -0,0 +1,657 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/ctype.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/types.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_autofs.h>
+
+#define tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
+
+/*
+ * mount(2) is significantly different between Linux and illumos. One of the
+ * main differences is between the set of flags. Some flags on Linux can be
+ * translated to an illumos equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ *
+ * Another big difference is that mounting NFS is fully handled in the kernel on
+ * Linux whereas on illumos a lot of preliminary work is done by the NFS mount
+ * command before calling mount(2). As a simplification, we forward NFS
+ * mount calls back out to the user-level library which does the same kind of
+ * preliminary processing that is done by the native user-level NFS mount code.
+ */
+#define LX_MS_MGC_VAL 0xC0ED0000
+#define LX_MS_RDONLY 0x00000001
+#define LX_MS_NOSUID 0x00000002
+#define LX_MS_NODEV 0x00000004
+#define LX_MS_NOEXEC 0x00000008
+#define LX_MS_SYNCHRONOUS 0x00000010
+#define LX_MS_REMOUNT 0x00000020
+#define LX_MS_MANDLOCK 0x00000040
+#define LX_MS_NOATIME 0x00000400
+#define LX_MS_NODIRATIME 0x00000800
+#define LX_MS_BIND 0x00001000
+#define LX_MS_MOVE 0x00002000
+#define LX_MS_REC 0x00004000
+#define LX_MS_SILENT 0x00008000
+#define LX_MS_POSIXACL 0x00010000
+#define LX_MS_UNBINDABLE 0x00020000
+#define LX_MS_PRIVATE 0x00040000
+#define LX_MS_SLAVE 0x00080000
+#define LX_MS_SHARED 0x00100000
+#define LX_MS_RELATIME 0x00200000
+#define LX_MS_KERNMOUNT 0x00400000
+#define LX_MS_I_VERSION 0x00800000
+#define LX_MS_STRICTATIME 0x01000000
+#define LX_MS_LAZYTIME 0x02000000
+
+/* Linux kernel-internal flags - ignored if passed in */
+#define LX_MS_NOSEC 0x10000000
+#define LX_MS_BORN 0x20000000
+#define LX_MS_ACTIVE 0x40000000
+#define LX_MS_NOUSER 0x80000000
+
+#define LX_MS_SUPPORTED (LX_MS_MGC_VAL | \
+ LX_MS_RDONLY | LX_MS_NOSUID | \
+ LX_MS_NODEV | LX_MS_NOEXEC | \
+ LX_MS_REMOUNT | LX_MS_NOATIME | \
+ LX_MS_BIND | LX_MS_SILENT | \
+ LX_MS_STRICTATIME | LX_MS_NOSEC | \
+ LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support definitions
+ */
+typedef enum mount_opt_type {
+ MOUNT_OPT_INVALID = 0,
+ MOUNT_OPT_NORMAL = 1, /* option value: none */
+ MOUNT_OPT_UINT = 2, /* option value: unsigned int */
+ MOUNT_OPT_PASSTHRU = 3 /* option value: validated downstream */
+} mount_opt_type_t;
+
+typedef struct mount_opt {
+ char *mo_name;
+ mount_opt_type_t mo_type;
+} mount_opt_t;
+
+/* From uts/common/syscall/umount.c */
+extern int umount2(char *, int);
+
+/* From lx_chown.c */
+extern long lx_vn_chown(vnode_t *, uid_t, gid_t);
+
+/*
+ * Globals
+ */
+static mount_opt_t lofs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_proc_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_sysfs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_tmpfs_options[] = {
+ { "size", MOUNT_OPT_PASSTHRU },
+ { "mode", MOUNT_OPT_UINT },
+ { "uid", MOUNT_OPT_UINT },
+ { "gid", MOUNT_OPT_UINT },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_autofs_options[] = {
+ { LX_MNTOPT_FD, MOUNT_OPT_UINT },
+ { LX_MNTOPT_PGRP, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MINPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MAXPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_INDIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_DIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_OFFSET, MOUNT_OPT_NORMAL },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+
+/*
+ * Check the mount options.
+ *
+ * On illumos all mount option verification is done by the user-level mount
+ * command. Invalid options are simply ignored by domount(). Thus, we check
+ * here for invalid/unsupported options.
+ */
+static int
+lx_mnt_opt_verify(char *opts, mount_opt_t *mop)
+{
+ int opts_len = strlen(opts);
+ char *opt, *tp;
+ int opt_len, i;
+ boolean_t last = B_FALSE;
+
+ ASSERT((opts != NULL) && (mop != NULL));
+
+ /* If no options were specified, nothing to do. */
+ if (opts_len == 0)
+ return (0);
+
+ /* If no options are allowed, fail. */
+ if (mop[0].mo_name == NULL)
+ return (ENOTSUP);
+
+ /* Don't accept leading or trailing ','. */
+ if ((opts[0] == ',') || (opts[opts_len] == ','))
+ return (EINVAL);
+
+ /* Don't accept sequential ','. */
+ for (i = 1; i < opts_len; i++) {
+ if ((opts[i - 1] == ',') && (opts[i] == ','))
+ return (EINVAL);
+ }
+
+ /*
+ * Verify each prop one at a time. There is no strtok in the kernel but
+ * it's easy to tokenize the entry ourselves.
+ */
+ opt = opts;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ for (;;) {
+ opt_len = strlen(opt);
+
+ /* Check for matching option/value pair. */
+ for (i = 0; mop[i].mo_name != NULL; i++) {
+ char *ovalue;
+ int ovalue_len, mo_len;
+
+ /* If the options is too short don't bother comparing */
+ mo_len = strlen(mop[i].mo_name);
+ if (opt_len < mo_len) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* Compare the option to an allowed option. */
+ if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
+ /* The option doesn't take a value. */
+ if (opt_len == mo_len) {
+ /* This option is ok. */
+ break;
+ } else {
+ /* Keep trying to find a match. */
+ continue;
+ }
+ }
+
+ /* This options takes a value. */
+ if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* We have an option match. Verify option value. */
+ ovalue = &opt[mo_len] + 1;
+ ovalue_len = strlen(ovalue);
+
+ /* Value can't be zero length string. */
+ if (ovalue_len == 0) {
+ goto bad;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_UINT) {
+ int j;
+ /* Verify that value is an unsigned int. */
+ for (j = 0; j < ovalue_len; j++) {
+ if (!ISDIGIT(ovalue[j])) {
+ goto bad;
+ }
+ }
+ } else if (mop[i].mo_type == MOUNT_OPT_PASSTHRU) {
+ /* Filesystem will do its own validation. */
+ break;
+ } else {
+ /* Unknown option type specified. */
+ goto bad;
+ }
+
+ /* The option is ok. */
+ break;
+ }
+
+ /* If there were no matches this is an unsupported option. */
+ if (mop[i].mo_name == NULL) {
+ goto bad;
+ }
+
+ /*
+ * This option is ok, either we're done or move on to the next
+ * option.
+ */
+ if (last)
+ break;
+
+ *tp = ',';
+ opt = tp + 1;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ };
+
+ /* We verified all the options. */
+ return (0);
+
+bad:
+ if (!last) {
+ *tp = ',';
+ }
+ return (EINVAL);
+}
+
+/*
+ * Remove an option from the string and save it in the provided buffer.
+ * The option string should have already been verified as valid.
+ * Return 0 if not present, -1 if error, and 1 if present and fine.
+ */
+static int
+lx_mnt_opt_rm(char *opts, char *rmopt, char *retstr, int retlen)
+{
+ int opts_len = strlen(opts);
+ char *optstart, *optend;
+ int optlen;
+
+ ASSERT((opts != NULL) && (rmopt != NULL));
+
+ retstr[0] = '\0';
+
+ /* If no options were specified, there's no problem. */
+ if (opts_len == 0)
+ return (0);
+
+ if ((optstart = strstr(opts, rmopt)) == NULL)
+ return (0);
+
+ for (optend = optstart; *optend != ',' && *optend != '\0'; optend++)
+ ;
+
+ /*LINTED*/
+ optlen = optend - optstart;
+ if (optlen >= retlen)
+ return (-1);
+ (void) strncpy(retstr, optstart, optlen);
+ retstr[optlen] = '\0';
+
+ if (*optend == ',')
+ optend++;
+
+ optlen = strlen(optend) + 1;
+ bcopy(optend, optstart, optlen);
+
+ if (*optstart == '\0' && optstart != opts) {
+ /* removed last opt and it had a preceeding opt, remove comma */
+ *(optstart - 1) = '\0';
+ }
+
+ return (1);
+}
+
+static int
+lx_mnt_opt_val(char *opt, int *valp)
+{
+ char *op, *ep;
+ long lval;
+
+ if ((op = strchr(opt, '=')) == NULL)
+ return (-1);
+
+ op++;
+ if (!ISDIGIT(*op))
+ return (-1);
+
+ if (ddi_strtoul(op, &ep, 10, (ulong_t *)&lval) != 0 || lval > INT_MAX) {
+ return (-1);
+ }
+
+ if (*ep != '\0')
+ return (-1);
+
+ *valp = (int)lval;
+ return (0);
+}
+
+static int
+lx_mnt_add_opt(char *option, char *buf, size_t buf_size)
+{
+ char *fmt_str = NULL;
+ size_t len;
+
+ ASSERT((option != NULL) && (strlen(option) > 0));
+ ASSERT((buf != NULL) && (buf_size > 0));
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s";
+ } else {
+ fmt_str = ",%s";
+ }
+
+ len = strlen(buf);
+ VERIFY(len <= buf_size);
+ buf_size -= len;
+ buf += len;
+
+ if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+ return (EOVERFLOW);
+ return (0);
+}
+
+static int
+lx_mnt_copyin_arg(const char *from, char *to, size_t len)
+{
+ size_t slen;
+ int rv;
+
+ rv = copyinstr(from, to, len, &slen);
+ if (rv == ENAMETOOLONG || slen == len)
+ return (ENAMETOOLONG);
+ if (rv != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+long
+lx_mount(const char *sourcep, const char *targetp, const char *fstypep,
+ uint_t flags, const void *datap)
+{
+ char fstype[16];
+ char source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ char options[MAX_MNTOPT_STR];
+ int sflags, rv;
+ struct mounta ma, *map = &ma;
+ vfs_t *vfsp;
+ vnode_t *vp = NULL;
+ int uid = -1;
+ int gid = -1;
+
+ if ((rv = lx_mnt_copyin_arg(fstypep, fstype, sizeof (fstype))) != 0) {
+ if (rv == ENAMETOOLONG)
+ return (set_errno(ENODEV));
+ return (set_errno(rv));
+ }
+
+ /*
+ * Vector back out to userland emulation for NFS.
+ */
+ if (strcmp(fstype, "nfs") == 0) {
+ uintptr_t uargs[5] = {(uintptr_t)sourcep, (uintptr_t)targetp,
+ (uintptr_t)fstypep, (uintptr_t)flags, (uintptr_t)datap};
+
+ /* The userspace emulation will do the lx_syscall_return() */
+ ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(ttolwp(curthread), LX_SYS32_mount,
+ uargs);
+ } else
+#endif
+ {
+ lx_emulate_user(ttolwp(curthread), LX_SYS_mount, uargs);
+ }
+ return (0);
+ }
+
+ sflags = MS_SYSSPACE | MS_OPTIONSTR;
+ options[0] = '\0';
+
+ /* Copy in parameters that are always present. */
+ if ((rv = lx_mnt_copyin_arg(sourcep, source, sizeof (source))) != 0)
+ return (set_errno(rv));
+
+ if ((rv = lx_mnt_copyin_arg(targetp, target, sizeof (target))) != 0)
+ return (set_errno(rv));
+
+ /*
+ * While SunOS is picky about mount(2) target paths being absolute,
+ * Linux is not so strict. In order to facilitate this looser
+ * requirement we must lookup the full path.
+ */
+ if (target[0] != '/') {
+ vnode_t *vp;
+
+ if ((rv = lookupnameatcred(target, UIO_SYSSPACE, FOLLOW,
+ NULLVPP, &vp, NULL, CRED())) != 0)
+ return (set_errno(rv));
+
+ rv = vnodetopath(NULL, vp, target, MAXPATHLEN, CRED());
+ VN_RELE(vp);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ /* Make sure we support the requested mount flags. */
+ if ((flags & ~LX_MS_SUPPORTED) != 0)
+ return (set_errno(ENOTSUP));
+
+ /* Copy in Linux mount options. */
+ if (datap != NULL &&
+ (rv = lx_mnt_copyin_arg(datap, options, sizeof (options))) != 0)
+ return (set_errno(rv));
+
+ /* Do filesystem specific mount work. */
+ if (flags & LX_MS_BIND) {
+ /* If MS_BIND is set, we turn this into a lofs mount. */
+ (void) strcpy(fstype, "lofs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lofs_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "tmpfs") == 0) {
+ char idstr[64];
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_tmpfs_options)) != 0)
+ return (set_errno(rv));
+
+ /*
+ * Linux defaults to mode=1777 for tmpfs mounts.
+ */
+ if (strstr(options, "mode=") == NULL) {
+ if (options[0] != '\0')
+ (void) strlcat(options, ",", sizeof (options));
+ (void) strlcat(options, "mode=1777", sizeof (options));
+ }
+
+ switch (lx_mnt_opt_rm(options, "uid=", idstr, sizeof (idstr))) {
+ case 0:
+ uid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &uid) < 0)
+ return (set_errno(EINVAL));
+ break;
+ default:
+ return (set_errno(E2BIG));
+ }
+ switch (lx_mnt_opt_rm(options, "gid=", idstr, sizeof (idstr))) {
+ case 0:
+ gid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &gid) < 0)
+ return (set_errno(EINVAL));
+ break;
+ default:
+ return (set_errno(E2BIG));
+ }
+
+ /*
+ * Linux seems to always allow overlay mounts. We allow this
+ * everywhere except under /dev where it interferes with device
+ * emulation.
+ */
+ if (strcmp(target, "/dev") != 0 &&
+ strncmp(target, "/dev/", 5) != 0)
+ sflags |= MS_OVERLAY;
+ } else if (strcmp(fstype, "proc") == 0) {
+ /* Translate proc mount requests to lx_proc requests. */
+ (void) strcpy(fstype, "lx_proc");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_proc_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "sysfs") == 0) {
+ /* Translate sysfs mount requests to lx_sysfs requests. */
+ (void) strcpy(fstype, "lx_sysfs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_sysfs_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "cgroup") == 0) {
+ /* Translate cgroup mount requests to lx_cgroup requests. */
+ (void) strcpy(fstype, "lx_cgroup");
+
+ /*
+ * Currently don't verify Linux mount options since we can
+ * have a subsystem string provided.
+ */
+ } else if (strcmp(fstype, "autofs") == 0) {
+ /* Translate autofs mount requests to lxautofs requests. */
+ (void) strcpy(fstype, LX_AUTOFS_NAME);
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_autofs_options)) != 0)
+ return (set_errno(rv));
+
+ /* Linux seems to always allow overlay mounts */
+ sflags |= MS_OVERLAY;
+ } else {
+ return (set_errno(ENODEV));
+ }
+
+ /* Convert some Linux flags to illumos flags. */
+ if (flags & LX_MS_RDONLY)
+ sflags |= MS_RDONLY;
+ if (flags & LX_MS_NOSUID)
+ sflags |= MS_NOSUID;
+ if (flags & LX_MS_REMOUNT)
+ sflags |= MS_REMOUNT;
+
+ /*
+ * Convert some Linux flags to illumos option strings.
+ */
+ if (flags & LX_MS_STRICTATIME) {
+ /*
+ * The "strictatime" mount option ensures that none of the
+ * weaker atime-related mode options are in effect.
+ */
+ flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+ }
+ if ((flags & LX_MS_NODEV) &&
+ (rv = lx_mnt_add_opt("nodev", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+ if ((flags & LX_MS_NOEXEC) &&
+ (rv = lx_mnt_add_opt("noexec", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+ if ((flags & LX_MS_NOATIME) &&
+ (rv = lx_mnt_add_opt("noatime", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+
+ if ((rv = lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0)
+ return (set_errno(rv));
+
+ /* If mounting proc over itself, just return ok */
+ if (strcmp(fstype, "lx_proc") == 0 && strcmp("lx_proc",
+ vfssw[vp->v_vfsp->vfs_fstype].vsw_name) == 0) {
+ VN_RELE(vp);
+ return (0);
+ }
+
+ map->spec = source;
+ map->dir = target;
+ map->flags = sflags;
+ map->fstype = fstype;
+ map->dataptr = NULL;
+ map->datalen = 0;
+ map->optptr = options;
+ map->optlen = sizeof (options);
+
+ rv = domount(NULL, map, vp, CRED(), &vfsp);
+ VN_RELE(vp);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ VFS_RELE(vfsp);
+ if (strcmp(fstype, "tmpfs") == 0 && (uid != -1 || gid != -1)) {
+ /* Handle tmpfs uid/gid mount options. */
+ if (lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &vp) == 0) {
+ (void) lx_vn_chown(vp, (uid_t)uid, (gid_t)gid);
+ VN_RELE(vp);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * umount() is identical to illumos, though implemented on top of umount2().
+ */
+long
+lx_umount(char *path)
+{
+ return (umount2(path, 0));
+}
+
+/*
+ * The Linux umount2() system call is identical to illumos but has a different
+ * value for MNT_FORCE (the logical equivalent to MS_FORCE).
+ */
+#define LX_MNT_FORCE 0x1
+
+long
+lx_umount2(char *path, int flg)
+{
+ int flags = 0;
+
+ if (flg & ~LX_MNT_FORCE)
+ return (set_errno(EINVAL));
+
+ if (flg & LX_MNT_FORCE)
+ flags |= MS_FORCE;
+
+ return (umount2(path, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_open.c b/usr/src/uts/common/brand/lx/syscall/lx_open.c
new file mode 100644
index 0000000000..a6d6877326
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_open.c
@@ -0,0 +1,260 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/mutex.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int openat(int, char *, int, int);
+extern int open(char *, int, int);
+extern int close(int);
+extern int cioctl(file_t *, int, intptr_t, int *);
+extern int lookupnameat(char *, enum uio_seg, int, vnode_t **, vnode_t **,
+ vnode_t *);
+
+
+static int
+ltos_open_flags(int input)
+{
+ int flags;
+
+ if (input & LX_O_PATH) {
+ input &= (LX_O_DIRECTORY | LX_O_NOFOLLOW | LX_O_CLOEXEC);
+ }
+
+ /* This depends on the Linux ACCMODE flags being the same as SunOS. */
+ flags = (input & LX_O_ACCMODE);
+
+ if (input & LX_O_CREAT) {
+ flags |= O_CREAT;
+ }
+
+ if (input & LX_O_EXCL)
+ flags |= O_EXCL;
+ if (input & LX_O_NOCTTY)
+ flags |= O_NOCTTY;
+ if (input & LX_O_TRUNC)
+ flags |= O_TRUNC;
+ if (input & LX_O_APPEND)
+ flags |= O_APPEND;
+ if (input & LX_O_NONBLOCK)
+ flags |= O_NONBLOCK;
+ if (input & LX_O_SYNC)
+ flags |= O_SYNC;
+ if (input & LX_O_LARGEFILE)
+ flags |= O_LARGEFILE;
+ if (input & LX_O_NOFOLLOW)
+ flags |= O_NOFOLLOW;
+ if (input & LX_O_CLOEXEC)
+ flags |= O_CLOEXEC;
+
+ /*
+ * Linux uses the LX_O_DIRECT flag to do raw, synchronous I/O to the
+ * device backing the fd in question. Illumos doesn't have similar
+ * functionality, but we can attempt to simulate it using the flags
+ * (O_RSYNC|O_SYNC) and directio(3C).
+ *
+ * The LX_O_DIRECT flag also requires that the transfer size and
+ * alignment of I/O buffers be a multiple of the logical block size for
+ * the underlying file system, but frankly there isn't an easy way to
+ * support that functionality without doing something like adding an
+ * fcntl(2) flag to denote LX_O_DIRECT mode.
+ *
+ * Since LX_O_DIRECT is merely a performance advisory, we'll just
+ * emulate what we can and trust that the only applications expecting
+ * an error when performing I/O from a misaligned buffer or when
+ * passing a transfer size is not a multiple of the underlying file
+ * system block size will be test suites.
+ */
+ if (input & LX_O_DIRECT)
+ flags |= (O_RSYNC|O_SYNC);
+
+ return (flags);
+}
+
+#define LX_POSTPROCESS_OPTS (LX_O_DIRECT | LX_O_ASYNC | LX_O_PATH)
+
+static int
+lx_open_postprocess(int fd, int fmode)
+{
+ file_t *fp;
+ int rv, error = 0;
+
+ if ((fmode & LX_POSTPROCESS_OPTS) == 0) {
+ /* Skip out early, if possible */
+ return (0);
+ }
+
+ if ((fp = getf(fd)) == NULL) {
+ /*
+ * It is possible that this fd was closed by the time we
+ * arrived here if some one is hammering away with close().
+ */
+ return (EIO);
+ }
+
+ if (fmode & LX_O_DIRECT && error == 0) {
+ (void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_ON,
+ fp->f_flag, fp->f_cred, &rv, NULL);
+ }
+
+ if (fmode & LX_O_ASYNC && error == 0) {
+ if ((error = VOP_SETFL(fp->f_vnode, fp->f_flag, FASYNC,
+ fp->f_cred, NULL)) == 0) {
+ mutex_enter(&fp->f_tlock);
+ fp->f_flag |= FASYNC;
+ mutex_exit(&fp->f_tlock);
+ }
+ }
+
+ if (fmode & LX_O_PATH && error == 0) {
+ /*
+ * While the O_PATH flag has no direct analog in SunOS, it is
+ * emulated by removing both FREAD and FWRITE from f_flag.
+ * This causes read(2) and write(2) result in EBADF and can be
+ * checked for in other syscalls to tigger the correct behavior
+ * there.
+ */
+ mutex_enter(&fp->f_tlock);
+ fp->f_flag &= ~(FREAD|FWRITE);
+ mutex_exit(&fp->f_tlock);
+ }
+
+ releasef(fd);
+ if (error != 0) {
+ (void) closeandsetf(fd, NULL);
+ }
+ return (error);
+}
+
+long
+lx_openat(int atfd, char *path, int fmode, int cmode)
+{
+ int flags, fd, error;
+ mode_t mode = 0;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ flags = ltos_open_flags(fmode);
+
+ /*
+ * We use the FSEARCH flag to make sure this is a directory. We have to
+ * explicitly add 1 to emulate the FREAD/FWRITE mapping of the OPENMODE
+ * macro since it won't get set via OPENMODE when FSEARCH is used.
+ */
+ if (fmode & LX_O_DIRECTORY) {
+ flags |= FSEARCH;
+ flags++;
+ }
+
+ if (flags & O_CREAT)
+ mode = (mode_t)cmode;
+
+ ttolwp(curthread)->lwp_errno = 0;
+ fd = openat(atfd, path, flags, mode);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ if ((fmode & LX_O_DIRECTORY) &&
+ ttolwp(curthread)->lwp_errno != ENOTDIR) {
+ /*
+ * We got an error trying to open a file as a directory.
+ * We need to determine if we should return the original
+ * error or ENOTDIR.
+ */
+ vnode_t *startvp;
+ vnode_t *vp;
+ int oerror, error = 0;
+
+ oerror = ttolwp(curthread)->lwp_errno;
+
+ if (atfd == AT_FDCWD) {
+ /* regular open */
+ startvp = NULL;
+ } else {
+ char startchar;
+
+ if (copyin(path, &startchar, sizeof (char)))
+ return (set_errno(oerror));
+
+ /* if startchar is / then startfd is ignored */
+ if (startchar == '/') {
+ startvp = NULL;
+ } else {
+ file_t *startfp;
+
+ if ((startfp = getf(atfd)) == NULL)
+ return (set_errno(oerror));
+ startvp = startfp->f_vnode;
+ VN_HOLD(startvp);
+ releasef(atfd);
+ }
+ }
+
+ if (lookupnameat(path, UIO_USERSPACE,
+ (fmode & LX_O_NOFOLLOW) ? NO_FOLLOW : FOLLOW,
+ NULLVPP, &vp, startvp) != 0) {
+ if (startvp != NULL)
+ VN_RELE(startvp);
+ return (set_errno(oerror));
+ }
+
+ if (startvp != NULL)
+ VN_RELE(startvp);
+
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+
+ VN_RELE(vp);
+ if (error != 0)
+ return (set_errno(ENOTDIR));
+
+ (void) set_errno(oerror);
+ }
+ return (ttolwp(curthread)->lwp_errno);
+ }
+
+ if ((error = lx_open_postprocess(fd, fmode)) != 0) {
+ return (set_errno(error));
+ }
+ return (fd);
+}
+
+long
+lx_open(char *path, int fmode, int cmode)
+{
+ return (lx_openat(LX_AT_FDCWD, path, fmode, cmode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_personality.c b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
new file mode 100644
index 0000000000..e7aa945b50
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
@@ -0,0 +1,112 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+
+/*
+ * These flags are for what Linux calls "bug emulation".
+ * (Descriptions from the personality(2) Linux man page.)
+ *
+ * Flags which are currently actionable in LX:
+ * - READ_IMPLIES_EXEC (since Linux 2.6.8)
+ * With this flag set, PROT_READ implies PROT_EXEC for mmap(2).
+ *
+ * Flags which are current accepted but ignored:
+ * - UNAME26 (since Linux 3.1)
+ * Have uname(2) report a 2.6.40+ version number rather than a 3.x version
+ * number. Added as a stopgap measure to support broken applications that
+ * could not handle the kernel version- numbering switch from 2.6.x to 3.x.
+ *
+ * - ADDR_NO_RANDOMIZE (since Linux 2.6.12)
+ * With this flag set, disable address-space-layout randomization.
+ *
+ * - FDPIC_FUNCPTRS (since Linux 2.6.11)
+ * User-space function pointers to signal handlers point (on certain
+ * architectures) to descriptors.
+ *
+ * - MMAP_PAGE_ZERO (since Linux 2.4.0)
+ * Map page 0 as read-only (to support binaries that depend on this SVr4
+ * behavior).
+ *
+ * - ADDR_COMPAT_LAYOUT (since Linux 2.6.9)
+ * With this flag set, provide legacy virtual address space layout.
+ *
+ * - ADDR_LIMIT_32BIT (since Linux 2.2)
+ * Limit the address space to 32 bits.
+ *
+ * - SHORT_INODE (since Linux 2.4.0)
+ * No effects(?).
+ *
+ * - WHOLE_SECONDS (since Linux 1.2.0)
+ * No effects(?).
+ *
+ * - STICKY_TIMEOUTS (since Linux 1.2.0)
+ * With this flag set, select(2), pselect(2), and ppoll(2) do not modify the
+ * returned timeout argument when interrupted by a signal handler.
+ *
+ * - ADDR_LIMIT_3GB (since Linux 2.4.0)
+ * With this flag set, use 0xc0000000 as the offset at which to search a
+ * virtual memory chunk on mmap(2); otherwise use 0xffffe000.
+ */
+
+#define LX_PER_GET 0xffffffff
+
+long
+lx_personality(unsigned int arg)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ unsigned int result = 0;
+
+ mutex_enter(&curproc->p_lock);
+ result = lxpd->l_personality;
+
+ if (arg == LX_PER_GET) {
+ mutex_exit(&curproc->p_lock);
+ return (result);
+ }
+
+ /*
+ * Prevent changes to the personality if the process is undergoing an
+ * exec. This will allow elfexec and friends to manipulate the
+ * personality without hinderance.
+ */
+ if ((curproc->p_flag & P_PR_EXEC) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Keep tabs when a non-Linux personality is set. This is silently
+ * allowed to succeed, even though the emulation required is almost
+ * certainly missing.
+ */
+ if ((arg & LX_PER_MASK) != LX_PER_LINUX) {
+ char buf[64];
+
+ (void) snprintf(buf, sizeof (buf), "invalid personality: %02X",
+ arg & LX_PER_MASK);
+ lx_unsupported(buf);
+ }
+
+ lxpd->l_personality = arg;
+ mutex_exit(&curproc->p_lock);
+ return (result);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c b/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c
new file mode 100644
index 0000000000..2acd9d431e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c
@@ -0,0 +1,189 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/lx_misc.h>
+
+#define LX_INIT_PGID 1
+#define LX_INIT_SID 1
+
+/* From uts/common/syscall/pgrpsys.c */
+extern int setpgrp(int, int, int);
+
+long
+lx_getpgrp(void)
+{
+ int pg;
+
+ /* getpgrp() */
+ pg = setpgrp(0, 0, 0);
+
+ /*
+ * If the pgrp is that of the init process, return the value Linux
+ * expects.
+ */
+ if (pg == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ return (pg);
+}
+
+long
+lx_getpgid(int pid)
+{
+ pid_t spid;
+ int tid;
+ int pg;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ /*
+ * If the supplied pid matches that of the init process, return the pgid
+ * Linux expects.
+ */
+ if (pid == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* getpgid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ pg = setpgrp(4, spid, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ /*
+ * If the pgid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (pg == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ return (pg);
+}
+
+long
+lx_setpgid(pid_t pid, pid_t pgid)
+{
+ pid_t spid, spgid;
+ int tid;
+ int pg;
+ int ret;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if (pgid < 0)
+ return (set_errno(EINVAL));
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ if (pgid == 0) {
+ spgid = spid;
+ } else if (lx_lpid_to_spair(pgid, &spgid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* setpgid() */
+ ret = setpgrp(5, spid, spgid);
+
+ if (ret == EPERM) {
+ /*
+ * On Linux, when calling setpgid with a desired pgid that is
+ * equal to the current pgid of the process, no error is
+ * emitted. This differs slightly from illumos which would
+ * return EPERM. To emulate the Linux behavior, we check
+ * specifically for matching pgids.
+ */
+
+ /* getpgid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ pg = setpgrp(4, spid, 0);
+ if (ttolwp(curthread)->lwp_errno == 0 && spgid == pg)
+ return (0);
+ return (set_errno(EPERM));
+ }
+
+ return (ret);
+}
+
+long
+lx_getsid(int pid)
+{
+ pid_t spid;
+ int tid;
+ int sid;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ /*
+ * If the supplied pid matches that of the init process, return the sid
+ * Linux expects.
+ */
+ if (pid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* getsid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ sid = setpgrp(2, spid, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+
+ /*
+ * If the sid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (sid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ return (sid);
+}
+
+long
+lx_setsid(void)
+{
+ int sid;
+
+ /* setsid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ sid = setpgrp(3, 0, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ /*
+ * If the sid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (sid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ return (sid);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_pipe.c b/usr/src/uts/common/brand/lx/syscall/lx_pipe.c
new file mode 100644
index 0000000000..519c742abc
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_pipe.c
@@ -0,0 +1,200 @@
+/*
+ * 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) 1984, 1986, 1987, 1988, 1989 AT&T. All Rights Reserved.
+ *
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cred.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/fs/fifonode.h>
+#include <sys/fcntl.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * Based on native pipe(2) system call, except that the pipe is half-duplex.
+ */
+static int
+lx_hd_pipe(intptr_t arg, int flags)
+{
+ vnode_t *vp1, *vp2;
+ struct file *fp1, *fp2;
+ int error = 0;
+ int flag1, flag2, iflags;
+ int fd1, fd2;
+
+ /*
+ * Validate allowed flags.
+ */
+ if ((flags & ~(FCLOEXEC|FNONBLOCK)) != 0) {
+ return (set_errno(EINVAL));
+ }
+ /*
+ * Allocate and initialize two vnodes.
+ */
+ makepipe(&vp1, &vp2);
+
+ /*
+ * Allocate and initialize two file table entries and two
+ * file pointers. The first file pointer is open for read and the
+ * second is open for write.
+ */
+ if ((error = falloc(vp1, FREAD, &fp1, &fd1)) != 0) {
+ VN_RELE(vp1);
+ VN_RELE(vp2);
+ return (set_errno(error));
+ }
+
+ if ((error = falloc(vp2, FWRITE, &fp2, &fd2)) != 0)
+ goto out2;
+
+ /*
+ * Create two stream heads and attach to each vnode.
+ */
+ if ((error = fifo_stropen(&vp1, FREAD, fp1->f_cred, 0, 0)) != 0)
+ goto out;
+
+ if ((error = fifo_stropen(&vp2, FWRITE, fp2->f_cred, 0, 0)) != 0) {
+ (void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0,
+ fp1->f_cred, NULL);
+ goto out;
+ }
+
+ strmate(vp1, vp2);
+
+ VTOF(vp1)->fn_ino = VTOF(vp2)->fn_ino = fifogetid();
+
+ /*
+ * Set the O_NONBLOCK flag if requested.
+ */
+ if (flags & FNONBLOCK) {
+ flag1 = fp1->f_flag;
+ flag2 = fp2->f_flag;
+ iflags = flags & FNONBLOCK;
+
+ if ((error = VOP_SETFL(vp1, flag1, iflags, fp1->f_cred,
+ NULL)) != 0) {
+ goto out_vop_close;
+ }
+ fp1->f_flag |= iflags;
+
+ if ((error = VOP_SETFL(vp2, flag2, iflags, fp2->f_cred,
+ NULL)) != 0) {
+ goto out_vop_close;
+ }
+ fp2->f_flag |= iflags;
+ }
+
+ /*
+ * Return the file descriptors to the user. They now
+ * point to two different vnodes which have different
+ * stream heads.
+ */
+ if (copyout(&fd1, &((int *)arg)[0], sizeof (int)) ||
+ copyout(&fd2, &((int *)arg)[1], sizeof (int))) {
+ error = EFAULT;
+ goto out_vop_close;
+ }
+
+ /*
+ * Now fill in the entries that falloc reserved
+ */
+ mutex_exit(&fp1->f_tlock);
+ mutex_exit(&fp2->f_tlock);
+ setf(fd1, fp1);
+ setf(fd2, fp2);
+
+ /*
+ * Optionally set the FCLOEXEC flag
+ */
+ if ((flags & FCLOEXEC) != 0) {
+ f_setfd(fd1, FD_CLOEXEC);
+ f_setfd(fd2, FD_CLOEXEC);
+ }
+
+ return (0);
+out_vop_close:
+ (void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0, fp1->f_cred, NULL);
+ (void) VOP_CLOSE(vp2, FWRITE, 1, (offset_t)0, fp2->f_cred, NULL);
+out:
+ setf(fd2, NULL);
+ unfalloc(fp2);
+out2:
+ setf(fd1, NULL);
+ unfalloc(fp1);
+ VN_RELE(vp1);
+ VN_RELE(vp2);
+ return (set_errno(error));
+}
+
+/*
+ * pipe(2) system call.
+ */
+long
+lx_pipe(intptr_t arg)
+{
+ return (lx_hd_pipe(arg, 0));
+}
+
+/*
+ * pipe2(2) system call.
+ */
+long
+lx_pipe2(intptr_t arg, int lxflags)
+{
+ int flags = 0;
+
+ /*
+ * Validate allowed flags.
+ */
+ if ((lxflags & ~(LX_O_NONBLOCK | LX_O_CLOEXEC)) != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Convert from Linux flags to illumos flags.
+ */
+ if (lxflags & LX_O_NONBLOCK) {
+ flags |= FNONBLOCK;
+ }
+ if (lxflags & LX_O_CLOEXEC) {
+ flags |= FCLOEXEC;
+ }
+
+ return (lx_hd_pipe(arg, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_poll.c b/usr/src/uts/common/brand/lx/syscall/lx_poll.c
new file mode 100644
index 0000000000..1d92a55ddf
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_poll.c
@@ -0,0 +1,762 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sunddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/poll_impl.h>
+#include <sys/schedctl.h>
+#include <sys/lx_signal.h>
+
+
+/* From uts/common/syscall/poll.c */
+extern int poll_copyin(pollstate_t *, pollfd_t *, nfds_t);
+extern int poll_common(pollstate_t *, pollfd_t *, nfds_t, timespec_t *, int *);
+
+/*
+ * These events are identical between Linux and SunOS
+ */
+#define LX_POLLIN 0x001
+#define LX_POLLPRI 0x002
+#define LX_POLLOUT 0x004
+#define LX_POLLERR 0x008
+#define LX_POLLHUP 0x010
+#define LX_POLLNVAL 0x020
+#define LX_POLLRDNORM 0x040
+#define LX_POLLRDBAND 0x080
+
+#define LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT | \
+ LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND)
+
+/*
+ * These events differ between Linux and SunOS
+ */
+#define LX_POLLWRNORM 0x0100
+#define LX_POLLWRBAND 0x0200
+#define LX_POLLRDHUP 0x2000
+
+
+#define LX_POLL_SUPPORTED_EVENTS \
+ (LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP)
+
+
+static int
+lx_poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+ int i, error = 0;
+ pollfd_t *pollfdp;
+
+ if ((error = poll_copyin(ps, fds, nfds)) != 0) {
+ return (error);
+ }
+ pollfdp = ps->ps_pollfd;
+
+ /* Convert the Linux events bitmask into SunOS equivalent. */
+ for (i = 0; i < nfds; i++) {
+ short lx_events = pollfdp[i].events;
+ short events;
+
+ /*
+ * If the caller is polling for an unsupported event, we
+ * have to bail out.
+ */
+ if (lx_events & ~LX_POLL_SUPPORTED_EVENTS) {
+ return (ENOTSUP);
+ }
+
+ events = lx_events & LX_POLL_COMMON_EVENTS;
+ if (lx_events & LX_POLLWRNORM)
+ events |= POLLWRNORM;
+ if (lx_events & LX_POLLWRBAND)
+ events |= POLLWRBAND;
+ if (lx_events & LX_POLLRDHUP)
+ events |= POLLRDHUP;
+ pollfdp[i].events = events;
+ oldevt[i] = lx_events;
+ }
+ return (0);
+}
+
+static int
+lx_poll_copyout(pollfd_t *pollfdp, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+ int i;
+
+ /*
+ * Convert SunOS revents bitmask into Linux equivalent and restore
+ * cached events field which was swizzled by lx_poll_copyin.
+ */
+ for (i = 0; i < nfds; i++) {
+ short revents = pollfdp[i].revents;
+ short lx_revents = revents & LX_POLL_COMMON_EVENTS;
+ short orig_events = oldevt[i];
+
+ if (revents & POLLWRBAND)
+ lx_revents |= LX_POLLWRBAND;
+ if (revents & POLLRDHUP)
+ lx_revents |= LX_POLLRDHUP;
+ /*
+ * Because POLLOUT and POLLWRNORM are native defined as the
+ * same value, care must be taken when translating them to
+ * Linux where they differ.
+ */
+ if (revents & POLLOUT) {
+ if ((orig_events & LX_POLLOUT) == 0)
+ lx_revents &= ~LX_POLLOUT;
+ if (orig_events & LX_POLLWRNORM)
+ lx_revents |= LX_POLLWRNORM;
+ }
+
+ pollfdp[i].revents = lx_revents;
+ pollfdp[i].events = orig_events;
+ }
+
+ if (copyout(pollfdp, fds, sizeof (pollfd_t) * nfds) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static long
+lx_poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL;
+ short *oldevt = NULL;
+ int error = 0, fdcnt = 0;
+
+ /*
+ * Reset our signal mask, if requested.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = *ksetp;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto pollout;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ */
+ if (nfds != 0) {
+ if (nfds > p->p_fno_ctl) {
+ mutex_enter(&p->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+ p->p_rctls, p, RCA_SAFE);
+ mutex_exit(&p->p_lock);
+ error = EINVAL;
+ goto pollout;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ /*
+ * Certain event types which are distinct on Linux are aliased
+ * against each other on illumos. In order properly translate
+ * back into the Linux format, the original events of interest
+ * are stored in 'oldevt' for use during lx_poll_copyout.
+ */
+ oldevt = kmem_alloc(nfds * sizeof (short), KM_SLEEP);
+ if ((error = lx_poll_copyin(ps, fds, nfds, oldevt)) != 0)
+ goto pollout;
+ pollfdp = ps->ps_pollfd;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
+pollout:
+ /*
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ if (lwp->lwp_cursig == 0) {
+ t->t_hold = lwp->lwp_sigoldmask;
+ t->t_flag &= ~T_TOMASK;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Copy out the events and return the fdcnt to the user.
+ */
+ if (nfds != 0 && error == 0) {
+ error = lx_poll_copyout(pollfdp, fds, nfds, oldevt);
+ }
+ if (oldevt != NULL) {
+ kmem_free(oldevt, nfds * sizeof (short));
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (fdcnt);
+}
+
+long
+lx_poll(pollfd_t *fds, nfds_t nfds, int timeout)
+{
+ timespec_t ts, *tsp = NULL;
+
+ if (timeout >= 0) {
+ ts.tv_sec = timeout / MILLISEC;
+ ts.tv_nsec = (timeout % MILLISEC) * MICROSEC;
+ tsp = &ts;
+ }
+
+ return (lx_poll_common(fds, nfds, tsp, NULL));
+}
+
+long
+lx_ppoll(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, lx_sigset_t *setp)
+{
+ timespec_t ts, *tsp = NULL;
+ k_sigset_t kset, *ksetp = NULL;
+
+ /*
+ * Copy in timeout and sigmask.
+ */
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+ if (setp != NULL) {
+ lx_sigset_t lset;
+
+ if (copyin(setp, &lset, sizeof (lset)))
+ return (set_errno(EFAULT));
+ lx_ltos_sigset(&lset, &kset);
+ ksetp = &kset;
+ }
+
+ return (lx_poll_common(fds, nfds, tsp, ksetp));
+}
+
+typedef struct lx_select_buf_s {
+ long *lsb_rfds;
+ long *lsb_wfds;
+ long *lsb_efds;
+ unsigned int lsb_size;
+} lx_select_buf_t;
+
+/*
+ * Size (in bytes) of buffer appropriate for fd_set copyin/copyout.
+ * Linux uses buffers of 'long' to accomplish this.
+ */
+#define LX_FD_SET_BYTES (sizeof (long))
+#define LX_FD_SET_BITS (8 * LX_FD_SET_BYTES)
+#define LX_FD_SET_SIZE(nfds) \
+ ((((nfds) + (LX_FD_SET_BITS - 1)) / LX_FD_SET_BITS) * LX_FD_SET_BYTES)
+
+static int
+lx_select_copyin(pollstate_t *ps, lx_select_buf_t *sbuf, int nfds,
+ long *rfds, long *wfds, long *efds)
+{
+ int n;
+ long *in, *out, *ex;
+ long absent = 0;
+ pollfd_t *pfd;
+ nfds_t old_nfds;
+
+ /*
+ * Just like pollsys and lx_poll, attempt to reuse ps_pollfd if it is
+ * appropriately sized. See poll_copyin for more detail.
+ */
+ old_nfds = ps->ps_nfds;
+ if (nfds != old_nfds) {
+ kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+ pfd = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+ ps->ps_pollfd = pfd;
+ ps->ps_nfds = nfds;
+ } else {
+ pfd = ps->ps_pollfd;
+ }
+
+ if (rfds != NULL) {
+ if (copyin(rfds, sbuf->lsb_rfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (wfds != NULL) {
+ if (copyin(wfds, sbuf->lsb_wfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (efds != NULL) {
+ if (copyin(efds, sbuf->lsb_efds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ /*
+ * For each fd, if any bits are set convert them into the appropriate
+ * pollfd struct. (Derived from libc's select logic)
+ */
+ in = (rfds != NULL) ? sbuf->lsb_rfds : &absent;
+ out = (wfds != NULL) ? sbuf->lsb_wfds : &absent;
+ ex = (efds != NULL) ? sbuf->lsb_efds : &absent;
+ for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+ unsigned long b, m, j;
+
+ b = (unsigned long)(*in | *out | *ex);
+ m = 1;
+ for (j = 0; j < LX_FD_SET_BITS; j++) {
+ int fd = n + j;
+
+ if (fd >= nfds)
+ return (0);
+ pfd->events = 0;
+ if (b & 1) {
+ pfd->fd = fd;
+ if (*in & m)
+ pfd->events |= POLLRDNORM;
+ if (*out & m)
+ pfd->events |= POLLWRNORM;
+ if (*ex & m)
+ pfd->events |= POLLRDBAND;
+ } else {
+ pfd->fd = -1;
+ }
+ pfd++;
+ b >>= 1;
+ m <<= 1;
+ }
+
+ if (rfds != NULL)
+ in++;
+ if (wfds != NULL)
+ out++;
+ if (efds != NULL)
+ ex++;
+ }
+ return (0);
+}
+
+static int
+lx_select_copyout(pollfd_t *pollfdp, lx_select_buf_t *sbuf, int nfds,
+ long *rfds, long *wfds, long *efds, int *fdcnt)
+{
+ int n;
+ pollfd_t *pfd;
+ long rv = 0;
+
+ /*
+ * If poll did not find any fds of interest, we can just zero out the
+ * fd_set fields for copyout.
+ */
+ if (*fdcnt == 0) {
+ if (rfds != NULL) {
+ bzero(sbuf->lsb_rfds, sbuf->lsb_size);
+ }
+ if (wfds != NULL) {
+ bzero(sbuf->lsb_wfds, sbuf->lsb_size);
+ }
+ if (efds != NULL) {
+ bzero(sbuf->lsb_efds, sbuf->lsb_size);
+ }
+ goto copyout;
+ }
+
+ /*
+ * For each fd, if any bits are set convert them into the appropriate
+ * pollfd struct. (Derived from libc's select logic)
+ */
+ pfd = pollfdp;
+ for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+ unsigned long m, j;
+ long in = 0, out = 0, ex = 0;
+
+ m = 1;
+ for (j = 0; j < LX_FD_SET_BITS; j++) {
+ if ((n + j) >= nfds)
+ break;
+ if (pfd->revents != 0) {
+ if (pfd->revents & POLLNVAL) {
+ return (EBADF);
+ }
+ if (pfd->revents & POLLRDNORM) {
+ in |= m;
+ rv++;
+ }
+ if (pfd->revents & POLLWRNORM) {
+ out |= m;
+ rv++;
+ }
+ if (pfd->revents & POLLRDBAND) {
+ ex |= m;
+ rv++;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about input conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLRDNORM)) {
+ if ((in & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ in |= m;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about output conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLWRNORM)) {
+ if ((out & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ out |= m;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about output conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLRDBAND)) {
+ if ((ex & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ ex |= m;
+ }
+ }
+ m <<= 1;
+ pfd++;
+ }
+ if (rfds != NULL)
+ sbuf->lsb_rfds[n / LX_FD_SET_BITS] = in;
+ if (wfds != NULL)
+ sbuf->lsb_wfds[n / LX_FD_SET_BITS] = out;
+ if (efds != NULL)
+ sbuf->lsb_efds[n / LX_FD_SET_BITS] = ex;
+ }
+
+copyout:
+ if (rfds != NULL) {
+ if (copyout(sbuf->lsb_rfds, rfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (wfds != NULL) {
+ if (copyout(sbuf->lsb_wfds, wfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (efds != NULL) {
+ if (copyout(sbuf->lsb_efds, efds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ *fdcnt = rv;
+ return (0);
+}
+
+
+static long
+lx_select_common(int nfds, long *rfds, long *wfds, long *efds,
+ timespec_t *tsp, k_sigset_t *ksetp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL, *fake_fds = NULL;
+ lx_select_buf_t sbuf = {0};
+ int error = 0, fdcnt = 0;
+
+ if (nfds < 0) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Reset our signal mask, if requested.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = *ksetp;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto out;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Because poll caching uses the userspace pollfd_t pointer to verify
+ * cache reuse validity, a simulated value must be supplied when
+ * emulating Linux select(2). The first non-NULL pointer from
+ * rfds/wfds/efds is used for this purpose.
+ */
+ if (rfds != NULL) {
+ fake_fds = (pollfd_t *)rfds;
+ } else if (wfds != NULL) {
+ fake_fds = (pollfd_t *)wfds;
+ } else if (efds != NULL) {
+ fake_fds = (pollfd_t *)efds;
+ } else {
+ /*
+ * A non-zero nfds was supplied but all three fd_set pointers
+ * were null. Fall back to doing a simple timeout.
+ */
+ nfds = 0;
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ */
+ if (nfds != 0) {
+ if (nfds > p->p_fno_ctl) {
+ mutex_enter(&p->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+ p->p_rctls, p, RCA_SAFE);
+ mutex_exit(&p->p_lock);
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ sbuf.lsb_size = LX_FD_SET_SIZE(nfds);
+ if (rfds != NULL)
+ sbuf.lsb_rfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+ if (wfds != NULL)
+ sbuf.lsb_wfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+ if (efds != NULL)
+ sbuf.lsb_efds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+
+ error = lx_select_copyin(ps, &sbuf, nfds, rfds, wfds, efds);
+ if (error != 0) {
+ goto out;
+ }
+
+ pollfdp = ps->ps_pollfd;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fake_fds, (nfds_t)nfds, tsp, &fdcnt);
+
+out:
+ /*
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ if (lwp->lwp_cursig == 0) {
+ t->t_hold = lwp->lwp_sigoldmask;
+ t->t_flag &= ~T_TOMASK;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Copy out the events and return the fdcnt to the user.
+ */
+ if (error == 0 && nfds != 0) {
+ error = lx_select_copyout(pollfdp, &sbuf, nfds, rfds, wfds,
+ efds, &fdcnt);
+ }
+ if (sbuf.lsb_size != 0) {
+ if (sbuf.lsb_rfds != NULL)
+ kmem_free(sbuf.lsb_rfds, sbuf.lsb_size);
+ if (sbuf.lsb_wfds != NULL)
+ kmem_free(sbuf.lsb_wfds, sbuf.lsb_size);
+ if (sbuf.lsb_efds != NULL)
+ kmem_free(sbuf.lsb_efds, sbuf.lsb_size);
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (fdcnt);
+}
+
+long
+lx_select(int nfds, long *rfds, long *wfds, long *efds,
+ struct timeval *timeoutp)
+{
+ timespec_t ts, *tsp = NULL;
+
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ struct timeval tv;
+
+ if (copyin(timeoutp, &tv, sizeof (tv)))
+ return (set_errno(EFAULT));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * (NANOSEC / MICROSEC);
+ } else {
+ struct timeval32 tv32;
+
+ if (copyin(timeoutp, &tv32, sizeof (tv32)))
+ return (set_errno(EFAULT));
+ ts.tv_sec = tv32.tv_sec;
+ ts.tv_nsec = tv32.tv_usec * (NANOSEC / MICROSEC);
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+
+ return (lx_select_common(nfds, rfds, wfds, efds, tsp, NULL));
+}
+
+
+typedef struct {
+ uintptr_t lpsa_addr;
+ unsigned long lpsa_len;
+} lx_pselect_sig_arg_t;
+
+#if defined(_LP64)
+typedef struct {
+ caddr32_t lpsa_addr;
+ uint32_t lpsa_len;
+} lx_pselect_sig_arg32_t;
+#endif /* defined(_LP64) */
+
+long
+lx_pselect(int nfds, long *rfds, long *wfds, long *efds,
+ timespec_t *timeoutp, void *setp)
+{
+ timespec_t ts, *tsp = NULL;
+ k_sigset_t kset, *ksetp = NULL;
+
+ /*
+ * Copy in timeout and sigmask.
+ */
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+ if (setp != NULL) {
+ lx_sigset_t lset, *sigaddr = NULL;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_pselect_sig_arg_t lpsa;
+
+ if (copyin(setp, &lpsa, sizeof (lpsa)) != 0)
+ return (set_errno(EFAULT));
+ /*
+ * Linux forces a size to be passed only so it can
+ * check that it's the size of a sigset_t.
+ */
+ if (lpsa.lpsa_len != sizeof (lx_sigset_t))
+ return (set_errno(EINVAL));
+
+ sigaddr = (lx_sigset_t *)lpsa.lpsa_addr;
+ }
+#if defined(_LP64)
+ else {
+ lx_pselect_sig_arg32_t lpsa32;
+
+ if (copyin(setp, &lpsa32, sizeof (lpsa32)) != 0)
+ return (set_errno(EFAULT));
+ /*
+ * Linux forces a size to be passed only so it can
+ * check that it's the size of a sigset_t.
+ */
+ if (lpsa32.lpsa_len != sizeof (lx_sigset_t))
+ return (set_errno(EINVAL));
+
+ sigaddr = (lx_sigset_t *)(uint64_t)lpsa32.lpsa_addr;
+ }
+#endif /* defined(_LP64) */
+
+ /* This is where we check if the sigset is *really* NULL. */
+ if (sigaddr != NULL) {
+ if (copyin(sigaddr, &lset, sizeof (lset)) != 0)
+ return (set_errno(EFAULT));
+
+ lx_ltos_sigset(&lset, &kset);
+ ksetp = &kset;
+ }
+ }
+
+ return (lx_select_common(nfds, rfds, wfds, efds, tsp, ksetp));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_prctl.c b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
new file mode 100644
index 0000000000..ac125b9b2d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
@@ -0,0 +1,211 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/priv.h>
+#include <sys/brand.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <lx_signum.h>
+
+#define LX_PR_SET_PDEATHSIG 1
+#define LX_PR_GET_PDEATHSIG 2
+#define LX_PR_GET_DUMPABLE 3
+#define LX_PR_SET_DUMPABLE 4
+#define LX_PR_GET_UNALIGN 5
+#define LX_PR_SET_UNALIGN 6
+#define LX_PR_GET_KEEPCAPS 7
+#define LX_PR_SET_KEEPCAPS 8
+#define LX_PR_GET_FPEMU 9
+#define LX_PR_SET_FPEMU 10
+#define LX_PR_GET_FPEXC 11
+#define LX_PR_SET_FPEXC 12
+#define LX_PR_GET_TIMING 13
+#define LX_PR_SET_TIMING 14
+#define LX_PR_SET_NAME 15
+#define LX_PR_GET_NAME 16
+#define LX_PR_GET_ENDIAN 19
+#define LX_PR_SET_ENDIAN 20
+#define LX_PR_GET_SECCOMP 21
+#define LX_PR_SET_SECCOMP 22
+#define LX_PR_CAPBSET_READ 23
+#define LX_PR_CAPBSET_DROP 24
+#define LX_PR_GET_TSC 25
+#define LX_PR_SET_TSC 26
+#define LX_PR_GET_SECUREBITS 27
+#define LX_PR_SET_SECUREBITS 28
+#define LX_PR_SET_TIMERSLACK 29
+#define LX_PR_GET_TIMERSLACK 30
+#define LX_PR_TASK_PERF_EVENTS_DISABLE 31
+#define LX_PR_TASK_PERF_EVENTS_ENABLE 32
+#define LX_PR_MCE_KILL 33
+#define LX_PR_MCE_KILL_GET 34
+#define LX_PR_SET_MM 35
+#define LX_PR_SET_CHILD_SUBREAPER 36
+#define LX_PR_GET_CHILD_SUBREAPER 37
+#define LX_PR_SET_NO_NEW_PRIVS 38
+#define LX_PR_GET_NO_NEW_PRIVS 39
+#define LX_PR_GET_TID_ADDRESS 40
+#define LX_PR_SET_THP_DISABLE 41
+#define LX_PR_GET_THP_DISABLE 42
+
+#define LX_PR_SET_NAME_NAMELEN 16
+
+long
+lx_prctl(int opt, uintptr_t data)
+{
+ long err;
+ char ebuf[64];
+
+ switch (opt) {
+ case LX_PR_GET_DUMPABLE: {
+ /* Indicate that process is always dumpable */
+ return (1);
+ }
+
+ case LX_PR_SET_DUMPABLE: {
+ if (data != 0 && data != 1) {
+ return (set_errno(EINVAL));
+ }
+ /* Lie about altering process dumpability */
+ return (0);
+ }
+
+ case LX_PR_GET_SECUREBITS: {
+ /* Our bits are always 0 */
+ return (0);
+ }
+
+ case LX_PR_SET_SECUREBITS: {
+ /* Ignore setting any bits from arg2 */
+ return (0);
+ }
+
+ case LX_PR_SET_KEEPCAPS: {
+ /*
+ * The closest illumos analog to SET_KEEPCAPS is the PRIV_AWARE
+ * flag. There are probably some cases where it's not exactly
+ * the same, but this will do for a first try.
+ */
+ if (data == 0) {
+ err = setpflags(PRIV_AWARE_RESET, 1, NULL);
+ } else {
+ err = setpflags(PRIV_AWARE, 1, NULL);
+ }
+
+ if (err != 0) {
+ return (set_errno(err));
+ }
+ return (0);
+ }
+
+ case LX_PR_SET_NAME: {
+ char name[LX_PR_SET_NAME_NAMELEN + 1];
+ proc_t *p = curproc;
+ /*
+ * In Linux, PR_SET_NAME sets the name of the thread, not the
+ * process. Due to the historical quirks of Linux's asinine
+ * thread model, this name is effectively the name of the
+ * process (as visible via ps(1)) if the thread is the first of
+ * its task group. The first thread is therefore special, and
+ * to best mimic Linux semantics (and absent a notion of
+ * per-LWP names), we do nothing (but return success) on LWPs
+ * other than LWP 1.
+ */
+ if (curthread->t_tid != 1) {
+ return (0);
+ }
+ if (copyin((void *)data, name, LX_PR_SET_NAME_NAMELEN) != 0) {
+ return (set_errno(EFAULT));
+ }
+ name[LX_PR_SET_NAME_NAMELEN] = '\0';
+ mutex_enter(&p->p_lock);
+ (void) strncpy(p->p_user.u_comm, name, MAXCOMLEN + 1);
+ (void) strncpy(p->p_user.u_psargs, name, PSARGSZ);
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ case LX_PR_GET_PDEATHSIG: {
+ int sig;
+ lx_proc_data_t *lxpd;
+
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ sig = lxpd->l_parent_deathsig;
+ mutex_exit(&curproc->p_lock);
+
+ return (sig);
+ }
+
+ case LX_PR_SET_PDEATHSIG: {
+ int sig = lx_ltos_signo((int)data, 0);
+ proc_t *pp = NULL;
+ lx_proc_data_t *lxpd;
+
+ if (sig == 0 && data != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ mutex_enter(&pidlock);
+ /* Set signal on our self */
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ lxpd->l_parent_deathsig = sig;
+ pp = curproc->p_parent;
+ mutex_exit(&curproc->p_lock);
+
+ /* Configure parent to potentially signal children on death */
+ mutex_enter(&pp->p_lock);
+ if (PROC_IS_BRANDED(pp)) {
+ VERIFY((lxpd = ptolxproc(pp)) != NULL);
+ /*
+ * Mark the parent as having children which wish to be
+ * signaled on death of parent.
+ */
+ lxpd->l_flags |= LX_PROC_CHILD_DEATHSIG;
+ } else {
+ /*
+ * If the parent is not a branded process, the needed
+ * hooks to facilitate this mechanism will not fire
+ * when it dies. We lie about success in this case.
+ */
+ /* EMPTY */
+ }
+ mutex_exit(&pp->p_lock);
+ mutex_exit(&pidlock);
+ return (0);
+ }
+
+ case LX_PR_CAPBSET_DROP: {
+ /*
+ * On recent versions of Linux the login svc drops capabilities
+ * and if that fails the svc dies and is restarted by systemd.
+ * For now we pretend dropping capabilities succeeded.
+ */
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ (void) snprintf(ebuf, 64, "prctl option %d", opt);
+ lx_unsupported(ebuf);
+ return (set_errno(EINVAL));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rename.c b/usr/src/uts/common/brand/lx/syscall/lx_rename.c
new file mode 100644
index 0000000000..2fad627771
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rename.c
@@ -0,0 +1,39 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/* From uts/common/syscall/rename.c */
+extern int rename(char *, char *);
+extern int renameat(int, char *, int, char *);
+
+long
+lx_rename(char *p1, char *p2)
+{
+ return (rename(p1, p2));
+}
+
+long
+lx_renameat(int atfd1, char *p1, int atfd2, char *p2)
+{
+ if (atfd1 == LX_AT_FDCWD)
+ atfd1 = AT_FDCWD;
+
+ if (atfd2 == LX_AT_FDCWD)
+ atfd2 = AT_FDCWD;
+
+ return (renameat(atfd1, p1, atfd2, p2));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c b/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c
new file mode 100644
index 0000000000..6581ead25b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c
@@ -0,0 +1,575 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/zone.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+#define LX_RLIMIT_CPU 0
+#define LX_RLIMIT_FSIZE 1
+#define LX_RLIMIT_DATA 2
+#define LX_RLIMIT_STACK 3
+#define LX_RLIMIT_CORE 4
+#define LX_RLIMIT_RSS 5
+#define LX_RLIMIT_NPROC 6
+#define LX_RLIMIT_NOFILE 7
+#define LX_RLIMIT_MEMLOCK 8
+#define LX_RLIMIT_AS 9
+#define LX_RLIMIT_LOCKS 10 /* NA limit on locks, early 2.4 only */
+#define LX_RLIMIT_SIGPENDING 11
+#define LX_RLIMIT_MSGQUEUE 12
+#define LX_RLIMIT_NICE 13 /* NA ceiling for nice */
+#define LX_RLIMIT_RTPRIO 14 /* NA ceiling on the RT priority */
+#define LX_RLIMIT_RTTIME 15 /* NA cpu limit for RT proc. */
+
+#define LX_RLIMIT_NLIMITS 16
+
+#define RCTL_INFINITE(x) \
+ ((x->rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+ (x->rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct {
+ ulong_t rlim_cur;
+ ulong_t rlim_max;
+} lx_rlimit_t;
+
+typedef struct {
+ uint32_t rlim_cur;
+ uint32_t rlim_max;
+} lx_rlimit32_t;
+
+/*
+ * Linux supports many of the same resources that we do, but on Illumos these
+ * are rctls. Instead of using rlimit, we use rctls for all of the limits.
+ * This table is used to translate Linux rlimit keys into the Illumos legacy
+ * rlimit. We then primarily use the rctl/rlimit compatability code to
+ * manage these.
+ */
+static int l_to_r[LX_RLIMIT_NLIMITS] = {
+ RLIMIT_CPU, /* 0 CPU */
+ RLIMIT_FSIZE, /* 1 FSIZE */
+ RLIMIT_DATA, /* 2 DATA */
+ RLIMIT_STACK, /* 3 STACK */
+ RLIMIT_CORE, /* 4 CORE */
+ -1, /* 5 RSS */
+ -1, /* 6 NPROC */
+ RLIMIT_NOFILE, /* 7 NOFILE */
+ -1, /* 8 MEMLOCK */
+ RLIMIT_AS, /* 9 AS */
+ -1, /* 10 LOCKS */
+ -1, /* 11 SIGPENDING */
+ -1, /* 12 MSGQUEUE */
+ -1, /* 13 NICE */
+ -1, /* 14 RTPRIO */
+ -1 /* 15 RTTIME */
+};
+
+/*
+ * Magic value Linux uses to indicate infinity
+ */
+#define LX_RLIM_INFINITY_N ULONG_MAX
+
+static void
+lx_get_rctl(char *nm, struct rlimit64 *rlp64)
+{
+ rctl_hndl_t hndl;
+ rctl_val_t *oval, *nval;
+
+ rlp64->rlim_cur = RLIM_INFINITY;
+ rlp64->rlim_max = RLIM_INFINITY;
+
+ nval = kmem_alloc(sizeof (rctl_val_t), KM_SLEEP);
+ mutex_enter(&curproc->p_lock);
+
+ hndl = rctl_hndl_lookup(nm);
+ oval = NULL;
+ while ((hndl != -1) && rctl_local_get(hndl, oval, nval, curproc) == 0) {
+ oval = nval;
+ switch (nval->rcv_privilege) {
+ case RCPRIV_BASIC:
+ if (!RCTL_INFINITE(nval))
+ rlp64->rlim_cur = nval->rcv_value;
+ break;
+ case RCPRIV_PRIVILEGED:
+ if (!RCTL_INFINITE(nval))
+ rlp64->rlim_max = nval->rcv_value;
+ break;
+ }
+ }
+
+ mutex_exit(&curproc->p_lock);
+ kmem_free(nval, sizeof (rctl_val_t));
+
+ if (rlp64->rlim_cur == RLIM_INFINITY &&
+ rlp64->rlim_max != RLIM_INFINITY)
+ rlp64->rlim_cur = rlp64->rlim_max;
+}
+
+static int
+lx_getrlimit_common(int lx_resource, uint64_t *rlim_curp, uint64_t *rlim_maxp)
+{
+ lx_proc_data_t *pd = ptolxproc(curproc);
+ int resource;
+ int64_t cur = -1;
+ boolean_t cur_inf = B_FALSE;
+ int64_t max = -1;
+ boolean_t max_inf = B_FALSE;
+ struct rlimit64 rlim64;
+
+ if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+ return (EINVAL);
+
+ switch (lx_resource) {
+ case LX_RLIMIT_LOCKS:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max;
+ break;
+
+ case LX_RLIMIT_NICE:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max;
+ break;
+
+ case LX_RLIMIT_RTPRIO:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max;
+ break;
+
+ case LX_RLIMIT_RTTIME:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max;
+ break;
+
+ case LX_RLIMIT_RSS:
+ /* zone.max-physical-memory */
+ rlim64.rlim_cur = rlim64.rlim_max = curzone->zone_phys_mem_ctl;
+ break;
+
+ case LX_RLIMIT_NPROC:
+ /* zone.max-lwps */
+ rlim64.rlim_cur = rlim64.rlim_max = curzone->zone_nlwps_ctl;
+ break;
+
+ case LX_RLIMIT_MEMLOCK:
+ /* zone.max-locked-memory */
+ rlim64.rlim_cur = rlim64.rlim_max =
+ curzone->zone_locked_mem_ctl;
+ break;
+
+ case LX_RLIMIT_SIGPENDING:
+ lx_get_rctl("process.max-sigqueue-size", &rlim64);
+ break;
+
+ case LX_RLIMIT_MSGQUEUE:
+ lx_get_rctl("process.max-msg-messages", &rlim64);
+ break;
+
+ default:
+ resource = l_to_r[lx_resource];
+
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_rlimit_get(rctlproc_legacy[resource], curproc,
+ &rlim64);
+ mutex_exit(&curproc->p_lock);
+ break;
+ }
+
+
+ if (rlim64.rlim_cur == RLIM64_INFINITY) {
+ cur = LX_RLIM_INFINITY_N;
+ } else {
+ cur = rlim64.rlim_cur;
+ }
+ if (rlim64.rlim_max == RLIM64_INFINITY) {
+ max = LX_RLIM_INFINITY_N;
+ } else {
+ max = rlim64.rlim_max;
+ }
+
+ if (lx_resource == LX_RLIMIT_STACK && cur > INT_MAX) {
+ /*
+ * Stunningly, Linux has somehow managed to confuse the concept
+ * of a "limit" with that of a "default" -- and the value of
+ * RLIMIT_STACK is used by NPTL as the _default_ stack size if
+ * it isn't specified. (!!) Even for a system that prides
+ * itself on slapdash castles of junk, this is an amazingly
+ * willful act of incompetence -- and one that is gleefully
+ * confessed in the pthread_create() man page: "if the
+ * RLIMIT_STACK soft resource limit at the time the program
+ * started has any value other than 'unlimited', then it
+ * determines the default stack size of new threads." A
+ * typical stack limit for us is 32TB; if it needs to be said,
+ * setting the default stack size to be 32TB doesn't work so
+ * well! Of course, glibc dropping a deuce in its pants
+ * becomes our problem -- so to prevent smelly accidents we
+ * tell Linux that any stack limit over the old (32-bit) values
+ * for infinity are just infinitely large.
+ */
+ cur_inf = B_TRUE;
+ max_inf = B_TRUE;
+ }
+
+ if (cur_inf) {
+ *rlim_curp = LX_RLIM64_INFINITY;
+ } else {
+ *rlim_curp = cur;
+ }
+
+ if (max_inf) {
+ *rlim_maxp = LX_RLIM64_INFINITY;
+ } else {
+ *rlim_maxp = max;
+ }
+
+ return (0);
+}
+
+/*
+ * This is the 'new' getrlimit, variously called getrlimit or ugetrlimit
+ * in Linux headers and code. The only difference between this and the old
+ * getrlimit (variously called getrlimit or old_getrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version. Modern code will
+ * use this version by default.
+ */
+long
+lx_getrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit_t rl;
+ uint64_t rlim_cur, rlim_max;
+
+ rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (rlim_cur == LX_RLIM64_INFINITY)
+ rl.rlim_cur = LX_RLIM_INFINITY_N;
+ else if (rlim_cur > LX_RLIM_INFINITY_N)
+ rl.rlim_cur = LX_RLIM_INFINITY_N;
+ else
+ rl.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max == LX_RLIM64_INFINITY)
+ rl.rlim_max = LX_RLIM_INFINITY_N;
+ else if (rlim_max > LX_RLIM_INFINITY_N)
+ rl.rlim_max = LX_RLIM_INFINITY_N;
+ else
+ rl.rlim_max = (ulong_t)rlim_max;
+
+ if (copyout(&rl, rlp, sizeof (rl)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ lx_rlimit32_t rl32;
+
+ if (rlim_cur > UINT_MAX)
+ rl.rlim_cur = UINT_MAX;
+ else
+ rl.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max > UINT_MAX)
+ rl.rlim_max = UINT_MAX;
+ else
+ rl.rlim_max = (ulong_t)rlim_max;
+
+ rl32.rlim_cur = rl.rlim_cur;
+ rl32.rlim_max = rl.rlim_max;
+
+ if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+ }
+#endif
+
+ return (0);
+}
+
+/*
+ * This is the 'old' getrlimit, variously called getrlimit or old_getrlimit
+ * in Linux headers and code. The only difference between this and the new
+ * getrlimit (variously called getrlimit or ugetrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version.
+ *
+ * This is only used for 32-bit code.
+ */
+long
+lx_oldgetrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit32_t rl32;
+ uint64_t rlim_cur, rlim_max;
+
+ rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ if (rlim_cur > INT_MAX)
+ rl32.rlim_cur = INT_MAX;
+ else
+ rl32.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max > INT_MAX)
+ rl32.rlim_max = INT_MAX;
+ else
+ rl32.rlim_max = (ulong_t)rlim_cur;
+
+ if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+static int
+lx_set_rctl(char *nm, struct rlimit64 *rlp64)
+{
+ int err;
+ rctl_hndl_t hndl;
+ rctl_alloc_gp_t *gp;
+
+ gp = rctl_rlimit_set_prealloc(1);
+
+ mutex_enter(&curproc->p_lock);
+
+ hndl = rctl_hndl_lookup(nm);
+
+ /*
+ * We're not supposed to do this but since we want all our rctls to
+ * behave like rlimits, we take advantage of this function to set up
+ * this way.
+ */
+ err = rctl_rlimit_set(hndl, curproc, rlp64, gp, RCTL_LOCAL_DENY, 0,
+ CRED());
+
+ mutex_exit(&curproc->p_lock);
+
+ rctl_prealloc_destroy(gp);
+
+ return (err);
+}
+
+static int
+lx_setrlimit_common(int lx_resource, uint64_t rlim_cur, uint64_t rlim_max)
+{
+ lx_proc_data_t *pd = ptolxproc(curproc);
+ int err;
+ int resource;
+ rctl_alloc_gp_t *gp;
+ struct rlimit64 rl64;
+
+ if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+ return (EINVAL);
+
+ switch (lx_resource) {
+ case LX_RLIMIT_LOCKS:
+ pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_NICE:
+ pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RTPRIO:
+ pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RTTIME:
+ pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RSS:
+ /*
+ * zone.max-physical-memory
+ * Since we're emulating the value via a zone rctl, we can't
+ * set that from within the zone. Lie and say we set the value.
+ */
+ break;
+
+ case LX_RLIMIT_NPROC:
+ /*
+ * zone.max-lwps
+ * Since we're emulating the value via a zone rctl, we can't
+ * set that from within the zone. Lie and say we set the value.
+ */
+ break;
+
+ case LX_RLIMIT_MEMLOCK:
+ /*
+ * zone.max-locked-memory
+ * Since we're emulating the value via a zone rctl, we can't
+ * set that from within the zone. Lie and say we set the value.
+ */
+ break;
+
+ case LX_RLIMIT_SIGPENDING:
+ /*
+ * On Ubuntu at least, the login and sshd processes expect to
+ * set this limit to 16k and login will fail if this fails. On
+ * Illumos we have a system limit of 8k and normally the
+ * privileged limit is 512. We simply pretend this works to
+ * allow login to work.
+ */
+ if (rlim_max > 8192)
+ return (0);
+
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ if ((err = lx_set_rctl("process.max-sigqueue-size", &rl64))
+ != 0)
+ return (set_errno(err));
+ break;
+
+ case LX_RLIMIT_MSGQUEUE:
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ if ((err = lx_set_rctl("process.max-msg-messages", &rl64)) != 0)
+ return (set_errno(err));
+ break;
+
+ default:
+ resource = l_to_r[lx_resource];
+
+ /*
+ * Linux limits the max number of open files to 1m and there is
+ * a test for this.
+ */
+ if (lx_resource == LX_RLIMIT_NOFILE && rlim_max > (1024 * 1024))
+ return (EPERM);
+
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ gp = rctl_rlimit_set_prealloc(1);
+
+ mutex_enter(&curproc->p_lock);
+ err = rctl_rlimit_set(rctlproc_legacy[resource], curproc,
+ &rl64, gp, rctlproc_flags[resource],
+ rctlproc_signals[resource], CRED());
+ mutex_exit(&curproc->p_lock);
+
+ rctl_prealloc_destroy(gp);
+ if (err != 0)
+ return (set_errno(err));
+ break;
+ }
+
+ return (0);
+}
+
+long
+lx_setrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit_t rl;
+ uint64_t rlim_cur, rlim_max;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(rlp, &rl, sizeof (rl)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ lx_rlimit32_t rl32;
+
+ if (copyin(rlp, &rl32, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+
+ rl.rlim_cur = rl32.rlim_cur;
+ rl.rlim_max = rl32.rlim_max;
+ }
+#endif
+
+ if ((rl.rlim_max != LX_RLIM_INFINITY_N &&
+ rl.rlim_cur == LX_RLIM_INFINITY_N) ||
+ rl.rlim_cur > rl.rlim_max)
+ return (set_errno(EINVAL));
+
+ if (rl.rlim_cur == LX_RLIM_INFINITY_N)
+ rlim_cur = LX_RLIM64_INFINITY;
+ else
+ rlim_cur = rl.rlim_cur;
+
+ if (rl.rlim_max == LX_RLIM_INFINITY_N)
+ rlim_max = LX_RLIM64_INFINITY;
+ else
+ rlim_max = rl.rlim_max;
+
+ rv = lx_setrlimit_common(resource, rlim_cur, rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ return (0);
+}
+
+/*
+ * From the man page:
+ * The Linux-specific prlimit() system call combines and extends the
+ * functionality of setrlimit() and getrlimit(). It can be used to both set
+ * and get the resource limits of an arbitrary process.
+ *
+ * If pid is 0, then the call applies to the calling process.
+ */
+long
+lx_prlimit64(pid_t pid, int resource, lx_rlimit64_t *nrlp, lx_rlimit64_t *orlp)
+{
+ int rv;
+ lx_rlimit64_t nrl, orl;
+
+ if (pid != 0) {
+ /* XXX TBD if needed */
+ char buf[80];
+
+ (void) snprintf(buf, sizeof (buf),
+ "setting prlimit %d for another process\n", resource);
+ lx_unsupported(buf);
+ return (ENOTSUP);
+ }
+
+ if (orlp != NULL) {
+ /* we first get the current limits */
+ rv = lx_getrlimit_common(resource, &orl.rlim_cur,
+ &orl.rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ if (nrlp != NULL) {
+ if (copyin(nrlp, &nrl, sizeof (nrl)) != 0)
+ return (set_errno(EFAULT));
+
+ if ((nrl.rlim_max != LX_RLIM64_INFINITY &&
+ nrl.rlim_cur == LX_RLIM64_INFINITY) ||
+ nrl.rlim_cur > nrl.rlim_max)
+ return (set_errno(EINVAL));
+
+ rv = lx_setrlimit_common(resource, nrl.rlim_cur, nrl.rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ if (orlp != NULL) {
+ /* now return the original limits, if necessary */
+ if (copyout(&orl, orlp, sizeof (orl)) != 0)
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rw.c b/usr/src/uts/common/brand/lx/syscall/lx_rw.c
new file mode 100644
index 0000000000..50d532ff51
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rw.c
@@ -0,0 +1,949 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/nbmlock.h>
+#include <sys/limits.h>
+
+/* uts/common/syscall/rw.c */
+extern size_t copyout_max_cached;
+
+
+/* Common routines */
+
+static int
+lx_iovec_copyin(void *uiovp, int iovcnt, iovec_t *kiovp, ssize_t *count)
+{
+#ifdef _SYSCALL32_IMPL
+ /*
+ * 32-bit callers need to have their iovec expanded, while ensuring
+ * that they can't move more than 2Gbytes of data in a single call.
+ */
+ if (get_udatamodel() == DATAMODEL_ILP32) {
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len = 0;
+ ssize32_t total32 = 0;
+ int i;
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+ }
+
+ if (copyin(uiovp, aiov32, iovcnt * sizeof (iovec32_t))) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EFAULT);
+ }
+
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32 = aiov32[i].iov_len;
+ total32 += iovlen32;
+ if (iovlen32 < 0 || total32 < 0) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EINVAL);
+ }
+ kiovp[i].iov_len = iovlen32;
+ kiovp[i].iov_base =
+ (caddr_t)(uintptr_t)aiov32[i].iov_base;
+ /* Linux does a basic sanity test on the address */
+ if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT32) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EFAULT);
+ }
+ }
+ *count = total32;
+
+ if (aiov32len != 0)
+ kmem_free(aiov32, aiov32len);
+ } else
+#endif
+ {
+ ssize_t total = 0;
+ int i;
+
+ if (copyin(uiovp, kiovp, iovcnt * sizeof (iovec_t)))
+ return (EFAULT);
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = kiovp[i].iov_len;
+ total += iovlen;
+ if (iovlen < 0 || total < 0) {
+ return (EINVAL);
+ }
+ /* Linux does a basic sanity test on the address */
+ if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT) {
+ return (EFAULT);
+ }
+ }
+ *count = total;
+ }
+ return (0);
+}
+
+static int
+lx_read_common(file_t *fp, uio_t *uiop, size_t *nread, boolean_t positioned)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error = 0, rwflag = 0, ioflag;
+ ssize_t count = uiop->uio_resid;
+ size_t rcount = 0;
+ struct cpu *cp;
+ boolean_t in_crit = B_FALSE;
+
+ /*
+ * We have to enter the critical region before calling VOP_RWLOCK
+ * to avoid a deadlock with ufs.
+ */
+ if (nbl_need_check(vp)) {
+ int svmand;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ error = nbl_svmand(vp, fp->f_cred, &svmand);
+ if (error != 0)
+ goto out;
+ if (nbl_conflict(vp, NBL_READ, uiop->uio_offset, count, svmand,
+ NULL) != 0) {
+ error = EACCES;
+ goto out;
+ }
+ }
+
+ (void) VOP_RWLOCK(vp, rwflag, NULL);
+ /*
+ * For non-positioned reads, recheck offset/count validity inside
+ * VOP_WRLOCK to prevent filesize from changing during validation.
+ */
+ if (!positioned) {
+ u_offset_t uoffset = (u_offset_t)(ulong_t)fp->f_offset;
+
+ if ((vp->v_type == VREG) && (uoffset >= OFFSET_MAX(fp))) {
+ struct vattr va;
+
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL);
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ if (error != 0)
+ goto out;
+ /* We have to return EOF if fileoff is >= file size. */
+ if (uoffset >= va.va_size)
+ goto out;
+ /*
+ * File is greater than or equal to maxoff and
+ * therefore we return EOVERFLOW.
+ */
+ error = EOVERFLOW;
+ goto out;
+ }
+ if ((vp->v_type == VREG) &&
+ (uoffset + count > OFFSET_MAX(fp))) {
+ count = (ssize_t)(OFFSET_MAX(fp) - uoffset);
+ uiop->uio_resid = count;
+ }
+ uiop->uio_offset = uoffset;
+ }
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ /* If read sync is not asked for, filter sync flags */
+ if ((ioflag & FRSYNC) == 0)
+ ioflag &= ~(FSYNC|FDSYNC);
+ error = VOP_READ(vp, uiop, ioflag, fp->f_cred, NULL);
+ rcount = count - uiop->uio_resid;
+ CPU_STATS_ENTER_K();
+ cp = CPU;
+ CPU_STATS_ADDQ(cp, sys, sysread, 1);
+ CPU_STATS_ADDQ(cp, sys, readch, (ulong_t)rcount);
+ CPU_STATS_EXIT_K();
+ ttolwp(curthread)->lwp_ru.ioch += (ulong_t)rcount;
+ /* Store offset for non-positioned reads */
+ if (!positioned) {
+ if (vp->v_type == VFIFO) {
+ /* Backward compatibility */
+ fp->f_offset = rcount;
+ } else if (((fp->f_flag & FAPPEND) == 0) ||
+ (vp->v_type != VREG) || (count != 0)) {
+ /* POSIX */
+ fp->f_offset = uiop->uio_loffset;
+ }
+ }
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+ if (in_crit)
+ nbl_end_crit(vp);
+ *nread = rcount;
+ return (error);
+}
+
+static int
+lx_write_common(file_t *fp, uio_t *uiop, size_t *nwrite, boolean_t positioned)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error = 0, rwflag = 1, ioflag;
+ ssize_t count = uiop->uio_resid;
+ size_t wcount = 0;
+ struct cpu *cp;
+ boolean_t in_crit = B_FALSE;
+
+ /*
+ * We have to enter the critical region before calling VOP_RWLOCK
+ * to avoid a deadlock with ufs.
+ */
+ if (nbl_need_check(vp)) {
+ int svmand;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ error = nbl_svmand(vp, fp->f_cred, &svmand);
+ if (error != 0)
+ goto out;
+ if (nbl_conflict(vp, NBL_WRITE, uiop->uio_loffset, count,
+ svmand, NULL) != 0) {
+ error = EACCES;
+ goto out;
+ }
+ }
+
+ (void) VOP_RWLOCK(vp, rwflag, NULL);
+
+ if (!positioned) {
+ /*
+ * For non-positioned writes, the value of fp->f_offset is
+ * re-queried while inside VOP_RWLOCK. This ensures that other
+ * writes which alter the filesize will be taken into account.
+ */
+ uiop->uio_loffset = fp->f_offset;
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ } else {
+ /*
+ * In a senseless departure from POSIX, positioned write calls
+ * on Linux do _not_ ignore the O_APPEND flag.
+ */
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ }
+ if (vp->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)(ulong_t)uiop->uio_loffset;
+
+ if (fileoff >= curproc->p_fsz_ctl) {
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff >= OFFSET_MAX(fp)) {
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff + count > OFFSET_MAX(fp)) {
+ count = (ssize_t)(OFFSET_MAX(fp) - fileoff);
+ uiop->uio_resid = count;
+ }
+ }
+
+ error = VOP_WRITE(vp, uiop, ioflag, fp->f_cred, NULL);
+ wcount = count - uiop->uio_resid;
+ CPU_STATS_ENTER_K();
+ cp = CPU;
+ CPU_STATS_ADDQ(cp, sys, syswrite, 1);
+ CPU_STATS_ADDQ(cp, sys, writech, (ulong_t)wcount);
+ CPU_STATS_EXIT_K();
+ ttolwp(curthread)->lwp_ru.ioch += (ulong_t)wcount;
+
+ /* Store offset for non-positioned writes */
+ if (!positioned) {
+ if (vp->v_type == VFIFO) {
+ /* Backward compatibility */
+ fp->f_offset = wcount;
+ } else if (((fp->f_flag & FAPPEND) == 0) ||
+ (vp->v_type != VREG) || (count != 0)) {
+ /* POSIX */
+ fp->f_offset = uiop->uio_loffset;
+ }
+ }
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+ if (in_crit)
+ nbl_end_crit(vp);
+ *nwrite = wcount;
+ return (error);
+}
+
+/*
+ * The Linux routines for reading and writing data from file descriptors behave
+ * differently from their SunOS counterparts in a few key ways:
+ *
+ * - Passing an iovcnt of 0 to the vectored functions results in an error on
+ * SunOS, but on Linux it yields return value of 0.
+ *
+ * - If any data is successfully read or written, Linux will return a success.
+ * This is unlike SunOS which would return an error code for the entire
+ * operation in cases where vectors had gone unprocessed.
+ *
+ * - Breaking from POSIX, Linux positioned writes (pwrite/pwritev) on Linux
+ * will obey the O_APPEND flag if it is set on the descriptor.
+ */
+
+ssize_t
+lx_read(int fdes, void *cbuf, size_t ccount)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+ if (error == EINTR) {
+ if (nread != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error != 0)
+ return (set_errno(error));
+ return ((ssize_t)nread);
+}
+
+ssize_t
+lx_write(int fdes, void *cbuf, size_t ccount)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+ if (error == EINTR) {
+ if (nwrite != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error != 0)
+ return (set_errno(error));
+ return (nwrite);
+}
+
+ssize_t
+lx_readv(int fdes, struct iovec *iovp, int iovcnt)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+ if (error != 0) {
+ if (nread != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nread);
+}
+
+ssize_t
+lx_writev(int fdes, struct iovec *iovp, int iovcnt)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+ if (error != 0) {
+ if (nwrite != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_pread(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write
+ * a file beyond the maximum offset representable in
+ * an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+ } else if (fp->f_vnode->v_type == VFIFO) {
+ error = ESPIPE;
+ goto out;
+ } else if (fp->f_vnode->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+ if (error == EINTR) {
+ if (nread != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error) {
+ return (set_errno(error));
+ }
+ return ((ssize_t)nread);
+
+}
+
+ssize_t
+lx_pwrite(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & (FWRITE)) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * return EINVAL for offsets that cannot be
+ * represented in an off_t.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Take appropriate action if we are trying to write above the
+ * resource limit.
+ */
+ if (fileoff >= curproc->p_fsz_ctl) {
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Don't allow pwrite to cause file sizes to exceed maxoffset.
+ */
+ if (fileoff == MAXOFFSET_T) {
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+ } else if (fp->f_vnode->v_type == VFIFO) {
+ error = ESPIPE;
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+ if (error == EINTR) {
+ if (nwrite != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_pread32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pread(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwrite32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pwrite(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_preadv(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write a file beyond
+ * the maximum offset representable in an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+ } else if (fp->f_vnode->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (fp->f_vnode->v_type == VFIFO) {
+ error = ESPIPE;
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+ if (error != 0) {
+ if (nread != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nread);
+}
+
+ssize_t
+lx_pwritev(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Take appropriate action if we are trying to write above the
+ * resource limit.
+ */
+ if (fileoff >= curproc->p_fsz_ctl) {
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Don't allow pwritev to cause file sizes to exceed maxoffset.
+ */
+ if (fileoff == MAXOFFSET_T) {
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write a file beyond
+ * the maximum offset representable in an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+ } else if (fp->f_vnode->v_type == VFIFO) {
+ error = ESPIPE;
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+ if (error != 0) {
+ if (nwrite != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_preadv32(int fdes, void *iovp, int iovcnt, uint32_t off_lo, uint32_t off_hi)
+{
+ return (lx_preadv(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwritev32(int fdes, void *iovp, int iovcnt, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pwritev(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sched.c b/usr/src/uts/common/brand/lx/syscall/lx_sched.c
new file mode 100644
index 0000000000..0def559e29
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sched.c
@@ -0,0 +1,524 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/cpu.h>
+#include <sys/rtpriocntl.h>
+#include <sys/tspriocntl.h>
+#include <sys/processor.h>
+#include <sys/brand.h>
+#include <sys/lx_sched.h>
+#include <sys/lx_brand.h>
+
+extern int yield();
+extern long priocntl_common(int, procset_t *, int, caddr_t, caddr_t, uio_seg_t);
+
+long
+lx_sched_yield(void)
+{
+ yield();
+
+ return (0);
+}
+
+int
+lx_sched_affinity(int cmd, uintptr_t pid, int len, uintptr_t maskp,
+ int64_t *rval)
+{
+ pid_t s_pid;
+ id_t s_tid;
+ kthread_t *t = curthread;
+ lx_lwp_data_t *lx_lwp;
+
+ if (cmd != B_GET_AFFINITY_MASK && cmd != B_SET_AFFINITY_MASK)
+ return (set_errno(EINVAL));
+
+ /*
+ * The caller wants to know how large the mask should be.
+ */
+ if (cmd == B_GET_AFFINITY_MASK && len == 0) {
+ *rval = sizeof (lx_affmask_t);
+ return (0);
+ }
+
+ /*
+ * Otherwise, ensure they have a large enough mask.
+ */
+ if (cmd == B_GET_AFFINITY_MASK && len < sizeof (lx_affmask_t)) {
+ *rval = -1;
+ return (set_errno(EINVAL));
+ }
+
+ if (pid == 0) {
+ s_pid = curproc->p_pid;
+ s_tid = curthread->t_tid;
+ } else if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) == -1) {
+ return (set_errno(ESRCH));
+ }
+
+ /*
+ * For now, we only support manipulating threads in the
+ * same process.
+ */
+ if (curproc->p_pid != s_pid)
+ return (set_errno(EPERM));
+
+ /*
+ * We must hold the process lock so that the thread list
+ * doesn't change while we're looking at it. We'll hold
+ * the lock until we no longer reference the
+ * corresponding lwp.
+ */
+
+ mutex_enter(&curproc->p_lock);
+
+ do {
+ if (t->t_tid == s_tid)
+ break;
+ t = t->t_forw;
+ } while (t != curthread);
+
+ /*
+ * If the given PID is in the current thread's process,
+ * then we _must_ find it in the process's thread list.
+ */
+ ASSERT(t->t_tid == s_tid);
+
+ lx_lwp = t->t_lwp->lwp_brand;
+
+ if (cmd == B_SET_AFFINITY_MASK) {
+ if (copyin_nowatch((void *)maskp, &lx_lwp->br_affinitymask,
+ sizeof (lx_affmask_t)) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (set_errno(EFAULT));
+ }
+
+ *rval = 0;
+ } else {
+ if (copyout_nowatch(&lx_lwp->br_affinitymask, (void *)maskp,
+ sizeof (lx_affmask_t)) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (set_errno(EFAULT));
+ }
+
+ *rval = sizeof (lx_affmask_t);
+ }
+
+ mutex_exit(&curproc->p_lock);
+ return (0);
+}
+
+long
+lx_sched_setscheduler(l_pid_t pid, int policy, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ procset_t procset_cid;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ struct lx_sched_param sched_param;
+ tsparms_t *tsp;
+ int prio, maxupri;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if ((rv = sched_setprocset(&procset, pid)))
+ return (rv);
+
+ if (copyin(param, &sched_param, sizeof (sched_param)))
+ return (set_errno(EFAULT));
+
+ prio = sched_param.lx_sched_prio;
+
+ if (policy < 0) {
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the current policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+ policy = LX_SCHED_OTHER;
+ } else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ } else {
+ return (set_errno(EINVAL));
+ }
+ }
+
+ bzero(&pcinfo, sizeof (pcinfo));
+ bzero(&pcparm, sizeof (pcparm));
+ setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (prio < 0 ||
+ prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+ return (set_errno(EINVAL));
+ pcparm.pc_cid = pcinfo.pc_cid;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+ policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+ break;
+
+ case LX_SCHED_OTHER:
+ (void) strcpy(pcinfo.pc_clname, "TS");
+ (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+ if (prio > maxupri || prio < -maxupri)
+ return (set_errno(EINVAL));
+
+ pcparm.pc_cid = pcinfo.pc_cid;
+ tsp = (tsparms_t *)pcparm.pc_clparms;
+ tsp->ts_upri = prio;
+ tsp->ts_uprilim = TS_NOCHANGE;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * finally set scheduling policy and parameters
+ */
+ (void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+ return (0);
+}
+
+long
+lx_sched_getscheduler(l_pid_t pid)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ int policy;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if ((rv = sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (strcmp(pcinfo.pc_clname, "TS") == 0)
+ policy = LX_SCHED_OTHER;
+ else if (strcmp(pcinfo.pc_clname, "RT") == 0)
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ else
+ policy = set_errno(EINVAL);
+
+ return (policy);
+}
+
+long
+lx_sched_setparam(l_pid_t pid, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ procset_t procset_cid;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ struct lx_sched_param sched_param;
+ tsparms_t *tsp;
+ int policy;
+ int prio, maxupri;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if ((rv = sched_setprocset(&procset, pid)))
+ return (rv);
+
+ if (copyin(param, &sched_param, sizeof (sched_param)))
+ return (set_errno(EFAULT));
+
+ prio = sched_param.lx_sched_prio;
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the current policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (strcmp(pcinfo.pc_clname, "TS") == 0)
+ policy = LX_SCHED_OTHER;
+ else if (strcmp(pcinfo.pc_clname, "RT") == 0)
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ else
+ return (set_errno(EINVAL));
+
+ bzero(&pcinfo, sizeof (pcinfo));
+ bzero(&pcparm, sizeof (pcparm));
+ setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (prio < 0 ||
+ prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+ return (set_errno(EINVAL));
+ pcparm.pc_cid = pcinfo.pc_cid;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+ policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+ break;
+
+ case LX_SCHED_OTHER:
+ (void) strcpy(pcinfo.pc_clname, "TS");
+ (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+ if (prio > maxupri || prio < -maxupri)
+ return (set_errno(EINVAL));
+
+ pcparm.pc_cid = pcinfo.pc_cid;
+ tsp = (tsparms_t *)pcparm.pc_clparms;
+ tsp->ts_upri = prio;
+ tsp->ts_uprilim = TS_NOCHANGE;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * finally set scheduling policy and parameters
+ */
+ (void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+ return (0);
+}
+
+long
+lx_sched_getparam(l_pid_t pid, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_sched_param local_param;
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ tsinfo_t *tsi;
+ int prio, scale;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if ((rv = sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ bzero(&local_param, sizeof (local_param));
+ if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+ /*
+ * I don't know if we need to do this, coz it can't be
+ * changed from zero anyway.....
+ */
+ tsi = (tsinfo_t *)pcinfo.pc_clinfo;
+ prio = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
+ scale = tsi->ts_maxupri;
+ if (scale == 0)
+ local_param.lx_sched_prio = 0;
+ else
+ local_param.lx_sched_prio = -(prio * 20) / scale;
+ } else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+ local_param.lx_sched_prio =
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
+ } else {
+ rv = set_errno(EINVAL);
+ }
+
+ if (rv == 0)
+ if (copyout(&local_param, param, sizeof (local_param)))
+ return (set_errno(EFAULT));
+
+ return (rv);
+}
+
+long
+lx_sched_rr_get_interval(l_pid_t pid, struct timespec *ival)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct timespec interval;
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if ((rv = sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ setprocset(&procset, POP_AND, P_PID, 0, P_ALL, 0);
+ bzero(&pcinfo, sizeof (pcinfo));
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) do_priocntlsys(PC_GETCID, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (pcparm.pc_cid == pcinfo.pc_cid &&
+ ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF) {
+ interval.tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs;
+ interval.tv_nsec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs;
+
+ if (copyout(&interval, ival, sizeof (interval)))
+ return (set_errno(EFAULT));
+
+ return (0);
+ }
+
+ return (set_errno(EINVAL));
+}
+
+int
+sched_setprocset(procset_t *procset, l_pid_t pid)
+{
+ id_t lid, rid;
+ idtype_t lidtype, ridtype;
+
+ /*
+ * define the target lwp
+ */
+ if (pid == 0) {
+ ridtype = P_ALL;
+ lidtype = P_PID;
+ rid = 0;
+ lid = P_MYID;
+ } else {
+ if (lx_lpid_to_spair(pid, &pid, &lid) < 0)
+ return (set_errno(ESRCH));
+ if (pid != curproc->p_pid)
+ return (set_errno(ESRCH));
+ rid = 0;
+ ridtype = P_ALL;
+ lidtype = P_LWPID;
+ }
+ setprocset(procset, POP_AND, lidtype, lid, ridtype, rid);
+
+ return (0);
+}
+
+long
+do_priocntlsys(int cmd, procset_t *procset, void *arg)
+{
+ return (priocntl_common(PC_VERSION, procset, cmd, (caddr_t)arg, 0,
+ UIO_SYSSPACE));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
new file mode 100644
index 0000000000..8e774924b5
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
@@ -0,0 +1,3966 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/sockio.h>
+#include <sys/thread.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kmem.h>
+#include <sys/un.h>
+#include <sys/sunddi.h>
+#include <sys/cred.h>
+#include <sys/ucred.h>
+#include <sys/model.h>
+#include <sys/brand.h>
+#include <sys/vmsystm.h>
+#include <sys/limits.h>
+#include <sys/fcntl.h>
+#include <sys/sysmacros.h>
+#include <netpacket/packet.h>
+#include <sockcommon.h>
+#include <socktpi_impl.h>
+#include <netinet/udp.h>
+#include <sys/sdt.h>
+#include <netinet/tcp.h>
+#include <netinet/igmp.h>
+#include <netinet/icmp6.h>
+#include <lx_errno.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_socket.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+
+/* From uts/common/fs/sockfs/socksyscalls.c */
+extern int listen(int, int, int);
+extern int shutdown(int, int, int);
+
+typedef struct lx_ucred {
+ pid_t lxu_pid;
+ lx_uid_t lxu_uid;
+ lx_gid_t lxu_gid;
+} lx_ucred_t;
+
+typedef struct lx_socket_aux_data
+{
+ kmutex_t lxsad_lock;
+ enum lxsad_status_t {
+ LXSS_NONE = 0,
+ LXSS_CONNECTING,
+ LXSS_CONNECTED
+ } lxsad_status;
+ boolean_t lxsad_stream_cred;
+} lx_socket_aux_data_t;
+
+static lx_socket_aux_data_t *lx_sad_acquire(vnode_t *);
+
+/* VSD key for lx-specific socket information */
+static uint_t lx_socket_vsd = 0;
+
+/* Convenience enum to enforce translation direction */
+typedef enum lx_xlate_dir {
+ SUNOS_TO_LX,
+ LX_TO_SUNOS
+} lx_xlate_dir_t;
+
+/* enum for getpeername/getsockname handling */
+typedef enum lx_getname_type {
+ LX_GETPEERNAME,
+ LX_GETSOCKNAME
+} lx_getname_type_t;
+
+/*
+ * What follows are a series of tables we use to translate Linux constants
+ * into equivalent Illumos constants and back again. I wish this were
+ * cleaner, more programmatic, and generally nicer. Sadly, life is messy,
+ * and Unix networking even more so.
+ */
+static const int ltos_family[LX_AF_MAX + 1] = {
+ AF_UNSPEC, /* LX_AF_UNSPEC */
+ AF_UNIX, /* LX_AF_UNIX */
+ AF_INET, /* LX_AF_INET */
+ AF_NOTSUPPORTED, /* LX_AF_AX25 */
+ AF_NOTSUPPORTED, /* LX_AF_IPX */
+ AF_NOTSUPPORTED, /* LX_AF_APPLETALK */
+ AF_NOTSUPPORTED, /* LX_AF_NETROM */
+ AF_NOTSUPPORTED, /* LX_AF_BRIDGE */
+ AF_NOTSUPPORTED, /* LX_AF_ATMPVC */
+ AF_NOTSUPPORTED, /* LX_AF_X25 */
+ AF_INET6, /* LX_AF_INET6 */
+ AF_NOTSUPPORTED, /* LX_AF_ROSE */
+ AF_NOTSUPPORTED, /* LX_AF_DECNET */
+ AF_NOTSUPPORTED, /* LX_AF_NETBEUI */
+ AF_NOTSUPPORTED, /* LX_AF_SECURITY */
+ AF_NOTSUPPORTED, /* LX_AF_KEY */
+ AF_LX_NETLINK, /* LX_AF_NETLINK */
+ AF_PACKET, /* LX_AF_PACKET */
+ AF_NOTSUPPORTED, /* LX_AF_ASH */
+ AF_NOTSUPPORTED, /* LX_AF_ECONET */
+ AF_NOTSUPPORTED, /* LX_AF_ATMSVC */
+ AF_NOTSUPPORTED, /* LX_AF_RDS */
+ AF_NOTSUPPORTED, /* LX_AF_SNA */
+ AF_NOTSUPPORTED, /* LX_AF_IRDA */
+ AF_NOTSUPPORTED, /* LX_AF_PPOX */
+ AF_NOTSUPPORTED, /* LX_AF_WANPIPE */
+ AF_NOTSUPPORTED, /* LX_AF_LLC */
+ AF_NOTSUPPORTED, /* NONE */
+ AF_NOTSUPPORTED, /* NONE */
+ AF_NOTSUPPORTED, /* LX_AF_CAN */
+ AF_NOTSUPPORTED, /* LX_AF_TIPC */
+ AF_NOTSUPPORTED, /* LX_AF_BLUETOOTH */
+ AF_NOTSUPPORTED, /* LX_AF_IUCV */
+ AF_NOTSUPPORTED /* LX_AF_RXRPC */
+ /* LX_AF_ISDN */
+ /* LX_AF_PHONET */
+ /* LX_AF_IEEE802154 */
+ /* LX_AF_CAIF */
+ /* LX_AF_ALG */
+ /* LX_AF_NFC */
+ /* LX_AF_VSOCK */
+};
+
+static const int stol_family[LX_AF_MAX + 1] = {
+ AF_UNSPEC, /* AF_UNSPEC */
+ AF_UNIX, /* AF_UNIX */
+ AF_INET, /* AF_INET */
+ AF_NOTSUPPORTED, /* AF_IMPLINK */
+ AF_NOTSUPPORTED, /* AF_PUP */
+ AF_NOTSUPPORTED, /* AF_CHAOS */
+ AF_NOTSUPPORTED, /* AF_NS */
+ AF_NOTSUPPORTED, /* AF_NBS */
+ AF_NOTSUPPORTED, /* AF_ECMA */
+ AF_NOTSUPPORTED, /* AF_DATAKIT */
+ AF_NOTSUPPORTED, /* AF_CCITT */
+ AF_NOTSUPPORTED, /* AF_SNA */
+ AF_NOTSUPPORTED, /* AF_DECNET */
+ AF_NOTSUPPORTED, /* AF_DLI */
+ AF_NOTSUPPORTED, /* AF_LAT */
+ AF_NOTSUPPORTED, /* AF_HYLINK */
+ AF_NOTSUPPORTED, /* AF_APPLETALK */
+ AF_NOTSUPPORTED, /* AF_NIT */
+ AF_NOTSUPPORTED, /* AF_802 */
+ AF_NOTSUPPORTED, /* AF_OSI */
+ AF_NOTSUPPORTED, /* AF_X25 */
+ AF_NOTSUPPORTED, /* AF_OSINET */
+ AF_NOTSUPPORTED, /* AF_GOSIP */
+ AF_NOTSUPPORTED, /* AF_IPX */
+ AF_NOTSUPPORTED, /* AF_ROUTE */
+ AF_NOTSUPPORTED, /* AF_LINK */
+ LX_AF_INET6, /* AF_INET6 */
+ AF_NOTSUPPORTED, /* AF_KEY */
+ AF_NOTSUPPORTED, /* AF_NCA */
+ AF_NOTSUPPORTED, /* AF_POLICY */
+ AF_NOTSUPPORTED, /* AF_INET_OFFLOAD */
+ AF_NOTSUPPORTED, /* AF_TRILL */
+ LX_AF_PACKET, /* AF_PACKET */
+ LX_AF_NETLINK /* AF_LX_NETLINK */
+};
+
+#define LTOS_FAMILY(d) ((d) <= LX_AF_MAX ? ltos_family[(d)] : AF_INVAL)
+#define STOL_FAMILY(d) ((d) <= LX_AF_MAX ? stol_family[(d)] : AF_INVAL)
+
+
+static const int ltos_socktype[LX_SOCK_PACKET + 1] = {
+ SOCK_NOTSUPPORTED, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
+ SOCK_RDM, SOCK_SEQPACKET, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED,
+ SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED
+};
+
+static const int stol_socktype[SOCK_SEQPACKET + 1] = {
+ SOCK_NOTSUPPORTED, LX_SOCK_DGRAM, LX_SOCK_STREAM, SOCK_NOTSUPPORTED,
+ LX_SOCK_RAW, LX_SOCK_RDM, LX_SOCK_SEQPACKET
+};
+
+#define LTOS_SOCKTYPE(t) \
+ ((t) <= LX_SOCK_PACKET ? ltos_socktype[(t)] : SOCK_INVAL)
+#define STOL_SOCKTYPE(t) \
+ ((t) <= SOCK_SEQPACKET ? stol_socktype[(t)] : SOCK_INVAL)
+
+
+/*
+ * This string is used to prefix all abstract namespace Unix sockets, ie all
+ * abstract namespace sockets are converted to regular sockets in the /tmp
+ * directory with .ABSK_ prefixed to their names.
+ */
+#define ABST_PRFX "/tmp/.ABSK_"
+#define ABST_PRFX_LEN (sizeof (ABST_PRFX) - 1)
+
+#define DATAFILT "datafilt"
+
+typedef enum {
+ lxa_none,
+ lxa_abstract,
+ lxa_devlog
+} lx_addr_type_t;
+
+static int
+ltos_pkt_proto(int protocol)
+{
+ switch (ntohs(protocol)) {
+ case LX_ETH_P_802_2:
+ return (ETH_P_802_2);
+ case LX_ETH_P_IP:
+ return (ETH_P_IP);
+ case LX_ETH_P_ARP:
+ return (ETH_P_ARP);
+ case LX_ETH_P_IPV6:
+ return (ETH_P_IPV6);
+ case LX_ETH_P_ALL:
+ case LX_ETH_P_802_3:
+ return (ETH_P_ALL);
+ default:
+ return (-1);
+ }
+}
+
+
+typedef struct lx_flag_map {
+ enum {
+ LXFM_MAP,
+ LXFM_IGNORE,
+ LXFM_UNSUP
+ } lxfm_action;
+ int lxfm_sunos_flag;
+ int lxfm_linux_flag;
+ char *lxfm_name;
+} lx_flag_map_t;
+
+static lx_flag_map_t lx_flag_map_tbl[] = {
+ { LXFM_MAP, MSG_OOB, LX_MSG_OOB, NULL },
+ { LXFM_MAP, MSG_PEEK, LX_MSG_PEEK, NULL },
+ { LXFM_MAP, MSG_DONTROUTE, LX_MSG_DONTROUTE, NULL },
+ { LXFM_MAP, MSG_CTRUNC, LX_MSG_CTRUNC, NULL },
+ { LXFM_MAP, MSG_TRUNC, LX_MSG_TRUNC, NULL },
+ { LXFM_MAP, MSG_DONTWAIT, LX_MSG_DONTWAIT, NULL },
+ { LXFM_MAP, MSG_EOR, LX_MSG_EOR, NULL },
+ { LXFM_MAP, MSG_WAITALL, LX_MSG_WAITALL, NULL },
+ /* MSG_CONFIRM is safe to ignore */
+ { LXFM_IGNORE, 0, LX_MSG_CONFIRM, NULL },
+ /*
+ * The NOSIGNAL and CMSG_CLOEXEC flags are handled by the emulation
+ * outside of the flag-conversion routine.
+ */
+ { LXFM_IGNORE, 0, LX_MSG_NOSIGNAL, NULL },
+ { LXFM_IGNORE, 0, LX_MSG_CMSG_CLOEXEC, NULL },
+ { LXFM_UNSUP, LX_MSG_PROXY, 0, "MSG_PROXY" },
+ { LXFM_UNSUP, LX_MSG_FIN, 0, "MSG_FIN" },
+ { LXFM_UNSUP, LX_MSG_SYN, 0, "MSG_SYN" },
+ { LXFM_UNSUP, LX_MSG_RST, 0, "MSG_RST" },
+ { LXFM_UNSUP, LX_MSG_ERRQUEUE, 0, "MSG_ERRQUEUE" },
+ { LXFM_UNSUP, LX_MSG_MORE, 0, "MSG_MORE" },
+ { LXFM_UNSUP, LX_MSG_WAITFORONE, 0, "MSG_WAITFORONE" },
+ { LXFM_UNSUP, LX_MSG_FASTOPEN, 0, "MSG_FASTOPEN" },
+};
+
+#define LX_FLAG_MAP_MAX \
+ (sizeof (lx_flag_map_tbl) / sizeof (lx_flag_map_tbl[0]))
+
+#define LX_UNSUP_BUFSZ 64
+
+static int
+lx_xlate_sock_flags(int inflags, lx_xlate_dir_t dir)
+{
+ int i, outflags = 0;
+ char buf[LX_UNSUP_BUFSZ];
+
+ VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+ for (i = 0; i < LX_FLAG_MAP_MAX; i++) {
+ lx_flag_map_t *map = &lx_flag_map_tbl[i];
+ int match, out;
+
+ if (dir == SUNOS_TO_LX) {
+ match = inflags & map->lxfm_sunos_flag;
+ out = map->lxfm_linux_flag;
+ } else {
+ match = inflags & map->lxfm_linux_flag;
+ out = map->lxfm_sunos_flag;
+ }
+ switch (map->lxfm_action) {
+ case LXFM_MAP:
+ if (match != 0) {
+ inflags &= ~(match);
+ outflags |= out;
+ }
+ break;
+ case LXFM_IGNORE:
+ if (match != 0) {
+ inflags &= ~(match);
+ }
+ break;
+ case LXFM_UNSUP:
+ if (match != 0) {
+ (void) snprintf(buf, LX_UNSUP_BUFSZ,
+ "unsupported sock flag %s", map->lxfm_name);
+ lx_unsupported(buf);
+ }
+ }
+ }
+ if (inflags != 0) {
+ (void) snprintf(buf, LX_UNSUP_BUFSZ,
+ "unsupported sock flags 0x%08x", inflags);
+ lx_unsupported(buf);
+ }
+
+ return (outflags);
+}
+
+typedef enum lx_sun_type {
+ LX_SUN_NORMAL,
+ LX_SUN_ABSTRACT,
+} lx_sun_type_t;
+
+static void
+ltos_sockaddr_ux(const struct sockaddr *inaddr, const socklen_t inlen,
+ struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+ struct sockaddr_un buf;
+ /* Calculate size of (sun_family + any padding) in sockaddr */
+ int sizediff = (sizeof (buf) - sizeof (buf.sun_path));
+ int len = inlen - sizediff;
+
+ VERIFY(len > 0);
+ VERIFY(len <= sizeof (buf.sun_path));
+ bzero(&buf, sizeof (buf));
+
+ if (inaddr->sa_data[0] == '\0') {
+ /*
+ * Linux supports abstract Unix sockets, which are simply
+ * sockets that do not exist on the file system. These sockets
+ * are denoted by beginning the path with a NULL character. To
+ * support these, we strip out the leading NULL character and
+ * change the path to point to a real place in /tmp directory,
+ * by prepending ABST_PRFX and replacing all illegal characters
+ * with * '_'.
+ *
+ * Since these sockets are supposed to exist outside the
+ * filesystem, they must be cleaned up after use. This removal
+ * is performed during bind().
+ */
+ int idx, odx;
+
+ /* Add our abstract prefix */
+ (void) strcpy(buf.sun_path, ABST_PRFX);
+ for (idx = 1, odx = ABST_PRFX_LEN;
+ idx < len && odx < sizeof (buf.sun_path);
+ idx++, odx++) {
+ char c = inaddr->sa_data[idx];
+ if (c == '\0' || c == '/') {
+ buf.sun_path[odx] = '_';
+ } else {
+ buf.sun_path[odx] = c;
+ }
+ }
+
+ /*
+ * Since abstract socket addresses might not be NUL terminated,
+ * we must explicitly NUL terminate the translated path.
+ * Care is taken not to overflow the buffer.
+ */
+ if (odx == sizeof (buf.sun_path)) {
+ buf.sun_path[odx - 1] = '\0';
+ } else {
+ buf.sun_path[odx] = '\0';
+ }
+
+ if (sun_type != NULL) {
+ *sun_type = LX_SUN_ABSTRACT;
+ }
+ } else {
+ /* Copy the address directly, minding termination */
+ (void) strncpy(buf.sun_path, inaddr->sa_data, len);
+ len = strnlen(buf.sun_path, len);
+ if (len == sizeof (buf.sun_path)) {
+ buf.sun_path[len - 1] = '\0';
+ } else {
+ VERIFY(len < sizeof (buf.sun_path));
+ buf.sun_path[len] = '\0';
+ }
+
+ if (sun_type != NULL) {
+ *sun_type = LX_SUN_NORMAL;
+ }
+ }
+ buf.sun_family = AF_UNIX;
+ *outlen = strlen(buf.sun_path) + 1 + sizediff;
+ VERIFY(*outlen <= sizeof (struct sockaddr_un));
+
+ *outaddr = kmem_alloc(*outlen, KM_SLEEP);
+ bcopy(&buf, *outaddr, *outlen);
+}
+
+/*
+ * Copy in a Linux-native socket address from userspace and convert it into
+ * illumos format. When successful, it will allocate an appropriately sized
+ * struct to be freed by the caller.
+ */
+static long
+ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen,
+ struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+ sa_family_t family;
+ struct sockaddr *laddr;
+ struct sockaddr_ll *sal;
+ int proto, error = 0;
+
+ VERIFY(inaddr != NULL);
+
+ if (inlen < sizeof (sa_family_t) ||
+ inlen > sizeof (struct sockaddr_storage)) {
+ return (EINVAL);
+ }
+ laddr = kmem_alloc(inlen, KM_SLEEP);
+ if (copyin(inaddr, laddr, inlen) != 0) {
+ kmem_free(laddr, inlen);
+ return (EFAULT);
+ }
+
+ family = LTOS_FAMILY(laddr->sa_family);
+ switch (family) {
+ case (sa_family_t)AF_NOTSUPPORTED:
+ error = EPROTONOSUPPORT;
+ break;
+
+ case (sa_family_t)AF_INVAL:
+ error = EAFNOSUPPORT;
+ break;
+
+ case AF_UNIX:
+ if (inlen < sizeof (sa_family_t) + 2 ||
+ inlen > sizeof (struct sockaddr_un)) {
+ error = EINVAL;
+ break;
+ }
+ ltos_sockaddr_ux(laddr, inlen, outaddr, outlen,
+ sun_type);
+
+ /* AF_UNIX bypasses the standard copy logic */
+ kmem_free(laddr, inlen);
+ return (0);
+
+ case AF_PACKET:
+ if (inlen < sizeof (struct sockaddr_ll)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr_ll);
+
+ /* sll_protocol must be translated */
+ /* LINTED: alignment */
+ sal = (struct sockaddr_ll *)laddr;
+ proto = ltos_pkt_proto(sal->sll_protocol);
+ if (proto < 0) {
+ error = EINVAL;
+ }
+ sal->sll_protocol = proto;
+ break;
+
+ case AF_INET:
+ if (inlen < sizeof (struct sockaddr)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr);
+ break;
+
+ case AF_INET6:
+ /*
+ * The illumos sockaddr_in6 has one more 32-bit field
+ * than the Linux version. We simply zero that field
+ * via kmem_zalloc.
+ */
+ if (inlen < sizeof (lx_sockaddr_in6_t)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr_in6);
+ *outaddr = (struct sockaddr *)kmem_zalloc(*outlen,
+ KM_SLEEP);
+ bcopy(laddr, *outaddr, sizeof (lx_sockaddr_in6_t));
+ (*outaddr)->sa_family = AF_INET6;
+ /* AF_INET6 bypasses the standard copy logic */
+ kmem_free(laddr, inlen);
+ return (0);
+
+ default:
+ *outlen = inlen;
+ }
+
+ if (error == 0) {
+ /*
+ * For most address families, just copying into a sockaddr of
+ * the correct size and updating sa_family is adequate.
+ */
+ VERIFY(inlen >= *outlen);
+
+ *outaddr = (struct sockaddr *)kmem_zalloc(*outlen, KM_SLEEP);
+ bcopy(laddr, *outaddr, *outlen);
+ (*outaddr)->sa_family = family;
+ }
+ kmem_free(laddr, inlen);
+ return (error);
+}
+
+/*
+ * Convert an illumos-native socket address into Linux format and copy it out
+ * to userspace.
+ */
+static long
+stol_sockaddr_copyout(struct sockaddr *inaddr, socklen_t inlen,
+ struct sockaddr *outaddr, void *outlenp, socklen_t orig)
+{
+ socklen_t size = inlen;
+ struct sockaddr_storage buf;
+ struct sockaddr *bufaddr;
+
+ /*
+ * Either we were passed a valid sockaddr (with length) or the length
+ * is set to 0.
+ */
+ VERIFY(inaddr != NULL || inlen == 0);
+
+ if (inlen == 0) {
+ goto finish;
+ }
+
+
+ switch (inaddr->sa_family) {
+ case AF_INET:
+ if (inlen != sizeof (struct sockaddr)) {
+ return (EINVAL);
+ }
+ break;
+
+ case AF_INET6:
+ if (inlen != sizeof (struct sockaddr_in6)) {
+ return (EINVAL);
+ }
+ /*
+ * The linux sockaddr_in6 is shorter than illumos.
+ * Truncate the extra field on the way out.
+ */
+ size = (sizeof (lx_sockaddr_in6_t));
+ inlen = (sizeof (lx_sockaddr_in6_t));
+ break;
+
+ case AF_UNIX:
+ if (inlen > sizeof (struct sockaddr_un)) {
+ return (EINVAL);
+ }
+
+ /*
+ * On Linux an empty AF_UNIX address is returned as NULL, which
+ * means setting the returned length to only encompass the
+ * address family part of the buffer. However, some code also
+ * references the address portion of the buffer and uses it,
+ * even though the returned length has been shortened. Thus, we
+ * clear the buffer to ensure that the address portion is NULL.
+ */
+ if (inaddr->sa_data[0] == '\0') {
+ bzero(&buf, sizeof (buf));
+ inlen = sizeof (inaddr->sa_family);
+ }
+ break;
+
+ case (sa_family_t)AF_NOTSUPPORTED:
+ return (EPROTONOSUPPORT);
+
+ case (sa_family_t)AF_INVAL:
+ return (EAFNOSUPPORT);
+
+ default:
+ break;
+ }
+
+ /*
+ * The input should be smaller than sockaddr_storage, the largest
+ * sockaddr we support.
+ */
+ VERIFY(inlen <= sizeof (buf));
+
+ bufaddr = (struct sockaddr *)&buf;
+ bcopy(inaddr, bufaddr, inlen);
+ bufaddr->sa_family = STOL_FAMILY(bufaddr->sa_family);
+
+ /*
+ * It is possible that userspace passed us a smaller buffer than we
+ * hope to output. When this is the case, we will truncate our output
+ * to the max size of their buffer but report the true size of the
+ * sockaddr when outputting the outlen value.
+ */
+ size = (orig < size) ? orig : size;
+
+ if (copyout(bufaddr, outaddr, size) != 0) {
+ return (EFAULT);
+ }
+
+finish:
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ int32_t len32 = (int32_t)inlen;
+ if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyout(&inlen, outlenp, sizeof (inlen)) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ return (0);
+}
+
+typedef struct lx_cmsg_xlate {
+ int lcx_sunos_level;
+ int lcx_sunos_type;
+ int (*lcx_stol_conv)(struct cmsghdr *, struct cmsghdr *);
+ int lcx_linux_level;
+ int lcx_linux_type;
+ int (*lcx_ltos_conv)(struct cmsghdr *, struct cmsghdr *);
+} lx_cmsg_xlate_t;
+
+static int cmsg_conv_generic(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int ltos_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_recvttl(struct cmsghdr *, struct cmsghdr *);
+
+/*
+ * Table describing SunOS <-> Linux cmsg translation mappings.
+ * Certain types (IP_RECVTTL) are only converted in one direction and are
+ * indicated by one of the translation functions being set to NULL.
+ */
+static lx_cmsg_xlate_t lx_cmsg_xlate_tbl[] = {
+ { SOL_SOCKET, SCM_RIGHTS, cmsg_conv_generic,
+ LX_SOL_SOCKET, LX_SCM_RIGHTS, cmsg_conv_generic },
+ { SOL_SOCKET, SCM_UCRED, stol_conv_ucred,
+ LX_SOL_SOCKET, LX_SCM_CRED, ltos_conv_ucred },
+ { SOL_SOCKET, SCM_TIMESTAMP, cmsg_conv_generic,
+ LX_SOL_SOCKET, LX_SCM_TIMESTAMP, cmsg_conv_generic },
+ { IPPROTO_IP, IP_PKTINFO, cmsg_conv_generic,
+ LX_IPPROTO_IP, LX_IP_PKTINFO, cmsg_conv_generic },
+ { IPPROTO_IP, IP_RECVTTL, stol_conv_recvttl,
+ LX_IPPROTO_IP, LX_IP_TTL, NULL },
+ { IPPROTO_IP, IP_TTL, cmsg_conv_generic,
+ LX_IPPROTO_IP, LX_IP_TTL, cmsg_conv_generic },
+ { IPPROTO_IPV6, IPV6_HOPLIMIT, cmsg_conv_generic,
+ LX_IPPROTO_IPV6, LX_IPV6_HOPLIMIT, cmsg_conv_generic },
+ { IPPROTO_IPV6, IPV6_PKTINFO, cmsg_conv_generic,
+ LX_IPPROTO_IPV6, LX_IPV6_PKTINFO, cmsg_conv_generic }
+};
+
+#define LX_MAX_CMSG_XLATE \
+ (sizeof (lx_cmsg_xlate_tbl) / sizeof (lx_cmsg_xlate_tbl[0]))
+
+#if defined(_LP64)
+
+typedef struct {
+ int64_t cmsg_len;
+ int32_t cmsg_level;
+ int32_t cmsg_type;
+} lx_cmsghdr64_t;
+
+/* The alignment/padding for 64bit Linux cmsghdr is not the same. */
+#define LX_CMSG64_ALIGNMENT 8
+#define ISALIGNED_LX_CMSG64(addr) \
+ (((uintptr_t)(addr) & (LX_CMSG64_ALIGNMENT - 1)) == 0)
+#define ROUNDUP_LX_CMSG64_LEN(len) \
+ (((len) + LX_CMSG64_ALIGNMENT - 1) & ~(LX_CMSG64_ALIGNMENT - 1))
+
+#define LX_CMSG64_IS_ALIGNED(m) \
+ (((uintptr_t)(m) & (_CMSG_DATA_ALIGNMENT - 1)) == 0)
+#define LX_CMSG64_DATA(c) ((unsigned char *)(((lx_cmsghdr64_t *)(c)) + 1))
+/*
+ * LX_CMSG64_VALID is closely derived from CMSG_VALID with one particularly
+ * important addition. Since cmsg_len is 64bit, (cmsg + cmsg_len) is checked
+ * against the start address as well. This prevents bogus inputs from wrapping
+ * around the address space.
+ */
+#define LX_CMSG64_VALID(cmsg, start, end) \
+ (ISALIGNED_LX_CMSG64(cmsg) && \
+ ((uintptr_t)(cmsg) >= (uintptr_t)(start)) && \
+ ((uintptr_t)(cmsg) < (uintptr_t)(end)) && \
+ ((cmsg)->cmsg_len >= sizeof (lx_cmsghdr64_t)) && \
+ ((uintptr_t)(cmsg) + (cmsg)->cmsg_len <= (uintptr_t)(end)) && \
+ ((uintptr_t)(cmsg) + (cmsg)->cmsg_len >= (uintptr_t)(start)))
+#define LX_CMSG64_NEXT(cmsg) \
+ (lx_cmsghdr64_t *)((uintptr_t)(cmsg) + \
+ ROUNDUP_LX_CMSG64_LEN((cmsg)->cmsg_len))
+#define LX_CMSG64_DIFF sizeof (uint32_t)
+
+#endif /* defined(_LP64) */
+
+/*
+ * convert ucred_s to lx_ucred.
+ */
+static int
+stol_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ /*
+ * Format the data correctly in the omsg buffer.
+ */
+ if (omsg != NULL) {
+ struct ucred_s *scred;
+ prcred_t *cr;
+ lx_ucred_t lcred;
+
+ scred = (struct ucred_s *)CMSG_CONTENT(inmsg);
+ lcred.lxu_pid = scred->uc_pid;
+ /* LINTED: alignment */
+ cr = UCCRED(scred);
+ if (cr != NULL) {
+ lcred.lxu_uid = cr->pr_euid;
+ lcred.lxu_gid = cr->pr_egid;
+ } else {
+ lcred.lxu_uid = lcred.lxu_gid = 0;
+ }
+
+ bcopy(&lcred, CMSG_CONTENT(omsg), sizeof (lx_ucred_t));
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (lx_ucred_t));
+}
+
+static int
+ltos_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ if (omsg != NULL) {
+ struct ucred_s *uc;
+ prcred_t *pc;
+ lx_ucred_t *lcred;
+
+ uc = (struct ucred_s *)CMSG_CONTENT(omsg);
+ /* LINTED: alignment */
+ pc = (prcred_t *)((char *)uc + sizeof (struct ucred_s));
+
+ uc->uc_credoff = sizeof (struct ucred_s);
+
+ lcred = (lx_ucred_t *)CMSG_CONTENT(inmsg);
+
+ uc->uc_pid = lcred->lxu_pid;
+ pc->pr_euid = lcred->lxu_uid;
+ pc->pr_egid = lcred->lxu_gid;
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (struct ucred_s) +
+ sizeof (prcred_t));
+
+}
+
+static int
+stol_conv_recvttl(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ /*
+ * SunOS communicates the TTL of incoming packets via IP_RECVTTL using
+ * a uint8_t value instead of IP_TTL using an int. This conversion is
+ * only needed in the one direction since Linux does not handle
+ * IP_RECVTTL in the sendmsg path.
+ */
+ if (omsg != NULL) {
+ uint8_t *inttl = (uint8_t *)CMSG_CONTENT(inmsg);
+ int *ottl = (int *)CMSG_CONTENT(omsg);
+
+ *ottl = (int)*inttl;
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (int));
+}
+
+static int
+cmsg_conv_generic(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ if (omsg != NULL) {
+ size_t data_len;
+
+ data_len = inmsg->cmsg_len - sizeof (struct cmsghdr);
+ bcopy(CMSG_CONTENT(inmsg), CMSG_CONTENT(omsg), data_len);
+ }
+
+ return (inmsg->cmsg_len);
+}
+
+static int
+lx_xlate_cmsg(struct cmsghdr *inmsg, struct cmsghdr *omsg, lx_xlate_dir_t dir)
+{
+ int i;
+ int len;
+
+ VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+ for (i = 0; i < LX_MAX_CMSG_XLATE; i++) {
+ lx_cmsg_xlate_t *xlate = &lx_cmsg_xlate_tbl[i];
+ if (dir == LX_TO_SUNOS &&
+ inmsg->cmsg_level == xlate->lcx_linux_level &&
+ inmsg->cmsg_type == xlate->lcx_linux_type &&
+ xlate->lcx_ltos_conv != NULL) {
+ len = xlate->lcx_ltos_conv(inmsg, omsg);
+ if (omsg != NULL) {
+ omsg->cmsg_len = len;
+ omsg->cmsg_level = xlate->lcx_sunos_level;
+ omsg->cmsg_type = xlate->lcx_sunos_type;
+ }
+ return (len);
+ } else if (dir == SUNOS_TO_LX &&
+ inmsg->cmsg_level == xlate->lcx_sunos_level &&
+ inmsg->cmsg_type == xlate->lcx_sunos_type &&
+ xlate->lcx_stol_conv != NULL) {
+ len = xlate->lcx_stol_conv(inmsg, omsg);
+ if (omsg != NULL) {
+ omsg->cmsg_len = len;
+ omsg->cmsg_level = xlate->lcx_linux_level;
+ omsg->cmsg_type = xlate->lcx_linux_type;
+ }
+ return (len);
+ }
+ }
+ /*
+ * The Linux man page for sendmsg does not define a specific error for
+ * unsupported cmsgs. While it is meant to indicated bad values for
+ * passed flags, EOPNOTSUPP appears to be the next closest choice.
+ */
+ return (-EOPNOTSUPP);
+}
+
+static long
+ltos_cmsgs_copyin(void *addr, socklen_t inlen, void **outmsg,
+ socklen_t *outlenp)
+{
+ void *inbuf, *obuf;
+ struct cmsghdr *inmsg, *omsg;
+ int slen = 0;
+
+ if (inlen < sizeof (struct cmsghdr) || inlen > SO_MAXARGSIZE) {
+ return (EINVAL);
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() == DATAMODEL_NATIVE &&
+ inlen < sizeof (lx_cmsghdr64_t)) {
+ /* The size requirements are more strict for 64bit. */
+ return (EINVAL);
+ }
+#endif /* defined(_LP64) */
+
+ inbuf = kmem_alloc(inlen, KM_SLEEP);
+ if (copyin(addr, inbuf, inlen) != 0) {
+ kmem_free(inbuf, inlen);
+ return (EFAULT);
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ /*
+ * Linux cmsg headers are longer than illumos under x86_64.
+ * Convert to regular cmsgs first.
+ */
+ lx_cmsghdr64_t *lmsg;
+ struct cmsghdr *smsg;
+ void *newbuf;
+ int len = 0;
+
+ /* Inventory the new cmsg size */
+ for (lmsg = (lx_cmsghdr64_t *)inbuf;
+ LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ lmsg = LX_CMSG64_NEXT(lmsg)) {
+ len += ROUNDUP_cmsglen(lmsg->cmsg_len - LX_CMSG64_DIFF);
+ }
+
+ VERIFY(len < inlen);
+ if (len == 0) {
+ /* Input was bogus, so we can give up early. */
+ kmem_free(inbuf, inlen);
+ *outmsg = NULL;
+ *outlenp = 0;
+ return (EINVAL);
+ }
+
+ newbuf = kmem_alloc(len, KM_SLEEP);
+
+ for (lmsg = (lx_cmsghdr64_t *)inbuf,
+ smsg = (struct cmsghdr *)newbuf;
+ LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ lmsg = LX_CMSG64_NEXT(lmsg), smsg = CMSG_NEXT(smsg)) {
+ smsg->cmsg_level = lmsg->cmsg_level;
+ smsg->cmsg_type = lmsg->cmsg_type;
+ smsg->cmsg_len = lmsg->cmsg_len - LX_CMSG64_DIFF;
+
+ /* The above length measurement should ensure this */
+ ASSERT(CMSG_VALID(smsg, newbuf,
+ (uintptr_t)newbuf + len));
+
+ bcopy(LX_CMSG64_DATA(lmsg), CMSG_CONTENT(smsg),
+ smsg->cmsg_len - sizeof (*smsg));
+ }
+
+ kmem_free(inbuf, inlen);
+ inbuf = newbuf;
+ inlen = len;
+ }
+#endif /* defined(_LP64) */
+
+ /*
+ * Now determine how much space we need for the conversion.
+ */
+ for (inmsg = (struct cmsghdr *)inbuf;
+ CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ int sz;
+
+ if ((sz = lx_xlate_cmsg(inmsg, NULL, LX_TO_SUNOS)) < 0) {
+ /* unsupported msg */
+ kmem_free(inbuf, inlen);
+ return (-sz);
+ }
+
+ slen += ROUNDUP_cmsglen(sz);
+ }
+
+ obuf = kmem_zalloc(slen, KM_SLEEP);
+
+ /*
+ * Now do the conversion.
+ */
+ for (inmsg = (struct cmsghdr *)inbuf, omsg = (struct cmsghdr *)obuf;
+ CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+ VERIFY(lx_xlate_cmsg(inmsg, omsg, LX_TO_SUNOS) >= 0);
+ }
+
+ kmem_free(inbuf, inlen);
+ *outmsg = obuf;
+ *outlenp = slen;
+ return (0);
+}
+
+static long
+stol_cmsgs_copyout(void *input, socklen_t inlen, void *addr,
+ void *outlenp, socklen_t orig_outlen)
+{
+ void *obuf;
+ struct cmsghdr *inmsg, *omsg;
+ int error = 0;
+ socklen_t lx_len = 0;
+#if defined(_LP64)
+ model_t model = get_udatamodel();
+#endif
+
+ if (inlen == 0) {
+ /* Simply output the zero controllen */
+ goto finish;
+ }
+
+ VERIFY(inlen > sizeof (struct cmsghdr));
+
+ /*
+ * First determine how much space we need for the conversion and
+ * make sure the caller has provided at least that much space to return
+ * results.
+ */
+ for (inmsg = (struct cmsghdr *)input;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ int sz;
+
+ if ((sz = lx_xlate_cmsg(inmsg, NULL, SUNOS_TO_LX)) < 0) {
+ /* unsupported msg */
+ return (-sz);
+ }
+
+#if defined(_LP64)
+ if (model == DATAMODEL_NATIVE) {
+ /*
+ * The converted 64-bit cmsgs require an additional 4
+ * bytes of header space and must be aligned to 8 bytes
+ * (instead of the typical 4 for x86)
+ */
+ sz = ROUNDUP_LX_CMSG64_LEN(sz + LX_CMSG64_DIFF);
+ } else
+#endif /* defined(_LP64) */
+ {
+ /*
+ * The converted 32-bit cmsgs do not require additional
+ * header space or padding for Linux conversion.
+ */
+ sz = ROUNDUP_cmsglen(sz);
+ }
+
+ /*
+ * Unlike SunOS, Linux requires that the last cmsg be
+ * adequately padded for alignment.
+ */
+ lx_len += sz;
+ }
+
+ if (lx_len > orig_outlen || addr == NULL) {
+ /* This will be interpreted by the caller */
+ error = EMSGSIZE;
+ lx_len = 0;
+ goto finish;
+ }
+
+ /*
+ * Since cmsgs are often padded to an aligned size, kmem_zalloc is
+ * necessary to prevent leaking the contents of uninitialized memory.
+ */
+ obuf = kmem_zalloc(lx_len, KM_SLEEP);
+
+ /*
+ * Convert the msgs.
+ */
+ for (inmsg = (struct cmsghdr *)input, omsg = (struct cmsghdr *)obuf;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+ VERIFY(lx_xlate_cmsg(inmsg, omsg, SUNOS_TO_LX) >= 0);
+ }
+
+#if defined(_LP64)
+ if (model == DATAMODEL_NATIVE) {
+ /* Linux cmsg headers are longer than illumos under x86_64. */
+ struct cmsghdr *smsg;
+ lx_cmsghdr64_t *lmsg;
+ void *newbuf;
+
+ /*
+ * Once again, kmem_zalloc is needed to avoid leaking the
+ * contents of uninialized memory
+ */
+ newbuf = kmem_zalloc(lx_len, KM_SLEEP);
+ for (smsg = (struct cmsghdr *)obuf,
+ lmsg = (lx_cmsghdr64_t *)newbuf;
+ CMSG_VALID(smsg, obuf, (uintptr_t)obuf + inlen) != 0;
+ smsg = CMSG_NEXT(smsg), lmsg = LX_CMSG64_NEXT(lmsg)) {
+ lmsg->cmsg_level = smsg->cmsg_level;
+ lmsg->cmsg_type = smsg->cmsg_type;
+ lmsg->cmsg_len = smsg->cmsg_len + LX_CMSG64_DIFF;
+
+ ASSERT(LX_CMSG64_VALID(lmsg, newbuf,
+ (uintptr_t)newbuf + lx_len) != 0);
+
+ bcopy(CMSG_CONTENT(smsg), LX_CMSG64_DATA(lmsg),
+ smsg->cmsg_len - sizeof (*smsg));
+ }
+
+ kmem_free(obuf, lx_len);
+ obuf = newbuf;
+ }
+#endif /* defined(_LP64) */
+
+ if (copyout(obuf, addr, lx_len) != 0) {
+ kmem_free(obuf, lx_len);
+ return (EFAULT);
+ }
+ kmem_free(obuf, lx_len);
+
+finish:
+ if (outlenp != NULL) {
+#if defined(_LP64)
+ if (model != DATAMODEL_NATIVE) {
+ int32_t len32 = (int32_t)lx_len;
+ if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyout(&lx_len, outlenp, sizeof (lx_len)) != 0) {
+ return (EFAULT);
+ }
+ }
+ }
+ return (error);
+}
+
+static void
+lx_cmsg_set_cloexec(void *input, socklen_t inlen)
+{
+ struct cmsghdr *inmsg;
+
+ if (inlen == 0) {
+ return;
+ }
+
+ for (inmsg = (struct cmsghdr *)input;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ if (inmsg->cmsg_level == SOL_SOCKET &&
+ inmsg->cmsg_type == SCM_RIGHTS) {
+ int *fds = (int *)CMSG_CONTENT(inmsg);
+ int i, num = (int)CMSG_CONTENTLEN(inmsg) / sizeof (int);
+ for (i = 0; i < num; i++) {
+ char flags;
+ file_t *fp;
+ /* set CLOEXEC on the fd */
+ fp = getf(fds[i]);
+ VERIFY(fp != NULL);
+ flags = f_getfd(fds[i]);
+ flags |= FD_CLOEXEC;
+ f_setfd(fds[i], flags);
+ releasef(fds[i]);
+ }
+ }
+ }
+}
+
+static int
+lx_cmsg_try_ucred(sonode_t *so, struct nmsghdr *msg, socklen_t origlen)
+{
+ lx_socket_aux_data_t *sad;
+ struct cmsghdr *cmsg = NULL;
+ int msgsize;
+ cred_t *cred;
+
+ if (origlen == 0) {
+ return (0);
+ }
+ sad = lx_sad_acquire(SOTOV(so));
+ if (!sad->lxsad_stream_cred) {
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ mutex_exit(&sad->lxsad_lock);
+
+ mutex_enter(&so->so_lock);
+ if (so->so_peercred == NULL) {
+ mutex_exit(&so->so_lock);
+ return (0);
+ }
+ crhold(cred = so->so_peercred);
+ mutex_exit(&so->so_lock);
+
+ msgsize = ucredminsize(cred) + sizeof (struct cmsghdr);
+ if (msg->msg_control == NULL) {
+ msg->msg_controllen = msgsize;
+ msg->msg_control = cmsg = kmem_zalloc(msgsize, KM_SLEEP);
+ } else {
+ /*
+ * The so_recvmsg operation may have allocated a msg_control
+ * buffer which precisely fits all returned cmsgs. We must
+ * manually verify the length of that cmsg data and reallocate
+ * the buffer if it lacks the necessary space.
+ */
+ uintptr_t start = (uintptr_t)msg->msg_control;
+ uintptr_t end = start + msg->msg_controllen;
+
+ ASSERT(msg->msg_controllen > 0);
+ cmsg = (struct cmsghdr *)msg->msg_control;
+ while (CMSG_VALID(cmsg, start, end) != 0) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_UCRED) {
+ /*
+ * If some later code change results in a ucred
+ * being attached anyways, there is no need for
+ * us to do it manually
+ */
+ crfree(cred);
+ return (0);
+ }
+ cmsg = CMSG_NEXT(cmsg);
+ }
+ if (((uintptr_t)cmsg + msgsize) > end) {
+ socklen_t offset = (uintptr_t)cmsg - start;
+ socklen_t newsize = offset + msgsize;
+ void *newbuf;
+
+ if (newsize < msg->msg_controllen) {
+ /* size overflow, bail */
+ crfree(cred);
+ return (-1);
+ }
+ newbuf = kmem_alloc(newsize, KM_SLEEP);
+ bcopy(msg->msg_control, newbuf, msg->msg_controllen);
+ kmem_free(msg->msg_control, msg->msg_controllen);
+
+ msg->msg_control = newbuf;
+ msg->msg_controllen = newsize;
+ cmsg = (struct cmsghdr *)((uintptr_t)newbuf + offset);
+ }
+ }
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_UCRED;
+ cmsg->cmsg_len = msgsize;
+ (void) cred2ucred(cred, so->so_cpid, CMSG_CONTENT(cmsg), CRED());
+ crfree(cred);
+ return (0);
+}
+
+static lx_socket_aux_data_t *
+lx_sad_acquire(vnode_t *vp)
+{
+ lx_socket_aux_data_t *cur, *created;
+
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+ if (cur == NULL) {
+ /* perform our allocation carefully */
+ mutex_exit(&vp->v_vsd_lock);
+
+ created = (lx_socket_aux_data_t *)kmem_zalloc(
+ sizeof (*created), KM_SLEEP);
+
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+ if (cur == NULL) {
+ mutex_init(&created->lxsad_lock, NULL, MUTEX_DEFAULT,
+ NULL);
+ (void) vsd_set(vp, lx_socket_vsd, created);
+ cur = created;
+ } else {
+ kmem_free(created, sizeof (*created));
+ }
+ }
+ mutex_exit(&vp->v_vsd_lock);
+ mutex_enter(&cur->lxsad_lock);
+ return (cur);
+}
+
+static int
+lx_convert_pkt_proto(int protocol)
+{
+ switch (ntohs(protocol)) {
+ case LX_ETH_P_802_2:
+ return (ETH_P_802_2);
+ case LX_ETH_P_IP:
+ return (ETH_P_IP);
+ case LX_ETH_P_ARP:
+ return (ETH_P_ARP);
+ case LX_ETH_P_IPV6:
+ return (ETH_P_IPV6);
+ case LX_ETH_P_ALL:
+ case LX_ETH_P_802_3:
+ return (ETH_P_ALL);
+ default:
+ return (-1);
+ }
+}
+
+static int
+lx_convert_sock_args(int in_dom, int in_type, int in_proto, int *out_dom,
+ int *out_type, int *out_options, int *out_proto)
+{
+ int domain, type, options;
+
+ if (in_dom < 0 || in_type < 0 || in_proto < 0)
+ return (EINVAL);
+
+ domain = LTOS_FAMILY(in_dom);
+ if (domain == AF_NOTSUPPORTED || domain == AF_UNSPEC)
+ return (EAFNOSUPPORT);
+ if (domain == AF_INVAL)
+ return (EINVAL);
+
+ type = LTOS_SOCKTYPE(in_type & LX_SOCK_TYPE_MASK);
+ if (type == SOCK_INVAL)
+ return (EINVAL);
+ /*
+ * Linux does not allow the app to specify IP Protocol for raw sockets.
+ * SunOS does, so bail out here.
+ */
+ if (type == SOCK_NOTSUPPORTED ||
+ (domain == AF_INET && type == SOCK_RAW && in_proto == IPPROTO_IP)) {
+ if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+ /*
+ * Use error appropriate for kernel version.
+ * See lx_socket_create for more detail.
+ */
+ return (ESOCKTNOSUPPORT);
+ }
+ return (EPROTONOSUPPORT);
+ }
+
+ options = 0;
+ in_type &= ~(LX_SOCK_TYPE_MASK);
+ if (in_type & LX_SOCK_NONBLOCK) {
+ in_type ^= LX_SOCK_NONBLOCK;
+ options |= SOCK_NONBLOCK;
+ }
+ if (in_type & LX_SOCK_CLOEXEC) {
+ in_type ^= LX_SOCK_CLOEXEC;
+ options |= SOCK_CLOEXEC;
+ }
+ if (in_type != 0) {
+ return (EINVAL);
+ }
+
+ /* Protocol definitions for PF_PACKET differ between Linux and SunOS */
+ if (domain == PF_PACKET &&
+ (in_proto = lx_convert_pkt_proto(in_proto)) < 0)
+ return (EINVAL);
+
+ *out_dom = domain;
+ *out_type = type;
+ *out_options = options;
+ *out_proto = in_proto;
+ return (0);
+}
+
+
+static int
+lx_socket_create(int domain, int type, int protocol, int options, file_t **fpp,
+ int *fdp)
+{
+ sonode_t *so;
+ vnode_t *vp;
+ file_t *fp;
+ int err, fd;
+
+ /* logic cloned from so_socket */
+ so = socket_create(domain, type, protocol, NULL, NULL, SOCKET_SLEEP,
+ SOV_DEFAULT, CRED(), &err);
+
+ if (so == NULL) {
+ switch (err) {
+ case EPROTOTYPE:
+ case EPROTONOSUPPORT:
+ if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+ /*
+ * Linux changed its socket error behavior in
+ * versions 2.6.15 and later. See git commit
+ * 86c8f9d158f68538a971a47206a46a22c7479bac in
+ * the Linux repository.
+ *
+ * LTP presently checks for version 2.6.16.
+ */
+ return (ESOCKTNOSUPPORT);
+ }
+ return (EPROTONOSUPPORT);
+ default:
+ return (err);
+ }
+ }
+
+ /* Allocate a file descriptor for the socket */
+ vp = SOTOV(so);
+ if ((err = falloc(vp, FWRITE|FREAD, &fp, &fd)) != 0) {
+ (void) socket_close(so, 0, CRED());
+ socket_destroy(so);
+ return (err);
+ }
+
+ /*
+ * Linux programs do not tolerate errors appearing from asynchronous
+ * events (such as ICMP messages arriving). Setting SM_DEFERERR will
+ * prevent checking/delivery of such errors.
+ */
+ so->so_mode |= SM_DEFERERR;
+
+ /* Now fill in the entries that falloc reserved */
+ if (options & SOCK_NONBLOCK) {
+ so->so_state |= SS_NONBLOCK;
+ fp->f_flag |= FNONBLOCK;
+ }
+ mutex_exit(&fp->f_tlock);
+ *fpp = fp;
+ *fdp = fd;
+ return (0);
+}
+
+static void
+lx_socket_destroy(file_t *fp, int fd)
+{
+ sonode_t *so = VTOSO(fp->f_vnode);
+
+ setf(fd, NULL);
+
+ mutex_enter(&fp->f_tlock);
+ unfalloc(fp);
+
+ (void) socket_close(so, 0, CRED());
+ socket_destroy(so);
+}
+
+long
+lx_socket(int domain, int type, int protocol)
+{
+ int error, options, fd = -1;
+ file_t *fp = NULL;
+
+ if ((error = lx_convert_sock_args(domain, type, protocol, &domain,
+ &type, &options, &protocol)) != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_socket_create(domain, type, protocol, options, &fp, &fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ setf(fd, fp);
+ if ((options & SOCK_CLOEXEC) != 0) {
+ f_setfd(fd, FD_CLOEXEC);
+ }
+ return (fd);
+}
+
+long
+lx_bind(long sock, uintptr_t name, socklen_t namelen)
+{
+ struct sonode *so;
+ struct sockaddr *addr = NULL;
+ socklen_t len = 0;
+ file_t *fp;
+ int error;
+ lx_sun_type_t sun_type;
+ boolean_t not_sock = B_FALSE;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ if (namelen != 0) {
+ error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+ &addr, &len, &sun_type);
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ }
+
+ if (addr != NULL && addr->sa_family == AF_UNIX) {
+ vnode_t *vp;
+
+ error = so_ux_lookup(so, (struct sockaddr_un *)addr, B_TRUE,
+ &vp);
+ if (error == 0) {
+ /* A valid socket exists and is open at this address. */
+ VN_RELE(vp);
+ } else {
+ /* Keep track of paths which are not valid sockets. */
+ if (error == ENOTSOCK) {
+ not_sock = B_TRUE;
+ }
+
+ /*
+ * When binding to an abstract namespace address or
+ * /dev/log, implicit clean-up must occur if there is
+ * not a valid socket at the specififed address. See
+ * ltos_sockaddr_copyin for details about why these
+ * socket types act differently.
+ */
+ if (sun_type == LX_SUN_ABSTRACT) {
+ (void) vn_removeat(NULL, addr->sa_data,
+ UIO_SYSSPACE, RMFILE);
+ }
+ }
+ }
+
+ error = socket_bind(so, addr, len, _SOBIND_XPG4_2, CRED());
+
+ /*
+ * Linux returns EADDRINUSE for attempts to bind to Unix domain
+ * sockets that aren't sockets.
+ */
+ if (error == EINVAL && addr != NULL && addr->sa_family == AF_UNIX &&
+ not_sock == B_TRUE) {
+ error = EADDRINUSE;
+ }
+
+ releasef(sock);
+
+ if (addr != NULL) {
+ kmem_free(addr, len);
+ }
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_connect(long sock, uintptr_t name, socklen_t namelen)
+{
+ struct sonode *so;
+ struct sockaddr *addr = NULL;
+ lx_socket_aux_data_t *sad = NULL;
+ socklen_t len = 0;
+ file_t *fp;
+ int error;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ /*
+ * Ensure the name is sized appropriately before we alloc memory and
+ * copy it in from userspace. We need at least the address family to
+ * make later sizing decisions.
+ */
+ if (namelen != 0) {
+ error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+ &addr, &len, NULL);
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ }
+
+ error = socket_connect(so, addr, len, fp->f_flag,
+ _SOCONNECT_XPG4_2, CRED());
+
+ /*
+ * Linux connect(2) behavior is rather strange when using the
+ * O_NONBLOCK flag. The first call will return EINPROGRESS, as
+ * expected. Provided that is successful, a second call to connect
+ * will return 0 instead of EISCONN. Subsequent connect calls will
+ * return EISCONN.
+ */
+ if ((fp->f_flag & FNONBLOCK) != 0 && error != 0) {
+ sad = lx_sad_acquire(SOTOV(so));
+ if (error == EISCONN &&
+ sad->lxsad_status == LXSS_CONNECTING) {
+ /* Report the one success */
+ sad->lxsad_status = LXSS_CONNECTED;
+ error = 0;
+ } else if (error == EINPROGRESS) {
+ sad->lxsad_status = LXSS_CONNECTING;
+ }
+ mutex_exit(&sad->lxsad_lock);
+ }
+
+ /*
+ * When connecting to a UDP socket, configure it so that future
+ * sendto/sendmsg operations are allowed to specify a destination
+ * address. See the Posix spec. for sendto(2). Linux allows this while
+ * illumos would return EISCONN if the option is not set.
+ */
+ if (error == 0 && so->so_protocol == IPPROTO_UDP &&
+ (so->so_family == AF_INET || so->so_family == AF_INET6)) {
+ int val = 1;
+
+ DTRACE_PROBE(lx__connect__udp);
+ (void) socket_setsockopt(so, IPPROTO_UDP, UDP_SND_TO_CONNECTED,
+ &val, sizeof (val), CRED());
+ }
+
+ releasef(sock);
+
+ if (addr != NULL) {
+ kmem_free(addr, len);
+ }
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+/*
+ * Custom version of socket_recvmsg for error-handling overrides.
+ */
+static int
+lx_socket_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+ cred_t *cr)
+{
+ int error;
+ ssize_t orig_resid = uiop->uio_resid;
+
+ /*
+ * Do not bypass the cache when reading data, as the application
+ * is likely to access the data shortly.
+ */
+ uiop->uio_extflg |= UIO_COPY_CACHED;
+
+ error = SOP_RECVMSG(so, msg, uiop, cr);
+
+ switch (error) {
+ case EINTR:
+ /* EAGAIN is EWOULDBLOCK */
+ case EWOULDBLOCK:
+ /* We did a partial read */
+ if (uiop->uio_resid != orig_resid)
+ error = 0;
+ break;
+ case ENOTCONN:
+ /*
+ * The rules are different for non-blocking sockets which are
+ * still in the process of making a connection
+ */
+ if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+ (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+ error = EAGAIN;
+ }
+ break;
+ default:
+ break;
+ }
+ return (error);
+}
+
+static long
+lx_recv_common(int sock, struct nmsghdr *msg, xuio_t *xuiop, int flags,
+ void *namelenp, void *controllenp, void *flagsp)
+{
+ struct sonode *so;
+ file_t *fp;
+ void *name;
+ socklen_t namelen;
+ void *control;
+ socklen_t controllen;
+ ssize_t len;
+ int error;
+ boolean_t fd_cloexec;
+ boolean_t is_peek_trunc;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ fd_cloexec = ((flags & LX_MSG_CMSG_CLOEXEC) != 0);
+ flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+ is_peek_trunc = (flags & (MSG_PEEK|MSG_TRUNC)) == (MSG_PEEK|MSG_TRUNC);
+ len = xuiop->xu_uio.uio_resid;
+ xuiop->xu_uio.uio_fmode = fp->f_flag;
+ xuiop->xu_uio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Linux accepts MSG_TRUNC as an input flag, unlike SunOS and many
+ * other UNIX distributions. When combined with MSG_PEEK, it causes
+ * recvmsg to return the size of the waiting message, regardless of
+ * buffer size. This behavior is commonly used with a 0-length buffer
+ * to interrogate the size of a queued message prior to allocating a
+ * buffer for it.
+ *
+ * In order to support this functionality, a custom XUIO type is used
+ * to communicate the total message size out from the depths of sockfs.
+ */
+ if (is_peek_trunc) {
+ xuiop->xu_uio.uio_extflg |= UIO_XUIO;
+ xuiop->xu_type = UIOTYPE_PEEKSIZE;
+ xuiop->xu_ext.xu_ps.xu_ps_set = B_FALSE;
+ xuiop->xu_ext.xu_ps.xu_ps_size = 0;
+ }
+
+ name = msg->msg_name;
+ namelen = msg->msg_namelen;
+ control = msg->msg_control;
+ controllen = msg->msg_controllen;
+
+ /*
+ * socket_recvmsg will allocate these if needed.
+ * NULL them out to prevent any confusion.
+ */
+ msg->msg_name = NULL;
+ msg->msg_control = NULL;
+
+ msg->msg_flags = flags & (MSG_OOB | MSG_PEEK | MSG_WAITALL |
+ MSG_DONTWAIT);
+ /* Default to XPG4.2 operation */
+ msg->msg_flags |= MSG_XPG4_2;
+
+ error = lx_socket_recvmsg(so, msg, (struct uio *)xuiop, CRED());
+ if (error) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ lwp_stat_update(LWP_STAT_MSGRCV, 1);
+ releasef(sock);
+
+ if (namelen != 0) {
+ error = stol_sockaddr_copyout(msg->msg_name, msg->msg_namelen,
+ name, namelenp, namelen);
+
+ if (msg->msg_namelen != 0) {
+ kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+ msg->msg_namelen = 0;
+ }
+
+ /*
+ * Errors during copyout of the name are not a concern to Linux
+ * callers at this point in the syscall
+ */
+ if (error != 0 && error != EFAULT) {
+ goto err;
+ }
+ }
+
+ if (controllen != 0) {
+ if (fd_cloexec) {
+ /*
+ * If CLOEXEC needs to set on file descriptors passed
+ * via SCM_RIGHTS, do so before formatting the cmsgs
+ * for Linux.
+ */
+ lx_cmsg_set_cloexec(msg->msg_control,
+ msg->msg_controllen);
+ }
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ /*
+ * It may be necessary to append a SCM_UCRED cmsg to
+ * the controls if SO_PASSCRED is set on a
+ * connection-oriented AF_UNIX socket.
+ *
+ * See lx_setsockopt_socket for more details.
+ */
+ if (lx_cmsg_try_ucred(so, msg, controllen) != 0) {
+ msg->msg_flags |= MSG_CTRUNC;
+ }
+ }
+
+ error = stol_cmsgs_copyout(msg->msg_control,
+ msg->msg_controllen, control, controllenp, controllen);
+
+ if (error != 0) {
+ /*
+ * If there was an error during cmsg translation or
+ * copyout, we need to clean up any FDs that are being
+ * passed back via SCM_RIGHTS. This prevents us from
+ * leaking those open files.
+ */
+ so_closefds(msg->msg_control, msg->msg_controllen, 0,
+ 0);
+
+ /*
+ * An error during cmsg_copyout means we had
+ * _something_ to process.
+ */
+ VERIFY(msg->msg_controllen != 0);
+
+ kmem_free(msg->msg_control,
+ (size_t)msg->msg_controllen);
+ msg->msg_controllen = 0;
+
+ if (error == EMSGSIZE) {
+ /* Communicate that messages were truncated */
+ msg->msg_flags |= MSG_CTRUNC;
+ error = 0;
+ } else {
+ goto err;
+ }
+ } else if (msg->msg_controllen != 0) {
+ kmem_free(msg->msg_control,
+ (size_t)msg->msg_controllen);
+ msg->msg_controllen = 0;
+ }
+ }
+
+ if (flagsp != NULL) {
+ int flags;
+
+ /* Clear internal flag. */
+ flags = msg->msg_flags & ~MSG_XPG4_2;
+ flags = lx_xlate_sock_flags(flags, SUNOS_TO_LX);
+
+ if (copyout(&flags, flagsp, sizeof (flags) != 0)) {
+ error = EFAULT;
+ goto err;
+ }
+ }
+
+ /*
+ * If both MSG_PEEK|MSG_TRUNC were set on the input flags and the
+ * socket layer was able to calculate the total message size for us,
+ * return that instead of the copied size.
+ */
+ if (is_peek_trunc && xuiop->xu_ext.xu_ps.xu_ps_set == B_TRUE) {
+ return (xuiop->xu_ext.xu_ps.xu_ps_size);
+ }
+
+ return (len - xuiop->xu_uio.uio_resid);
+
+err:
+ if (msg->msg_controllen != 0) {
+ /* Prevent FD leakage (see above) */
+ so_closefds(msg->msg_control, msg->msg_controllen, 0, 0);
+ kmem_free(msg->msg_control, (size_t)msg->msg_controllen);
+ }
+ if (msg->msg_namelen != 0) {
+ kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+ }
+ return (set_errno(error));
+}
+
+long
+lx_recv(int sock, void *buffer, size_t len, int flags)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec uiov;
+
+ if ((ssize_t)len < 0) {
+ /*
+ * The input len is unsigned, so limit it to SSIZE_MAX since
+ * the return value is signed.
+ */
+ return (set_errno(EINVAL));
+ }
+
+ uiov.iov_base = buffer;
+ uiov.iov_len = len;
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = &uiov;
+ xuio.xu_uio.uio_iovcnt = 1;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ smsg.msg_namelen = 0;
+ smsg.msg_controllen = 0;
+ smsg.msg_flags = 0;
+ return (lx_recv_common(sock, &smsg, &xuio, flags, NULL, NULL, NULL));
+}
+
+long
+lx_recvfrom(int sock, void *buffer, size_t len, int flags,
+ struct sockaddr *srcaddr, socklen_t *addrlenp)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec uiov;
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ uiov.iov_base = buffer;
+ uiov.iov_len = len;
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = &uiov;
+ xuio.xu_uio.uio_iovcnt = 1;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ smsg.msg_name = (char *)srcaddr;
+ if (addrlenp != NULL && srcaddr != NULL) {
+ /*
+ * Despite addrlenp being defined as a socklen_t *, Linux
+ * treats it internally as an int *. Certain LTP tests depend
+ * upon this behavior, so we must emulate it as well.
+ */
+ int namelen;
+
+ if (copyin(addrlenp, &namelen, sizeof (namelen)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (namelen < 0) {
+ return (set_errno(EINVAL));
+ }
+ smsg.msg_namelen = namelen;
+ } else {
+ smsg.msg_namelen = 0;
+ }
+ smsg.msg_controllen = 0;
+ smsg.msg_flags = 0;
+
+ return (lx_recv_common(sock, &smsg, &xuio, flags, addrlenp, NULL,
+ NULL));
+}
+
+long
+lx_recvmsg(int sock, void *msg, int flags)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec luiov[IOV_MAX_STACK], *uiov;
+ int i, iovcnt, iovsize;
+ long res;
+ ssize_t len = 0;
+ void *namelenp, *controllenp, *flagsp;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_msghdr32_t lmsg32;
+ if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+ smsg.msg_namelen = lmsg32.msg_namelen;
+ smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+ smsg.msg_iovlen = lmsg32.msg_iovlen;
+ smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+ smsg.msg_controllen = lmsg32.msg_controllen;
+ smsg.msg_flags = lmsg32.msg_flags;
+
+ namelenp = &((lx_msghdr32_t *)msg)->msg_namelen;
+ controllenp = &((lx_msghdr32_t *)msg)->msg_controllen;
+ flagsp = &((lx_msghdr32_t *)msg)->msg_flags;
+ } else
+#endif /* defined(_LP64) */
+ {
+ lx_msghdr_t lmsg;
+ if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = lmsg.msg_name;
+ smsg.msg_namelen = lmsg.msg_namelen;
+ smsg.msg_iov = lmsg.msg_iov;
+ smsg.msg_iovlen = lmsg.msg_iovlen;
+ smsg.msg_control = lmsg.msg_control;
+ smsg.msg_controllen = lmsg.msg_controllen;
+ smsg.msg_flags = lmsg.msg_flags;
+
+ namelenp = &((lx_msghdr_t *)msg)->msg_namelen;
+ controllenp = &((lx_msghdr_t *)msg)->msg_controllen;
+ flagsp = &((lx_msghdr_t *)msg)->msg_flags;
+ }
+
+ iovcnt = smsg.msg_iovlen;
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EMSGSIZE));
+ }
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ uiov = kmem_alloc(iovsize, KM_SLEEP);
+ } else if (iovcnt > 0) {
+ iovsize = 0;
+ uiov = luiov;
+ } else {
+ iovsize = 0;
+ uiov = NULL;
+ goto noiov;
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ /* convert from 32bit iovec structs */
+ struct iovec32 luiov32[IOV_MAX_STACK], *uiov32;
+ ssize_t iov32size;
+ ssize32_t count32;
+
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0) {
+ uiov32 = kmem_alloc(iov32size, KM_SLEEP);
+ } else {
+ uiov32 = luiov32;
+ }
+
+ if (copyin((struct iovec32 *)smsg.msg_iov, uiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ kmem_free(uiov, iovsize);
+ }
+
+ return (set_errno(EFAULT));
+ }
+
+ count32 = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32;
+
+ iovlen32 = uiov32[i].iov_len;
+ count32 += iovlen32;
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ kmem_free(uiov, iovsize);
+ }
+
+ return (set_errno(EINVAL));
+ }
+
+ uiov[i].iov_len = iovlen32;
+ uiov[i].iov_base =
+ (caddr_t)(uintptr_t)uiov32[i].iov_base;
+ }
+ len = count32;
+
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyin(smsg.msg_iov, uiov,
+ iovcnt * sizeof (struct iovec)) != 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+ return (set_errno(EFAULT));
+ }
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = uiov[i].iov_len;
+ len += iovlen;
+ if (iovlen < 0 || len < 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+ return (set_errno(EINVAL));
+ }
+ }
+ }
+
+noiov:
+ /* Since the iovec is passed via the uio, NULL it out in the msg */
+ smsg.msg_iov = NULL;
+
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = uiov;
+ xuio.xu_uio.uio_iovcnt = iovcnt;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ res = lx_recv_common(sock, &smsg, &xuio, flags, namelenp, controllenp,
+ flagsp);
+
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+
+ return (res);
+}
+
+/*
+ * Custom version of socket_sendmsg for error-handling overrides.
+ */
+static int
+lx_socket_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+ cred_t *cr, boolean_t nosig)
+{
+ int error = 0;
+ ssize_t orig_resid = uiop->uio_resid;
+
+ /*
+ * Do not bypass the cache if we are doing a local (AF_UNIX) write.
+ */
+ if (so->so_family == AF_UNIX) {
+ uiop->uio_extflg |= UIO_COPY_CACHED;
+ } else {
+ uiop->uio_extflg &= ~UIO_COPY_CACHED;
+ }
+
+ error = SOP_SENDMSG(so, msg, uiop, cr);
+
+ switch (error) {
+ case EINTR:
+ case ENOMEM:
+ /* EAGAIN is EWOULDBLOCK */
+ case EWOULDBLOCK:
+ /* We did a partial send */
+ if (uiop->uio_resid != orig_resid) {
+ error = 0;
+ }
+ break;
+
+ case ENOTCONN:
+ /*
+ * The rules are different for non-blocking sockets which are
+ * still in the process of making a connection
+ */
+ if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+ (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+ error = EAGAIN;
+ break;
+ }
+
+ /* Appease LTP and match behavior detailed in the man page */
+ error = EPIPE;
+ /* FALLTHROUGH */
+ case EPIPE:
+ if (nosig == B_FALSE) {
+ tsignal(curthread, SIGPIPE);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return (error);
+}
+
+static long
+lx_send_common(int sock, struct nmsghdr *msg, struct uio *uiop, int flags)
+{
+ struct sonode *so;
+ file_t *fp;
+ struct sockaddr *name = NULL;
+ socklen_t namelen;
+ void *control = NULL;
+ socklen_t controllen;
+ ssize_t len = 0;
+ int error;
+ boolean_t nosig;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ uiop->uio_fmode = fp->f_flag;
+
+ /* Allocate and copyin name and control */
+ if (msg->msg_name != NULL && msg->msg_namelen != 0) {
+ ASSERT(MUTEX_NOT_HELD(&so->so_lock));
+
+ error = ltos_sockaddr_copyin((struct sockaddr *)msg->msg_name,
+ msg->msg_namelen, &name, &namelen, NULL);
+ if (error != 0) {
+ goto done;
+ }
+ /* copyin_name null terminates addresses for AF_UNIX */
+ msg->msg_namelen = namelen;
+ msg->msg_name = name;
+ } else {
+ msg->msg_name = name = NULL;
+ msg->msg_namelen = namelen = 0;
+ }
+
+ if (msg->msg_control != NULL && msg->msg_controllen != 0) {
+ /*
+ * Verify that the length is not excessive to prevent
+ * an application from consuming all of kernel memory.
+ */
+ if (msg->msg_controllen > SO_MAXARGSIZE) {
+ error = EINVAL;
+ goto done;
+ }
+ if ((error = ltos_cmsgs_copyin(msg->msg_control,
+ msg->msg_controllen, &control, &controllen)) != 0) {
+ goto done;
+ }
+ msg->msg_control = control;
+ msg->msg_controllen = controllen;
+ } else {
+ msg->msg_control = control = NULL;
+ msg->msg_controllen = controllen = 0;
+ }
+
+ len = uiop->uio_resid;
+ msg->msg_flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+ /* Default to XPG4.2 operation */
+ msg->msg_flags |= MSG_XPG4_2;
+ nosig = ((flags & LX_MSG_NOSIGNAL) != 0);
+
+ error = lx_socket_sendmsg(so, msg, uiop, CRED(), nosig);
+done:
+ if (control != NULL) {
+ kmem_free(control, controllen);
+ }
+ if (name != NULL) {
+ kmem_free(name, namelen);
+ }
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ lwp_stat_update(LWP_STAT_MSGSND, 1);
+ releasef(sock);
+ return (len - uiop->uio_resid);
+}
+
+long
+lx_send(int sock, void *buffer, size_t len, int flags)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec aiov[1];
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ aiov[0].iov_base = buffer;
+ aiov[0].iov_len = len;
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ smsg.msg_name = NULL;
+ smsg.msg_control = NULL;
+ return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendto(int sock, void *buffer, size_t len, int flags,
+ struct sockaddr *dstaddr, socklen_t addrlen)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec aiov[1];
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ aiov[0].iov_base = buffer;
+ aiov[0].iov_len = len;
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ smsg.msg_name = (char *)dstaddr;
+ smsg.msg_namelen = addrlen;
+ smsg.msg_control = NULL;
+ return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendmsg(int sock, void *msg, int flags)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov;
+ int i, iovcnt, iovsize;
+ long res;
+ ssize_t len = 0;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_msghdr32_t lmsg32;
+ if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+ smsg.msg_namelen = lmsg32.msg_namelen;
+ smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+ smsg.msg_iovlen = lmsg32.msg_iovlen;
+ smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+ smsg.msg_controllen = lmsg32.msg_controllen;
+ smsg.msg_flags = lmsg32.msg_flags;
+ } else
+#endif /* defined(_LP64) */
+ {
+ lx_msghdr_t lmsg;
+ if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = lmsg.msg_name;
+ smsg.msg_namelen = lmsg.msg_namelen;
+ smsg.msg_iov = lmsg.msg_iov;
+ smsg.msg_iovlen = lmsg.msg_iovlen;
+ smsg.msg_control = lmsg.msg_control;
+ smsg.msg_controllen = lmsg.msg_controllen;
+ smsg.msg_flags = lmsg.msg_flags;
+ }
+
+ iovcnt = smsg.msg_iovlen;
+ if (iovcnt <= 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EMSGSIZE));
+ }
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ aiov = kmem_alloc(iovsize, KM_SLEEP);
+ } else {
+ iovsize = 0;
+ aiov = buf;
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ /* convert from 32bit iovec structs */
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ ssize_t iov32size;
+ ssize32_t count32;
+
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0) {
+ aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+ }
+
+ if (copyin((struct iovec32 *)smsg.msg_iov, aiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
+ return (set_errno(EFAULT));
+ }
+
+ count32 = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32;
+
+ iovlen32 = aiov32[i].iov_len;
+ count32 += iovlen32;
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
+ return (set_errno(EINVAL));
+ }
+
+ aiov[i].iov_len = iovlen32;
+ aiov[i].iov_base =
+ (caddr_t)(uintptr_t)aiov32[i].iov_base;
+ }
+ len = count32;
+
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyin(smsg.msg_iov, aiov,
+ iovcnt * sizeof (struct iovec)) != 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+ return (set_errno(EFAULT));
+ }
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = aiov[i].iov_len;
+
+ len += iovlen;
+ if (iovlen < 0 || len < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+ return (set_errno(EINVAL));
+ }
+ }
+ }
+ /* Since the iovec is passed via the uio, NULL it out in the msg */
+ smsg.msg_iov = NULL;
+
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ res = lx_send_common(sock, &smsg, &auio, flags);
+
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+
+ return (res);
+}
+
+/*
+ * Linux socket option type definitions
+ *
+ * The protocol `levels` are well defined (see in.h) The option values are
+ * not so well defined. Linux often uses different values vs. Illumos
+ * although they mean the same thing. For example, IP_TOS in Linux is
+ * defined as value 1 but in Illumos it is defined as value 3. This table
+ * maps all the Protocol levels to their options and maps them between
+ * Linux and Illumos and vice versa. Hence the reason for the complexity.
+ *
+ * For a certain subset of sockopts, Linux will implicitly truncate optval
+ * input, so long as optlen meets a minimum size. Because illumos is strict
+ * about optlen, we must cap optlen for those options.
+ */
+
+typedef struct lx_sockopt_map {
+ const int lsm_opt; /* Illumos-native equivalent */
+ const int lsm_lcap; /* Cap optlen to this size. (Ignored if 0) */
+} lx_sockopt_map_t;
+
+typedef struct lx_proto_opts {
+ const lx_sockopt_map_t *lpo_entries; /* Linux->SunOS map entries */
+ unsigned int lpo_max; /* max entries in table */
+} lx_proto_opts_t;
+
+#define OPTNOTSUP -1 /* we don't support it */
+
+#define PROTO_SOCKOPTS(opts) \
+ { (opts), sizeof ((opts)) / sizeof ((opts)[0]) }
+
+/* Shorten name so the columns can line up */
+#define IP_MREQ_SZ sizeof (struct ip_mreq)
+
+static const lx_sockopt_map_t ltos_ip_sockopts[LX_IP_UNICAST_IF + 1] = {
+ { OPTNOTSUP, 0 },
+ { IP_TOS, sizeof (int) }, /* IP_TOS */
+ { IP_TTL, sizeof (int) }, /* IP_TTL */
+ { IP_HDRINCL, sizeof (int) }, /* IP_HDRINCL */
+ { IP_OPTIONS, 0 }, /* IP_OPTIONS */
+ { OPTNOTSUP, 0 }, /* IP_ROUTER_ALERT */
+ { IP_RECVOPTS, sizeof (int) }, /* IP_RECVOPTS */
+ { IP_RETOPTS, sizeof (int) }, /* IP_RETOPTS */
+ { IP_PKTINFO, sizeof (int) }, /* IP_PKTINFO */
+ { OPTNOTSUP, 0 }, /* IP_PKTOPTIONS */
+ { OPTNOTSUP, 0 }, /* IP_MTUDISCOVER */
+ { OPTNOTSUP, 0 }, /* IP_RECVERR */
+ { IP_RECVTTL, sizeof (int) }, /* IP_RECVTTL */
+ { OPTNOTSUP, 0 }, /* IP_RECVTOS */
+ { OPTNOTSUP, 0 }, /* IP_MTU */
+ { OPTNOTSUP, 0 }, /* IP_FREEBIND */
+ { OPTNOTSUP, 0 }, /* IP_IPSEC_POLICY */
+ { OPTNOTSUP, 0 }, /* IP_XFRM_POLICY */
+ { OPTNOTSUP, 0 }, /* IP_PASSSEC */
+ { OPTNOTSUP, 0 }, /* IP_TRANSPARENT */
+ { OPTNOTSUP, 0 }, /* IP_ORIGDSTADDR */
+ { OPTNOTSUP, 0 }, /* IP_MINTTL */
+ { OPTNOTSUP, 0 }, /* IP_NODEFRAG */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IP_MULTICAST_IF, sizeof (int) }, /* IP_MULTICAST_IF */
+ { IP_MULTICAST_TTL, sizeof (int) }, /* IP_MULTICAST_TTL */
+ { IP_MULTICAST_LOOP, sizeof (int) }, /* IP_MULTICAST_LOOP */
+ { IP_ADD_MEMBERSHIP, IP_MREQ_SZ }, /* IP_ADD_MEMBERSHIP */
+ { IP_DROP_MEMBERSHIP, IP_MREQ_SZ }, /* IP_DROP_MEMBERSHIP */
+ { IP_UNBLOCK_SOURCE, 0 }, /* IP_UNBLOCK_SOURCE */
+ { IP_BLOCK_SOURCE, 0 }, /* IP_BLOCK_SOURCE */
+ { IP_ADD_SOURCE_MEMBERSHIP, 0 }, /* IP_ADD_SOURCE_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* IP_DROP_SOURCE_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* IP_MSFILTER */
+ { OPTNOTSUP, 0 }, /* MCAST_JOIN_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_BLOCK_SOURCE */
+ { OPTNOTSUP, 0 }, /* MCAST_UNBLOCK_SOURCE */
+ { OPTNOTSUP, 0 }, /* MCAST_LEAVE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_JOIN_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_LEAVE_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_MSFILTER */
+ { OPTNOTSUP, 0 }, /* IP_MULTICAST_ALL */
+ { OPTNOTSUP, 0 } /* IP_UNICAST_IF */
+};
+
+static const lx_sockopt_map_t ltos_ipv6_sockopts[LX_IPV6_TCLASS + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* IPV6_ADDRFORM */
+ { OPTNOTSUP, 0 }, /* IPV6_2292PKTINFO */
+ { OPTNOTSUP, 0 }, /* IPV6_2292HOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_2292DSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_2292RTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_2292PKTOPTIONS */
+ { IPV6_CHECKSUM, sizeof (int) }, /* IPV6_CHECKSUM */
+ { OPTNOTSUP, 0 }, /* IPV6_2292HOPLIMIT */
+ { OPTNOTSUP, 0 }, /* IPV6_NEXTHOP */
+ { OPTNOTSUP, 0 }, /* IPV6_AUTHHDR */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IPV6_UNICAST_HOPS, sizeof (int) }, /* IPV6_UNICAST_HOPS */
+ { IPV6_MULTICAST_IF, sizeof (int) }, /* IPV6_MULTICAST_IF */
+ { IPV6_MULTICAST_HOPS, sizeof (int) }, /* IPV6_MULTICAST_HOPS */
+ { IPV6_MULTICAST_LOOP, sizeof (int) }, /* IPV6_MULTICAST_LOOP */
+ { OPTNOTSUP, 0 }, /* IPV6_JOIN_GROUP */
+ { OPTNOTSUP, 0 }, /* IPV6_LEAVE_GROUP */
+ { OPTNOTSUP, 0 }, /* IPV6_ROUTER_ALERT */
+ { OPTNOTSUP, 0 }, /* IPV6_MTU_DISCOVER */
+ { OPTNOTSUP, 0 }, /* IPV6_MTU */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVERR */
+ { IPV6_V6ONLY, sizeof (int) }, /* IPV6_V6ONLY */
+ { OPTNOTSUP, 0 }, /* IPV6_JOIN_ANYCAST */
+ { OPTNOTSUP, 0 }, /* IPV6_LEAVE_ANYCAST */
+ { OPTNOTSUP, 0 }, /* IPV6_IPSEC_POLICY */
+ { OPTNOTSUP, 0 }, /* IPV6_XFRM_POLICY */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IPV6_RECVPKTINFO, sizeof (int) }, /* IPV6_RECVPKTINFO */
+ { IPV6_PKTINFO, 0 }, /* IPV6_PKTINFO */
+ { IPV6_RECVHOPLIMIT, sizeof (int) }, /* IPV6_RECVHOPLIMIT */
+ { IPV6_HOPLIMIT, 0 }, /* IPV6_HOPLIMIT */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVHOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_HOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_RTHDRDSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVRTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_RTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVDSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_DSTOPTS */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* IPV6_RECVTCLASS */
+ { IPV6_TCLASS, sizeof (int) } /* IPV6_TCLASS */
+};
+
+static const lx_sockopt_map_t ltos_icmpv6_sockopts[LX_ICMP6_FILTER + 1] = {
+ { OPTNOTSUP, 0 },
+ { ICMP6_FILTER, 0 } /* ICMP6_FILTER */
+};
+
+static const lx_sockopt_map_t ltos_tcp_sockopts[LX_TCP_NOTSENT_LOWAT + 1] = {
+ { OPTNOTSUP, 0 },
+ { TCP_NODELAY, sizeof (int) }, /* TCP_NODELAY */
+ { TCP_MAXSEG, sizeof (int) }, /* TCP_MAXSEG */
+ { TCP_CORK, sizeof (int) }, /* TCP_CORK */
+ { TCP_KEEPIDLE, sizeof (int) }, /* TCP_KEEPIDLE */
+ { TCP_KEEPINTVL, sizeof (int) }, /* TCP_KEEPINTVL */
+ { TCP_KEEPCNT, sizeof (int) }, /* TCP_KEEPCNT */
+ { OPTNOTSUP, 0 }, /* TCP_SYNCNT */
+ { TCP_LINGER2, sizeof (int) }, /* TCP_LINGER2 */
+ { OPTNOTSUP, 0 }, /* TCP_DEFER_ACCEPT */
+ { OPTNOTSUP, 0 }, /* TCP_WINDOW_CLAMP */
+ { OPTNOTSUP, 0 }, /* TCP_INFO */
+ { OPTNOTSUP, 0 }, /* TCP_QUICKACK */
+ { OPTNOTSUP, 0 }, /* TCP_CONGESTION */
+ { OPTNOTSUP, 0 }, /* TCP_MD5SIG */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* TCP_THIN_LINEAR_TIMEOUTS */
+ { OPTNOTSUP, 0 }, /* TCP_THIN_DUPACK */
+ { OPTNOTSUP, 0 }, /* TCP_USER_TIMEOUT */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR_QUEUE */
+ { OPTNOTSUP, 0 }, /* TCP_QUEUE_SEQ */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR_OPTIONS */
+ { OPTNOTSUP, 0 }, /* TCP_FASTOPEN */
+ { OPTNOTSUP, 0 }, /* TCP_TIMESTAMP */
+ { OPTNOTSUP, 0 } /* TCP_NOTSENT_LOWAT */
+};
+
+static const lx_sockopt_map_t ltos_igmp_sockopts[IGMP_MTRACE + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MINLEN, 0 }, /* IGMP_MINLEN */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MEMBERSHIP_QUERY, 0 }, /* IGMP_HOST_MEMBERSHIP_QUERY */
+ { IGMP_V1_MEMBERSHIP_REPORT, 0 }, /* IGMP_HOST_MEMBERSHIP_REPORT */
+ { IGMP_DVMRP, 0 }, /* IGMP_DVMRP */
+ { IGMP_PIM, 0 }, /* IGMP_PIM */
+ { OPTNOTSUP, 0 }, /* IGMP_TRACE */
+ { IGMP_V2_MEMBERSHIP_REPORT, 0 }, /* IGMPV2_HOST_MEMBERSHIP_REPORT */
+ { IGMP_V2_LEAVE_GROUP, 0 }, /* IGMP_HOST_LEAVE_MESSAGE */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MTRACE_RESP, 0 }, /* IGMP_MTRACE_RESP */
+ { IGMP_MTRACE, 0 } /* IGMP_MTRACE */
+};
+
+static const lx_sockopt_map_t ltos_socket_sockopts[LX_SO_BPF_EXTENSIONS + 1] = {
+ { OPTNOTSUP, 0 },
+ { SO_DEBUG, sizeof (int) }, /* SO_DEBUG */
+ { SO_REUSEADDR, sizeof (int) }, /* SO_REUSEADDR */
+ { SO_TYPE, 0 }, /* SO_TYPE */
+ { SO_ERROR, 0 }, /* SO_ERROR */
+ { SO_DONTROUTE, sizeof (int) }, /* SO_DONTROUTE */
+ { SO_BROADCAST, sizeof (int) }, /* SO_BROADCAST */
+ { SO_SNDBUF, sizeof (int) }, /* SO_SNDBUF */
+ { SO_RCVBUF, sizeof (int) }, /* SO_RCVBUF */
+ { SO_KEEPALIVE, sizeof (int) }, /* SO_KEEPALIVE */
+ { SO_OOBINLINE, sizeof (int) }, /* SO_OOBINLINE */
+ { OPTNOTSUP, 0 }, /* SO_NO_CHECK */
+ { OPTNOTSUP, 0 }, /* SO_PRIORITY */
+ { SO_LINGER, 0 }, /* SO_LINGER */
+ { OPTNOTSUP, 0 }, /* SO_BSDCOMPAT */
+ { SO_REUSEPORT, sizeof (int) }, /* SO_REUSEPORT */
+ { SO_RECVUCRED, sizeof (int) }, /* SO_PASSCRED */
+ { OPTNOTSUP, 0 }, /* SO_PEERCRED */
+ { SO_RCVLOWAT, sizeof (int) }, /* SO_RCVLOWAT */
+ { SO_SNDLOWAT, sizeof (int) }, /* SO_SNDLOWAT */
+ { SO_RCVTIMEO, 0 }, /* SO_RCVTIMEO */
+ { SO_SNDTIMEO, 0 }, /* SO_SNDTIMEO */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_AUTHENTICATION */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_ENCRYPTION_TRANSPORT */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_ENCRYPTION_NETWORK */
+ { OPTNOTSUP, 0 }, /* SO_BINDTODEVICE */
+ { SO_ATTACH_FILTER, 0 }, /* SO_ATTACH_FILTER */
+ { SO_DETACH_FILTER, 0 }, /* SO_DETACH_FILTER */
+ { OPTNOTSUP, 0 }, /* SO_PEERNAME */
+ { SO_TIMESTAMP, sizeof (int) }, /* SO_TIMESTAMP */
+ { SO_ACCEPTCONN, 0 }, /* SO_ACCEPTCONN */
+ { OPTNOTSUP, 0 }, /* SO_PEERSEC */
+ { SO_SNDBUF, sizeof (int) }, /* SO_SNDBUFFORCE */
+ { SO_RCVBUF, sizeof (int) }, /* SO_RCVBUFFORCE */
+ { OPTNOTSUP, 0 }, /* SO_PASSSEC */
+ { OPTNOTSUP, 0 }, /* SO_TIMESTAMPNS */
+ { OPTNOTSUP, 0 }, /* SO_MARK */
+ { OPTNOTSUP, 0 }, /* SO_TIMESTAMPING */
+ { SO_PROTOTYPE, 0 }, /* SO_PROTOCOL */
+ { SO_DOMAIN, 0 }, /* SO_DOMAIN */
+ { OPTNOTSUP, 0 }, /* SO_RXQ_OVFL */
+ { OPTNOTSUP, 0 }, /* SO_WIFI_STATUS */
+ { OPTNOTSUP, 0 }, /* SO_PEEK_OFF */
+ { OPTNOTSUP, 0 }, /* SO_NOFCS */
+ { OPTNOTSUP, 0 }, /* SO_LOCK_FILTER */
+ { OPTNOTSUP, 0 }, /* SO_SELECT_ERR_QUEUE */
+ { OPTNOTSUP, 0 }, /* SO_BUSY_POLL */
+ { OPTNOTSUP, 0 }, /* SO_MAX_PACING_RATE */
+ { OPTNOTSUP, 0 } /* SO_BPF_EXTENSIONS */
+};
+
+static const lx_sockopt_map_t ltos_raw_sockopts[LX_ICMP_FILTER + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 } /* ICMP_FILTER */
+};
+
+static const lx_sockopt_map_t ltos_packet_sockopts[LX_PACKET_STATISTICS + 1] = {
+ { OPTNOTSUP, 0 },
+ { PACKET_ADD_MEMBERSHIP, 0 }, /* PACKET_ADD_MEMBERSHIP */
+ { PACKET_DROP_MEMBERSHIP, 0 }, /* PACKET_DROP_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* PACKET_RECV_OUTPUT */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* PACKET_RX_RING */
+ { PACKET_STATISTICS, 0 } /* PACKET_STATISTICS */
+};
+
+/* Needed for SO_ATTACH_FILTER */
+struct lx_bpf_program {
+ unsigned short bf_len;
+ caddr_t bf_insns;
+};
+
+/* Invert filter fields as Linux expects */
+#define LX_ICMP6_FILTER_INVERT(filterp) ( \
+ ((filterp)->__icmp6_filt[0] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[1] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[2] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[3] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[4] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[5] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[6] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[7] ^= 0xFFFFFFFFU))
+
+static boolean_t
+lx_sockopt_lookup(lx_proto_opts_t tbl, int *optname, socklen_t *optlen)
+{
+ const lx_sockopt_map_t *entry;
+
+ if (*optname > tbl.lpo_max) {
+ return (B_FALSE);
+ }
+ entry = &tbl.lpo_entries[*optname];
+ if (entry->lsm_opt == OPTNOTSUP) {
+ return (B_FALSE);
+ }
+ *optname = entry->lsm_opt;
+ /* Truncate the optlen if needed/allowed */
+ if (entry->lsm_lcap != 0 && *optlen > entry->lsm_lcap) {
+ *optlen = entry->lsm_lcap;
+ }
+ return (B_TRUE);
+}
+
+static int
+lx_setsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+ switch (optname) {
+ case LX_IP_RECVERR:
+ /*
+ * Ping sets this option to receive errors on raw sockets.
+ * Currently we just ignore it to make ping happy. From the
+ * Linux ip.7 man page:
+ *
+ * For raw sockets, IP_RECVERR enables passing of all
+ * received ICMP errors to the application.
+ *
+ * Programs known to depend upon this:
+ * - ping
+ * - traceroute
+ * - mount.nfs
+ */
+ return (0);
+
+ case LX_IP_MTU_DISCOVER: {
+ int val;
+
+ /*
+ * We translate Linux's IP_MTU_DISCOVER into our IP_DONTFRAG,
+ * allowing this be a byte or an integer and observing the
+ * inverted sense of the two relative to one another (and
+ * translating accordingly).
+ */
+ if (optlen < sizeof (int)) {
+ val = *((uint8_t *)optval);
+ } else {
+ val = *((int *)optval);
+ }
+
+ switch (val) {
+ case LX_IP_PMTUDISC_DONT:
+ val = 1;
+ break;
+
+ case LX_IP_PMTUDISC_DO:
+ case LX_IP_PMTUDISC_WANT:
+ val = 0;
+ break;
+
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IP, IP_DONTFRAG,
+ &val, sizeof (val), CRED());
+ return (error);
+ }
+
+ case LX_IP_MULTICAST_TTL:
+ case LX_IP_MULTICAST_LOOP:
+ /*
+ * For IP_MULTICAST_TTL and IP_MULTICAST_LOOP, Linux defines
+ * the option value to be an integer while we define it to be
+ * an unsigned character. To prevent the kernel from spitting
+ * back an error on an illegal length, verify that the option
+ * value is less than UCHAR_MAX before truncating optlen.
+ */
+ if (optlen <= 0 || optlen > sizeof (int) ||
+ *intval > UINT8_MAX) {
+ return (EINVAL);
+ }
+ optlen = sizeof (uchar_t);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+ if (optname == LX_IPV6_MTU) {
+ /*
+ * There isn't a good translation for IPV6_MTU and certain apps
+ * such as bind9 will bail if it cannot be set.
+ * We just lie about the success for now.
+ */
+ return (0);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+ error = socket_setsockopt(so, IPPROTO_IPV6, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_icmpv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+ if (optname == LX_ICMP6_FILTER && optval != NULL) {
+ /*
+ * Surprise! The input to ICMP6_FILTER on Linux is inverted
+ * when compared to illumos.
+ */
+ if (optlen != sizeof (icmp6_filter_t)) {
+ return (EINVAL);
+ }
+ LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+ error = socket_setsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+
+ if (optname == LX_TCP_DEFER_ACCEPT) {
+ int *intval;
+ char *dfp;
+
+ /*
+ * Emulate TCP_DEFER_ACCEPT using the datafilt(7M) socket
+ * filter but we can't emulate the timeout aspect so treat any
+ * non-zero value as enabling and zero as disabling.
+ */
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ intval = (int *)optval;
+
+ /*
+ * socket_setsockopt asserts that the optval is aligned, so
+ * we use kmem_alloc to ensure this.
+ */
+ dfp = (char *)kmem_alloc(sizeof (DATAFILT), KM_SLEEP);
+ (void) strcpy(dfp, DATAFILT);
+
+ if (*intval > 0) {
+ error = socket_setsockopt(so, SOL_FILTER, FIL_ATTACH,
+ dfp, 9, CRED());
+ if (error == EEXIST) {
+ error = 0;
+ }
+ } else {
+ error = socket_setsockopt(so, SOL_FILTER, FIL_DETACH,
+ dfp, 9, CRED());
+ if (error == ENXIO) {
+ error = 0;
+ }
+ }
+ kmem_free(dfp, sizeof (DATAFILT));
+ return (error);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_socket(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+ struct lx_bpf_program *lbp;
+ int *intval;
+ struct bpf_program bp;
+
+ switch (optname) {
+ case LX_SO_BSDCOMPAT:
+ /* Linux ignores this option. */
+ return (0);
+
+ case LX_SO_TIMESTAMP:
+ /*
+ * SO_TIMESTAMP is not supported on AF_UNIX sockets but we have
+ * some of those which apps use for logging, etc., so pretend
+ * this worked.
+ */
+ if (so->so_family == AF_UNIX) {
+ return (0);
+ }
+ break;
+
+ case LX_SO_ATTACH_FILTER:
+ /*
+ * Convert bpf program struct
+ */
+ if (optlen != sizeof (struct lx_bpf_program)) {
+ return (EINVAL);
+ }
+ lbp = (struct lx_bpf_program *)optval;
+ bp.bf_len = lbp->bf_len;
+ /* LINTED: alignment */
+ bp.bf_insns = (struct bpf_insn *)lbp->bf_insns;
+ optval = &bp;
+ break;
+
+ case LX_SO_PASSSEC:
+ /*
+ * SO_PASSSEC is very similar to SO_PASSCRED (emulated by
+ * SO_RECVUCRED) in that it requests that cmsgs containing
+ * identity information be attached to recieved messages.
+ * Instead of ucred information, security-module-specific
+ * information such as selinux label is expected
+ *
+ * Since LX does not at all support selinux today, the
+ * option is silently accepted.
+ */
+ return (0);
+
+ case LX_SO_PASSCRED:
+ /*
+ * In many cases, the Linux SO_PASSCRED is mapped to the SunOS
+ * SO_RECVUCRED to enable the passing of peer credential
+ * information via received cmsgs. One exception is for
+ * connection-oriented AF_UNIX sockets which do not yet support
+ * that option. Instead, we track the setting internally and,
+ * when there is appropriate cmsg space, emulate the credential
+ * passing by querying the STREAMS ioctl.
+ */
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ lx_socket_aux_data_t *sad;
+
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ intval = (int *)optval;
+ sad = lx_sad_acquire(SOTOV(so));
+ sad->lxsad_stream_cred = !(*intval == 0);
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, SOL_SOCKET, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+ switch (optname) {
+ case LX_ICMP_FILTER:
+ /*
+ * This option is currently ignored to appease ping.
+ */
+ return (0);
+
+ case LX_IPV6_CHECKSUM:
+ /*
+ * Ping6 tries to set the IPV6_CHECKSUM offset in a way that
+ * illumos won't allow. Quietly ignore this to prevent it from
+ * complaining.
+ */
+ return (0);
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_packet(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+ struct packet_mreq *mr;
+
+ switch (optname) {
+ case LX_PACKET_ADD_MEMBERSHIP:
+ case LX_PACKET_DROP_MEMBERSHIP:
+ /* Convert Linux mr_type to illumos */
+ if (optlen != sizeof (struct packet_mreq)) {
+ return (EINVAL);
+ }
+ mr = (struct packet_mreq *)optval;
+ if (--mr->mr_type > PACKET_MR_ALLMULTI)
+ return (EINVAL);
+ optval = mr;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, SOL_PACKET, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IGMP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IP, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IPV6, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_icmpv6(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+ if (optname == LX_ICMP6_FILTER) {
+ error = socket_getsockopt(so, IPPROTO_ICMPV6, ICMP6_FILTER,
+ optval, optlen, 0, CRED());
+
+ /*
+ * ICMP6_FILTER is inverted on Linux. Make it so before copying
+ * back to caller's buffer.
+ */
+ if (error == 0) {
+ LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+ }
+ return (error);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+ 0, CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+
+ switch (optname) {
+ case LX_TCP_CORK:
+ /*
+ * We do not support TCP_CORK but some apps rely on it. Rather
+ * than return an error we just return 0. This isn't exactly a
+ * lie, since this option really isn't set, but it's not the
+ * whole truth either. Fortunately, we aren't under oath.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ *intval = 0;
+ }
+ *optlen = sizeof (int);
+ return (error);
+
+ case LX_TCP_DEFER_ACCEPT:
+ /*
+ * We do support TCP_DEFER_ACCEPT using the datafilt(7M) socket
+ * filter but we don't emulate the timeout aspect so treat the
+ * existence as 1 and absence as 0.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ struct fil_info fi[10];
+ int i;
+ socklen_t len = sizeof (fi);
+
+ if ((error = socket_getsockopt(so, SOL_FILTER,
+ FIL_LIST, fi, &len, 0, CRED())) != 0) {
+ *optlen = sizeof (int);
+ return (error);
+ }
+
+ *intval = 0;
+ len = len / sizeof (struct fil_info);
+ for (i = 0; i < len; i++) {
+ if (fi[i].fi_flags == FILF_PROG &&
+ strcmp(fi[i].fi_name, "datafilt") == 0) {
+ *intval = 1;
+ break;
+ }
+ }
+ }
+ *optlen = sizeof (int);
+ return (error);
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_TCP, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_socket(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+
+ switch (optname) {
+ case LX_SO_PASSSEC:
+ /*
+ * Communicate value of 0 since selinux-related functionality
+ * is not supported.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ *intval = 0;
+ }
+ *optlen = sizeof (int);
+ return (error);
+
+ case LX_SO_PASSCRED:
+ /*
+ * Special handling for connection-oriented AF_UNIX sockets.
+ * See lx_setsockopt_socket for more details.
+ */
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ lx_socket_aux_data_t *sad;
+
+ if (*optlen < sizeof (int)) {
+ return (EINVAL);
+ }
+ sad = lx_sad_acquire(SOTOV(so));
+ *intval = sad->lxsad_stream_cred;
+ *optlen = sizeof (int);
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ break;
+
+ case LX_SO_PEERCRED:
+ if (*optlen < sizeof (struct lx_ucred)) {
+ error = EINVAL;
+ } else {
+ struct lx_ucred *lcred = (struct lx_ucred *)optval;
+
+ mutex_enter(&so->so_lock);
+ if ((so->so_mode & SM_CONNREQUIRED) == 0) {
+ error = ENOTSUP;
+ } else if (so->so_peercred == NULL) {
+ error = EINVAL;
+ } else {
+ lcred->lxu_uid = crgetuid(so->so_peercred);
+ lcred->lxu_gid = crgetgid(so->so_peercred);
+ lcred->lxu_pid = so->so_cpid;
+ }
+ mutex_exit(&so->so_lock);
+ }
+ *optlen = sizeof (struct lx_ucred);
+ return (error);
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, SOL_SOCKET, optname, optval, optlen, 0,
+ CRED());
+
+ if (error == 0) {
+ switch (optname) {
+ case SO_TYPE:
+ /* translate our type back to Linux */
+ *intval = STOL_SOCKTYPE(*intval);
+ break;
+
+ case SO_ERROR:
+ *intval = lx_errno(*intval, EINVAL);
+ break;
+ default:
+ break;
+ }
+ }
+ return (error);
+}
+
+static int
+lx_getsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_RAW, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_packet(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, SOL_PACKET, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IGMP, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+long
+lx_setsockopt(int sock, int level, int optname, void *optval, socklen_t optlen)
+{
+ struct sonode *so;
+ file_t *fp;
+ int buflen = 0;
+ intptr_t stkbuf[2];
+ void *optbuf = stkbuf;
+ int error = 0;
+
+ if (optlen != 0) {
+ if (optlen > SO_MAXARGSIZE) {
+ return (set_errno(EINVAL));
+ }
+ if (optlen > sizeof (stkbuf)) {
+ buflen = optlen;
+ optbuf = kmem_alloc(optlen, KM_SLEEP);
+ } else {
+ /*
+ * Zero the on-stack buffer to avoid poisoning smaller
+ * optvals with stack garbage.
+ */
+ stkbuf[0] = 0;
+ stkbuf[1] = 0;
+ }
+ if (copyin(optval, optbuf, optlen) != 0) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(EFAULT));
+ }
+ } else {
+ optbuf = NULL;
+ }
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(error));
+ }
+
+ switch (level) {
+ case LX_IPPROTO_IP:
+ error = lx_setsockopt_ip(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_IPV6:
+ error = lx_setsockopt_ipv6(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_ICMPV6:
+ error = lx_setsockopt_icmpv6(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_TCP:
+ error = lx_setsockopt_tcp(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_SOCKET:
+ error = lx_setsockopt_socket(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_RAW:
+ error = lx_setsockopt_raw(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_PACKET:
+ error = lx_setsockopt_packet(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_IGMP:
+ error = lx_setsockopt_igmp(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_NETLINK:
+ /*
+ * Since our netlink implmentation is modeled after Linux,
+ * sockopts can be passed directly through.
+ */
+ error = socket_setsockopt(so, LX_SOL_NETLINK, optname, optval,
+ optlen, CRED());
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+
+ if (error == ENOPROTOOPT) {
+ char buf[LX_UNSUP_BUFSZ];
+
+ (void) snprintf(buf, LX_UNSUP_BUFSZ, "setsockopt(%d, %d)",
+ level, optname);
+ lx_unsupported(buf);
+ }
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ releasef(sock);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_getsockopt(int sock, int level, int optname, void *optval,
+ socklen_t *optlenp)
+{
+ struct sonode *so;
+ file_t *fp;
+ int error = 0, buflen = 0;
+ socklen_t optlen;
+ intptr_t stkbuf[2];
+ void *optbuf = stkbuf;
+
+ if (copyin(optlenp, &optlen, sizeof (optlen)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (optlen != 0) {
+ if (optlen > SO_MAXARGSIZE) {
+ return (set_errno(EINVAL));
+ }
+ if (optlen > sizeof (stkbuf)) {
+ buflen = optlen;
+ optbuf = kmem_zalloc(optlen, KM_SLEEP);
+ } else {
+ /* zero the on-stack buffer, just in case */
+ stkbuf[0] = 0;
+ stkbuf[1] = 0;
+ }
+ } else {
+ optbuf = NULL;
+ }
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(error));
+ }
+
+ switch (level) {
+ case LX_IPPROTO_IP:
+ error = lx_getsockopt_ip(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_IPV6:
+ error = lx_getsockopt_ipv6(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_ICMPV6:
+ error = lx_getsockopt_icmpv6(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_TCP:
+ error = lx_getsockopt_tcp(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_SOCKET:
+ error = lx_getsockopt_socket(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_RAW:
+ error = lx_getsockopt_raw(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_PACKET:
+ error = lx_getsockopt_packet(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_IGMP:
+ error = lx_getsockopt_igmp(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_NETLINK:
+ /*
+ * Since our netlink implmentation is modeled after Linux,
+ * sockopts can be passed directly through.
+ */
+ error = socket_getsockopt(so, LX_SOL_NETLINK, optname, optval,
+ &optlen, 0, CRED());
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ if (error == ENOPROTOOPT) {
+ char buf[LX_UNSUP_BUFSZ];
+
+ (void) snprintf(buf, LX_UNSUP_BUFSZ, "getsockopt(%d, %d)",
+ level, optname);
+ lx_unsupported(buf);
+ }
+ if (copyout(&optlen, optlenp, sizeof (optlen)) != 0) {
+ error = EFAULT;
+ }
+ if (error == 0 && optlen > 0) {
+ VERIFY(optlen <= sizeof (stkbuf) || optlen <= buflen);
+ if (copyout(optbuf, optval, optlen) != 0) {
+ error = EFAULT;
+ }
+ }
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ releasef(sock);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_getname_common(lx_getname_type_t type, int sockfd, void *np, int *nlp)
+{
+ struct sockaddr_storage buf;
+ struct sockaddr *name = (struct sockaddr *)&buf;
+ socklen_t namelen, namelen_orig;
+ int err, tmp;
+ struct sonode *so;
+
+ /* We need to validate the name address up front to pass LTP. */
+ if (copyin(np, &tmp, sizeof (tmp)) != 0)
+ return (set_errno(EFAULT));
+
+ if (copyin(nlp, &namelen, sizeof (socklen_t)) != 0)
+ return (set_errno(EFAULT));
+ namelen_orig = namelen;
+
+ /* LTP can pass -1 */
+ if ((int)namelen < 0)
+ return (set_errno(EINVAL));
+
+ if ((so = getsonode(sockfd, &err, NULL)) == NULL)
+ return (set_errno(err));
+
+ bzero(&buf, sizeof (buf));
+ namelen = sizeof (struct sockaddr_storage);
+ if (type == LX_GETPEERNAME) {
+ err = socket_getpeername(so, name, &namelen, B_FALSE, CRED());
+ } else {
+ err = socket_getsockname(so, name, &namelen, CRED());
+ }
+
+ if (err == 0) {
+ ASSERT(namelen <= so->so_max_addr_len);
+ err = stol_sockaddr_copyout(name, namelen,
+ (struct sockaddr *)np, (socklen_t *)nlp, namelen_orig);
+ }
+
+ releasef(sockfd);
+ return (err != 0 ? set_errno(err) : 0);
+}
+
+long
+lx_getpeername(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETPEERNAME, sockfd, np, nlp));
+}
+
+long
+lx_getsockname(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETSOCKNAME, sockfd, np, nlp));
+}
+
+static int
+lx_accept_common(int sock, struct sockaddr *name, socklen_t *nlp, int flags)
+{
+ struct sonode *so;
+ file_t *fp;
+ int error;
+ socklen_t namelen;
+ struct sonode *nso;
+ struct vnode *nvp;
+ struct file *nfp;
+ int nfd;
+ int arg;
+
+ if (flags & ~(LX_SOCK_CLOEXEC | LX_SOCK_NONBLOCK)) {
+ return (set_errno(EINVAL));
+ }
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL)
+ return (set_errno(error));
+
+ if (name != NULL) {
+ /*
+ * The Linux man page says that -1 is returned and errno is set
+ * to EFAULT if the "name" address is bad, but it is silent on
+ * what to set errno to if the "namelen" address is bad.
+ * LTP expects EINVAL.
+ *
+ * Note that we must first check the name pointer, as the Linux
+ * docs state nothing is copied out if the "name" pointer is
+ * NULL. If it is NULL, we don't care about the namelen
+ * pointer's value or about dereferencing it.
+ */
+ if (copyin(nlp, &namelen, sizeof (namelen))) {
+ releasef(sock);
+ return (set_errno(EINVAL));
+ }
+ if (namelen == 0) {
+ name = NULL;
+ }
+ } else {
+ namelen = 0;
+ }
+
+ /*
+ * Allocate the user fd before socket_accept() in order to
+ * catch EMFILE errors before calling socket_accept().
+ */
+ if ((error = falloc(NULL, FWRITE|FREAD, &nfp, &nfd)) != 0) {
+ eprintsoline(so, EMFILE);
+ releasef(sock);
+ return (set_errno(error));
+ }
+ if ((error = socket_accept(so, fp->f_flag, CRED(), &nso)) != 0) {
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ nvp = SOTOV(nso);
+
+ if (namelen != 0) {
+ socklen_t addrlen = sizeof (struct sockaddr_storage);
+ struct sockaddr_storage buf;
+ struct sockaddr *addrp = (struct sockaddr *)&buf;
+
+ if ((error = socket_getpeername(nso, addrp, &addrlen, B_TRUE,
+ CRED())) == 0) {
+ error = stol_sockaddr_copyout(addrp, addrlen,
+ name, nlp, namelen);
+ /*
+ * Logic might dictate that we should check if we can
+ * write to the namelen pointer earlier so we don't
+ * accept a pending connection only to fail the call
+ * because we can't write the namelen value back out.
+ * However, testing shows Linux does indeed fail the
+ * call after accepting the connection so we must
+ * behave in a compatible manner.
+ */
+ } else {
+ ASSERT(error == EINVAL || error == ENOTCONN);
+ error = ECONNABORTED;
+ }
+ }
+
+ if (error != 0) {
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ (void) socket_close(nso, 0, CRED());
+ socket_destroy(nso);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ /* Fill in the entries that falloc reserved */
+ nfp->f_vnode = nvp;
+ mutex_exit(&nfp->f_tlock);
+ setf(nfd, nfp);
+
+ /* Act on LX_SOCK_CLOEXEC from flags */
+ if (flags & LX_SOCK_CLOEXEC) {
+ f_setfd(nfd, FD_CLOEXEC);
+ }
+
+ /*
+ * In Linux, accept()ed sockets do not inherit anything set by fcntl(),
+ * so either explicitly set the flags or filter those out.
+ *
+ * The VOP_SETFL code is a simplification of the F_SETFL code in
+ * fcntl(). Ignore any errors from VOP_SETFL.
+ */
+ arg = 0;
+ if (flags & LX_SOCK_NONBLOCK)
+ arg |= FNONBLOCK;
+
+ error = VOP_SETFL(nvp, nfp->f_flag, arg, nfp->f_cred, NULL);
+ if (error != 0) {
+ eprintsoline(so, error);
+ error = 0;
+ } else {
+ mutex_enter(&nfp->f_tlock);
+ nfp->f_flag &= ~FMASK | (FREAD|FWRITE);
+ nfp->f_flag |= arg;
+ mutex_exit(&nfp->f_tlock);
+ }
+
+ releasef(sock);
+ return (nfd);
+}
+
+long
+lx_accept(int sockfd, void *np, int *nlp)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, 0));
+}
+
+long
+lx_accept4(int sockfd, void *np, int *nlp, int flags)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, flags));
+}
+
+long
+lx_listen(int sockfd, int backlog)
+{
+ return (listen(sockfd, backlog, 0));
+}
+
+long
+lx_shutdown(int sockfd, int how)
+{
+ return (shutdown(sockfd, how, 0));
+}
+
+/*
+ * Connect two sockets together for a socketpair. This is derived from
+ * so_socketpair, but forgoes the task of dealing with file descriptors.
+ */
+static int
+lx_socketpair_connect(file_t *fp1, file_t *fp2)
+{
+ sonode_t *so1, *so2;
+ sotpi_info_t *sti1, *sti2;
+ struct sockaddr_ux name;
+ int error;
+
+ so1 = VTOSO(fp1->f_vnode);
+ so2 = VTOSO(fp2->f_vnode);
+ sti1 = SOTOTPI(so1);
+ sti2 = SOTOTPI(so2);
+
+ VERIFY(so1->so_ops == &sotpi_sonodeops &&
+ so2->so_ops == &sotpi_sonodeops);
+
+ if (so1->so_type == SOCK_DGRAM) {
+ /*
+ * Bind both sockets and connect them with each other.
+ */
+ error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+ error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+ name.sou_family = AF_UNIX;
+ name.sou_addr = sti2->sti_ux_laddr;
+ error = socket_connect(so1, (struct sockaddr *)&name,
+ (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+ if (error) {
+ return (error);
+ }
+ name.sou_addr = sti1->sti_ux_laddr;
+ error = socket_connect(so2, (struct sockaddr *)&name,
+ (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+ return (error);
+ } else {
+ sonode_t *nso;
+
+ /*
+ * Bind both sockets, with 'so1' being a listener. Connect
+ * 'so2' to 'so1', doing so as nonblocking to avoid waiting for
+ * soaccept to complete. Accept the connection on 'so1',
+ * replacing the socket/vnode in 'fp1' with the new connection.
+ *
+ * We could simply call socket_listen() here (which would do the
+ * binding automatically) if the code didn't rely on passing
+ * _SOBIND_NOXLATE to the TPI implementation of socket_bind().
+ */
+ error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC|
+ _SOBIND_NOXLATE|_SOBIND_LISTEN|_SOBIND_SOCKETPAIR, CRED());
+ if (error) {
+ return (error);
+ }
+ error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+
+ name.sou_family = AF_UNIX;
+ name.sou_addr = sti1->sti_ux_laddr;
+ error = socket_connect(so2,
+ (struct sockaddr *)&name,
+ (socklen_t)sizeof (name),
+ FNONBLOCK, _SOCONNECT_NOXLATE, CRED());
+ if (error != 0 && error != EINPROGRESS) {
+ return (error);
+ }
+
+ error = socket_accept(so1, 0, CRED(), &nso);
+ if (error) {
+ return (error);
+ }
+
+ /* wait for so2 being SS_CONNECTED */
+ mutex_enter(&so2->so_lock);
+ error = sowaitconnected(so2, 0, 0);
+ mutex_exit(&so2->so_lock);
+ if (error != 0) {
+ (void) socket_close(nso, 0, CRED());
+ socket_destroy(nso);
+ return (error);
+ }
+
+ (void) socket_close(so1, 0, CRED());
+ socket_destroy(so1);
+ fp1->f_vnode = SOTOV(nso);
+ }
+ return (0);
+}
+
+long
+lx_socketpair(int domain, int type, int protocol, int *sv)
+{
+ int err, options, fds[2];
+ file_t *fps[2];
+
+ if ((err = lx_convert_sock_args(domain, type, protocol, &domain, &type,
+ &options, &protocol)) != 0) {
+ return (set_errno(err));
+ }
+
+ if ((err = lx_socket_create(domain, type, protocol, options, &fps[0],
+ &fds[0])) != 0) {
+ return (set_errno(err));
+ }
+
+ /*
+ * While it seems silly to check the family after socket creation, this
+ * is done to appease LTP when it tries some outlandish combinations of
+ * domain/type/protocol. The socket_create function is relied upon to
+ * emit the expected errors.
+ */
+ if (VTOSO(fps[0]->f_vnode)->so_family != AF_UNIX) {
+ lx_socket_destroy(fps[0], fds[0]);
+ return (set_errno(EOPNOTSUPP));
+ }
+
+ if ((err = lx_socket_create(domain, type, protocol, options, &fps[1],
+ &fds[1])) != 0) {
+ lx_socket_destroy(fps[0], fds[0]);
+ return (set_errno(err));
+ }
+
+ err = lx_socketpair_connect(fps[0], fps[1]);
+ if (err != 0) {
+ lx_socket_destroy(fps[0], fds[0]);
+ lx_socket_destroy(fps[1], fds[1]);
+ return (set_errno(err));
+ }
+
+ setf(fds[0], fps[0]);
+ setf(fds[1], fps[1]);
+ if ((options & SOCK_CLOEXEC) != 0) {
+ f_setfd(fds[0], FD_CLOEXEC);
+ f_setfd(fds[1], FD_CLOEXEC);
+ }
+ if (copyout(fds, sv, sizeof (fds)) != 0) {
+ (void) closeandsetf(fds[0], NULL);
+ (void) closeandsetf(fds[1], NULL);
+ return (set_errno(EFAULT));
+ }
+ return (0);
+}
+
+
+#if defined(_SYSCALL32_IMPL)
+
+#define LX_SYS_SOCKETCALL 102
+#define LX_SOCKETCALL_MAX 20
+
+typedef long (*lx_sockfn_t)();
+
+static struct {
+ lx_sockfn_t s_fn; /* Function implementing the subcommand */
+ int s_nargs; /* Number of arguments the function takes */
+} lx_socketcall_fns[] = {
+ lx_socket, 3, /* socket */
+ lx_bind, 3, /* bind */
+ lx_connect, 3, /* connect */
+ lx_listen, 2, /* listen */
+ lx_accept, 3, /* accept */
+ lx_getsockname, 3, /* getsockname */
+ lx_getpeername, 3, /* getpeername */
+ lx_socketpair, 4, /* socketpair */
+ lx_send, 4, /* send */
+ lx_recv, 4, /* recv */
+ lx_sendto, 6, /* sendto */
+ lx_recvfrom, 6, /* recvfrom */
+ lx_shutdown, 2, /* shutdown */
+ lx_setsockopt, 5, /* setsockopt */
+ lx_getsockopt, 5, /* getsockopt */
+ lx_sendmsg, 3, /* sendmsg */
+ lx_recvmsg, 3, /* recvmsg */
+ lx_accept4, 4, /* accept4 */
+ NULL, 5, /* recvmmsg */
+ NULL, 4 /* sendmmsg */
+};
+
+long
+lx_socketcall(long p1, uint32_t *p2)
+{
+ int subcmd, i;
+ unsigned long args[6] = { 0, 0, 0, 0, 0, 0 };
+
+ /* incoming subcmds are 1-indexed */
+ subcmd = (int)p1 - 1;
+
+ if (subcmd < 0 || subcmd >= LX_SOCKETCALL_MAX ||
+ lx_socketcall_fns[subcmd].s_fn == NULL) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Copy the arguments to the subcommand in from the app's address
+ * space, returning EFAULT if we get a bogus pointer.
+ */
+ for (i = 0; i < lx_socketcall_fns[subcmd].s_nargs; i++) {
+ uint32_t arg;
+
+ if (copyin(&p2[i], &arg, sizeof (uint32_t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ args[i] = (unsigned long)arg;
+ }
+
+ return ((lx_socketcall_fns[subcmd].s_fn)(args[0], args[1], args[2],
+ args[3], args[4], args[5]));
+}
+
+#endif /* defined(_SYSCALL32_IMPL) */
+
+static void
+lx_socket_vsd_free(void *data)
+{
+ lx_socket_aux_data_t *entry;
+
+ entry = (lx_socket_aux_data_t *)data;
+ mutex_destroy(&entry->lxsad_lock);
+ kmem_free(entry, sizeof (*entry));
+}
+
+void
+lx_socket_init()
+{
+ vsd_create(&lx_socket_vsd, lx_socket_vsd_free);
+}
+
+void
+lx_socket_fini()
+{
+ vsd_destroy(&lx_socket_vsd);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_stat.c b/usr/src/uts/common/brand/lx/syscall/lx_stat.c
new file mode 100644
index 0000000000..81e37dafe2
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_stat.c
@@ -0,0 +1,474 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/model.h>
+#include <sys/mode.h>
+#include <sys/stat.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+#include <sys/brand.h>
+#include <sys/ddi.h>
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+
+typedef struct lx_timespec32 {
+ int32_t ts_sec;
+ int32_t ts_nsec;
+} lx_timespec32_t;
+
+typedef struct lx_timespec64 {
+ int64_t ts_sec;
+ int64_t ts_nsec;
+}lx_timespec64_t;
+
+struct lx_stat32 {
+ uint16_t st_dev;
+ uint16_t st_pad1;
+ uint32_t st_ino;
+ uint16_t st_mode;
+ uint16_t st_nlink;
+ uint16_t st_uid;
+ uint16_t st_gid;
+ uint16_t st_rdev;
+ uint16_t st_pad2;
+ uint32_t st_size;
+ uint32_t st_blksize;
+ uint32_t st_blocks;
+ lx_timespec32_t st_atime;
+ lx_timespec32_t st_mtime;
+ lx_timespec32_t st_ctime;
+ uint32_t st_pad3;
+ uint32_t st_pad4;
+};
+
+#pragma pack(4)
+struct lx_stat64_32 {
+ uint64_t st_dev;
+ uint32_t st_pad1;
+ uint32_t st_small_ino;
+ uint32_t st_mode;
+ uint32_t st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_rdev;
+ uint32_t st_pad2;
+ uint64_t st_size;
+ uint32_t st_blksize;
+ uint64_t st_blocks;
+ lx_timespec32_t st_atime;
+ lx_timespec32_t st_mtime;
+ lx_timespec32_t st_ctime;
+ uint64_t st_ino;
+};
+#pragma pack()
+
+#if defined(_LP64)
+struct lx_stat64_64 {
+ uint64_t st_dev;
+ uint64_t st_ino;
+ uint64_t st_nlink; /* yes, the order really is */
+ uint32_t st_mode; /* different for these two */
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint32_t st_pad0;
+ uint64_t st_rdev;
+ int64_t st_size;
+ int64_t st_blksize;
+ int64_t st_blocks;
+ lx_timespec64_t st_atime;
+ lx_timespec64_t st_mtime;
+ lx_timespec64_t st_ctime;
+ int64_t st_unused[3];
+};
+#endif /* defined(_LP64) */
+
+typedef enum lx_stat_fmt {
+ LXF_STAT32,
+ LXF_STAT64_32,
+ LXF_STAT64_64
+} lx_stat_fmt_t;
+
+static void
+lx_stat_xlate_dev(vattr_t *vattr)
+{
+ lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+ dev_t dev;
+ lx_virt_disk_t *vd;
+ boolean_t is_dev;
+
+ if (S_ISCHR(vattr->va_mode) || S_ISBLK(vattr->va_mode)) {
+ dev = vattr->va_rdev;
+ is_dev = B_TRUE;
+ } else {
+ dev = vattr->va_fsid;
+ is_dev = B_FALSE;
+ }
+
+ /*
+ * See if this is the /dev/zfs device. If it is, the device number has
+ * already been converted to Linux format in the lx devfs so we have
+ * to check for that and not a native major/minor style.
+ */
+ if (S_ISCHR(vattr->va_mode) &&
+ LX_GETMAJOR(dev) == getmajor(lxzd->lxzd_zfs_dev) &&
+ LX_GETMINOR(dev) == 0) {
+ /*
+ * We use the /dev/zfs device as a placeholder for our in-zone
+ * fabricated /dev/zfsds0 device that we're pretending / is
+ * mounted on. lx_zone_get_zfsds has pre-allocated this
+ * entry in the emulated device list. Reset dev so we can
+ * properly match in the following loop.
+ */
+ dev = curproc->p_zone->zone_rootvp->v_vfsp->vfs_dev;
+ }
+
+ /* Substitute emulated major/minor on zvols or mounted datasets. */
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_real_dev == dev) {
+ dev = vd->lxvd_emul_dev;
+ /*
+ * We only update rdev for matching zfds/zvol devices
+ * so that the other devices are unchanged.
+ */
+ if (is_dev) {
+ vattr->va_rdev = LX_MAKEDEVICE(getmajor(dev),
+ getminor(dev));
+ }
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ /* Mangle st_dev into expected format */
+ vattr->va_fsid = LX_MAKEDEVICE(getmajor(dev), getminor(dev));
+}
+
+static long
+lx_stat_common(vnode_t *vp, cred_t *cr, void *outp, lx_stat_fmt_t fmt)
+{
+ vattr_t vattr;
+ mode_t mode;
+ int error;
+
+ vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, 0, cr, NULL)) != 0) {
+ return (error);
+ }
+
+ mode = VTTOIF(vattr.va_type) | vattr.va_mode;
+ if ((mode & S_IFMT) == S_IFBLK) {
+ /* Linux seems to report a 0 st_size for all block devices */
+ vattr.va_size = 0;
+ }
+ if (vattr.va_rdev == NODEV) {
+ /* Linux leaves st_rdev zeroed when it is absent */
+ vattr.va_rdev = 0;
+ }
+
+ lx_stat_xlate_dev(&vattr);
+
+ if (fmt == LXF_STAT32) {
+ struct lx_stat32 sb;
+
+ if (vattr.va_fsid > USHRT_MAX || vattr.va_rdev > USHRT_MAX ||
+ vattr.va_nlink > USHRT_MAX || vattr.va_size > INT_MAX) {
+ return (EOVERFLOW);
+ }
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = LX_UID32_TO_UID16(vattr.va_uid);
+ sb.st_gid = LX_GID32_TO_GID16(vattr.va_gid);
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ } else if (fmt == LXF_STAT64_32) {
+ struct lx_stat64_32 sb;
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_small_ino = (vattr.va_nodeid & UINT_MAX);
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = vattr.va_uid;
+ sb.st_gid = vattr.va_gid;
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ } else if (fmt == LXF_STAT64_64) {
+#if defined(_LP64)
+ struct lx_stat64_64 sb;
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = vattr.va_uid;
+ sb.st_gid = vattr.va_gid;
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+#else
+ /* Invalid output format on 32-bit */
+ VERIFY(0);
+#endif
+ }
+
+ /* Invalid output format */
+ VERIFY(0);
+ return (0);
+}
+
+long
+lx_stat32(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp, LXF_STAT32);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fstat32(int fd, void *outp)
+{
+ file_t *fp;
+ int error;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ error = lx_stat_common(fp->f_vnode, fp->f_cred, outp, LXF_STAT32);
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lstat32(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp, LXF_STAT32);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_stat64(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fstat64(int fd, void *outp)
+{
+ file_t *fp;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ error = lx_stat_common(fp->f_vnode, fp->f_cred, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+#define LX_FSTATAT_ALLOWED (LX_AT_SYMLINK_NOFOLLOW | LX_AT_EMPTY_PATH | \
+ LX_AT_NO_AUTOMOUNT)
+
+long
+lx_fstatat64(int fd, char *name, void *outp, int flag)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ enum symfollow follow = FOLLOW;
+ int error;
+ char c;
+
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+ if ((flag & ~LX_FSTATAT_ALLOWED) != 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((flag & LX_AT_NO_AUTOMOUNT) != 0) {
+ /*
+ * While AT_NO_AUTOMOUNT is a legal flag for fstatat64, it is
+ * not yet supported by lx_autofs.
+ */
+ lx_unsupported("fstatat(AT_NO_AUTOMOUNT)");
+ return (set_errno(EINVAL));
+ }
+ if ((flag & LX_AT_SYMLINK_NOFOLLOW) != 0) {
+ follow = NO_FOLLOW;
+ }
+
+ if (copyin(name, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (c == '\0') {
+ if ((flag & LX_AT_EMPTY_PATH) == 0) {
+ return (set_errno(ENOENT));
+ }
+
+ /*
+ * When AT_EMPTY_PATH is set and and empty string has been
+ * passed for the name parameter, direct the lookup against the
+ * vnode for that fd.
+ */
+ if (fd == AT_FDCWD) {
+ vp = PTOU(curproc)->u_cdir;
+ VN_HOLD(vp);
+ cr = CRED();
+ crhold(cr);
+ } else {
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ cr = fp->f_cred;
+ crhold(cr);
+ releasef(fd);
+ }
+ } else {
+ if ((error = cstatat_getvp(fd, name, follow, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ }
+
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lstat64(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sync.c b/usr/src/uts/common/brand/lx/syscall/lx_sync.c
new file mode 100644
index 0000000000..614afca0b0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sync.c
@@ -0,0 +1,86 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+long
+lx_syncfs(int fd)
+{
+ file_t *fp;
+ vfs_t *vfsp;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ vfsp = fp->f_vnode->v_vfsp;
+ releasef(fd);
+
+ (void) (vfsp->vfs_op->vfs_sync)(vfsp, 0, CRED());
+
+ return (0);
+}
+
+#define LX_SYNC_FILE_RANGE_WAIT_BEFORE 0x1
+#define LX_SYNC_FILE_RANGE_WRITE 0x2
+#define LX_SYNC_FILE_RANGE_WAIT_AFTER 0x4
+
+#define LX_SYNC_FILE_RANGE_VALID (LX_SYNC_FILE_RANGE_WAIT_BEFORE | \
+ LX_SYNC_FILE_RANGE_WRITE | LX_SYNC_FILE_RANGE_WAIT_AFTER)
+
+
+long
+lx_sync_file_range(int fd, off_t offset, off_t nbytes, int flags)
+{
+ file_t *fp;
+ int error, sflags = 0;
+
+ if ((flags & ~LX_SYNC_FILE_RANGE_VALID) != 0)
+ return (set_errno(EINVAL));
+ if (offset < 0 || nbytes < 0)
+ return (set_errno(EINVAL));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ /*
+ * Since sync_file_range is implemented in terms of VOP_PUTPAGE, both
+ * SYNC_FILE_RANGE_WAIT flags are treated as forcing synchronous
+ * operation. While this differs from the Linux behavior where
+ * BEFORE/AFTER are distinct, it achieves an adequate level of safety
+ * since the requested data is synced out at the end of the call.
+ */
+ if ((flags & (LX_SYNC_FILE_RANGE_WAIT_BEFORE |
+ LX_SYNC_FILE_RANGE_WAIT_AFTER)) == 0) {
+ sflags |= B_ASYNC;
+ }
+
+ error = VOP_PUTPAGE(fp->f_vnode, offset, nbytes, sflags, CRED(), NULL);
+ if (error == ENOSYS) {
+ error = ESPIPE;
+ }
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c b/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c
new file mode 100644
index 0000000000..449d5882d4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c
@@ -0,0 +1,218 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <vm/anon.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/zone.h>
+#include <sys/time.h>
+
+typedef struct lx_sysinfo {
+ int64_t si_uptime; /* Seconds since boot */
+ uint64_t si_loads[3]; /* 1, 5, and 15 minute avg runq length */
+ uint64_t si_totalram; /* Total memory size */
+ uint64_t si_freeram; /* Available memory */
+ uint64_t si_sharedram; /* Shared memory */
+ uint64_t si_bufferram; /* Buffer memory */
+ uint64_t si_totalswap; /* Total swap space */
+ uint64_t si_freeswap; /* Avail swap space */
+ uint16_t si_procs; /* Process count */
+ uint16_t si_pad; /* Padding */
+ uint64_t si_totalhigh; /* High memory size */
+ uint64_t si_freehigh; /* Avail high memory */
+ uint32_t si_mem_unit; /* Unit size of memory fields */
+} lx_sysinfo_t;
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit usermode struct.
+ */
+#pragma pack(4)
+typedef struct lx_sysinfo32 {
+ int32_t si_uptime; /* Seconds since boot */
+ uint32_t si_loads[3]; /* 1, 5, and 15 minute avg runq length */
+ uint32_t si_totalram; /* Total memory size */
+ uint32_t si_freeram; /* Available memory */
+ uint32_t si_sharedram; /* Shared memory */
+ uint32_t si_bufferram; /* Buffer memory */
+ uint32_t si_totalswap; /* Total swap space */
+ uint32_t si_freeswap; /* Avail swap space */
+ uint16_t si_procs; /* Process count */
+ uint16_t si_pad; /* Padding */
+ uint32_t si_totalhigh; /* High memory size */
+ uint32_t si_freehigh; /* Avail high memory */
+ uint32_t si_mem_unit; /* Unit size of memory fields */
+ char __si_pad[8];
+} lx_sysinfo32_t;
+#pragma pack()
+#endif
+
+extern pgcnt_t swapfs_minfree;
+
+static void
+lx_sysinfo_common(lx_sysinfo_t *si)
+{
+ zone_t *zone = curthread->t_procp->p_zone;
+ uint64_t zphysmem, zfreemem, ztotswap, zfreeswap;
+
+ si->si_uptime = gethrestime_sec() - zone->zone_boot_time;
+
+ si->si_loads[0] = zone->zone_hp_avenrun[0];
+ si->si_loads[1] = zone->zone_hp_avenrun[1];
+ si->si_loads[2] = zone->zone_hp_avenrun[2];
+
+ /*
+ * In linux each thread looks like a process, so we conflate the
+ * two in this stat as well.
+ */
+ si->si_procs = (int32_t)zone->zone_nlwps;
+
+ /*
+ * If memory or swap limits are set on the zone, use those, otherwise
+ * use the system values. physmem and freemem are in pages, but the
+ * zone values are in bytes. Likewise, ani_max and ani_free are in
+ * pages.
+ */
+ if (zone->zone_phys_mem_ctl == UINT64_MAX) {
+ zphysmem = physmem;
+ zfreemem = freemem;
+ } else {
+ zphysmem = btop(zone->zone_phys_mem_ctl);
+ zfreemem = btop(zone->zone_phys_mem_ctl - zone->zone_phys_mem);
+ }
+
+ if (zone->zone_max_swap_ctl == UINT64_MAX) {
+ ztotswap = k_anoninfo.ani_max;
+ zfreeswap = k_anoninfo.ani_free;
+ } else {
+ /*
+ * See the comment in swapctl for a description of how free is
+ * calculated within a zone.
+ */
+ rctl_qty_t used;
+ spgcnt_t avail;
+ uint64_t max;
+
+ avail = MAX((spgcnt_t)(availrmem - swapfs_minfree), 0);
+ max = k_anoninfo.ani_max + k_anoninfo.ani_mem_resv + avail;
+
+ mutex_enter(&zone->zone_mem_lock);
+ ztotswap = btop(zone->zone_max_swap_ctl);
+ used = btop(zone->zone_max_swap);
+ mutex_exit(&zone->zone_mem_lock);
+
+ zfreeswap = MIN(ztotswap, max) - used;
+ }
+
+ /*
+ * If the maximum memory stat is less than 1^20 pages (i.e. 4GB),
+ * then we report the result in bytes. Otherwise we use pages.
+ * Once we start supporting >1TB systems/zones, we'll need a third
+ * option.
+ */
+ if (MAX(zphysmem, ztotswap) < 1024 * 1024) {
+ si->si_totalram = ptob(zphysmem);
+ si->si_freeram = ptob(zfreemem);
+ si->si_totalswap = ptob(ztotswap);
+ si->si_freeswap = ptob(zfreeswap);
+ si->si_mem_unit = 1;
+ } else {
+ si->si_totalram = zphysmem;
+ si->si_freeram = zfreemem;
+ si->si_totalswap = ztotswap;
+ si->si_freeswap = zfreeswap;
+ si->si_mem_unit = PAGESIZE;
+ }
+ si->si_bufferram = 0;
+ si->si_sharedram = 0;
+
+ /*
+ * These two stats refer to high physical memory. If an
+ * application running in a Linux zone cares about this, then
+ * either it or we are broken.
+ */
+ si->si_totalhigh = 0;
+ si->si_freehigh = 0;
+}
+
+long
+lx_sysinfo64(caddr_t sip)
+{
+ lx_sysinfo_t si;
+
+ bzero(&si, sizeof (si));
+ lx_sysinfo_common(&si);
+
+ if (copyout(&si, sip, sizeof (si)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+long
+lx_sysinfo32(caddr_t sip)
+{
+ lx_sysinfo_t si;
+ lx_sysinfo32_t si32;
+ int i;
+
+ lx_sysinfo_common(&si);
+
+ /*
+ * Convert the lx_sysinfo_t into the legacy 32-bit view:
+ */
+ bzero(&si32, sizeof (si32));
+ si32.si_uptime = si.si_uptime;
+
+ for (i = 0; i < 3; i++) {
+ if ((si.si_loads[i]) > 0x7fffffff)
+ si32.si_loads[i] = 0x7fffffff;
+ else
+ si32.si_loads[i] = si.si_loads[i];
+ }
+
+ si32.si_procs = si.si_procs;
+ si32.si_totalram = si.si_totalram;
+ si32.si_freeram = si.si_freeram;
+ si32.si_totalswap = si.si_totalswap;
+ si32.si_freeswap = si.si_freeswap;
+ si32.si_mem_unit = si.si_mem_unit;
+
+ si32.si_bufferram = si.si_bufferram;
+ si32.si_sharedram = si.si_sharedram;
+
+ si32.si_totalhigh = si.si_totalhigh;
+ si32.si_freehigh = si.si_freehigh;
+
+ if (copyout(&si32, sip, sizeof (si32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+#endif
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c b/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c
new file mode 100644
index 0000000000..50607fac9b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c
@@ -0,0 +1,197 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/cpuvar.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <lx_syscall.h>
+
+/* ARGSUSED */
+long
+lx_arch_prctl(int code, ulong_t addr)
+{
+#if defined(__amd64)
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *llwp = lwptolxlwp(lwp);
+ pcb_t *pcb = &lwp->lwp_pcb;
+
+ switch (code) {
+ case LX_ARCH_GET_FS:
+ if (copyout(&llwp->br_lx_fsbase, (void *)addr,
+ sizeof (llwp->br_lx_fsbase)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ break;
+
+ case LX_ARCH_SET_FS:
+ llwp->br_lx_fsbase = addr;
+
+ kpreempt_disable();
+ if (pcb->pcb_fsbase != llwp->br_lx_fsbase) {
+ pcb->pcb_fsbase = llwp->br_lx_fsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ pcb->pcb_rupdate = 1;
+ }
+ kpreempt_enable();
+ break;
+
+ case LX_ARCH_GET_GS:
+ if (copyout(&llwp->br_lx_gsbase, (void *)addr,
+ sizeof (llwp->br_lx_gsbase)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ break;
+
+ case LX_ARCH_SET_GS:
+ llwp->br_lx_gsbase = addr;
+
+ kpreempt_disable();
+ if (pcb->pcb_gsbase != llwp->br_lx_gsbase) {
+ pcb->pcb_gsbase = llwp->br_lx_gsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ pcb->pcb_rupdate = 1;
+ }
+ kpreempt_enable();
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+#endif
+
+ return (0);
+}
+
+long
+lx_get_thread_area(struct ldt_info *inf)
+{
+ struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+ struct ldt_info ldt_inf;
+ user_desc_t *dscrp;
+ int entry;
+
+ if (fuword32(&inf->entry_number, (uint32_t *)&entry))
+ return (set_errno(EFAULT));
+
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+ /*
+ * convert the solaris ldt to the linux format expected by the
+ * caller
+ */
+ DESC_TO_LDT_INFO(dscrp, &ldt_inf);
+ ldt_inf.entry_number = entry;
+
+ if (copyout(&ldt_inf, inf, sizeof (struct ldt_info)))
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_set_thread_area(struct ldt_info *inf)
+{
+ struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+ struct ldt_info ldt_inf;
+ user_desc_t *dscrp;
+ int entry;
+ int i;
+
+ /* Check that casts for accessing the words in user_desc are valid */
+ ASSERT(sizeof (user_desc_t) == 8);
+
+ if (copyin(inf, &ldt_inf, sizeof (ldt_inf)))
+ return (set_errno(EFAULT));
+
+ entry = ldt_inf.entry_number;
+ if (entry == -1) {
+ /*
+ * Find an empty entry in the tls for this thread.
+ * The casts assume each user_desc_t entry is 8 bytes.
+ */
+ for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++) {
+ if (((uint_t *)dscrp)[0] == 0 &&
+ ((uint_t *)dscrp)[1] == 0)
+ break;
+ }
+
+ if (i < LX_TLSNUM) {
+ /*
+ * found one
+ */
+ entry = i + GDT_TLSMIN;
+ if (suword32(&inf->entry_number, entry))
+ return (set_errno(EFAULT));
+ } else {
+ return (set_errno(ESRCH));
+ }
+ }
+
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ /*
+ * convert the linux ldt info to standard intel descriptor
+ */
+ dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+ if (LDT_INFO_EMPTY(&ldt_inf)) {
+ ((uint_t *)dscrp)[0] = 0;
+ ((uint_t *)dscrp)[1] = 0;
+ } else {
+ LDT_INFO_TO_DESC(&ldt_inf, dscrp);
+ }
+
+ /*
+ * update the gdt with the new descriptor
+ */
+ kpreempt_disable();
+
+ for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++)
+ lx_set_gdt(GDT_TLSMIN + i, dscrp);
+
+ kpreempt_enable();
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_timer.c b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
new file mode 100644
index 0000000000..17ca59b534
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
@@ -0,0 +1,388 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * The illumos kernel provides two clock backends: CLOCK_REALTIME, the
+ * adjustable system wall clock; and CLOCK_HIGHRES, the monotonically
+ * increasing time source that is not subject to drift or adjustment. By
+ * contrast, the Linux kernel is furnished with an overabundance of narrowly
+ * differentiated clock types.
+ *
+ * Fortunately, most of the commonly used Linux clock types are either similar
+ * enough to the native clock backends that they can be directly mapped, or
+ * represent queries to the per-process and per-LWP microstate counters.
+ *
+ * CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except that it takes into
+ * account time that the system is suspended. Since that is uninteresting to
+ * us, we treat it the same.
+ */
+
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+
+/*
+ * From "uts/common/os/timer.c":
+ */
+extern int clock_settime(clockid_t, timespec_t *);
+extern int clock_gettime(clockid_t, timespec_t *);
+extern int clock_getres(clockid_t, timespec_t *);
+extern int nanosleep(timespec_t *, timespec_t *);
+
+
+static int lx_emul_clock_getres(clockid_t, timespec_t *);
+static int lx_emul_clock_gettime(clockid_t, timespec_t *);
+static int lx_emul_clock_settime(clockid_t, timespec_t *);
+
+typedef struct lx_clock_backend {
+ clockid_t lclk_ntv_id;
+ int (*lclk_clock_getres)(clockid_t, timespec_t *);
+ int (*lclk_clock_gettime)(clockid_t, timespec_t *);
+ int (*lclk_clock_settime)(clockid_t, timespec_t *);
+} lx_clock_backend_t;
+
+/*
+ * NOTE: The Linux man pages state this structure is obsolete and is
+ * unsupported, so it is declared here for sizing purposes only.
+ */
+struct lx_timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+/*
+ * Use the native clock_* system call implementation, but with a translated
+ * clock identifier:
+ */
+#define NATIVE(ntv_id) \
+ { ntv_id, clock_getres, clock_gettime, clock_settime }
+
+/*
+ * This backend is not supported, so we provide an emulation handler:
+ */
+#define EMUL(ntv_id) \
+ { ntv_id, lx_emul_clock_getres, lx_emul_clock_gettime, \
+ lx_emul_clock_settime }
+
+static lx_clock_backend_t lx_clock_backends[] = {
+ NATIVE(CLOCK_REALTIME), /* LX_CLOCK_REALTIME */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC */
+ EMUL(CLOCK_PROCESS_CPUTIME_ID), /* LX_CLOCK_PROCESS_CPUTIME_ID */
+ EMUL(CLOCK_THREAD_CPUTIME_ID), /* LX_CLOCK_THREAD_CPUTIME_ID */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC_RAW */
+ NATIVE(CLOCK_REALTIME), /* LX_CLOCK_REALTIME_COARSE */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC_COARSE */
+ NATIVE(CLOCK_HIGHRES) /* LX_CLOCK_BOOTTIME */
+};
+
+#define LX_CLOCK_MAX \
+ (sizeof (lx_clock_backends) / sizeof (lx_clock_backends[0]))
+#define LX_CLOCK_BACKEND(clk) \
+ ((clk) < LX_CLOCK_MAX && (clk) >= 0 ? &lx_clock_backends[(clk)] : NULL)
+
+/* ARGSUSED */
+static int
+lx_emul_clock_settime(clockid_t clock, timespec_t *tp)
+{
+ return (set_errno(EINVAL));
+}
+
+static int
+lx_emul_clock_gettime(clockid_t clock, timespec_t *tp)
+{
+ timespec_t t;
+
+ switch (clock) {
+ case CLOCK_PROCESS_CPUTIME_ID: {
+ proc_t *p = ttoproc(curthread);
+ hrtime_t snsecs, unsecs;
+
+ /*
+ * Based on getrusage() in "rusagesys.c":
+ */
+ mutex_enter(&p->p_lock);
+ unsecs = mstate_aggr_state(p, LMS_USER);
+ snsecs = mstate_aggr_state(p, LMS_SYSTEM);
+ mutex_exit(&p->p_lock);
+
+ hrt2ts(unsecs + snsecs, &t);
+ break;
+ }
+
+ case CLOCK_THREAD_CPUTIME_ID: {
+ klwp_t *lwp = ttolwp(curthread);
+ struct mstate *ms = &lwp->lwp_mstate;
+ hrtime_t snsecs, unsecs;
+
+ /*
+ * Based on getrusage_lwp() in "rusagesys.c":
+ */
+ unsecs = ms->ms_acct[LMS_USER];
+ snsecs = ms->ms_acct[LMS_SYSTEM] + ms->ms_acct[LMS_TRAP];
+
+ scalehrtime(&unsecs);
+ scalehrtime(&snsecs);
+
+ hrt2ts(unsecs + snsecs, &t);
+ break;
+ }
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ if (TIMESPEC_OVERFLOW(&t)) {
+ return (set_errno(EOVERFLOW));
+ }
+ TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+ if (copyout(&t32, tp, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+#endif
+
+ if (copyout(&t, tp, sizeof (t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+static int
+lx_emul_clock_getres(clockid_t clock, timespec_t *tp)
+{
+ timespec_t t;
+
+ if (tp == NULL) {
+ return (0);
+ }
+
+ switch (clock) {
+ case CLOCK_PROCESS_CPUTIME_ID:
+ case CLOCK_THREAD_CPUTIME_ID:
+ /*
+ * These clock backends return microstate accounting values for
+ * the LWP or the entire process. The Linux kernel claims they
+ * have nanosecond resolution; so will we.
+ */
+ t.tv_sec = 0;
+ t.tv_nsec = 1;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ if (TIMESPEC_OVERFLOW(&t)) {
+ return (set_errno(EOVERFLOW));
+ }
+ TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+ if (copyout(&t32, tp, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+#endif
+
+ if (copyout(&t, tp, sizeof (t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+static void
+lx_clock_unsupported(int clock)
+{
+ char buf[100];
+
+ (void) snprintf(buf, sizeof (buf), "unsupported clock: %d", clock);
+ lx_unsupported(buf);
+}
+
+long
+lx_clock_settime(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ return (backend->lclk_clock_settime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_gettime(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ return (backend->lclk_clock_gettime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_getres(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * It is important this check is performed after the clock
+ * check. Both glibc and musl, in their clock_getcpuclockid(),
+ * use clock_getres() with a NULL tp to validate a clock
+ * value. Performing the tp check before the clock check could
+ * indicate a valid clock to libc when it shouldn't.
+ */
+ if (tp == NULL) {
+ return (0);
+ }
+
+ return (backend->lclk_clock_getres(backend->lclk_ntv_id, tp));
+}
+
+
+long
+lx_gettimeofday(struct timeval *tvp, struct lx_timezone *tzp)
+{
+ struct lx_timezone tz;
+
+ bzero(&tz, sizeof (tz));
+
+ /*
+ * We want to be similar to libc which just does a fasttrap to
+ * gethrestime and simply converts that result. We follow how uniqtime
+ * does the conversion but we can't use that code since it does some
+ * extra work which can cause the result to bounce around based on which
+ * CPU we run on.
+ */
+ if (tvp != NULL) {
+ struct timeval tv;
+ timestruc_t ts;
+ int usec, nsec;
+
+ gethrestime(&ts);
+ nsec = ts.tv_nsec;
+ usec = nsec + (nsec >> 2);
+ usec = nsec + (usec >> 1);
+ usec = nsec + (usec >> 2);
+ usec = nsec + (usec >> 4);
+ usec = nsec - (usec >> 3);
+ usec = nsec + (usec >> 2);
+ usec = nsec + (usec >> 3);
+ usec = nsec + (usec >> 4);
+ usec = nsec + (usec >> 1);
+ usec = nsec + (usec >> 6);
+ usec = usec >> 10;
+
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = usec;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&tv, tvp, sizeof (tv)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ struct timeval32 tv32;
+
+ if (TIMEVAL_OVERFLOW(&tv))
+ return (set_errno(EOVERFLOW));
+ TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+ if (copyout(&tv32, tvp, sizeof (tv32)))
+ return (set_errno(EFAULT));
+ }
+#endif
+ }
+
+ /*
+ * The Linux man page states use of the second parameter is obsolete,
+ * but gettimeofday(2) should still return EFAULT if it is set
+ * to a bad non-NULL pointer (sigh...)
+ */
+ if (tzp != NULL && copyout(&tz, tzp, sizeof (tz)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/*
+ * On Linux a bad buffer will set errno to EFAULT, and on Illumos the failure
+ * mode is documented as "undefined."
+ */
+long
+lx_time(time_t *tp)
+{
+ timestruc_t ts;
+ struct timeval tv;
+
+ gethrestime(&ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = 0;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (tp != NULL &&
+ copyout(&tv.tv_sec, tp, sizeof (tv.tv_sec)) != 0)
+ return (set_errno(EFAULT));
+
+ return (tv.tv_sec);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ struct timeval32 tv32;
+
+ if (TIMEVAL_OVERFLOW(&tv))
+ return (set_errno(EOVERFLOW));
+ TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+ if (tp != NULL &&
+ copyout(&tv32.tv_sec, tp, sizeof (tv32.tv_sec)))
+ return (set_errno(EFAULT));
+
+ return (tv32.tv_sec);
+ }
+#endif /* _SYSCALL32_IMPL */
+ /* NOTREACHED */
+}
+
+long
+lx_nanosleep(timespec_t *rqtp, timespec_t *rmtp)
+{
+ return (nanosleep(rqtp, rmtp));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_umask.c b/usr/src/uts/common/brand/lx/syscall/lx_umask.c
new file mode 100644
index 0000000000..130af6c776
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_umask.c
@@ -0,0 +1,25 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+
+/* From usr/src/uts/common/syscall/umask.c */
+extern int umask(int);
+
+long
+lx_umask(mode_t cmask)
+{
+ return (umask(cmask));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_uname.c b/usr/src/uts/common/brand/lx/syscall/lx_uname.c
new file mode 100644
index 0000000000..2d18408eaa
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_uname.c
@@ -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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+
+struct lx_utsname {
+ char lxu_sysname[LX_SYS_UTS_LN];
+ char lxu_nodename[LX_SYS_UTS_LN];
+ char lxu_release[LX_SYS_UTS_LN];
+ char lxu_version[LX_SYS_UTS_LN];
+ char lxu_machine[LX_SYS_UTS_LN];
+ char lxu_domainname[LX_SYS_UTS_LN];
+};
+
+long
+lx_uname(void *uptr)
+{
+ proc_t *p = curproc;
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ lx_zone_data_t *lxzd = ztolxzd(p->p_zone);
+ struct lx_utsname un;
+
+ bzero(&un, sizeof (un));
+
+ (void) strlcpy(un.lxu_sysname, LX_UNAME_SYSNAME, LX_SYS_UTS_LN);
+ (void) strlcpy(un.lxu_nodename, p->p_zone->zone_nodename,
+ LX_SYS_UTS_LN);
+
+ mutex_enter(&lxzd->lxzd_lock);
+
+ if (lxpd->l_uname_release[0] != '\0') {
+ (void) strlcpy(un.lxu_release, lxpd->l_uname_release,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_release, lxzd->lxzd_kernel_release,
+ LX_SYS_UTS_LN);
+ }
+ if (lxpd->l_uname_version[0] != '\0') {
+ (void) strlcpy(un.lxu_version, lxpd->l_uname_version,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_version, lxzd->lxzd_kernel_version,
+ LX_SYS_UTS_LN);
+ }
+
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (get_udatamodel() == DATAMODEL_LP64) {
+ (void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE64,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE32,
+ LX_SYS_UTS_LN);
+ }
+ (void) strlcpy(un.lxu_domainname, p->p_zone->zone_domain,
+ LX_SYS_UTS_LN);
+
+ if (copyout(&un, uptr, sizeof (un)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_wait.c b/usr/src/uts/common/brand/lx/syscall/lx_wait.c
new file mode 100644
index 0000000000..e8358f9f69
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_wait.c
@@ -0,0 +1,377 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * wait() family of functions.
+ *
+ * The first minor difference between the Linux and Solaris family of wait()
+ * calls is that the values for WNOHANG and WUNTRACED are different. Thankfully,
+ * the exit status values are identical between the two implementations.
+ *
+ * Things get very different and very complicated when we introduce the Linux
+ * threading model. Under linux, both threads and child processes are
+ * represented as processes. However, the behavior of wait() with respect to
+ * each child varies according to the flags given to clone()
+ *
+ * SIGCHLD The SIGCHLD signal should be sent on termination
+ * CLONE_THREAD The child shares the same thread group as the parent
+ * CLONE_DETACHED The parent receives no notification when the child exits
+ *
+ * The following flags control the Linux behavior w.r.t. the above attributes:
+ *
+ * __WALL Wait on all children, regardless of type
+ * __WCLONE Wait only on non-SIGCHLD children
+ * __WNOTHREAD Don't wait on children of other threads in this group
+ *
+ * The following chart shows whether wait() returns when the child exits:
+ *
+ * default __WCLONE __WALL
+ * no SIGCHLD - X X
+ * SIGCHLD X - X
+ *
+ * The following chart shows whether wait() returns when the grandchild exits:
+ *
+ * default __WNOTHREAD
+ * no CLONE_THREAD - -
+ * CLONE_THREAD X -
+ *
+ * The CLONE_DETACHED flag is universal - when the child exits, no state is
+ * stored and wait() has no effect.
+ *
+ * XXX Support the above combination of options, or some reasonable subset that
+ * covers at least fork() and pthread_create().
+ */
+
+#include <sys/wait.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_errno.h>
+#include <lx_syscall.h>
+
+/*
+ * From "uts/common/os/exit.c" and "uts/common/syscall/rusagesys.c":
+ */
+extern int waitid(idtype_t, id_t, k_siginfo_t *, int);
+extern int rusagesys(int, void *, void *, void *, void *);
+
+/*
+ * Convert between Linux options and Solaris options, returning -1 if any
+ * invalid flags are found.
+ */
+#define LX_WNOHANG 0x00000001
+#define LX_WUNTRACED 0x00000002
+#define LX_WSTOPPED LX_WUNTRACED
+#define LX_WEXITED 0x00000004
+#define LX_WCONTINUED 0x00000008
+#define LX_WNOWAIT 0x01000000
+
+#define LX_WNOTHREAD 0x20000000
+#define LX_WALL 0x40000000
+#define LX_WCLONE 0x80000000
+
+#define LX_P_ALL 0x0
+#define LX_P_PID 0x1
+#define LX_P_GID 0x2
+
+/*
+ * Split the passed waitpid/waitid options into two separate variables:
+ * those for the native illumos waitid(2), and the extra Linux-specific
+ * options we will handle in our brand-specific code.
+ */
+static int
+ltos_options(uintptr_t options, int *native_options, int *extra_options)
+{
+ int newoptions = 0;
+
+ if (((options) & ~(LX_WNOHANG | LX_WUNTRACED | LX_WEXITED |
+ LX_WCONTINUED | LX_WNOWAIT | LX_WNOTHREAD | LX_WALL |
+ LX_WCLONE)) != 0) {
+ return (-1);
+ }
+
+ *extra_options = options & (LX_WNOTHREAD | LX_WALL | LX_WCLONE);
+
+ if (options & LX_WNOHANG)
+ newoptions |= WNOHANG;
+ if (options & LX_WUNTRACED)
+ newoptions |= WUNTRACED;
+ if (options & LX_WEXITED)
+ newoptions |= WEXITED;
+ if (options & LX_WCONTINUED)
+ newoptions |= WCONTINUED;
+ if (options & LX_WNOWAIT)
+ newoptions |= WNOWAIT;
+
+ /*
+ * The trapped option is implicit on Linux.
+ */
+ newoptions |= WTRAPPED;
+
+ *native_options = newoptions;
+ return (0);
+}
+
+static int
+lx_wstat(int code, int status)
+{
+ int stat = 0;
+
+ switch (code) {
+ case CLD_EXITED:
+ stat = status << 8;
+ break;
+ case CLD_DUMPED:
+ stat = lx_stol_signo(status, SIGKILL) | WCOREFLG;
+ break;
+ case CLD_KILLED:
+ stat = lx_stol_signo(status, SIGKILL);
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ stat = (lx_stol_status(status, SIGKILL) << 8) | WSTOPFLG;
+ break;
+ case CLD_CONTINUED:
+ stat = WCONTFLG;
+ break;
+ }
+
+ return (stat);
+}
+
+static int
+lx_call_waitid(idtype_t idtype, id_t id, k_siginfo_t *sip, int native_options,
+ int extra_options)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ int error;
+
+ /*
+ * Our brand-specific waitid helper only understands a subset of
+ * the possible idtypes. Ensure we keep to that subset here:
+ */
+ if (idtype != P_ALL && idtype != P_PID && idtype != P_PGID) {
+ return (EINVAL);
+ }
+
+ /*
+ * Enable the return of emulated ptrace(2) stop conditions
+ * through lx_waitid_helper, and stash the Linux-specific
+ * extra waitid() flags.
+ */
+ lwpd->br_waitid_emulate = B_TRUE;
+ lwpd->br_waitid_flags = extra_options;
+
+ if ((error = waitid(idtype, id, sip, native_options)) == EINTR) {
+ /*
+ * According to signal(7), the wait4(2), waitid(2), and
+ * waitpid(2) system calls are restartable.
+ */
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+
+ lwpd->br_waitid_emulate = B_FALSE;
+ lwpd->br_waitid_flags = 0;
+
+ return (error);
+}
+
+long
+lx_wait4(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ k_siginfo_t info = { 0 };
+ idtype_t idtype;
+ id_t id;
+ int status = 0;
+ pid_t pid = (pid_t)p1;
+ int error;
+ int native_options, extra_options;
+ int *statusp = (int *)p2;
+ void *rup = (void *)p4;
+
+ if (ltos_options(p3, &native_options, &extra_options) == -1) {
+ return (set_errno(EINVAL));
+ }
+
+ if (pid > maxpid) {
+ return (set_errno(ECHILD));
+ }
+
+ /*
+ * While not listed as a valid return code, Linux's wait4(2) does,
+ * in fact, get an EFAULT if either the status pointer or rusage
+ * pointer is invalid. Since a failed waitpid should leave child
+ * process in a state where a future wait4(2) will succeed, we
+ * check them by copying out the values their buffers originally
+ * contained. (We need to do this as a failed system call should
+ * never affect the contents of a passed buffer.)
+ *
+ * This will fail if the buffers in question are write-only.
+ */
+ if (statusp != NULL) {
+ if (copyin(statusp, &status, sizeof (status)) != 0 ||
+ copyout(&status, statusp, sizeof (status)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ /*
+ * Do the same check for the "struct rusage" pointer, which differs
+ * in size for 32- and 64-bit processes.
+ */
+ if (rup != NULL) {
+ struct rusage ru;
+ void *krup = &ru;
+ size_t rusz = sizeof (ru);
+#if defined(_SYSCALL32_IMPL)
+ struct rusage32 ru32;
+
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ krup = &ru32;
+ rusz = sizeof (ru32);
+ }
+#endif
+
+ if (copyin(rup, krup, rusz) != 0 ||
+ copyout(krup, rup, rusz) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ if (pid < -1) {
+ idtype = P_PGID;
+ id = -pid;
+ } else if (pid == -1) {
+ idtype = P_ALL;
+ id = 0;
+ } else if (pid == 0) {
+ idtype = P_PGID;
+ mutex_enter(&pidlock);
+ id = curproc->p_pgrp;
+ mutex_exit(&pidlock);
+ } else {
+ idtype = P_PID;
+ id = pid;
+ }
+
+ native_options |= (WEXITED | WTRAPPED);
+
+ if ((error = lx_call_waitid(idtype, id, &info, native_options,
+ extra_options)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * If the WNOHANG flag was specified and no child was found return 0.
+ */
+ if ((native_options & WNOHANG) && info.si_pid == 0) {
+ return (0);
+ }
+
+ status = lx_wstat(info.si_code, info.si_status);
+
+ /*
+ * Unfortunately if this attempt to copy out either the status or the
+ * rusage fails, the process will be in an inconsistent state as
+ * subsequent calls to wait for the same child will fail where they
+ * should succeed on a Linux system. This, however, is rather
+ * unlikely since we tested the validity of both above.
+ */
+ if (statusp != NULL) {
+ if (copyout(&status, statusp, sizeof (status)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ if (rup != NULL) {
+ if ((error = rusagesys(_RUSAGESYS_GETRUSAGE_CHLD, rup, NULL,
+ NULL, NULL)) != 0) {
+ return (set_errno(error));
+ }
+ }
+
+ return (info.si_pid);
+}
+
+long
+lx_waitpid(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ return (lx_wait4(p1, p2, p3, NULL));
+}
+
+long
+lx_waitid(uintptr_t idtype, uintptr_t id, uintptr_t infop, uintptr_t opt)
+{
+ int error;
+ int native_options, extra_options;
+ k_siginfo_t info = { 0 };
+
+ if (ltos_options(opt, &native_options, &extra_options) == -1) {
+ return (set_errno(EINVAL));
+ }
+
+ if (((opt) & (LX_WEXITED | LX_WSTOPPED | LX_WCONTINUED)) == 0) {
+ return (set_errno(EINVAL));
+ }
+
+ switch (idtype) {
+ case LX_P_ALL:
+ idtype = P_ALL;
+ break;
+ case LX_P_PID:
+ idtype = P_PID;
+ break;
+ case LX_P_GID:
+ idtype = P_PGID;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if ((error = lx_call_waitid(idtype, id, &info, native_options,
+ extra_options)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * If the WNOHANG flag was specified and no child was found return 0.
+ */
+ if ((native_options & WNOHANG) && info.si_pid == 0) {
+ return (0);
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ return (stol_ksiginfo32_copyout(&info, (void *)infop));
+ } else
+#endif
+ {
+ return (stol_ksiginfo_copyout(&info, (void *)infop));
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_xattr.c b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
new file mode 100644
index 0000000000..bd7667226f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
@@ -0,0 +1,371 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+
+
+#define LX_XATTR_NAME_MAX 255
+#define LX_XATTR_SIZE_MAX 65536
+#define LX_XATTR_LIST_MAX 65536
+
+#define LX_XATTR_FLAG_CREATE 0x1
+#define LX_XATTR_FLAG_REPLACE 0x2
+#define LX_XATTR_FLAGS_VALID (LX_XATTR_FLAG_CREATE | LX_XATTR_FLAG_REPLACE)
+
+#define LX_CAP_XATTR_NAME "security.capability"
+
+/*
+ * *xattr() family of functions.
+ *
+ * These are largely unimplemented. In most cases we return EOPNOTSUPP, rather
+ * than using NOSYS_NO_EQUIV to avoid unwanted stderr output from ls(1).
+ *
+ * Note that CRED() is used instead of f_cred in the f*xattr functions. This
+ * is intentional as Linux does not have the same notion of per-fd credentials.
+ */
+
+/* ARGSUSED */
+static int
+lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t size,
+ int flags)
+{
+ int error;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ size_t name_len;
+
+ if ((flags & ~LX_XATTR_FLAGS_VALID) != 0) {
+ return (EINVAL);
+ }
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+ if (size > LX_XATTR_SIZE_MAX) {
+ return (E2BIG);
+ }
+
+ /*
+ * In order to keep package management software happy, despite lacking
+ * support for file-based Linux capabilities via xattrs, we fake
+ * success when root attempts a setxattr on that attribute.
+ */
+ if (crgetuid(CRED()) == 0 &&
+ strcmp(name_buf, LX_CAP_XATTR_NAME) == 0) {
+ return (0);
+ }
+
+
+ return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t size,
+ ssize_t *osize)
+{
+ int error;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ size_t name_len;
+
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * Only parameter validation is attempted for now.
+ */
+ return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_listxattr_common(vnode_t *vp, char *list, size_t size, ssize_t *osize)
+{
+ return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_removexattr_common(vnode_t *vp, char *name)
+{
+ int error;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ size_t name_len;
+
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+
+ /*
+ * Only parameter validation is attempted for now.
+ */
+ return (EOPNOTSUPP);
+}
+
+
+long
+lx_setxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_setxattr_common(vp, name, value, size, flags);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lsetxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_setxattr_common(vp, name, value, size, flags);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fsetxattr(int fd, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_setxattr_common(fp->f_vnode, name, value, size, flags);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+ssize_t
+lx_getxattr(char *path, char *name, void *value, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_getxattr_common(vp, name, value, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_lgetxattr(char *path, char *name, void *value, size_t size)
+{
+
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_getxattr_common(vp, name, value, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_fgetxattr(int fd, char *name, void *value, size_t size)
+{
+ int error;
+ file_t *fp;
+ ssize_t osize;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_getxattr_common(fp->f_vnode, name, value, size, &osize);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_listxattr(char *path, char *list, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_listxattr_common(vp, list, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_llistxattr(char *path, char *list, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_listxattr_common(vp, list, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_flistxattr(int fd, char *list, size_t size)
+{
+ int error;
+ file_t *fp;
+ ssize_t osize;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_listxattr_common(fp->f_vnode, list, size, &osize);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+int
+lx_removexattr(char *path, char *name)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_removexattr_common(vp, name);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+int
+lx_lremovexattr(char *path, char *name)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_removexattr_common(vp, name);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+int
+lx_fremovexattr(int fd, char *name)
+{
+ int error;
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_removexattr_common(fp->f_vnode, name);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h b/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h
new file mode 100644
index 0000000000..93dc316c1e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h
@@ -0,0 +1,196 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LXSYSFS_H
+#define _LXSYSFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_sysfs.h: declarations, data structures and macros for lx_sysfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+
+/*
+ * Convert a vnode into an lxsys_mnt_t
+ */
+#define VTOLXSM(vp) ((lxsys_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxsys_node
+ */
+#define VTOLXS(vp) ((lxsys_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxsys_node into a vnode
+ */
+#define LXSTOV(lxsnp) ((lxsnp)->lxsys_vnode)
+
+/*
+ * convert a lxsys_node into zone for fs
+ */
+#define LXSTOZ(lxsnp) \
+ (((lxsys_mnt_t *)(lxsnp)->lxsys_vnode->v_vfsp->vfs_data)->lxsysm_zone)
+
+#define LXSNSIZ 256 /* max size of lx /sys file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXSYS_SDSIZE 16
+
+/* Root sysfs lxsys_instance */
+#define LXSYS_INST_ROOT 0
+
+/*
+ * Node/file types for lx /sys files
+ * (directories and files contained therein).
+ */
+typedef enum lxsys_nodetype {
+ LXSYS_NONE, /* None-type to keep inodes non-zero */
+ LXSYS_STATIC, /* Statically defined entries */
+ LXSYS_CLASS_NET, /* /sys/class/net/<iface> */
+ LXSYS_DEV_NET, /* /sys/devices/virtual/net/<iface> */
+ LXSYS_BLOCK, /* /sys/block/<dev> */
+ LXSYS_DEV_ZFS, /* /sys/devices/zfs/<dev> */
+ LXSYS_DEV_SYS_CPU, /* /sys/devices/system/cpu/<cpu> */
+ LXSYS_DEV_SYS_CPUINFO, /* /sys/devices/system/cpu/cpuN/<info> */
+ LXSYS_DEV_SYS_NODE, /* /sys/devices/system/node/node0/<info> */
+ LXSYS_MAXTYPE, /* type limit */
+} lxsys_nodetype_t;
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+ unsigned int d_idnum;
+ char *d_name;
+} lxsys_dirent_t;
+
+typedef struct {
+ unsigned int dl_instance;
+ lxsys_dirent_t *dl_list;
+ int dl_length;
+} lxsys_dirlookup_t;
+
+/*
+ * This is the lx sysfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+struct lxsys_node;
+typedef struct lxsys_node lxsys_node_t;
+struct lxsys_node {
+ lxsys_nodetype_t lxsys_type; /* type ID of node */
+ unsigned int lxsys_instance; /* instance ID node */
+ unsigned int lxsys_endpoint; /* endpoint ID node */
+ vnode_t *lxsys_vnode; /* vnode for the node */
+ vnode_t *lxsys_parentvp; /* parent directory */
+ lxsys_node_t *lxsys_next; /* next list entry */
+ timestruc_t lxsys_time; /* creation time */
+ mode_t lxsys_mode; /* file mode bits */
+ uid_t lxsys_uid; /* file owner */
+ gid_t lxsys_gid; /* file group owner */
+ ino_t lxsys_ino; /* node id */
+};
+
+/*
+ * This is the lxsysfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxsys_mnt {
+ kmutex_t lxsysm_lock; /* protects fields */
+ lxsys_node_t *lxsysm_node; /* node at root of sys mount */
+ zone_t *lxsysm_zone; /* zone for this mount */
+} lxsys_mnt_t;
+
+extern vnodeops_t *lxsys_vnodeops;
+
+typedef struct mounta mounta_t;
+
+extern void lxsys_initnodecache();
+extern void lxsys_fininodecache();
+extern ino_t lxsys_inode(lxsys_nodetype_t, unsigned int, unsigned int);
+extern ino_t lxsys_parentinode(lxsys_node_t *);
+extern lxsys_node_t *lxsys_getnode(vnode_t *, lxsys_nodetype_t, unsigned int,
+ unsigned int);
+extern lxsys_node_t *lxsys_getnode_static(vnode_t *, unsigned int);
+extern void lxsys_freenode(lxsys_node_t *);
+
+extern netstack_t *lxsys_netstack(lxsys_node_t *);
+extern ill_t *lxsys_find_ill(ip_stack_t *, uint_t);
+
+typedef struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t bufsize;
+ char *pos;
+ size_t beg;
+ int error;
+} lxsys_uiobuf_t;
+
+extern lxsys_uiobuf_t *lxsys_uiobuf_new(uio_t *);
+extern void lxsys_uiobuf_free(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_seterr(lxsys_uiobuf_t *, int);
+extern int lxsys_uiobuf_flush(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_write(lxsys_uiobuf_t *, const char *, size_t);
+extern void lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef islower
+#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LXSYSFS_H */
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c b/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c
new file mode 100644
index 0000000000..e80acbcfe5
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c
@@ -0,0 +1,452 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_syssubr.c: Various functions for the /sys vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include "lx_sysfs.h"
+
+#define LXSYSCACHE_NAME "lxsys_cache"
+
+static int lxsys_node_constructor(void *, void *, int);
+static void lxsys_node_destructor(void *, void *);
+
+static kmem_cache_t *lxsys_node_cache;
+
+void
+lxsys_initnodecache()
+{
+ lxsys_node_cache = kmem_cache_create(LXSYSCACHE_NAME,
+ sizeof (lxsys_node_t), 0,
+ lxsys_node_constructor, lxsys_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxsys_fininodecache()
+{
+ kmem_cache_destroy(lxsys_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxsys_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxsys_node_t *lxsnp = buf;
+ vnode_t *vp;
+
+ vp = lxsnp->lxsys_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxsys_vnodeops);
+ vp->v_data = lxsnp;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxsys_node_destructor(void *buf, void *un)
+{
+ lxsys_node_t *lxsnp = buf;
+
+ vn_free(LXSTOV(lxsnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxsys node
+ */
+ino_t
+lxsys_inode(lxsys_nodetype_t type, unsigned int instance,
+ unsigned int endpoint)
+{
+ /*
+ * Sysfs Inode format:
+ * 0000AABBBBCC
+ *
+ * AA - TYPE
+ * BBBB - INSTANCE
+ * CC - ENDPOINT
+ */
+ ASSERT(instance <= 0xffff);
+ ASSERT(endpoint <= 0xff);
+
+ return ((ino_t)(type << 24)|(instance << 8)|endpoint);
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxsys_parentinode(lxsys_node_t *lxsnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxsnp->lxsys_type == LXSYS_STATIC &&
+ lxsnp->lxsys_instance == LXSYS_INST_ROOT) {
+ return (lxsnp->lxsys_ino);
+ } else {
+ return (VTOLXS(lxsnp->lxsys_parentvp)->lxsys_ino);
+ }
+}
+
+/*
+ * Allocate a new lxsys node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxsys_node_t *
+lxsys_getnode(vnode_t *dp, lxsys_nodetype_t type, unsigned int instance,
+ unsigned int endpoint)
+{
+ lxsys_node_t *lxsnp;
+ vnode_t *vp;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_innactive
+ */
+ lxsnp = kmem_cache_alloc(lxsys_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxsnp->lxsys_type = type;
+ lxsnp->lxsys_instance = instance;
+ lxsnp->lxsys_endpoint = endpoint;
+ lxsnp->lxsys_next = NULL;
+ lxsnp->lxsys_parentvp = dp;
+ VN_HOLD(dp);
+
+ lxsnp->lxsys_time = now;
+ lxsnp->lxsys_uid = lxsnp->lxsys_gid = 0;
+ lxsnp->lxsys_ino = lxsys_inode(type, instance, endpoint);
+
+ /* initialize the vnode data */
+ vp = lxsnp->lxsys_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Default to a directory with open permissions.
+ * Specific components will override this
+ */
+ if (type == LXSYS_STATIC && instance == LXSYS_INST_ROOT) {
+ vp->v_flag |= VROOT;
+ }
+ vp->v_type = VDIR;
+ lxsnp->lxsys_mode = 0555;
+
+ return (lxsnp);
+}
+
+lxsys_node_t *
+lxsys_getnode_static(vnode_t *dp, unsigned int instance)
+{
+ lxsys_mnt_t *lxsm = VTOLXSM(dp);
+ lxsys_node_t *lnp, *tail = NULL;
+
+ mutex_enter(&lxsm->lxsysm_lock);
+ for (lnp = lxsm->lxsysm_node; lnp != NULL; lnp = lnp->lxsys_next) {
+ if (lnp->lxsys_instance == instance) {
+ VERIFY(lnp->lxsys_parentvp == dp);
+
+ VN_HOLD(lnp->lxsys_vnode);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (lnp);
+ } else if (lnp->lxsys_next == NULL) {
+ /* Found no match by the end of the list */
+ tail = lnp;
+ break;
+ }
+ }
+
+ tail->lxsys_next = lxsys_getnode(dp, LXSYS_STATIC, instance, 0);
+ lnp = tail->lxsys_next;
+ /* Allow mounts on static entries */
+ LXSTOV(lnp)->v_flag &= (~VNOMOUNT);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (lnp);
+}
+
+/* Clean up persistence for static lxsys_node */
+int
+lxsys_freenode_static(lxsys_node_t *lnp)
+{
+ lxsys_node_t *plnp;
+ vnode_t *vp = LXSTOV(lnp);
+ lxsys_mnt_t *lxsm = VTOLXSM(vp);
+
+ if (lnp->lxsys_instance == LXSYS_INST_ROOT) {
+ /*
+ * The root vnode does not need special cleanup since it
+ * anchors the list and is freed by lxsys_unmount.
+ */
+ return (0);
+ }
+
+ mutex_enter(&lxsm->lxsysm_lock);
+
+ /*
+ * It is possible that a different process acquired a fresh reference
+ * to this vnode via lookup while we were waiting on the lxsysm_lock.
+ * To avoid freeing the vnode out from under them, we will double-check
+ * v_count and bail from the fop_inactive if it was grabbed.
+ */
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count != 1) {
+ VERIFY(vp->v_count > 0);
+
+ /* Release our hold before bailing out of lxsys_inactive */
+ vp->v_count--;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (-1);
+ }
+ mutex_exit(&vp->v_lock);
+
+ /* search for the record pointing to lnp */
+ plnp = lxsm->lxsysm_node;
+ while (plnp != NULL && plnp->lxsys_next != lnp) {
+ plnp = plnp->lxsys_next;
+ }
+ /* entry should always be found */
+ VERIFY(plnp != NULL);
+ plnp->lxsys_next = lnp->lxsys_next;
+
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (0);
+}
+
+/*
+ * Free the storage obtained from lxsys_getnode().
+ */
+void
+lxsys_freenode(lxsys_node_t *lxsnp)
+{
+ vnode_t *vp = LXSTOV(lxsnp);
+
+ VERIFY(vp != NULL);
+
+ if (lxsnp->lxsys_type == LXSYS_STATIC) {
+ if (lxsys_freenode_static(lxsnp) != 0) {
+ return;
+ }
+ }
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxsnp->lxsys_parentvp != NULL)
+ VN_RELE(lxsnp->lxsys_parentvp);
+
+ /*
+ * Release the lxsysnode.
+ */
+ kmem_cache_free(lxsys_node_cache, lxsnp);
+}
+
+/*
+ * Get the netstack associated with this lxsys mount
+ */
+netstack_t *
+lxsys_netstack(lxsys_node_t *lnp)
+{
+ zone_t *zone = VTOLXSM(LXSTOV(lnp))->lxsysm_zone;
+ netstack_t *ns = zone->zone_netstack;
+
+ VERIFY(ns != NULL);
+
+ if (ns->netstack_flags & (NSF_UNINIT|NSF_CLOSING)) {
+ ns = NULL;
+ } else {
+ netstack_hold(ns);
+ }
+
+ return (ns);
+}
+
+ill_t *
+lxsys_find_ill(ip_stack_t *ipst, uint_t ifindex)
+{
+ ill_t *ill;
+ phyint_t *phyi;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+ (void *) &ifindex, NULL);
+ if (phyi != NULL) {
+ /*
+ * Since interface information presented via /sys is not
+ * specific to IPv4 or IPv6, an ill reference from either
+ * protocol will be adequate. Check both, starting with IPv4
+ * for a valid reference to use.
+ */
+ for (ill = phyi->phyint_illv4; ill != phyi->phyint_illv6;
+ ill = phyi->phyint_illv6) {
+ if (ill != NULL) {
+ mutex_enter(&ill->ill_lock);
+ if (!ILL_IS_CONDEMNED(ill)) {
+ ill_refhold_locked(ill);
+ mutex_exit(&ill->ill_lock);
+ rw_exit(&ipst->ips_ill_g_lock);
+ return (ill);
+ }
+ mutex_exit(&ill->ill_lock);
+ }
+ }
+ }
+ rw_exit(&ipst->ips_ill_g_lock);
+ return (NULL);
+}
+
+
+#define LXSYSUIOBUFSZ 4096
+
+lxsys_uiobuf_t *
+lxsys_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxsys_uiobuf and output buffer */
+ int bufsize = LXSYSUIOBUFSZ;
+ lxsys_uiobuf_t *uiobuf =
+ kmem_alloc(sizeof (lxsys_uiobuf_t) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->bufsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxsys_uiobuf_free(lxsys_uiobuf_t *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (lxsys_uiobuf_t) + uiobuf->bufsize);
+}
+
+void
+lxsys_uiobuf_seterr(lxsys_uiobuf_t *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxsys_uiobuf_flush(lxsys_uiobuf_t *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxsys_uiobuf_write(lxsys_uiobuf_t *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->bufsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxsys_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxsys_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxsys_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c b/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c
new file mode 100644
index 0000000000..9bb1d70527
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c
@@ -0,0 +1,348 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * lxsysvfsops.c: vfs operations for lx sysfs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+
+#include "lx_sysfs.h"
+
+/* Module level parameters */
+static int lxsysfstype;
+static dev_t lxsysdev;
+static kmutex_t lxsys_mount_lock;
+
+static int lxsys_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxsys_unmount(vfs_t *, int, cred_t *);
+static int lxsys_root(vfs_t *, vnode_t **);
+static int lxsys_statvfs(vfs_t *, statvfs64_t *);
+static int lxsys_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_sysfs",
+ lxsys_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand sysfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ /*
+ * destroy lxsys_node cache
+ */
+ lxsys_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxsysfstype);
+ vn_freevnodeops(lxsys_vnodeops);
+
+ mutex_destroy(&lxsys_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxsys_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxsys_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxsys_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxsys_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxsys_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxsys_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxsys_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ lxsysfstype = fstype;
+ ASSERT(lxsysfstype != 0);
+
+ mutex_init(&lxsys_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxsys_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxsys_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxsys_vnodeops_template, &lxsys_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxsys_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxsys_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxsysdev = makedevice(dev, 0);
+
+ /*
+ * Initialise cache for lxsys_nodes
+ */
+ lxsys_initnodecache();
+
+ return (0);
+}
+
+static int
+lxsys_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxsys_mnt_t *lxsys_mnt;
+ zone_t *zone = curproc->p_zone;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (zone == global_zone) {
+ zone_t *mntzone;
+
+ mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+ zone_rele(mntzone);
+ if (zone != mntzone)
+ return (EBUSY);
+ }
+
+ /*
+ * Having the resource be anything but "lxsys" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxsys", 0);
+
+ lxsys_mnt = kmem_alloc(sizeof (*lxsys_mnt), KM_SLEEP);
+
+ mutex_enter(&lxsys_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxsys_mount_lock);
+ kmem_free(lxsys_mnt, sizeof ((*lxsys_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+
+ mutex_init(&lxsys_mnt->lxsysm_lock, NULL, MUTEX_DEFAULT, NULL);
+ zone_hold(lxsys_mnt->lxsysm_zone = zone);
+
+ /* Arbitrarily set the parent vnode to the mounted over directory */
+ lxsys_mnt->lxsysm_node = lxsys_getnode(mvp, LXSYS_STATIC,
+ LXSYS_INST_ROOT, 0);
+ lxsys_mnt->lxsysm_node->lxsys_next = NULL;
+
+ /* Correctly set the fs for the root node */
+ lxsys_mnt->lxsysm_node->lxsys_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxsysdev, lxsysfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxsysfstype;
+ vfsp->vfs_data = (caddr_t)lxsys_mnt;
+ vfsp->vfs_dev = lxsysdev;
+
+ mutex_exit(&lxsys_mount_lock);
+
+ return (0);
+}
+
+static int
+lxsys_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxsys_mnt_t *lxsys_mnt = (lxsys_mnt_t *)vfsp->vfs_data;
+ lxsys_node_t *lnp;
+ vnode_t *vp;
+ int count;
+
+ VERIFY(lxsys_mnt != NULL);
+
+ mutex_enter(&lxsys_mount_lock);
+
+ /* must be root to unmount */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxsys_mount_lock);
+ return (EPERM);
+ }
+
+ /* forced unmount is not supported by this fs */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxsys_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /* Ensure that no vnodes are in use on this mount point. */
+ lnp = lxsys_mnt->lxsysm_node;
+ vp = LXSTOV(lnp);
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxsys_mount_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * If there are no references to the root vnode the list of persistent
+ * static vnodes should be empty
+ */
+ VERIFY(lnp->lxsys_next == NULL);
+
+ (void) dnlc_purge_vfsp(vfsp, 0);
+
+ lxsys_mnt->lxsysm_node = NULL;
+ lxsys_freenode(lnp);
+ zone_rele(lxsys_mnt->lxsysm_zone);
+ vfsp->vfs_data = NULL;
+ kmem_free(lxsys_mnt, sizeof (*lxsys_mnt));
+
+ mutex_exit(&lxsys_mount_lock);
+
+ return (0);
+}
+
+static int
+lxsys_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxsys_mnt_t *lxsm = (lxsys_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+
+ VERIFY(lxsm != NULL);
+ VERIFY(lxsm->lxsysm_node != NULL);
+
+ vp = LXSTOV(lxsm->lxsysm_node);
+ VN_HOLD(vp);
+ *vpp = vp;
+
+ return (0);
+}
+
+static int
+lxsys_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ dev32_t d32;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)3;
+ sp->f_ffree = (fsfilcnt64_t)0; /* none */
+ sp->f_favail = (fsfilcnt64_t)0; /* none */
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxsysfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+ /* We know f_fstr is 32 chars */
+ (void) strcpy(sp->f_fstr, "/sys");
+ (void) strcpy(&sp->f_fstr[6], "/sys");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c b/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c
new file mode 100644
index 0000000000..3fe074540b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c
@@ -0,0 +1,1802 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_sysfs -- a Linux-compatible /sys for the LX brand
+ */
+
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/lx_misc.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/sunddi.h>
+#include <sys/vnode.h>
+#include <sys/netstack.h>
+#include <sys/ethernet.h>
+#include <inet/ip_arp.h>
+
+#include "lx_sysfs.h"
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxsys_init() in lx_sysvfsops.c
+ */
+vnodeops_t *lxsys_vnodeops;
+
+static int lxsys_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxsys_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxsys_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxsys_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxsys_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxsys_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxsys_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxsys_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxsys_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxsys_sync(void);
+static void lxsys_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxsys_lookup_static(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_class_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_virtual_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_blockdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_zfsdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpu(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpuinfo(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_sysnode(lxsys_node_t *, char *);
+
+static int lxsys_read_static(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_virtual_net(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_zfs_block(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_sysnode(lxsys_node_t *, lxsys_uiobuf_t *);
+
+static int lxsys_readdir_devices_syscpu(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_syscpuinfo(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_sysnode(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_static(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_class_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_virtual_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_blockdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_zfsdir(lxsys_node_t *, uio_t *, int *);
+
+static int lxsys_readlink_class_net(lxsys_node_t *, char *, size_t);
+static int lxsys_readlink_block(lxsys_node_t *, char *, size_t);
+
+/*
+ * The lx /sys vnode operations vector
+ */
+const fs_operation_def_t lxsys_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxsys_open },
+ VOPNAME_CLOSE, { .vop_close = lxsys_close },
+ VOPNAME_READ, { .vop_read = lxsys_read },
+ VOPNAME_GETATTR, { .vop_getattr = lxsys_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxsys_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxsys_lookup },
+ VOPNAME_READDIR, { .vop_readdir = lxsys_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxsys_readlink },
+ VOPNAME_FSYNC, { .error = lxsys_sync },
+ VOPNAME_SEEK, { .error = lxsys_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxsys_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxsys_cmp },
+ NULL, NULL
+};
+
+/*
+ * Sysfs Inode format:
+ * 0000AABBBBCC
+ *
+ * AA - TYPE
+ * BBBB - INSTANCE
+ * CC - ENDPOINT
+ *
+ * Where TYPE is one of:
+ * 1 - SYS_STATIC
+ * 2 - SYS_CLASS_NET
+ * 3 - SYS_DEV_NET
+ * 4 - SYS_BLOCK
+ * 5 - SYS_DEV_ZFS
+ * 6 - SYS_DEV_SYS_CPU
+ * 7 - SYS_DEV_SYS_CPUINFO
+ * 8 - SYS_DEV_SYS_NODE
+ *
+ * Static entries will have assigned INSTANCE identifiers:
+ * - 0x00: /sys
+ * - 0x01: /sys/class
+ * - 0x02: /sys/devices
+ * - 0x03: /sys/fs
+ * - 0x04: /sys/class/net
+ * - 0x05: /sys/devices/virtual
+ * - 0x06: /sys/devices/system
+ * - 0x07: /sys/fs/cgroup
+ * - 0x08: /sys/devices/virtual/net
+ * - 0x09: /sys/block
+ * - 0x0a: /sys/devices/zfs
+ * - 0x0b: /sys/devices/system/cpu
+ * - 0x0c: /sys/devices/system/cpu/kernel_max
+ * - 0x0d: /sys/devices/system/node
+ *
+ * Dynamic /sys/class/net/<interface> symlinks will use an INSTANCE derived
+ * from the corresonding ifindex.
+ *
+ * Dynamic /sys/devices/virtual/net/<interface>/<entries> directories will use
+ * an INSTANCE derived from the ifindex and statically assigned ENDPOINT IDs
+ * for the contained entries.
+ *
+ * Dynamic /sys/block/<dev> symlinks will use an INSTANCE derived from the
+ * device major and instance from records listed in kstat or zvols.
+ *
+ * Dynamic /sys/devices/zfs/<dev> directories will use an INSTANCE derived from
+ * the emulated minor number.
+ *
+ * Static/Dynamic /sys/devices/system/cpu contains a static kernel_max file
+ * and a dynamic set of cpuN subdirectories.
+ *
+ * Static/Dynamic /sys/devices/system/node/node0 currently only contains a
+ * static cpulist file, but will likely need future dynamic entries for cpuN
+ * symlinks, and perhaps other static files. By only providing 'node0' we
+ * pretend that there is only a single NUMA node available to a zone (trying to
+ * be NUMA-aware inside a zone is generally not going to work anyway).
+ */
+
+#define LXSYS_INST_CLASSDIR 0x1
+#define LXSYS_INST_DEVICESDIR 0x2
+#define LXSYS_INST_FSDIR 0x3
+#define LXSYS_INST_CLASS_NETDIR 0x4
+#define LXSYS_INST_DEVICES_VIRTUALDIR 0x5
+#define LXSYS_INST_DEVICES_SYSTEMDIR 0x6
+#define LXSYS_INST_FS_CGROUPDIR 0x7
+#define LXSYS_INST_DEVICES_VIRTUAL_NETDIR 0x8
+#define LXSYS_INST_BLOCKDIR 0x9
+#define LXSYS_INST_DEVICES_ZFSDIR 0xa
+#define LXSYS_INST_DEVICES_SYSCPU 0xb
+#define LXSYS_INST_DEV_SYSCPU_KMAX 0xc
+#define LXSYS_INST_DEVICES_SYSNODE 0xd
+
+/*
+ * file contents of an lx /sys directory.
+ */
+static lxsys_dirent_t dirlist_root[] = {
+ { LXSYS_INST_BLOCKDIR, "block" },
+ { LXSYS_INST_CLASSDIR, "class" },
+ { LXSYS_INST_DEVICESDIR, "devices" },
+ { LXSYS_INST_FSDIR, "fs" }
+};
+static lxsys_dirent_t dirlist_class[] = {
+ { LXSYS_INST_CLASS_NETDIR, "net" }
+};
+static lxsys_dirent_t dirlist_fs[] = {
+ { LXSYS_INST_FS_CGROUPDIR, "cgroup" }
+};
+static lxsys_dirent_t dirlist_devices[] = {
+ { LXSYS_INST_DEVICES_SYSTEMDIR, "system" },
+ { LXSYS_INST_DEVICES_VIRTUALDIR, "virtual" },
+ { LXSYS_INST_DEVICES_ZFSDIR, "zfs" }
+};
+static lxsys_dirent_t dirlist_devices_virtual[] = {
+ { LXSYS_INST_DEVICES_VIRTUAL_NETDIR, "net" }
+};
+
+/*
+ * XXX: The presence of the cpu tree in sysfs triggers new behavior in various
+ * applications. The glibc code which accesses this part of the tree expects
+ * dirents to have the d_type field populated. We cannot implement the 'cpu'
+ * hierarchy until that is addressed. One such application is java, which
+ * becomes unstable due to the incorrect data from glibc.
+ */
+static lxsys_dirent_t dirlist_devices_system[] = {
+ /* { LXSYS_INST_DEVICES_SYSCPU, "cpu" }, */
+ { LXSYS_INST_DEVICES_SYSNODE, "node" }
+};
+
+#define LXSYS_ENDP_NET_ADDRESS 1
+#define LXSYS_ENDP_NET_ADDRLEN 2
+#define LXSYS_ENDP_NET_FLAGS 3
+#define LXSYS_ENDP_NET_IFINDEX 4
+#define LXSYS_ENDP_NET_MTU 5
+#define LXSYS_ENDP_NET_TXQLEN 6
+#define LXSYS_ENDP_NET_TYPE 7
+
+#define LXSYS_ENDP_BLOCK_DEVICE 1
+
+#define LXSYS_ENDP_NODE_CPULIST 1
+
+static lxsys_dirent_t dirlist_devices_virtual_net[] = {
+ { LXSYS_ENDP_NET_ADDRESS, "address" },
+ { LXSYS_ENDP_NET_ADDRLEN, "addr_len" },
+ { LXSYS_ENDP_NET_FLAGS, "flags" },
+ { LXSYS_ENDP_NET_IFINDEX, "ifindex" },
+ { LXSYS_ENDP_NET_MTU, "mtu" },
+ { LXSYS_ENDP_NET_TXQLEN, "tx_queue_len" },
+ { LXSYS_ENDP_NET_TYPE, "type" }
+};
+
+static lxsys_dirent_t dirlist_devices_zfs_block[] = {
+ { LXSYS_ENDP_BLOCK_DEVICE, "device" }
+};
+
+static lxsys_dirent_t dirlist_devices_sysnode[] = {
+ { LXSYS_ENDP_NODE_CPULIST, "cpulist" }
+};
+
+#define SYSDIRLISTSZ(l) (sizeof (l) / sizeof ((l)[0]))
+
+#define SYSDLENT(i, l) { i, l, SYSDIRLISTSZ(l) }
+static lxsys_dirlookup_t lxsys_dirlookup[] = {
+ SYSDLENT(LXSYS_INST_ROOT, dirlist_root),
+ SYSDLENT(LXSYS_INST_CLASSDIR, dirlist_class),
+ SYSDLENT(LXSYS_INST_FSDIR, dirlist_fs),
+ { LXSYS_INST_FS_CGROUPDIR, NULL, 0 },
+ SYSDLENT(LXSYS_INST_DEVICESDIR, dirlist_devices),
+ SYSDLENT(LXSYS_INST_DEVICES_SYSTEMDIR, dirlist_devices_system),
+ SYSDLENT(LXSYS_INST_DEVICES_VIRTUALDIR, dirlist_devices_virtual),
+ SYSDLENT(LXSYS_INST_DEVICES_SYSNODE, dirlist_devices_sysnode)
+};
+
+
+/*
+ * Array of lookup functions, indexed by lx /sys file type.
+ */
+static vnode_t *(*lxsys_lookup_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_lookup_static, /* LXSYS_STATIC */
+ lxsys_lookup_class_netdir, /* LXSYS_CLASS_NET */
+ lxsys_lookup_devices_virtual_netdir, /* LXSYS_DEV_NET */
+ lxsys_lookup_blockdir, /* LXSYS_BLOCK */
+ lxsys_lookup_devices_zfsdir, /* LXSYS_DEV_ZFS */
+ lxsys_lookup_devices_syscpu, /* LXSYS_DEV_SYS_CPU */
+ lxsys_lookup_devices_syscpuinfo, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_lookup_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of readdir functions, indexed by /sys file type.
+ */
+static int (*lxsys_readdir_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_readdir_static, /* LXSYS_STATIC */
+ lxsys_readdir_class_netdir, /* LXSYS_CLASS_NET */
+ lxsys_readdir_devices_virtual_netdir, /* LXSYS_DEV_NET */
+ lxsys_readdir_blockdir, /* LXSYS_BLOCK */
+ lxsys_readdir_devices_zfsdir, /* LXSYS_DEV_ZFS */
+ lxsys_readdir_devices_syscpu, /* LXSYS_DEV_SYS_CPU */
+ lxsys_readdir_devices_syscpuinfo, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_readdir_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of read functions, indexed by /sys file type.
+ */
+static int (*lxsys_read_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_read_static, /* LXSYS_STATIC */
+ NULL, /* LXSYS_CLASS_NET */
+ lxsys_read_devices_virtual_net, /* LXSYS_DEV_NET */
+ NULL, /* LXSYS_BLOCK */
+ lxsys_read_devices_zfs_block, /* LXSYS_DEV_ZFS */
+ NULL, /* LXSYS_DEV_SYS_CPU */
+ NULL, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_read_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of readlink functions, indexed by /sys file type.
+ */
+static int (*lxsys_readlink_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ NULL, /* LXSYS_STATIC */
+ lxsys_readlink_class_net, /* LXSYS_CLASS_NET */
+ NULL, /* LXSYS_DEV_NET */
+ lxsys_readlink_block, /* LXSYS_BLOCK */
+ NULL, /* LXSYS_DEV_ZFS */
+ NULL, /* LXSYS_DEV_SYS_CPU */
+ NULL, /* LXSYS_DEV_SYS_CPUINFO */
+ NULL, /* LXSYS_DEV_SYS_NODE */
+};
+
+typedef struct lxsys_cpu_info {
+ processorid_t cpu_id;
+ processorid_t cpu_seqid;
+} lxsys_cpu_info_t;
+
+/*
+ * lxsys_open(): Vnode operation for VOP_OPEN()
+ */
+/* ARGSUSED */
+static int
+lxsys_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ /*
+ * We only allow reading in this file system
+ */
+ if (flag & FWRITE)
+ return (EROFS);
+
+ return (0);
+}
+
+
+/*
+ * lxsys_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxsys_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ return (0);
+}
+
+
+/*
+ * lxsys_read(): Vnode operation for VOP_READ()
+ * All we currently have in this fs are directories.
+ */
+/* ARGSUSED */
+static int
+lxsys_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxsys_node_t *lnp = VTOLXS(vp);
+ lxsys_nodetype_t type = lnp->lxsys_type;
+ int (*rlfunc)();
+ int error;
+ lxsys_uiobuf_t *luio;
+
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ if (vp->v_type == VDIR) {
+ return (EISDIR);
+ }
+
+ rlfunc = lxsys_read_function[type];
+ if (rlfunc != NULL) {
+ luio = lxsys_uiobuf_new(uiop);
+ if ((error = rlfunc(lnp, luio)) == 0) {
+ error = lxsys_uiobuf_flush(luio);
+ }
+ lxsys_uiobuf_free(luio);
+ } else {
+ error = EIO;
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_getattr(): Vnode operation for VOP_GETATTR()
+ */
+/* ARGSUSED */
+static int
+lxsys_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxsys_node_t *lxsnp = VTOLXS(vp);
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxsnp->lxsys_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxsnp->lxsys_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxsnp->lxsys_uid;
+ vap->va_gid = lxsnp->lxsys_gid;
+ vap->va_nodeid = lxsnp->lxsys_ino;
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxsys_access(): Vnode operation for VOP_ACCESS()
+ */
+/* ARGSUSED */
+static int
+lxsys_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxsys_node_t *lxsnp = VTOLXS(vp);
+ int shift = 0;
+
+ /*
+ * Although our lx sysfs is basically a read only file system, Linux
+ * expects it to be writable so we can't just error if (mode & VWRITE).
+ */
+
+ /* If user is root allow access regardless of permission bits */
+ if (secpolicy_proc_access(cr) == 0)
+ return (0);
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxsnp->lxsys_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxsnp->lxsys_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxsnp->lxsys_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+/*
+ * lxsys_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxsys_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxsys_node_t *lxsnp = VTOLXS(dp);
+ lxsys_nodetype_t type = lxsnp->lxsys_type;
+ int error;
+
+ VERIFY(dp->v_type == VDIR);
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxsys_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxsnp->lxsys_parentvp);
+ *vpp = lxsnp->lxsys_parentvp;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxsys_lookup_function[type](lxsnp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+static lxsys_node_t *
+lxsys_lookup_disk(lxsys_node_t *ldp, char *comp, lxsys_nodetype_t type)
+{
+ lxsys_node_t *lnp = NULL;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (NULL);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ int inst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+ if (strcmp(vd->lxvd_name, comp) == 0 && inst != 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, type, inst, 0);
+ break;
+ }
+
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ return (lnp);
+}
+
+static vnode_t *
+lxsys_lookup_static(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_dirent_t *dirent = NULL;
+ int i, len = 0;
+
+ for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+ if (ldp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+ dirent = lxsys_dirlookup[i].dl_list;
+ len = lxsys_dirlookup[i].dl_length;
+ break;
+ }
+ }
+ if (dirent == NULL) {
+ return (NULL);
+ }
+
+ for (i = 0; i < len; i++) {
+ if (strncmp(comp, dirent[i].d_name, MAXPATHLEN) == 0) {
+ lxsys_nodetype_t node_type = ldp->lxsys_type;
+ unsigned int node_instance = 0;
+ lxsys_node_t *lnp;
+
+ switch (dirent[i].d_idnum) {
+ case LXSYS_INST_BLOCKDIR:
+ node_type = LXSYS_BLOCK;
+ break;
+ case LXSYS_INST_CLASS_NETDIR:
+ node_type = LXSYS_CLASS_NET;
+ break;
+ case LXSYS_INST_DEVICES_VIRTUAL_NETDIR:
+ node_type = LXSYS_DEV_NET;
+ break;
+ case LXSYS_INST_DEVICES_ZFSDIR:
+ node_type = LXSYS_DEV_ZFS;
+ break;
+ case LXSYS_INST_DEVICES_SYSCPU:
+ node_type = LXSYS_DEV_SYS_CPU;
+ break;
+ case LXSYS_INST_DEVICES_SYSNODE:
+ node_type = LXSYS_DEV_SYS_NODE;
+ break;
+ default:
+ /* Another static node */
+ node_instance = dirent[i].d_idnum;
+ }
+ if (node_type == LXSYS_STATIC) {
+ lnp = lxsys_getnode_static(ldp->lxsys_vnode,
+ node_instance);
+ } else {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ node_type, node_instance, 0);
+ }
+ return (lnp->lxsys_vnode);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_class_netdir(lxsys_node_t *ldp, char *comp)
+{
+ vnode_t *result = NULL;
+ lxsys_node_t *lnp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ char ifname[LIFNAMSIZ];
+
+ if (ldp->lxsys_type != LXSYS_CLASS_NET ||
+ ldp->lxsys_instance != 0) {
+ /* Lookups only allowed at directory level */
+ return (NULL);
+ }
+
+ (void) strncpy(ifname, comp, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ return (NULL);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+ phyi = avl_find(phytree, ifname, NULL);
+ if (phyi != NULL) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ phyi->phyint_ifindex, 0);
+ result = lnp->lxsys_vnode;
+ result->v_type = VLNK;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ return (result);
+}
+
+static vnode_t *
+lxsys_lookup_devices_virtual_netdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level interface listing */
+ vnode_t *result = NULL;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ char ifname[LIFNAMSIZ];
+
+ (void) strncpy(ifname, comp, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ return (NULL);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+ phyi = avl_find(phytree, ifname, NULL);
+ if (phyi != NULL) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ phyi->phyint_ifindex, 0);
+ result = lnp->lxsys_vnode;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ return (result);
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* interface-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ size = SYSDIRLISTSZ(dirlist_devices_virtual_net);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_virtual_net[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_blockdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ lnp = lxsys_lookup_disk(ldp, comp, LXSYS_BLOCK);
+
+ if (lnp != NULL) {
+ lnp->lxsys_vnode->v_type = VLNK;
+ return (lnp->lxsys_vnode);
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_zfsdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ lnp = lxsys_lookup_disk(ldp, comp, LXSYS_DEV_ZFS);
+
+ if (lnp != NULL) {
+ return (lnp->lxsys_vnode);
+ }
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* disk-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ /*
+ * All of these entries currently look like regular files
+ * but on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ size = SYSDIRLISTSZ(dirlist_devices_zfs_block);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_zfs_block[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_syscpu(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp = NULL;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level cpu listing */
+
+ /* If fixed entry */
+ if (strcmp(comp, "kernel_max") == 0) {
+ lnp = lxsys_getnode_static(ldp->lxsys_vnode,
+ LXSYS_INST_DEV_SYSCPU_KMAX);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else {
+ /* Else dynamic cpuN entry */
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ char cpunm[16];
+
+ (void) snprintf(cpunm, sizeof (cpunm), "cpu%d",
+ cp->cpu_seqid);
+
+ if (strcmp(comp, cpunm) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ LXSYS_DEV_SYS_CPUINFO,
+ cp->cpu_id + 1, 0);
+ break;
+ }
+ if (pools_enabled) {
+ cp = cp->cpu_next_part;
+ } else {
+ cp = cp->cpu_next;
+ }
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+ }
+
+ if (lnp != NULL) {
+ return (lnp->lxsys_vnode);
+ }
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* cpu-level sub-item listing, currently empty */
+ /* EMPTY */
+ }
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxsys_lookup_devices_syscpuinfo(lxsys_node_t *ldp, char *comp)
+{
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_sysnode(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp = NULL;
+
+ if (ldp->lxsys_instance == 0) {
+ /*
+ * The system is presently represented as a single node,
+ * regardless of any NUMA topology which exists.
+ * The instances are offset by 1 to account for the top level
+ * directory occupying instance 0.
+ */
+ if (strcmp(comp, "node0") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ 1, 0);
+ return (lnp->lxsys_vnode);
+ }
+ } else {
+ /* interface-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ size = SYSDIRLISTSZ(dirlist_devices_sysnode);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_sysnode[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+lxsys_read_devices_virtual_net(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ netstack_t *ns;
+ ill_t *ill;
+ uint_t ifindex = lnp->lxsys_instance;
+ uint8_t *addr;
+ uint64_t flags;
+ int error = 0;
+
+ if (ifindex == 0 || lnp->lxsys_endpoint == 0) {
+ return (EISDIR);
+ }
+
+ if ((ns = lxsys_netstack(lnp)) == NULL) {
+ return (EIO);
+ }
+
+ ill = lxsys_find_ill(ns->netstack_ip, ifindex);
+ if (ill == NULL) {
+ netstack_rele(ns);
+ return (EIO);
+ }
+
+ switch (lnp->lxsys_endpoint) {
+ case LXSYS_ENDP_NET_ADDRESS:
+ if (ill->ill_phys_addr_length != ETHERADDRL) {
+ lxsys_uiobuf_printf(luio, "00:00:00:00:00:00\n");
+ break;
+ }
+ addr = ill->ill_phys_addr;
+ lxsys_uiobuf_printf(luio,
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ break;
+ case LXSYS_ENDP_NET_ADDRLEN:
+ lxsys_uiobuf_printf(luio, "%u\n",
+ IS_LOOPBACK(ill) ? ETHERADDRL : ill->ill_phys_addr_length);
+ break;
+ case LXSYS_ENDP_NET_FLAGS:
+ flags = (ill->ill_flags | ill->ill_ipif->ipif_flags |
+ ill->ill_phyint->phyint_flags) & 0xffff;
+ lx_ifflags_convert(&flags, LX_IF_FROMNATIVE);
+ lxsys_uiobuf_printf(luio, "0x%x\n", flags);
+ break;
+ case LXSYS_ENDP_NET_IFINDEX:
+ lxsys_uiobuf_printf(luio, "%u\n", ifindex);
+ break;
+ case LXSYS_ENDP_NET_MTU:
+ lxsys_uiobuf_printf(luio, "%u\n", ill->ill_mtu);
+ break;
+ case LXSYS_ENDP_NET_TXQLEN:
+ /* perpetuate the txqlen lie */
+ if (IS_LOOPBACK(ill)) {
+ lxsys_uiobuf_printf(luio, "0\n");
+ } else {
+ lxsys_uiobuf_printf(luio, "1\n");
+ }
+ break;
+ case LXSYS_ENDP_NET_TYPE:
+ lxsys_uiobuf_printf(luio, "%u\n",
+ IS_LOOPBACK(ill) ? LX_ARPHRD_LOOPBACK :
+ arp_hw_type(ill->ill_mactype));
+ break;
+ default:
+ error = EIO;
+ }
+
+ ill_refrele(ill);
+ netstack_rele(ns);
+ return (error);
+}
+
+/* ARGSUSED1 */
+static int
+lxsys_read_devices_zfs_block(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ uint_t dskindex = lnp->lxsys_instance;
+
+ if (dskindex == 0 || lnp->lxsys_endpoint == 0) {
+ return (EISDIR);
+ }
+
+ return (EIO);
+}
+
+static int
+lxsys_read_devices_sysnode(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ if (lnp->lxsys_instance == 1 &&
+ lnp->lxsys_endpoint == LXSYS_ENDP_NODE_CPULIST) {
+ /* Show the range of CPUs */
+ cpu_t *cp, *cpstart;
+ int pools_enabled, maxid = -1;
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ if (cp->cpu_seqid > maxid)
+ maxid = cp->cpu_seqid;
+
+ if (pools_enabled) {
+ cp = cp->cpu_next_part;
+ } else {
+ cp = cp->cpu_next;
+ }
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ lxsys_uiobuf_printf(luio, "0-%d\n", maxid);
+ return (0);
+ }
+ return (EISDIR);
+
+}
+
+static int
+lxsys_read_static(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ uint_t inst = lnp->lxsys_instance;
+
+ if (inst == LXSYS_INST_DEV_SYSCPU_KMAX) {
+ lxsys_uiobuf_printf(luio, "%d\n", NCPU);
+ return (0);
+ }
+
+ /* All other static nodes are directories */
+ return (EISDIR);
+}
+
+/*
+ * lxsys_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxsys_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxsys_node_t *lxsnp = VTOLXS(dp);
+ lxsys_nodetype_t type = lxsnp->lxsys_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error, leof;
+
+ ASSERT(dp->v_type == VDIR);
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxsys_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXSYS_SDSIZE)
+ return (ENOENT);
+
+ /* Free lower functions from having to check eofp == NULL */
+ if (eofp == NULL) {
+ eofp = &leof;
+ }
+
+ return (lxsys_readdir_function[lxsnp->lxsys_type](lxsnp, uiop, eofp));
+}
+
+static int
+lxsys_dirent_out(dirent64_t *d, ushort_t n, struct uio *uio)
+{
+ int error;
+ off_t offset = uio->uio_offset;
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset by the
+ * same amount. But we want uiop->uio_offset to change in increments
+ * of LXSYS_SDSIZE, which is different from the number of bytes being
+ * returned to the user. To accomplish this, we set uiop->uio_offset
+ * separately on success, overriding what uiomove() does.
+ */
+ d->d_off = (off64_t)(offset + LXSYS_SDSIZE);
+ d->d_reclen = n;
+ if ((error = uiomove(d, n, UIO_READ, uio)) != 0) {
+ return (error);
+ }
+ uio->uio_offset = offset + LXSYS_SDSIZE;
+ return (0);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxsys_readdir_common(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+ lxsys_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Satisfy user request */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXSYS_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxsnp->lxsys_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXSYS_SDSIZE) {
+
+ dirent->d_ino = lxsys_parentinode(lxsnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxsys_inode(LXSYS_STATIC,
+ dirtab[dirindex].d_idnum, 0);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ *eofp = 1;
+ return (0);
+ }
+
+ /*
+ * If the size of the data to transfer is greater than the
+ * user-provided buffer, we cannot continue.
+ */
+ if (reclen > uresid) {
+ /* Error if no entries have been returned yet. */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ return (error);
+ }
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ *eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ? 1 : 0;
+ return (0);
+}
+
+static int
+lxsys_readdir_subdir(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+ lxsys_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ VERIFY(dirtab != NULL || dirtablen == 0);
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Satisfy user request */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXSYS_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxsnp->lxsys_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXSYS_SDSIZE) {
+
+ dirent->d_ino = lxsys_parentinode(lxsnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxsys_inode(lxsnp->lxsys_type,
+ lxsnp->lxsys_instance, dirtab[dirindex].d_idnum);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ *eofp = 1;
+ return (0);
+ }
+
+ /*
+ * If the size of the data to transfer is greater than the
+ * user-provided buffer, we cannot continue.
+ */
+ if (reclen > uresid) {
+ /* Error if no entries have been returned yet. */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ return (error);
+ }
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ *eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ? 1 : 0;
+ return (0);
+}
+
+static int
+lxsys_readdir_ifaces(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+ lxsys_nodetype_t type)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ int error, i;
+
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ *eofp = 1;
+ return (0);
+ }
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+ phyi = avl_first(phytree);
+ if (phyi == NULL) {
+ *eofp = 1;
+ }
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Skip records we have already passed with the offset.
+ * This accounts for the two "." and ".." records already seen.
+ */
+ for (i = (uiop->uio_offset/LXSYS_SDSIZE) - 2; i > 0; i--) {
+ if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+ *eofp = 1;
+ break;
+ }
+ }
+
+ while ((uresid = uiop->uio_resid) > 0 && phyi != NULL) {
+ uint_t ifindex;
+ int reclen;
+
+ ifindex = phyi->phyint_ifindex;
+ (void) strncpy(dirent->d_name, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(dirent->d_name, LX_IF_FROMNATIVE);
+ dirent->d_ino = lxsys_inode(type, ifindex, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+
+ if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+ *eofp = 1;
+ break;
+ }
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+ return (error);
+}
+
+static int
+lxsys_readdir_disks(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+ lxsys_nodetype_t type)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int skip, error;
+ int reclen;
+ uint_t instance;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (EINVAL);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ if (skip > 0) {
+ skip--;
+ goto next;
+ }
+
+ if (strnlen(vd->lxvd_name, sizeof (vd->lxvd_name)) > LXSNSIZ)
+ goto next;
+
+ (void) strncpy(dirent->d_name, vd->lxvd_name, LXSNSIZ);
+
+ instance = getminor(vd->lxvd_emul_dev) & 0xffff;
+ if (instance == 0)
+ goto next;
+
+ dirent->d_ino = lxsys_inode(type, instance, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+
+next:
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ /* Indicate EOF if we reached the end of the virtual disks. */
+ if (vd == NULL) {
+ *eofp = 1;
+ }
+
+ return (error);
+}
+
+
+static int
+lxsys_readdir_static(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ lxsys_dirent_t *dirent = NULL;
+ int i, len = 0;
+
+ for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+ if (lnp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+ dirent = lxsys_dirlookup[i].dl_list;
+ len = lxsys_dirlookup[i].dl_length;
+ break;
+ }
+ }
+
+ if (dirent == NULL) {
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_common(lnp, uiop, eofp, dirent, len));
+}
+
+static int
+lxsys_readdir_class_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ if (lnp->lxsys_type != LXSYS_CLASS_NET ||
+ lnp->lxsys_instance != 0) {
+ /*
+ * Since /sys/class/net contains only symlinks, readdir
+ * operations should not be performed anywhere except the top
+ * level (instance == 0).
+ */
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_ifaces(lnp, uiop, eofp, LXSYS_CLASS_NET));
+}
+
+static int
+lxsys_readdir_devices_virtual_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level interface listing */
+ error = lxsys_readdir_ifaces(lnp, uiop, eofp,
+ LXSYS_DEV_NET);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* interface-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_virtual_net,
+ SYSDIRLISTSZ(dirlist_devices_virtual_net));
+ } else {
+ /* there shouldn't be subdirs below this */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+static int
+lxsys_readdir_blockdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ if (lnp->lxsys_type != LXSYS_BLOCK ||
+ lnp->lxsys_instance != 0) {
+ /*
+ * Since /sys/block contains only symlinks, readdir operations
+ * should not be performed anywhere except the top level
+ * (instance == 0).
+ */
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_disks(lnp, uiop, eofp, LXSYS_BLOCK));
+}
+
+static int
+lxsys_readdir_devices_zfsdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ error = lxsys_readdir_disks(lnp, uiop, eofp,
+ LXSYS_DEV_ZFS);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* disk-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_zfs_block,
+ SYSDIRLISTSZ(dirlist_devices_zfs_block));
+ } else {
+ /*
+ * Currently there shouldn't be subdirs below this but
+ * on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+static int
+lxsys_readdir_cpu(lxsys_node_t *ldp, struct uio *uiop, int *eofp)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int skip, error;
+ int reclen;
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ int i, cpucnt;
+ lxsys_cpu_info_t cpu_info[NCPU];
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ /* Fixed entries */
+ if (skip > 0) {
+ skip--;
+ } else {
+ (void) strncpy(dirent->d_name, "kernel_max", LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_STATIC,
+ LXSYS_INST_DEV_SYSCPU_KMAX, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ goto done;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ goto done;
+ }
+ }
+
+ /* Collect a list of CPU info */
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cpucnt = 0;
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ cpu_info[cpucnt].cpu_id = cp->cpu_id;
+ cpu_info[cpucnt++].cpu_seqid = cp->cpu_seqid;
+ ASSERT(cpucnt < NCPU);
+ if (pools_enabled) {
+ cp = cp->cpu_next_part;
+ } else {
+ cp = cp->cpu_next;
+ }
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ /* Output dynamic CPU info */
+ for (i = 0; i < cpucnt; i++) {
+ char cpunm[16];
+
+ if (skip > 0) {
+ skip--;
+ continue;
+ }
+
+ (void) snprintf(cpunm, sizeof (cpunm), "cpu%d",
+ cpu_info[i].cpu_seqid);
+ (void) strncpy(dirent->d_name, cpunm, LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_CPU,
+ cpu_info[i].cpu_id + 1, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+ }
+
+ /* Indicate EOF if we reached the end of the CPU list. */
+ if (i == cpucnt) {
+ *eofp = 1;
+ }
+
+done:
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpu(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level cpu listing */
+ error = lxsys_readdir_cpu(lnp, uiop, eofp);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* cpu-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp, NULL, 0);
+ } else {
+ /*
+ * Currently there shouldn't be subdirs below this but
+ * on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpuinfo(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_type != LXSYS_DEV_SYS_CPUINFO) {
+ /*
+ * Since /sys/devices/system/cpu/cpuN is empty, readdir
+ * operations should not be performed anywhere except the top
+ * level.
+ */
+ return (ENOTDIR);
+ }
+
+ /*
+ * Emit "." and ".." entries
+ * All cpuN directories are currently empty.
+ */
+ error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ /* Indicate EOF */
+ *eofp = 1;
+
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_sysnode(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level node listing */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int reclen, skip;
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ /* Fixed entries */
+ if (skip > 0) {
+ skip--;
+ } else {
+ (void) strncpy(dirent->d_name, "node0", LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_NODE,
+ 1, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ return (EINVAL);
+ }
+ return (0);
+ }
+ error = lxsys_dirent_out(dirent, reclen, uiop);
+ }
+ /* Indicate EOF */
+ if (error == 0) {
+ *eofp = 1;
+ }
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* node-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_sysnode,
+ SYSDIRLISTSZ(dirlist_devices_sysnode));
+ } else {
+ /* there shouldn't be subdirs below this */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxsys_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char buf[MAXPATHLEN + 1];
+ lxsys_node_t *lnp = VTOLXS(vp);
+ lxsys_nodetype_t type = lnp->lxsys_type;
+ int (*rlfunc)();
+ int error;
+
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ if (vp->v_type != VLNK) {
+ return (EINVAL);
+ }
+
+ rlfunc = lxsys_readlink_function[lnp->lxsys_type];
+ if (rlfunc != NULL) {
+ if ((error = rlfunc(lnp, buf, sizeof (buf))) == 0) {
+ error = uiomove(buf, strlen(buf), UIO_READ, uiop);
+ }
+ } else {
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+
+static int
+lxsys_readlink_class_net(lxsys_node_t *lnp, char *buf, size_t len)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ uint_t ifindex;
+ char ifname[LIFNAMSIZ];
+ int error = EINVAL;
+
+ if ((ifindex = lnp->lxsys_instance) == 0) {
+ return (error);
+ }
+
+ if ((ns = lxsys_netstack(lnp)) == NULL) {
+ return (error);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+ phyi = avl_find(phytree, &ifindex, NULL);
+ if (phyi != NULL) {
+ (void) strncpy(ifname, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+ (void) snprintf(buf, len, "/sys/devices/virtual/net/%s",
+ ifname);
+ error = 0;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+ return (error);
+}
+
+static int
+lxsys_readlink_block(lxsys_node_t *lnp, char *buf, size_t len)
+{
+ int inst, error = EINVAL;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ if ((inst = lnp->lxsys_instance) == 0) {
+ return (error);
+ }
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (error);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ int vinst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+ if (vinst == inst) {
+ (void) snprintf(buf, len,
+ "../devices/zfs/%s", vd->lxvd_name);
+ error = 0;
+ break;
+ }
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxsys_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxsys_freenode(VTOLXS(vp));
+}
+
+/*
+ * lxsys_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxsys_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxsys_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxsys_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ if (vn_matchops(vp1, lxsys_vnodeops) ||
+ vn_matchops(vp2, lxsys_vnodeops))
+ return (vp1 == vp2);
+ return (VOP_CMP(vp1, vp2, ct));
+}
diff --git a/usr/src/uts/common/brand/sn1/sn1_brand.c b/usr/src/uts/common/brand/sn1/sn1_brand.c
index d61928d578..3d3acd6036 100644
--- a/usr/src/uts/common/brand/sn1/sn1_brand.c
+++ b/usr/src/uts/common/brand/sn1/sn1_brand.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/errno.h>
@@ -42,43 +43,66 @@
char *sn1_emulation_table = NULL;
-void sn1_init_brand_data(zone_t *);
+void sn1_init_brand_data(zone_t *, kmutex_t *);
void sn1_free_brand_data(zone_t *);
void sn1_setbrand(proc_t *);
int sn1_getattr(zone_t *, int, void *, size_t *);
int sn1_setattr(zone_t *, int, void *, size_t);
int sn1_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void sn1_copy_procdata(proc_t *, proc_t *);
-void sn1_proc_exit(struct proc *, klwp_t *);
+void sn1_proc_exit(struct proc *);
void sn1_exec();
-int sn1_initlwp(klwp_t *);
+void sn1_initlwp(klwp_t *, void *);
void sn1_forklwp(klwp_t *, klwp_t *);
void sn1_freelwp(klwp_t *);
void sn1_lwpexit(klwp_t *);
int sn1_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- long *, int, caddr_t, cred_t *, int);
+ long *, int, caddr_t, cred_t *, int *);
/* sn1 brand */
struct brand_ops sn1_brops = {
- sn1_init_brand_data,
- sn1_free_brand_data,
- sn1_brandsys,
- sn1_setbrand,
- sn1_getattr,
- sn1_setattr,
- sn1_copy_procdata,
- sn1_proc_exit,
- sn1_exec,
- lwp_setrval,
- sn1_initlwp,
- sn1_forklwp,
- sn1_freelwp,
- sn1_lwpexit,
- sn1_elfexec,
- NULL,
- NULL,
- NSIG,
+ sn1_init_brand_data, /* b_init_brand_data */
+ sn1_free_brand_data, /* b_free_brand_data */
+ sn1_brandsys, /* b_brandsys */
+ sn1_setbrand, /* b_setbrand */
+ sn1_getattr, /* b_getattr */
+ sn1_setattr, /* b_setattr */
+ sn1_copy_procdata, /* b_copy_procdata */
+ sn1_proc_exit, /* b_proc_exit */
+ sn1_exec, /* b_exec */
+ lwp_setrval, /* b_lwp_setrval */
+ NULL, /* b_lwpdata_alloc */
+ NULL, /* b_lwpdata_free */
+ sn1_initlwp, /* b_initlwp */
+ NULL, /* b_initlwp_post */
+ sn1_forklwp, /* b_forklwp */
+ sn1_freelwp, /* b_freelwp */
+ sn1_lwpexit, /* b_lwpexit */
+ sn1_elfexec, /* b_elfexec */
+ NULL, /* b_sigset_native_to_brand */
+ NULL, /* b_sigset_brand_to_native */
+ NULL, /* b_sigfd_translate */
+ NSIG, /* b_nsig */
+ NULL, /* b_exit_with_sig */
+ NULL, /* b_wait_filter */
+ NULL, /* b_native_exec */
+ NULL, /* b_map32limit */
+ NULL, /* b_stop_notify */
+ NULL, /* b_waitid_helper */
+ NULL, /* b_sigcld_repost */
+ NULL, /* b_issig_stop */
+ NULL, /* b_sig_ignorable */
+ NULL, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ NULL, /* b_savecontext32 */
+#endif
+ NULL, /* b_restorecontext */
+ NULL, /* b_sendsig_stack */
+ NULL, /* b_sendsig */
+ NULL, /* b_setid_clear */
+ NULL, /* b_pagefault */
+ B_TRUE /* b_intp_parse_arg */
};
#ifdef sparc
@@ -94,9 +118,12 @@ struct brand_mach_ops sn1_mops = {
struct brand_mach_ops sn1_mops = {
sn1_brand_sysenter_callback,
+ NULL,
sn1_brand_int91_callback,
sn1_brand_syscall_callback,
- sn1_brand_syscall32_callback
+ sn1_brand_syscall32_callback,
+ NULL,
+ NULL
};
#else /* ! __amd64 */
@@ -104,7 +131,10 @@ struct brand_mach_ops sn1_mops = {
struct brand_mach_ops sn1_mops = {
sn1_brand_sysenter_callback,
NULL,
+ NULL,
sn1_brand_syscall_callback,
+ NULL,
+ NULL,
NULL
};
#endif /* __amd64 */
@@ -115,7 +145,8 @@ struct brand sn1_brand = {
BRAND_VER_1,
"sn1",
&sn1_brops,
- &sn1_mops
+ &sn1_mops,
+ sizeof (brand_proc_data_t),
};
static struct modlbrand modlbrand = {
@@ -148,10 +179,10 @@ sn1_setattr(zone_t *zone, int attr, void *buf, size_t bufsize)
return (EINVAL);
}
-/*ARGSUSED*/
+/* ARGSUSED5 */
int
sn1_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg3, uintptr_t arg4)
{
int res;
@@ -171,9 +202,9 @@ sn1_copy_procdata(proc_t *child, proc_t *parent)
}
void
-sn1_proc_exit(struct proc *p, klwp_t *l)
+sn1_proc_exit(struct proc *p)
{
- brand_solaris_proc_exit(p, l, &sn1_brand);
+ brand_solaris_proc_exit(p, &sn1_brand);
}
void
@@ -182,10 +213,10 @@ sn1_exec()
brand_solaris_exec(&sn1_brand);
}
-int
-sn1_initlwp(klwp_t *l)
+void
+sn1_initlwp(klwp_t *l, void *bd)
{
- return (brand_solaris_initlwp(l, &sn1_brand));
+ brand_solaris_initlwp(l, &sn1_brand);
}
void
@@ -214,18 +245,18 @@ sn1_free_brand_data(zone_t *zone)
/*ARGSUSED*/
void
-sn1_init_brand_data(zone_t *zone)
+sn1_init_brand_data(zone_t *zone, kmutex_t *zsl)
{
}
int
sn1_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
- int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
+ int *brand_action)
{
return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
setid, exec_file, cred, brand_action, &sn1_brand, SN1_BRANDNAME,
- SN1_LIB, SN1_LIB32, SN1_LINKER, SN1_LINKER32));
+ SN1_LIB, SN1_LIB32));
}
int
diff --git a/usr/src/uts/common/brand/sn1/sn1_brand.h b/usr/src/uts/common/brand/sn1/sn1_brand.h
index b487745e21..fef9dc128b 100644
--- a/usr/src/uts/common/brand/sn1/sn1_brand.h
+++ b/usr/src/uts/common/brand/sn1/sn1_brand.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#ifndef _SN1_BRAND_H
@@ -37,20 +38,14 @@ extern "C" {
#define SN1_VERSION SN1_VERSION_1
#define SN1_LIB_NAME "sn1_brand.so.1"
-#define SN1_LINKER_NAME "ld.so.1"
#define SN1_LIB32 BRAND_NATIVE_DIR "usr/lib/" SN1_LIB_NAME
-#define SN1_LINKER32 "/lib/" SN1_LINKER_NAME
-
#define SN1_LIB64 BRAND_NATIVE_DIR "usr/lib/64/" SN1_LIB_NAME
-#define SN1_LINKER64 "/lib/64/" SN1_LINKER_NAME
#if defined(_LP64)
#define SN1_LIB SN1_LIB64
-#define SN1_LINKER SN1_LINKER64
#else /* !_LP64 */
#define SN1_LIB SN1_LIB32
-#define SN1_LINKER SN1_LINKER32
#endif /* !_LP64 */
#if defined(_KERNEL)
diff --git a/usr/src/uts/common/brand/solaris10/s10_brand.c b/usr/src/uts/common/brand/solaris10/s10_brand.c
index f24b864eef..50997fba02 100644
--- a/usr/src/uts/common/brand/solaris10/s10_brand.c
+++ b/usr/src/uts/common/brand/solaris10/s10_brand.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#include <sys/errno.h>
@@ -45,45 +46,68 @@
char *s10_emulation_table = NULL;
-void s10_init_brand_data(zone_t *);
+void s10_init_brand_data(zone_t *, kmutex_t *);
void s10_free_brand_data(zone_t *);
void s10_setbrand(proc_t *);
int s10_getattr(zone_t *, int, void *, size_t *);
int s10_setattr(zone_t *, int, void *, size_t);
int s10_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void s10_copy_procdata(proc_t *, proc_t *);
-void s10_proc_exit(struct proc *, klwp_t *);
+void s10_proc_exit(struct proc *);
void s10_exec();
-int s10_initlwp(klwp_t *);
+void s10_initlwp(klwp_t *, void *);
void s10_forklwp(klwp_t *, klwp_t *);
void s10_freelwp(klwp_t *);
void s10_lwpexit(klwp_t *);
int s10_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- long *, int, caddr_t, cred_t *, int);
+ long *, int, caddr_t, cred_t *, int *);
void s10_sigset_native_to_s10(sigset_t *);
void s10_sigset_s10_to_native(sigset_t *);
/* s10 brand */
struct brand_ops s10_brops = {
- s10_init_brand_data,
- s10_free_brand_data,
- s10_brandsys,
- s10_setbrand,
- s10_getattr,
- s10_setattr,
- s10_copy_procdata,
- s10_proc_exit,
- s10_exec,
- lwp_setrval,
- s10_initlwp,
- s10_forklwp,
- s10_freelwp,
- s10_lwpexit,
- s10_elfexec,
- s10_sigset_native_to_s10,
- s10_sigset_s10_to_native,
- S10_NSIG,
+ s10_init_brand_data, /* b_init_brand_data */
+ s10_free_brand_data, /* b_free_brand_data */
+ s10_brandsys, /* b_brandsys */
+ s10_setbrand, /* b_setbrand */
+ s10_getattr, /* b_getattr */
+ s10_setattr, /* b_setattr */
+ s10_copy_procdata, /* b_copy_procdata */
+ s10_proc_exit, /* b_proc_exit */
+ s10_exec, /* b_exec */
+ lwp_setrval, /* b_lwp_setrval */
+ NULL, /* b_lwpdata_alloc */
+ NULL, /* b_lwpdata_free */
+ s10_initlwp, /* b_initlwp */
+ NULL, /* b_initlwp_post */
+ s10_forklwp, /* b_forklwp */
+ s10_freelwp, /* b_freelwp */
+ s10_lwpexit, /* b_lwpexit */
+ s10_elfexec, /* b_elfexec */
+ s10_sigset_native_to_s10, /* b_sigset_native_to_brand */
+ s10_sigset_s10_to_native, /* b_sigset_brand_to_native */
+ NULL, /* b_sigfd_translate */
+ S10_NSIG, /* b_nsig */
+ NULL, /* b_exit_with_sig */
+ NULL, /* b_wait_filter */
+ NULL, /* b_native_exec */
+ NULL, /* b_map32limit */
+ NULL, /* b_stop_notify */
+ NULL, /* b_waitid_helper */
+ NULL, /* b_sigcld_repost */
+ NULL, /* b_issig_stop */
+ NULL, /* b_sig_ignorable */
+ NULL, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ NULL, /* b_savecontext32 */
+#endif
+ NULL, /* b_restorecontext */
+ NULL, /* b_sendsig_stack */
+ NULL, /* b_sendsig */
+ NULL, /* b_setid_clear */
+ NULL, /* b_pagefault */
+ B_TRUE /* b_intp_parse_arg */
};
#ifdef sparc
@@ -99,9 +123,12 @@ struct brand_mach_ops s10_mops = {
struct brand_mach_ops s10_mops = {
s10_brand_sysenter_callback,
+ NULL,
s10_brand_int91_callback,
s10_brand_syscall_callback,
- s10_brand_syscall32_callback
+ s10_brand_syscall32_callback,
+ NULL,
+ NULL
};
#else /* ! __amd64 */
@@ -109,7 +136,10 @@ struct brand_mach_ops s10_mops = {
struct brand_mach_ops s10_mops = {
s10_brand_sysenter_callback,
NULL,
+ NULL,
s10_brand_syscall_callback,
+ NULL,
+ NULL,
NULL
};
#endif /* __amd64 */
@@ -120,7 +150,8 @@ struct brand s10_brand = {
BRAND_VER_1,
"solaris10",
&s10_brops,
- &s10_mops
+ &s10_mops,
+ sizeof (brand_proc_data_t),
};
static struct modlbrand modlbrand = {
@@ -249,10 +280,10 @@ s10_native(void *cmd, void *args)
return (0);
}
-/*ARGSUSED*/
+/* ARGSUSED5 */
int
s10_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg3, uintptr_t arg4)
{
proc_t *p = curproc;
int res;
@@ -326,9 +357,9 @@ s10_copy_procdata(proc_t *child, proc_t *parent)
}
void
-s10_proc_exit(struct proc *p, klwp_t *l)
+s10_proc_exit(struct proc *p)
{
- brand_solaris_proc_exit(p, l, &s10_brand);
+ brand_solaris_proc_exit(p, &s10_brand);
}
void
@@ -337,10 +368,10 @@ s10_exec()
brand_solaris_exec(&s10_brand);
}
-int
-s10_initlwp(klwp_t *l)
+void
+s10_initlwp(klwp_t *l, void *bd)
{
- return (brand_solaris_initlwp(l, &s10_brand));
+ brand_solaris_initlwp(l, &s10_brand);
}
void
@@ -380,7 +411,7 @@ s10_free_brand_data(zone_t *zone)
}
void
-s10_init_brand_data(zone_t *zone)
+s10_init_brand_data(zone_t *zone, kmutex_t *zsl)
{
ASSERT(zone->zone_brand == &s10_brand);
ASSERT(zone->zone_brand_data == NULL);
@@ -389,12 +420,12 @@ s10_init_brand_data(zone_t *zone)
int
s10_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
- int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
+ int *brand_action)
{
return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
setid, exec_file, cred, brand_action, &s10_brand, S10_BRANDNAME,
- S10_LIB, S10_LIB32, S10_LINKER, S10_LINKER32));
+ S10_LIB, S10_LIB32));
}
void
diff --git a/usr/src/uts/common/brand/solaris10/s10_brand.h b/usr/src/uts/common/brand/solaris10/s10_brand.h
index 11f9853f48..ffef485e12 100644
--- a/usr/src/uts/common/brand/solaris10/s10_brand.h
+++ b/usr/src/uts/common/brand/solaris10/s10_brand.h
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#ifndef _S10_BRAND_H
@@ -42,17 +43,12 @@ extern "C" {
#define S10_LINKER_NAME "ld.so.1"
#define S10_LIB32 BRAND_NATIVE_DIR "usr/lib/" S10_LIB_NAME
-#define S10_LINKER32 "/lib/" S10_LINKER_NAME
-
#define S10_LIB64 BRAND_NATIVE_DIR "usr/lib/64/" S10_LIB_NAME
-#define S10_LINKER64 "/lib/64/" S10_LINKER_NAME
#if defined(_LP64)
#define S10_LIB S10_LIB64
-#define S10_LINKER S10_LINKER64
#else /* !_LP64 */
#define S10_LIB S10_LIB32
-#define S10_LINKER S10_LINKER32
#endif /* !_LP64 */
/*
diff --git a/usr/src/uts/common/conf/param.c b/usr/src/uts/common/conf/param.c
index 6b3ba51f31..a71be771fd 100644
--- a/usr/src/uts/common/conf/param.c
+++ b/usr/src/uts/common/conf/param.c
@@ -22,6 +22,7 @@
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
*/
@@ -559,8 +560,8 @@ char *isa_list = architecture;
static pgcnt_t original_physmem = 0;
#define MIN_DEFAULT_MAXUSERS 8u
-#define MAX_DEFAULT_MAXUSERS 2048u
-#define MAX_MAXUSERS 4096u
+#define MAX_DEFAULT_MAXUSERS 10000u
+#define MAX_MAXUSERS 20000u
void
param_preset(void)
@@ -572,7 +573,7 @@ void
param_calc(int platform_max_nprocs)
{
/*
- * Default to about one "user" per megabyte, taking into
+ * Default to about one "user" per 8MB, taking into
* account both physical and virtual constraints.
* Note: 2^20 is a meg; shifting right by (20 - PAGESHIFT)
* converts pages to megs without integer overflow.
@@ -586,8 +587,9 @@ param_calc(int platform_max_nprocs)
if (maxusers == 0) {
pgcnt_t physmegs = physmem >> (20 - PAGESHIFT);
pgcnt_t virtmegs = vmem_size(heap_arena, VMEM_FREE) >> 20;
- maxusers = MIN(MAX(MIN(physmegs, virtmegs),
- MIN_DEFAULT_MAXUSERS), MAX_DEFAULT_MAXUSERS);
+ maxusers = MIN(physmegs, virtmegs) >> 3; /* divide by 8 */
+ maxusers = MAX(maxusers, MIN_DEFAULT_MAXUSERS);
+ maxusers = MIN(maxusers, MAX_DEFAULT_MAXUSERS);
}
if (maxusers > MAX_MAXUSERS) {
maxusers = MAX_MAXUSERS;
diff --git a/usr/src/uts/common/contract/process.c b/usr/src/uts/common/contract/process.c
index 9fd23fdb61..e92f87eecf 100644
--- a/usr/src/uts/common/contract/process.c
+++ b/usr/src/uts/common/contract/process.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/mutex.h>
@@ -955,6 +956,18 @@ contract_process_exit(cont_process_t *ctp, proc_t *p, int exitstatus)
(void) cte_publish_all(ct, event, nvl, NULL);
mutex_enter(&ct->ct_lock);
}
+
+ /*
+ * CT_PR_EV_EXIT is not part of the CT_PR_ALLFATAL definition since
+ * we never allow including this in the fatal set via a user-land
+ * application, but we do allow CT_PR_EV_EXIT in the contract's fatal
+ * set for a process setup for zone init. See zone_start_init().
+ */
+ if (EVFATALP(ctp, CT_PR_EV_EXIT)) {
+ ASSERT(MUTEX_HELD(&ct->ct_lock));
+ contract_process_kill(ct, p, B_TRUE);
+ }
+
if (empty) {
/*
* Send EMPTY message.
@@ -1057,6 +1070,17 @@ contract_process_fork(ctmpl_process_t *rtmpl, proc_t *cp, proc_t *pp,
event->cte_type = CT_PR_EV_FORK;
(void) cte_publish_all(ct, event, nvl, NULL);
}
+
+ /*
+ * Because the CT_PR_KEEP_EXEC flag is meant to be used by applications
+ * which are not contract aware, we can assume that these applications
+ * will never explicitly abandon the child's new contract. Thus, we
+ * abandon it now.
+ */
+ if (ctp->conp_params & CT_PR_KEEP_EXEC) {
+ contract_abandon(ct, pp, 1);
+ }
+
return (ctp);
}
diff --git a/usr/src/uts/common/crypto/api/kcf_random.c b/usr/src/uts/common/crypto/api/kcf_random.c
index bc72fa984a..75072fb686 100644
--- a/usr/src/uts/common/crypto/api/kcf_random.c
+++ b/usr/src/uts/common/crypto/api/kcf_random.c
@@ -70,6 +70,7 @@
#include <sys/cpuvar.h>
#include <sys/taskq.h>
#include <rng/fips_random.h>
+#include <sys/strlog.h>
#define RNDPOOLSIZE 1024 /* Pool size in bytes */
#define MINEXTRACTBYTES 20
@@ -933,7 +934,8 @@ rnd_handler(void *arg)
int len = 0;
if (!rng_prov_found && rng_ok_to_log) {
- cmn_err(CE_WARN, "No randomness provider enabled for "
+ (void) strlog(0, 0, 0, SL_NOTE,
+ "No randomness provider enabled for "
"/dev/random. Use cryptoadm(1M) to enable a provider.");
rng_ok_to_log = B_FALSE;
}
diff --git a/usr/src/uts/common/crypto/core/kcf_sched.c b/usr/src/uts/common/crypto/core/kcf_sched.c
index f461fe048c..8b2760b237 100644
--- a/usr/src/uts/common/crypto/core/kcf_sched.c
+++ b/usr/src/uts/common/crypto/core/kcf_sched.c
@@ -1027,9 +1027,9 @@ kcfpool_svc(void *arg)
case 0:
case -1:
/*
- * Woke up with no work to do. Check
- * if this thread should exit. We keep
- * at least kcf_minthreads.
+ * Woke up with no work to do. Check if we
+ * should lwp_exit() (which won't return). We
+ * keep at least kcf_minthreads.
*/
if (kcfpool->kp_threads > kcf_minthreads) {
KCF_ATOMIC_DECR(kcfpool->kp_threads);
diff --git a/usr/src/uts/common/ctf/ctf_mod.c b/usr/src/uts/common/ctf/ctf_mod.c
index b34cf400cd..421b922c96 100644
--- a/usr/src/uts/common/ctf/ctf_mod.c
+++ b/usr/src/uts/common/ctf/ctf_mod.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/sysmacros.h>
#include <sys/modctl.h>
#include <sys/debug.h>
@@ -117,6 +115,15 @@ ctf_version(int version)
/*ARGSUSED*/
ctf_file_t *
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
+{
+ if (errp != NULL)
+ *errp = ENOTSUP;
+ return (NULL);
+}
+
+/*ARGSUSED*/
+ctf_file_t *
ctf_modopen(struct module *mp, int *error)
{
ctf_sect_t ctfsect, symsect, strsect;
diff --git a/usr/src/uts/common/disp/cmt.c b/usr/src/uts/common/disp/cmt.c
index 1c5e1f79a9..3ecbf39393 100644
--- a/usr/src/uts/common/disp/cmt.c
+++ b/usr/src/uts/common/disp/cmt.c
@@ -201,13 +201,15 @@ pg_cmt_cpu_startup(cpu_t *cp)
/*
* Return non-zero if thread can migrate between "from" and "to"
- * without a performance penalty
+ * without a performance penalty. This is true only if we share a core on
+ * virtually any CPU; sharing the last-level cache is insufficient to make
+ * migration possible without penalty.
*/
int
pg_cmt_can_migrate(cpu_t *from, cpu_t *to)
{
- if (from->cpu_physid->cpu_cacheid ==
- to->cpu_physid->cpu_cacheid)
+ if (from->cpu_physid->cpu_coreid ==
+ to->cpu_physid->cpu_coreid)
return (1);
return (0);
}
diff --git a/usr/src/uts/common/disp/cpucaps.c b/usr/src/uts/common/disp/cpucaps.c
index 46f53faab6..2a4365ff73 100644
--- a/usr/src/uts/common/disp/cpucaps.c
+++ b/usr/src/uts/common/disp/cpucaps.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2013 Joyent, Inc. All rights reserved.
*/
#include <sys/disp.h>
@@ -74,6 +75,32 @@
* Putting threads on wait queues in random places while running in the
* kernel might lead to all kinds of locking problems.
*
+ * Bursting
+ * ========
+ *
+ * CPU bursting occurs when the CPU usage is over the baseline but under the
+ * cap. The baseline CPU (zone.cpu-baseline) is set in a multi-tenant
+ * environment so that we know how much CPU is allocated for a tenant under
+ * normal utilization. We can then track how much time a zone is spending
+ * over the "normal" CPU utilization expected for that zone using the
+ * "above_base_sec" kstat. This kstat is cumulative.
+ *
+ * If the zone has a burst limit (zone.cpu-burst-time) then the zone can
+ * burst for that period of time (in seconds) before the effective cap is
+ * lowered to the baseline. Once the effective cap is lowered, the zone
+ * will run at the baseline for the burst limit before the effective cap is
+ * raised again to the full value. This will allow the zone to burst again.
+ * We can watch this behavior using the kstats. The "effective" kstat shows
+ * which cap is being used, the baseline value or the burst value. The
+ * "burst_limit_sec" shows the value of the zone.cpu-burst-time rctl and the
+ * "bursting_sec" kstat shows how many seconds the zone has currently been
+ * bursting. When the CPU load is continuously greater than the baseline,
+ * bursting_sec will increase, up to the burst_limit_sec value, then the
+ * effective kstat will drop to the baseline and the bursting_sec value will
+ * decrease until it hits 0, at which time the effective kstat will return to
+ * the full burst value and the bursting_sec value will begin to increase
+ * again.
+ *
* Accounting
* ==========
*
@@ -203,18 +230,28 @@ static void caps_update();
*/
struct cap_kstat {
kstat_named_t cap_value;
+ kstat_named_t cap_baseline;
+ kstat_named_t cap_effective;
+ kstat_named_t cap_burst_limit;
+ kstat_named_t cap_bursting;
kstat_named_t cap_usage;
kstat_named_t cap_nwait;
kstat_named_t cap_below;
kstat_named_t cap_above;
+ kstat_named_t cap_above_base;
kstat_named_t cap_maxusage;
kstat_named_t cap_zonename;
} cap_kstat = {
{ "value", KSTAT_DATA_UINT64 },
+ { "baseline", KSTAT_DATA_UINT64 },
+ { "effective", KSTAT_DATA_UINT64 },
+ { "burst_limit_sec", KSTAT_DATA_UINT64 },
+ { "bursting_sec", KSTAT_DATA_UINT64 },
{ "usage", KSTAT_DATA_UINT64 },
{ "nwait", KSTAT_DATA_UINT64 },
{ "below_sec", KSTAT_DATA_UINT64 },
{ "above_sec", KSTAT_DATA_UINT64 },
+ { "above_base_sec", KSTAT_DATA_UINT64 },
{ "maxusage", KSTAT_DATA_UINT64 },
{ "zonename", KSTAT_DATA_STRING },
};
@@ -311,7 +348,7 @@ cap_enable(list_t *l, cpucap_t *cap, hrtime_t value)
cap->cap_below = cap->cap_above = 0;
cap->cap_maxusage = 0;
cap->cap_usage = 0;
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
waitq_unblock(&cap->cap_waitq);
if (CPUCAPS_OFF()) {
cpucaps_enabled = B_TRUE;
@@ -340,19 +377,21 @@ cap_disable(list_t *l, cpucap_t *cap)
ASSERT(CAP_ENABLED(cap));
waitq_block(&cap->cap_waitq);
+
+ /* do this first to avoid race with cap_kstat_update */
+ if (cap->cap_kstat != NULL) {
+ kstat_delete(cap->cap_kstat);
+ cap->cap_kstat = NULL;
+ }
+
list_remove(l, cap);
if (list_is_empty(&capped_projects) && list_is_empty(&capped_zones)) {
cpucaps_enabled = B_FALSE;
cpucaps_clock_callout = NULL;
}
- cap->cap_value = 0;
+ cap->cap_value = cap->cap_chk_value = 0;
cap->cap_project = NULL;
cap->cap_zone = NULL;
- if (cap->cap_kstat != NULL) {
- kstat_delete(cap->cap_kstat);
- cap->cap_kstat = NULL;
- }
-
}
/*
@@ -487,6 +526,8 @@ cap_walk(list_t *l, void (*cb)(cpucap_t *, int64_t))
* The waitq_isempty check is performed without the waitq lock. If a new thread
* is placed on the waitq right after the check, it will be picked up during the
* next invocation of cap_poke_waitq().
+ *
+ * Called once per tick for zones.
*/
/* ARGSUSED */
static void
@@ -494,15 +535,92 @@ cap_poke_waitq(cpucap_t *cap, int64_t gen)
{
ASSERT(MUTEX_HELD(&caps_lock));
- if (cap->cap_usage >= cap->cap_value) {
+ if (cap->cap_base != 0) {
+ /*
+ * Because of the way usage is calculated and decayed, its
+ * possible for the zone to be slightly over its cap, but we
+ * don't want to count that after we have reduced the effective
+ * cap to the baseline. That way the zone will be able to
+ * burst again after the burst_limit has expired.
+ */
+ if (cap->cap_usage > cap->cap_base &&
+ cap->cap_chk_value == cap->cap_value) {
+ cap->cap_above_base++;
+
+ /*
+ * If bursting is limited and we've been bursting
+ * longer than we're supposed to, then set the
+ * effective cap to the baseline.
+ */
+ if (cap->cap_burst_limit != 0) {
+ cap->cap_bursting++;
+ if (cap->cap_bursting >= cap->cap_burst_limit)
+ cap->cap_chk_value = cap->cap_base;
+ }
+ } else if (cap->cap_bursting > 0) {
+ /*
+ * We're not bursting now, but we were, decay the
+ * bursting timer.
+ */
+ cap->cap_bursting--;
+ /*
+ * Reset the effective cap once we decay to 0 so we
+ * can burst again.
+ */
+ if (cap->cap_bursting == 0 &&
+ cap->cap_chk_value != cap->cap_value)
+ cap->cap_chk_value = cap->cap_value;
+ }
+ }
+
+ if (cap->cap_usage >= cap->cap_chk_value) {
cap->cap_above++;
} else {
waitq_t *wq = &cap->cap_waitq;
cap->cap_below++;
- if (!waitq_isempty(wq))
- waitq_runone(wq);
+ if (!waitq_isempty(wq)) {
+ int i, ndequeue, p;
+
+ /*
+ * Since this function is only called once per tick,
+ * we can hit a situation where we have artificially
+ * limited the project/zone below its cap. This would
+ * happen if we have multiple threads queued up but
+ * only dequeued one thread/tick. To avoid this we
+ * dequeue multiple threads, calculated based on the
+ * usage percentage of the cap. It is possible that we
+ * could dequeue too many threads and some of them
+ * might be put back on the wait queue quickly, but
+ * since we know that threads are on the wait queue
+ * because we're capping, we know that there is unused
+ * CPU cycles anyway, so this extra work would not
+ * hurt. Also, the ndequeue number is only an upper
+ * bound and we might dequeue less, depending on how
+ * many threads are actually in the wait queue. The
+ * ndequeue values are empirically derived and could be
+ * adjusted or calculated in another way if necessary.
+ */
+ p = (int)((100 * cap->cap_usage) / cap->cap_chk_value);
+ if (p >= 98)
+ ndequeue = 10;
+ else if (p >= 95)
+ ndequeue = 20;
+ else if (p >= 90)
+ ndequeue = 40;
+ else if (p >= 85)
+ ndequeue = 80;
+ else
+ ndequeue = 160;
+
+ for (i = 0; i < ndequeue; i++) {
+ waitq_runone(wq);
+ if (waitq_isempty(wq))
+ break;
+ }
+ DTRACE_PROBE2(cpucaps__pokeq, int, p, int, i);
+ }
}
}
@@ -629,14 +747,14 @@ cap_project_zone_modify_walker(kproject_t *kpj, void *arg)
* Remove all projects in this zone without caps
* from the capped_projects list.
*/
- if (project_cap->cap_value == MAX_USAGE) {
+ if (project_cap->cap_chk_value == MAX_USAGE) {
cap_project_disable(kpj);
}
} else if (CAP_DISABLED(project_cap)) {
/*
* Add the project to capped_projects list.
*/
- ASSERT(project_cap->cap_value == 0);
+ ASSERT(project_cap->cap_chk_value == 0);
cap_project_enable(kpj, MAX_USAGE);
}
mutex_exit(&caps_lock);
@@ -746,7 +864,7 @@ cpucaps_zone_set(zone_t *zone, rctl_qty_t cap_val)
/*
* No state transitions, just change the value
*/
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
}
ASSERT(MUTEX_HELD(&caps_lock));
@@ -757,6 +875,108 @@ cpucaps_zone_set(zone_t *zone, rctl_qty_t cap_val)
}
/*
+ * Set zone's base cpu value to base_val
+ */
+int
+cpucaps_zone_set_base(zone_t *zone, rctl_qty_t base_val)
+{
+ cpucap_t *cap = NULL;
+ hrtime_t value;
+
+ ASSERT(base_val <= MAXCAP);
+ if (base_val > MAXCAP)
+ base_val = MAXCAP;
+
+ if (CPUCAPS_OFF() || !ZONE_IS_CAPPED(zone))
+ return (0);
+
+ if (zone->zone_cpucap == NULL)
+ cap = cap_alloc();
+
+ mutex_enter(&caps_lock);
+
+ if (cpucaps_busy) {
+ mutex_exit(&caps_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * Double-check whether zone->zone_cpucap is NULL, now with caps_lock
+ * held. If it is still NULL, assign a newly allocated cpucap to it.
+ */
+ if (zone->zone_cpucap == NULL) {
+ zone->zone_cpucap = cap;
+ } else if (cap != NULL) {
+ cap_free(cap);
+ }
+
+ cap = zone->zone_cpucap;
+
+ value = base_val * cap_tick_cost;
+ if (value < 0 || value > cap->cap_value)
+ value = 0;
+
+ cap->cap_base = value;
+
+ mutex_exit(&caps_lock);
+
+ return (0);
+}
+
+/*
+ * Set zone's maximum burst time in seconds. A burst time of 0 means that
+ * the zone can run over its baseline indefinitely.
+ */
+int
+cpucaps_zone_set_burst_time(zone_t *zone, rctl_qty_t base_val)
+{
+ cpucap_t *cap = NULL;
+ hrtime_t value;
+
+ ASSERT(base_val <= INT_MAX);
+ /* Treat the default as 0 - no limit */
+ if (base_val == INT_MAX)
+ base_val = 0;
+ if (base_val > INT_MAX)
+ base_val = INT_MAX;
+
+ if (CPUCAPS_OFF() || !ZONE_IS_CAPPED(zone))
+ return (0);
+
+ if (zone->zone_cpucap == NULL)
+ cap = cap_alloc();
+
+ mutex_enter(&caps_lock);
+
+ if (cpucaps_busy) {
+ mutex_exit(&caps_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * Double-check whether zone->zone_cpucap is NULL, now with caps_lock
+ * held. If it is still NULL, assign a newly allocated cpucap to it.
+ */
+ if (zone->zone_cpucap == NULL) {
+ zone->zone_cpucap = cap;
+ } else if (cap != NULL) {
+ cap_free(cap);
+ }
+
+ cap = zone->zone_cpucap;
+
+ value = SEC_TO_TICK(base_val);
+ if (value < 0)
+ value = 0;
+
+ cap->cap_burst_limit = value;
+
+ mutex_exit(&caps_lock);
+
+ return (0);
+}
+
+/*
* The project is going away so disable its cap.
*/
void
@@ -902,7 +1122,7 @@ cpucaps_project_set(kproject_t *kpj, rctl_qty_t cap_val)
if (CAP_DISABLED(cap))
cap_project_enable(kpj, value);
else
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
} else if (CAP_ENABLED(cap)) {
/*
* User requested to drop a cap on the project. If it is part of
@@ -910,7 +1130,7 @@ cpucaps_project_set(kproject_t *kpj, rctl_qty_t cap_val)
* otherwise disable the cap.
*/
if (ZONE_IS_CAPPED(kpj->kpj_zone)) {
- cap->cap_value = MAX_USAGE;
+ cap->cap_value = cap->cap_chk_value = MAX_USAGE;
} else {
cap_project_disable(kpj);
}
@@ -948,6 +1168,26 @@ cpucaps_zone_get(zone_t *zone)
}
/*
+ * Get current zone baseline.
+ */
+rctl_qty_t
+cpucaps_zone_get_base(zone_t *zone)
+{
+ return (zone->zone_cpucap != NULL ?
+ (rctl_qty_t)(zone->zone_cpucap->cap_base / cap_tick_cost) : 0);
+}
+
+/*
+ * Get current zone maximum burst time.
+ */
+rctl_qty_t
+cpucaps_zone_get_burst_time(zone_t *zone)
+{
+ return (zone->zone_cpucap != NULL ?
+ (rctl_qty_t)(TICK_TO_SEC(zone->zone_cpucap->cap_burst_limit)) : 0);
+}
+
+/*
* Charge project of thread t the time thread t spent on CPU since previously
* adjusted.
*
@@ -1045,7 +1285,7 @@ cpucaps_charge(kthread_id_t t, caps_sc_t *csc, cpucaps_charge_t charge_type)
project_cap = kpj->kpj_cpucap;
- if (project_cap->cap_usage >= project_cap->cap_value) {
+ if (project_cap->cap_usage >= project_cap->cap_chk_value) {
t->t_schedflag |= TS_PROJWAITQ;
rc = B_TRUE;
} else if (t->t_schedflag & TS_PROJWAITQ) {
@@ -1059,7 +1299,7 @@ cpucaps_charge(kthread_id_t t, caps_sc_t *csc, cpucaps_charge_t charge_type)
} else {
cpucap_t *zone_cap = zone->zone_cpucap;
- if (zone_cap->cap_usage >= zone_cap->cap_value) {
+ if (zone_cap->cap_usage >= zone_cap->cap_chk_value) {
t->t_schedflag |= TS_ZONEWAITQ;
rc = B_TRUE;
} else if (t->t_schedflag & TS_ZONEWAITQ) {
@@ -1119,6 +1359,7 @@ cpucaps_enforce(kthread_t *t)
/*
* Convert internal cap statistics into values exported by cap kstat.
+ * Note that the kstat is held throughout this function but caps_lock is not.
*/
static int
cap_kstat_update(kstat_t *ksp, int rw)
@@ -1133,6 +1374,12 @@ cap_kstat_update(kstat_t *ksp, int rw)
capsp->cap_value.value.ui64 =
ROUND_SCALE(cap->cap_value, cap_tick_cost);
+ capsp->cap_baseline.value.ui64 =
+ ROUND_SCALE(cap->cap_base, cap_tick_cost);
+ capsp->cap_effective.value.ui64 =
+ ROUND_SCALE(cap->cap_chk_value, cap_tick_cost);
+ capsp->cap_burst_limit.value.ui64 =
+ ROUND_SCALE(cap->cap_burst_limit, tick_sec);
capsp->cap_usage.value.ui64 =
ROUND_SCALE(cap->cap_usage, cap_tick_cost);
capsp->cap_maxusage.value.ui64 =
@@ -1140,6 +1387,10 @@ cap_kstat_update(kstat_t *ksp, int rw)
capsp->cap_nwait.value.ui64 = cap->cap_waitq.wq_count;
capsp->cap_below.value.ui64 = ROUND_SCALE(cap->cap_below, tick_sec);
capsp->cap_above.value.ui64 = ROUND_SCALE(cap->cap_above, tick_sec);
+ capsp->cap_above_base.value.ui64 =
+ ROUND_SCALE(cap->cap_above_base, tick_sec);
+ capsp->cap_bursting.value.ui64 =
+ ROUND_SCALE(cap->cap_bursting, tick_sec);
kstat_named_setstr(&capsp->cap_zonename, zonename);
return (0);
diff --git a/usr/src/uts/common/disp/disp.c b/usr/src/uts/common/disp/disp.c
index 0c2c0b4993..5f9c2c68a2 100644
--- a/usr/src/uts/common/disp/disp.c
+++ b/usr/src/uts/common/disp/disp.c
@@ -23,6 +23,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -105,7 +109,7 @@ static void cpu_resched(cpu_t *cp, pri_t tpri);
/*
* If this is set, only interrupt threads will cause kernel preemptions.
* This is done by changing the value of kpreemptpri. kpreemptpri
- * will either be the max sysclass pri + 1 or the min interrupt pri.
+ * will either be the max sysclass pri or the min interrupt pri.
*/
int only_intr_kpreempt;
@@ -252,7 +256,23 @@ dispinit(void)
maxglobpri = cl_maxglobpri;
}
}
- kpreemptpri = (pri_t)v.v_maxsyspri + 1;
+
+ /*
+ * Historically, kpreemptpri was set to v_maxsyspri + 1 -- which is
+ * to say, maxclsyspri + 1. However, over time, the system has used
+ * more and more asynchronous kernel threads, with an increasing number
+ * of these doing work on direct behalf of higher-level software (e.g.,
+ * network processing). This has led to potential priority inversions:
+ * threads doing low-priority lengthy kernel work can effectively
+ * delay kernel-level processing of higher-priority data. To minimize
+ * such inversions, we set kpreemptpri to be v_maxsyspri; anything in
+ * the kernel that runs at maxclsyspri will therefore induce kernel
+ * preemption, and this priority should be used if/when an asynchronous
+ * thread (or, as is often the case, task queue) is performing a task
+ * on behalf of higher-level software (or any task that is otherwise
+ * latency-sensitve).
+ */
+ kpreemptpri = (pri_t)v.v_maxsyspri;
if (kpqpri == KPQPRI)
kpqpri = kpreemptpri;
@@ -2258,7 +2278,7 @@ disp_getbest(disp_t *dp)
* placed earlier.
*/
if (tcp == NULL ||
- pri >= minclsyspri ||
+ (pri >= minclsyspri && tp->t_procp == &p0) ||
tp->t_cpu != tcp)
break;
diff --git a/usr/src/uts/common/disp/fx.c b/usr/src/uts/common/disp/fx.c
index ab5ba278a0..8260680a07 100644
--- a/usr/src/uts/common/disp/fx.c
+++ b/usr/src/uts/common/disp/fx.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -71,16 +71,6 @@ static struct modlinkage modlinkage = {
};
-/*
- * control flags (kparms->fx_cflags).
- */
-#define FX_DOUPRILIM 0x01 /* change user priority limit */
-#define FX_DOUPRI 0x02 /* change user priority */
-#define FX_DOTQ 0x04 /* change FX time quantum */
-
-
-#define FXMAXUPRI 60 /* maximum user priority setting */
-
#define FX_MAX_UNPRIV_PRI 0 /* maximum unpriviledge priority */
/*
diff --git a/usr/src/uts/common/disp/rt.c b/usr/src/uts/common/disp/rt.c
index f87f8c56ce..115e42ccb8 100644
--- a/usr/src/uts/common/disp/rt.c
+++ b/usr/src/uts/common/disp/rt.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -103,13 +103,6 @@ _info(struct modinfo *modinfop)
pri_t rt_maxpri = RTMAXPRI; /* maximum real-time priority */
rtdpent_t *rt_dptbl; /* real-time dispatcher parameter table */
-/*
- * control flags (kparms->rt_cflags).
- */
-#define RT_DOPRI 0x01 /* change priority */
-#define RT_DOTQ 0x02 /* change RT time quantum */
-#define RT_DOSIG 0x04 /* change RT time quantum signal */
-
static int rt_admin(caddr_t, cred_t *);
static int rt_enterclass(kthread_t *, id_t, void *, cred_t *, void *);
static int rt_fork(kthread_t *, kthread_t *, void *);
diff --git a/usr/src/uts/common/disp/rt_dptbl.c b/usr/src/uts/common/disp/rt_dptbl.c
index 768b499ef2..cc88ed72fc 100644
--- a/usr/src/uts/common/disp/rt_dptbl.c
+++ b/usr/src/uts/common/disp/rt_dptbl.c
@@ -28,8 +28,6 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/proc.h>
#include <sys/priocntl.h>
#include <sys/class.h>
@@ -70,8 +68,6 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
-#define RTGPPRIO0 100 /* Global priority for RT priority 0 */
-
rtdpent_t config_rt_dptbl[] = {
/* prilevel Time quantum */
diff --git a/usr/src/uts/common/disp/thread.c b/usr/src/uts/common/disp/thread.c
index f2685af534..ae6c5eef16 100644
--- a/usr/src/uts/common/disp/thread.c
+++ b/usr/src/uts/common/disp/thread.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -75,6 +75,10 @@
#include <sys/cpucaps.h>
#include <sys/kiconv.h>
+#ifndef STACK_GROWTH_DOWN
+#error Stacks do not grow downward; 3b2 zombie attack detected!
+#endif
+
struct kmem_cache *thread_cache; /* cache of free threads */
struct kmem_cache *lwp_cache; /* cache of free lwps */
struct kmem_cache *turnstile_cache; /* cache of free turnstiles */
@@ -372,7 +376,7 @@ thread_create(
if (stksize <= sizeof (kthread_t) + PTR24_ALIGN)
cmn_err(CE_PANIC, "thread_create: proposed stack size"
" too small to hold thread.");
-#ifdef STACK_GROWTH_DOWN
+
stksize -= SA(sizeof (kthread_t) + PTR24_ALIGN - 1);
stksize &= -PTR24_ALIGN; /* make thread aligned */
t = (kthread_t *)(stk + stksize);
@@ -381,13 +385,6 @@ thread_create(
audit_thread_create(t);
t->t_stk = stk + stksize;
t->t_stkbase = stk;
-#else /* stack grows to larger addresses */
- stksize -= SA(sizeof (kthread_t));
- t = (kthread_t *)(stk);
- bzero(t, sizeof (kthread_t));
- t->t_stk = stk + sizeof (kthread_t);
- t->t_stkbase = stk + stksize + sizeof (kthread_t);
-#endif /* STACK_GROWTH_DOWN */
t->t_flag |= T_TALLOCSTK;
t->t_swap = stk;
} else {
@@ -400,13 +397,8 @@ thread_create(
* Initialize t_stk to the kernel stack pointer to use
* upon entry to the kernel
*/
-#ifdef STACK_GROWTH_DOWN
t->t_stk = stk + stksize;
t->t_stkbase = stk;
-#else
- t->t_stk = stk; /* 3b2-like */
- t->t_stkbase = stk + stksize;
-#endif /* STACK_GROWTH_DOWN */
}
if (kmem_stackinfo != 0) {
@@ -589,6 +581,9 @@ thread_exit(void)
if ((t->t_proc_flag & TP_ZTHREAD) != 0)
cmn_err(CE_PANIC, "thread_exit: zthread_exit() not called");
+ if ((t->t_flag & T_SPLITSTK) != 0)
+ cmn_err(CE_PANIC, "thread_exit: called when stack is split");
+
tsd_exit(); /* Clean up this thread's TSD */
kcpc_passivate(); /* clean up performance counter state */
@@ -1050,6 +1045,8 @@ installctx(
ctx->free_op = free;
ctx->arg = arg;
ctx->next = t->t_ctx;
+ ctx->save_ts = 0;
+ ctx->restore_ts = 0;
t->t_ctx = ctx;
}
@@ -1124,9 +1121,12 @@ savectx(kthread_t *t)
struct ctxop *ctx;
ASSERT(t == curthread);
- for (ctx = t->t_ctx; ctx != 0; ctx = ctx->next)
- if (ctx->save_op != NULL)
+ for (ctx = t->t_ctx; ctx != 0; ctx = ctx->next) {
+ if (ctx->save_op != NULL) {
+ ctx->save_ts = gethrtime_unscaled();
(ctx->save_op)(ctx->arg);
+ }
+ }
}
void
@@ -1135,9 +1135,12 @@ restorectx(kthread_t *t)
struct ctxop *ctx;
ASSERT(t == curthread);
- for (ctx = t->t_ctx; ctx != 0; ctx = ctx->next)
- if (ctx->restore_op != NULL)
+ for (ctx = t->t_ctx; ctx != 0; ctx = ctx->next) {
+ if (ctx->restore_op != NULL) {
+ ctx->restore_ts = gethrtime_unscaled();
(ctx->restore_op)(ctx->arg);
+ }
+ }
}
void
@@ -1883,6 +1886,103 @@ thread_change_pri(kthread_t *t, pri_t disp_pri, int front)
return (on_rq);
}
+
+/*
+ * There are occasions in the kernel when we need much more stack than we
+ * allocate by default, but we do not wish to have that work done
+ * asynchronously by another thread. To accommodate these scenarios, we allow
+ * for a split stack (also known as a "segmented stack") whereby a new stack
+ * is dynamically allocated and the current thread jumps onto it for purposes
+ * of executing the specified function. After the specified function returns,
+ * the stack is deallocated and control is returned to the caller. This
+ * functionality is implemented by thread_splitstack(), below; there are a few
+ * constraints on its use:
+ *
+ * - The caller must be in a context where it is safe to block for memory.
+ * - The caller cannot be in a t_onfault context
+ * - The called function must not call thread_exit() while on the split stack
+ *
+ * The code will explicitly panic if these constraints are violated. Notably,
+ * however, thread_splitstack() _can_ be called on a split stack -- there
+ * is no limit to the level that split stacks can nest.
+ *
+ * When the stack is split, it is constructed such that stack backtraces
+ * from kernel debuggers continue to function -- though note that DTrace's
+ * stack() action and stackdepth function will only show the stack up to and
+ * including thread_splitstack_run(); DTrace explicitly bounds itself to
+ * pointers that exist within the current declared stack as a safety
+ * mechanism.
+ */
+void
+thread_splitstack(void (*func)(void *), void *arg, size_t stksize)
+{
+ kthread_t *t = curthread;
+ caddr_t ostk, ostkbase, stk;
+ ushort_t otflag;
+
+ if (t->t_onfault != NULL)
+ panic("thread_splitstack: called with non-NULL t_onfault");
+
+ ostk = t->t_stk;
+ ostkbase = t->t_stkbase;
+ otflag = t->t_flag;
+
+ stksize = roundup(stksize, PAGESIZE);
+
+ if (stksize < default_stksize)
+ stksize = default_stksize;
+
+ if (stksize == default_stksize) {
+ stk = (caddr_t)segkp_cache_get(segkp_thread);
+ } else {
+ stksize = roundup(stksize, PAGESIZE);
+ stk = (caddr_t)segkp_get(segkp, stksize,
+ (KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED));
+ }
+
+ /*
+ * We're going to lock ourselves before we set T_SPLITSTK to assure
+ * that we're not swapped out in the meantime. (Note that we don't
+ * bother to set t_swap, as we're not going to be swapped out.)
+ */
+ thread_lock(t);
+
+ if (!(otflag & T_SPLITSTK))
+ t->t_flag |= T_SPLITSTK;
+
+ t->t_stk = stk + stksize;
+ t->t_stkbase = stk;
+
+ thread_unlock(t);
+
+ /*
+ * Now actually run on the new (split) stack...
+ */
+ thread_splitstack_run(t->t_stk, func, arg);
+
+ /*
+ * We're back onto our own stack; lock ourselves and restore our
+ * pre-split state.
+ */
+ thread_lock(t);
+
+ t->t_stk = ostk;
+ t->t_stkbase = ostkbase;
+
+ if (!(otflag & T_SPLITSTK))
+ t->t_flag &= ~T_SPLITSTK;
+
+ thread_unlock(t);
+
+ /*
+ * Now that we are entirely back on our own stack, call back into
+ * the platform layer to perform any platform-specific cleanup.
+ */
+ thread_splitstack_cleanup();
+
+ segkp_release(segkp, stk);
+}
+
/*
* Tunable kmem_stackinfo is set, fill the kernel thread stack with a
* specific pattern.
diff --git a/usr/src/uts/common/disp/thread_intr.c b/usr/src/uts/common/disp/thread_intr.c
index 67ccc6922f..c840bdf31a 100644
--- a/usr/src/uts/common/disp/thread_intr.c
+++ b/usr/src/uts/common/disp/thread_intr.c
@@ -23,19 +23,10 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
/*
- * FILE NOTICE BEGIN
- *
- * This file should not be modified. If you wish to modify it or have it
- * modified, please contact Sun Microsystems at <LFI149367@-sun-.-com->
- * (without anti-spam dashes)
- *
- * FILE NOTICE END
+ * Copyright 2015, Joyent, Inc.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/cpuvar.h>
#include <sys/stack.h>
#include <vm/seg_kp.h>
@@ -44,6 +35,17 @@
#include <sys/sysmacros.h>
/*
+ * Use a slightly larger thread stack size for interrupt threads rather than the
+ * default. This is useful for cases where the networking stack may do an rx and
+ * a tx in the context of a single interrupt and when combined with various
+ * promisc hooks that need memory, can cause us to get dangerously close to the
+ * edge of the traditional stack sizes. This is only a few pages more than a
+ * traditional stack and given that we don't have that many interrupt threads,
+ * the memory costs end up being more than worthwhile.
+ */
+#define LL_INTR_STKSZ (32 * 1024)
+
+/*
* Create and initialize an interrupt thread.
*/
static void
@@ -51,7 +53,7 @@ thread_create_intr(cpu_t *cp)
{
kthread_t *tp;
- tp = thread_create(NULL, 0,
+ tp = thread_create(NULL, LL_INTR_STKSZ,
(void (*)())thread_create_intr, NULL, 0, &p0, TS_ONPROC, 0);
/*
@@ -97,9 +99,12 @@ thread_create_intr(cpu_t *cp)
}
/*
- * Allocate a given number of interrupt threads for a given CPU.
- * These threads will get freed by cpu_destroy_bound_threads()
- * when CPU gets unconfigured.
+ * Allocate a given number of interrupt threads for a given CPU. These threads
+ * will get freed by cpu_destroy_bound_threads() when CPU gets unconfigured.
+ *
+ * Note, high level interrupts are always serviced using cpu_intr_stack and are
+ * not allowed to block. Low level interrupts or soft-interrupts use the
+ * kthread_t's that we create through the calls to thread_create_intr().
*/
void
cpu_intr_alloc(cpu_t *cp, int n)
@@ -110,6 +115,6 @@ cpu_intr_alloc(cpu_t *cp, int n)
thread_create_intr(cp);
cp->cpu_intr_stack = (caddr_t)segkp_get(segkp, INTR_STACK_SIZE,
- KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED) +
- INTR_STACK_SIZE - SA(MINFRAME);
+ KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED) +
+ INTR_STACK_SIZE - SA(MINFRAME);
}
diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c
index 3aeeef27f7..ba87747b79 100644
--- a/usr/src/uts/common/dtrace/dtrace.c
+++ b/usr/src/uts/common/dtrace/dtrace.c
@@ -7718,7 +7718,7 @@ dtrace_cred2priv(cred_t *cr, uint32_t *privp, uid_t *uidp, zoneid_t *zoneidp)
priv = DTRACE_PRIV_ALL;
} else {
*uidp = crgetuid(cr);
- *zoneidp = crgetzoneid(cr);
+ *zoneidp = crgetzonedid(cr);
priv = 0;
if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE))
@@ -8214,7 +8214,7 @@ dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv,
provider->dtpv_priv.dtpp_flags = priv;
if (cr != NULL) {
provider->dtpv_priv.dtpp_uid = crgetuid(cr);
- provider->dtpv_priv.dtpp_zoneid = crgetzoneid(cr);
+ provider->dtpv_priv.dtpp_zoneid = crgetzonedid(cr);
}
provider->dtpv_pops = *pops;
@@ -8825,6 +8825,7 @@ dtrace_probe_enable(const dtrace_probedesc_t *desc, dtrace_enabling_t *enab)
uint32_t priv;
uid_t uid;
zoneid_t zoneid;
+ dtrace_state_t *state = enab->dten_vstate->dtvs_state;
ASSERT(MUTEX_HELD(&dtrace_lock));
dtrace_ecb_create_cache = NULL;
@@ -8839,8 +8840,22 @@ dtrace_probe_enable(const dtrace_probedesc_t *desc, dtrace_enabling_t *enab)
}
dtrace_probekey(desc, &pkey);
- dtrace_cred2priv(enab->dten_vstate->dtvs_state->dts_cred.dcr_cred,
- &priv, &uid, &zoneid);
+ dtrace_cred2priv(state->dts_cred.dcr_cred, &priv, &uid, &zoneid);
+
+ if ((priv & DTRACE_PRIV_ZONEOWNER) &&
+ state->dts_options[DTRACEOPT_ZONE] != DTRACEOPT_UNSET) {
+ /*
+ * If we have the privilege of instrumenting all zones but we
+ * have been told to instrument but one, we will spoof this up
+ * depriving ourselves of DTRACE_PRIV_ZONEOWNER for purposes
+ * of dtrace_match(). (Note that DTRACEOPT_ZONE is not for
+ * security but rather for performance: it allows the global
+ * zone to instrument USDT probes in a local zone without
+ * requiring all zones to be instrumented.)
+ */
+ priv &= ~DTRACE_PRIV_ZONEOWNER;
+ zoneid = state->dts_options[DTRACEOPT_ZONE];
+ }
return (dtrace_match(&pkey, priv, uid, zoneid, dtrace_ecb_create_enable,
enab));
diff --git a/usr/src/uts/common/dtrace/sdt_subr.c b/usr/src/uts/common/dtrace/sdt_subr.c
index 157acc25fc..3d350ff278 100644
--- a/usr/src/uts/common/dtrace/sdt_subr.c
+++ b/usr/src/uts/common/dtrace/sdt_subr.c
@@ -97,6 +97,10 @@ static dtrace_pattr_t iscsi_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
};
+/*
+ * When adding a new provider you must add it before sdt as sdt is a catch all
+ * for remaining probes.
+ */
sdt_provider_t sdt_providers[] = {
{ "vtrace", "__vtrace_", &vtrace_attr },
{ "sysinfo", "__cpu_sysinfo_", &info_attr, DTRACE_PRIV_USER },
@@ -117,6 +121,7 @@ sdt_provider_t sdt_providers[] = {
{ "fc", "__fc_", &fc_attr },
{ "srp", "__srp_", &fc_attr },
{ "sysevent", "__sysevent_", &stab_attr },
+ { "vnd", "__vnd_", &stab_attr },
{ "sdt", NULL, &sdt_attr },
{ NULL }
};
@@ -1151,6 +1156,34 @@ sdt_argdesc_t sdt_args[] = {
{ "fc", "abts-receive", 2, 2, "fct_i_remote_port_t *",
"fc_port_info_t *" },
+ { "vnd", "flow-blocked", 0, 0, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "flow-blocked", 1, 1, "uint64_t", "uint64_t" },
+ { "vnd", "flow-blocked", 2, 2, "uintptr_t", "uintptr_t" },
+ { "vnd", "flow-resumed", 0, 0, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "flow-resumed", 1, 1, "uint64_t", "uint64_t" },
+ { "vnd", "flow-resumed", 2, 2, "uintptr_t", "uintptr_t" },
+ { "vnd", "drop-in", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-in", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-in", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-in", 3, 3, "const char *", "const char *" },
+ { "vnd", "drop-out", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-out", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-out", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-out", 3, 3, "const char *", "const char *" },
+ { "vnd", "drop-ctl", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-ctl", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-ctl", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-ctl", 3, 3, "const char *", "const char *" },
+ { "vnd", "send", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "send", 1, 1, "void *", "csinfo_t *" },
+ { "vnd", "send", 2, 2, "void *", "ipinfo_t *" },
+ { "vnd", "send", 3, 3, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "send", 4, 4, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "recv", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "recv", 1, 1, "void *", "csinfo_t *" },
+ { "vnd", "recv", 2, 2, "void *", "ipinfo_t *" },
+ { "vnd", "recv", 3, 3, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "recv", 4, 4, "mblk_t *", "etherinfo_t *" },
{ NULL }
};
diff --git a/usr/src/uts/common/exec/aout/aout.c b/usr/src/uts/common/exec/aout/aout.c
index fc45bd9544..5dbb2ed28c 100644
--- a/usr/src/uts/common/exec/aout/aout.c
+++ b/usr/src/uts/common/exec/aout/aout.c
@@ -22,6 +22,7 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2011 Bayard G. Bell. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -54,7 +55,7 @@
static int aoutexec(vnode_t *vp, execa_t *uap, uarg_t *args,
intpdata_t *idatap, int level, long *execsz, int setid,
- caddr_t exec_file, cred_t *cred, int brand_action);
+ caddr_t exec_file, cred_t *cred, int *brand_action);
static int get_aout_head(struct vnode **vpp, struct exdata *edp, long *execsz,
int *isdyn);
static int aoutcore(vnode_t *vp, proc_t *pp, cred_t *credp,
@@ -130,7 +131,7 @@ _info(struct modinfo *modinfop)
static int
aoutexec(vnode_t *vp, struct execa *uap, struct uarg *args,
struct intpdata *idatap, int level, long *execsz, int setid,
- caddr_t exec_file, cred_t *cred, int brand_action)
+ caddr_t exec_file, cred_t *cred, int *brand_action)
{
auxv32_t auxflags_auxv32;
int error;
diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c
index dc04b292b0..83a6372a66 100644
--- a/usr/src/uts/common/exec/elf/elf.c
+++ b/usr/src/uts/common/exec/elf/elf.c
@@ -26,7 +26,7 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -66,6 +66,11 @@
#include <sys/sdt.h>
#include <sys/siginfo.h>
+#if defined(__x86)
+#include <sys/comm_page_util.h>
+#endif /* defined(__x86) */
+
+
extern int at_flags;
#define ORIGIN_STR "ORIGIN"
@@ -163,12 +168,16 @@ dtrace_safe_phdr(Phdr *phdrp, struct uarg *args, uintptr_t base)
}
/*
- * Map in the executable pointed to by vp. Returns 0 on success.
+ * Map in the executable pointed to by vp. Returns 0 on success. Note that
+ * this function currently has the maximum number of arguments allowed by
+ * modstubs on x86 (MAXNARG)! Do _not_ add to this function signature without
+ * adding to MAXNARG. (Better yet, do not add to this monster of a function
+ * signature!)
*/
int
mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
- intptr_t *voffset, caddr_t exec_file, int *interp, caddr_t *bssbase,
- caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap)
+ intptr_t *voffset, caddr_t exec_file, char **interpp, caddr_t *bssbase,
+ caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap, uintptr_t *minaddrp)
{
size_t len;
struct vattr vat;
@@ -180,6 +189,7 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
Phdr *junk = NULL;
Phdr *dynphdr = NULL;
Phdr *dtrphdr = NULL;
+ char *interp = NULL;
uintptr_t lddata;
long execsz;
intptr_t minaddr;
@@ -187,6 +197,9 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
if (lddatap != NULL)
*lddatap = NULL;
+ if (minaddrp != NULL)
+ *minaddrp = NULL;
+
if (error = execpermissions(vp, &vat, args)) {
uprintf("%s: Cannot execute %s\n", exec_file, args->pathname);
return (error);
@@ -212,25 +225,89 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
&junk, &dtrphdr, NULL, bssbase, brkbase, voffset, &minaddr,
len, &execsz, brksize)) {
uprintf("%s: Cannot map %s\n", exec_file, args->pathname);
+ if (uphdr != NULL && uphdr->p_flags == 0)
+ kmem_free(uphdr, sizeof (Phdr));
kmem_free(phdrbase, phdrsize);
return (error);
}
+ if (minaddrp != NULL)
+ *minaddrp = minaddr;
+
/*
- * Inform our caller if the executable needs an interpreter.
+ * If the executable requires an interpreter, determine its name.
*/
- *interp = (dynphdr == NULL) ? 0 : 1;
+ if (dynphdr != NULL) {
+ ssize_t resid;
+
+ if (dynphdr->p_filesz > MAXPATHLEN || dynphdr->p_filesz == 0) {
+ uprintf("%s: Invalid interpreter\n", exec_file);
+ kmem_free(phdrbase, phdrsize);
+ return (ENOEXEC);
+ }
+
+ interp = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ if ((error = vn_rdwr(UIO_READ, vp, interp, dynphdr->p_filesz,
+ (offset_t)dynphdr->p_offset, UIO_SYSSPACE, 0,
+ (rlim64_t)0, CRED(), &resid)) != 0 || resid != 0 ||
+ interp[dynphdr->p_filesz - 1] != '\0') {
+ uprintf("%s: Cannot obtain interpreter pathname\n",
+ exec_file);
+ kmem_free(interp, MAXPATHLEN);
+ kmem_free(phdrbase, phdrsize);
+ return (error != 0 ? error : ENOEXEC);
+ }
+ }
/*
* If this is a statically linked executable, voffset should indicate
* the address of the executable itself (it normally holds the address
* of the interpreter).
*/
- if (ehdr->e_type == ET_EXEC && *interp == 0)
+ if (ehdr->e_type == ET_EXEC && interp == NULL)
*voffset = minaddr;
+ /*
+ * If the caller has asked for the interpreter name, return it (it's
+ * up to the caller to free it); if the caller hasn't asked for it,
+ * free it ourselves.
+ */
+ if (interpp != NULL) {
+ *interpp = interp;
+ } else if (interp != NULL) {
+ kmem_free(interp, MAXPATHLEN);
+ }
+
if (uphdr != NULL) {
*uphdr_vaddr = uphdr->p_vaddr;
+
+ if (uphdr->p_flags == 0)
+ kmem_free(uphdr, sizeof (Phdr));
+ } else if (ehdr->e_type == ET_DYN) {
+ /*
+ * If we don't have a uphdr, we'll apply the logic found
+ * in mapelfexec() and use the p_vaddr of the first PT_LOAD
+ * section as the base address of the object.
+ */
+ Phdr *phdr = (Phdr *)phdrbase;
+ int i, hsize = ehdr->e_phentsize;
+
+ for (i = nphdrs; i > 0; i--) {
+ if (phdr->p_type == PT_LOAD) {
+ *uphdr_vaddr = (uintptr_t)phdr->p_vaddr +
+ ehdr->e_phoff;
+ break;
+ }
+
+ phdr = (Phdr *)((caddr_t)phdr + hsize);
+ }
+
+ /*
+ * If we don't have a PT_LOAD segment, we should have returned
+ * ENOEXEC when elfsize() returned 0, above.
+ */
+ VERIFY(i > 0);
} else {
*uphdr_vaddr = (Addr)-1;
}
@@ -243,13 +320,13 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
int
elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int *brand_action)
{
caddr_t phdrbase = NULL;
caddr_t bssbase = 0;
caddr_t brkbase = 0;
size_t brksize = 0;
- ssize_t dlnsize;
+ ssize_t dlnsize, nsize = 0;
aux_entry_t *aux;
int error;
ssize_t resid;
@@ -273,6 +350,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
int hasauxv = 0;
int hasdy = 0;
int branded = 0;
+ int dynuphdr = 0;
struct proc *p = ttoproc(curthread);
struct user *up = PTOU(p);
@@ -327,7 +405,9 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*execsz = btopr(SINCR) + btopr(SSIZE) + btopr(NCARGS32-1);
} else {
args->to_model = DATAMODEL_LP64;
- args->stk_prot &= ~PROT_EXEC;
+ if (!args->stk_prot_override) {
+ args->stk_prot &= ~PROT_EXEC;
+ }
#if defined(__i386) || defined(__amd64)
args->dat_prot &= ~PROT_EXEC;
#endif
@@ -339,11 +419,25 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
#endif /* _LP64 */
/*
- * We delay invoking the brand callback until we've figured out
- * what kind of elf binary we're trying to run, 32-bit or 64-bit.
- * We do this because now the brand library can just check
- * args->to_model to see if the target is 32-bit or 64-bit without
- * having do duplicate all the code above.
+ * We delay invoking the brand callback until we've figured out what
+ * kind of elf binary we're trying to run, 32-bit or 64-bit. We do this
+ * because now the brand library can just check args->to_model to see if
+ * the target is 32-bit or 64-bit without having do duplicate all the
+ * code above.
+ *
+ * We also give the brand a chance to indicate that based on the ELF
+ * OSABI of the target binary it should become unbranded and optionally
+ * indicate that it should be treated as existing in a specific prefix.
+ *
+ * Note that if a brand opts to go down this route it does not actually
+ * end up being debranded. In other words, future programs that exec
+ * will still be considered for branding unless this escape hatch is
+ * used. Consider the case of lx brand for example. If a user runs
+ * /native/usr/sbin/dtrace -c /bin/ls, the isaexec and normal executable
+ * of DTrace that's in /native will take this escape hatch and be run
+ * and interpreted using the normal system call table; however, the
+ * execution of a non-illumos binary in the form of /bin/ls will still
+ * be branded and be subject to all of the normal actions of the brand.
*
* The level checks associated with brand handling below are used to
* prevent a loop since the brand elfexec function typically comes back
@@ -351,8 +445,20 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* handling in the #! interpreter code will increment the level before
* calling gexec to run the final elfexec interpreter.
*/
+ if ((level <= INTP_MAXDEPTH) && (*brand_action != EBA_NATIVE) &&
+ (PROC_IS_BRANDED(p)) && (BROP(p)->b_native_exec != NULL)) {
+ if (BROP(p)->b_native_exec(ehdrp->e_ident[EI_OSABI],
+ &args->brand_nroot) == B_TRUE) {
+ ASSERT(ehdrp->e_ident[EI_OSABI]);
+ *brand_action = EBA_NATIVE;
+ /* Add one for the trailing '/' in the path */
+ if (args->brand_nroot != NULL)
+ nsize = strlen(args->brand_nroot) + 1;
+ }
+ }
+
if ((level <= INTP_MAXDEPTH) &&
- (brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+ (*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
error = BROP(p)->b_elfexec(vp, uap, args,
idatap, level + 1, execsz, setid, exec_file, cred,
brand_action);
@@ -423,14 +529,15 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* AT_BASE
* AT_FLAGS
* AT_PAGESZ
+ * AT_RANDOM (added in stk_copyout)
* AT_SUN_AUXFLAGS
* AT_SUN_HWCAP
* AT_SUN_HWCAP2
- * AT_SUN_PLATFORM (added in stk_copyout)
- * AT_SUN_EXECNAME (added in stk_copyout)
+ * AT_SUN_PLATFORM (added in stk_copyout)
+ * AT_SUN_EXECNAME (added in stk_copyout)
* AT_NULL
*
- * total == 9
+ * total == 10
*/
if (hasdy && hasu) {
/*
@@ -445,7 +552,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*
* total = 5
*/
- args->auxsize = (9 + 5) * sizeof (aux_entry_t);
+ args->auxsize = (10 + 5) * sizeof (aux_entry_t);
} else if (hasdy) {
/*
* Has PT_INTERP but no PT_PHDR
@@ -455,9 +562,9 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*
* total = 2
*/
- args->auxsize = (9 + 2) * sizeof (aux_entry_t);
+ args->auxsize = (10 + 2) * sizeof (aux_entry_t);
} else {
- args->auxsize = 9 * sizeof (aux_entry_t);
+ args->auxsize = 10 * sizeof (aux_entry_t);
}
} else {
args->auxsize = 0;
@@ -470,13 +577,42 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
if (args->emulator != NULL)
args->auxsize += sizeof (aux_entry_t);
- if ((brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+ /*
+ * If this is a native binary that's been given a modified interpreter
+ * root, inform it that the native system exists at that root.
+ */
+ if (args->brand_nroot != NULL) {
+ args->auxsize += sizeof (aux_entry_t);
+ }
+
+
+ /*
+ * On supported kernels (x86_64) make room in the auxv for the
+ * AT_SUN_COMMPAGE entry. This will go unpopulated on i86xpv systems
+ * which do not provide such functionality.
+ */
+#if defined(__amd64)
+ args->auxsize += sizeof (aux_entry_t);
+#endif /* defined(__amd64) */
+
+ /*
+ * If we have user credentials, we'll supply the following entries:
+ * AT_SUN_UID
+ * AT_SUN_RUID
+ * AT_SUN_GID
+ * AT_SUN_RGID
+ */
+ if (cred != NULL) {
+ args->auxsize += 4 * sizeof (aux_entry_t);
+ }
+
+ if ((*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
branded = 1;
/*
- * We will be adding 4 entries to the aux vectors. One for
- * the the brandname and 3 for the brand specific aux vectors.
+ * We will be adding 5 entries to the aux vectors. One for
+ * the the brandname and 4 for the brand specific aux vectors.
*/
- args->auxsize += 4 * sizeof (aux_entry_t);
+ args->auxsize += 5 * sizeof (aux_entry_t);
}
/* Hardware/Software capabilities */
@@ -507,7 +643,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
aux = bigwad->elfargs;
/*
* Move args to the user's stack.
- * This can fill in the AT_SUN_PLATFORM and AT_SUN_EXECNAME aux entries.
+ * This can fill in the AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM
+ * aux entries.
*/
if ((error = exec_args(uap, args, idatap, (void **)&aux)) != 0) {
if (error == -1) {
@@ -534,6 +671,14 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
len, execsz, &brksize)) != 0)
goto bad;
+ if (uphdr != NULL) {
+ /*
+ * Our uphdr has been dynamically allocated if (and only if)
+ * its program header flags are clear.
+ */
+ dynuphdr = (uphdr->p_flags == 0);
+ }
+
if (uphdr != NULL && dyphdr == NULL)
goto bad;
@@ -548,17 +693,22 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
char *p;
struct vnode *nvp;
- dlnsize = dyphdr->p_filesz;
+ dlnsize = dyphdr->p_filesz + nsize;
if (dlnsize > MAXPATHLEN || dlnsize <= 0)
goto bad;
+ if (nsize != 0) {
+ bcopy(args->brand_nroot, dlnp, nsize - 1);
+ dlnp[nsize - 1] = '/';
+ }
+
/*
* Read in "interpreter" pathname.
*/
- if ((error = vn_rdwr(UIO_READ, vp, dlnp, dyphdr->p_filesz,
- (offset_t)dyphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0,
- CRED(), &resid)) != 0) {
+ if ((error = vn_rdwr(UIO_READ, vp, dlnp + nsize,
+ dyphdr->p_filesz, (offset_t)dyphdr->p_offset, UIO_SYSSPACE,
+ 0, (rlim64_t)0, CRED(), &resid)) != 0) {
uprintf("%s: Cannot obtain interpreter pathname\n",
exec_file);
goto bad;
@@ -703,9 +853,10 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
dtrphdr = NULL;
- error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, &junk, &junk,
+ error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, NULL, &junk,
&junk, &dtrphdr, NULL, NULL, NULL, &voffset, NULL, len,
execsz, NULL);
+
if (error || junk != NULL) {
VN_RELE(nvp);
uprintf("%s: Cannot map %s\n", exec_file, dlnp);
@@ -732,9 +883,10 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
if (hasauxv) {
int auxf = AF_SUN_HWCAPVERIFY;
+
/*
- * Note: AT_SUN_PLATFORM and AT_SUN_EXECNAME were filled in via
- * exec_args()
+ * Note: AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM were
+ * filled in via exec_args()
*/
ADDAUX(aux, AT_BASE, voffset)
ADDAUX(aux, AT_FLAGS, at_flags)
@@ -762,7 +914,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* malicious user within the zone from crafting a wrapper to
* run native suid commands with unsecure libraries interposed.
*/
- if ((brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
+ if ((*brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
(setid &= ~EXECSETID_SETID) != 0))
auxf &= ~AF_SUN_SETUGID;
@@ -775,6 +927,18 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
((char *)&aux->a_type -
(char *)bigwad->elfargs));
ADDAUX(aux, AT_SUN_AUXFLAGS, auxf);
+
+ /*
+ * Record information about the real and effective user and
+ * group IDs.
+ */
+ if (cred != NULL) {
+ ADDAUX(aux, AT_SUN_UID, crgetuid(cred));
+ ADDAUX(aux, AT_SUN_RUID, crgetruid(cred));
+ ADDAUX(aux, AT_SUN_GID, crgetgid(cred));
+ ADDAUX(aux, AT_SUN_RGID, crgetrgid(cred));
+ }
+
/*
* Hardware capability flag word (performance hints)
* Used for choosing faster library routines.
@@ -804,7 +968,24 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
ADDAUX(aux, AT_SUN_BRAND_AUX1, 0)
ADDAUX(aux, AT_SUN_BRAND_AUX2, 0)
ADDAUX(aux, AT_SUN_BRAND_AUX3, 0)
+ ADDAUX(aux, AT_SUN_BRAND_AUX4, 0)
+ }
+
+ /*
+ * Add the comm page auxv entry, mapping it in if needed.
+ */
+#if defined(__amd64)
+ if (args->commpage != NULL ||
+ (args->commpage = (uintptr_t)comm_page_mapin()) != NULL) {
+ ADDAUX(aux, AT_SUN_COMMPAGE, args->commpage)
+ } else {
+ /*
+ * If the comm page cannot be mapped, pad out the auxv
+ * to satisfy later size checks.
+ */
+ ADDAUX(aux, AT_NULL, 0)
}
+#endif /* defined(__amd64) */
ADDAUX(aux, AT_NULL, 0)
postfixsize = (char *)aux - (char *)bigwad->elfargs;
@@ -845,6 +1026,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
}
bzero(up->u_auxv, sizeof (up->u_auxv));
+ up->u_commpagep = args->commpage;
if (postfixsize) {
int num_auxv;
@@ -911,6 +1093,8 @@ bad:
if (error == 0)
error = ENOEXEC;
out:
+ if (dynuphdr)
+ kmem_free(uphdr, sizeof (Phdr));
if (phdrbase != NULL)
kmem_free(phdrbase, phdrsize);
if (cap != NULL)
@@ -1177,6 +1361,29 @@ getelfshdr(vnode_t *vp, cred_t *credp, const Ehdr *ehdr,
return (0);
}
+
+#ifdef _ELF32_COMPAT
+int
+elf32readhdr(vnode_t *vp, cred_t *credp, Ehdr *ehdrp, int *nphdrs,
+ caddr_t *phbasep, ssize_t *phsizep)
+#else
+int
+elfreadhdr(vnode_t *vp, cred_t *credp, Ehdr *ehdrp, int *nphdrs,
+ caddr_t *phbasep, ssize_t *phsizep)
+#endif
+{
+ int error, nshdrs, shstrndx;
+
+ if ((error = getelfhead(vp, credp, ehdrp, &nshdrs, &shstrndx,
+ nphdrs)) != 0 ||
+ (error = getelfphdr(vp, credp, ehdrp, *nphdrs, phbasep,
+ phsizep)) != 0) {
+ return (error);
+ }
+ return (0);
+}
+
+
static int
mapelfexec(
vnode_t *vp,
@@ -1197,7 +1404,7 @@ mapelfexec(
size_t *brksize)
{
Phdr *phdr;
- int i, prot, error;
+ int i, prot, error, lastprot = 0;
caddr_t addr = NULL;
size_t zfodsz;
int ptload = 0;
@@ -1205,43 +1412,78 @@ mapelfexec(
off_t offset;
int hsize = ehdr->e_phentsize;
caddr_t mintmp = (caddr_t)-1;
+ uintptr_t lastaddr = NULL;
extern int use_brk_lpg;
if (ehdr->e_type == ET_DYN) {
- /*
- * Obtain the virtual address of a hole in the
- * address space to map the "interpreter".
- */
- map_addr(&addr, len, (offset_t)0, 1, 0);
- if (addr == NULL)
- return (ENOMEM);
- *voffset = (intptr_t)addr;
+ caddr_t vaddr;
/*
- * Calculate the minimum vaddr so it can be subtracted out.
- * According to the ELF specification, since PT_LOAD sections
- * must be sorted by increasing p_vaddr values, this is
- * guaranteed to be the first PT_LOAD section.
+ * Despite the fact that mmapobj(2) refuses to load them, we
+ * need to support executing ET_DYN objects that have a
+ * non-NULL p_vaddr. When found in the wild, these objects
+ * are likely to be due to an old (and largely obviated) Linux
+ * facility, prelink(8), that rewrites shared objects to
+ * prefer specific (disjoint) virtual address ranges. (Yes,
+ * this is putatively for performance -- and yes, it has
+ * limited applicability, many edge conditions and grisly
+ * failure modes; even for Linux, it's insane.) As ELF
+ * mandates that the PT_LOAD segments be in p_vaddr order, we
+ * find the lowest p_vaddr by finding the first PT_LOAD
+ * segment.
*/
phdr = (Phdr *)phdrbase;
for (i = nphdrs; i > 0; i--) {
if (phdr->p_type == PT_LOAD) {
- *voffset -= (uintptr_t)phdr->p_vaddr;
+ addr = (caddr_t)(uintptr_t)phdr->p_vaddr;
break;
}
phdr = (Phdr *)((caddr_t)phdr + hsize);
}
+ /*
+ * We have a non-zero p_vaddr in the first PT_LOAD segment --
+ * presumably because we're directly executing a prelink(8)'d
+ * ld-linux.so. While we could correctly execute such an
+ * object without locating it at its desired p_vaddr (it is,
+ * after all, still relocatable), our inner antiquarian
+ * derives a perverse pleasure in accommodating the steampunk
+ * prelink(8) contraption -- goggles on!
+ */
+ if ((vaddr = addr) != NULL) {
+ if (as_gap(curproc->p_as, len,
+ &addr, &len, AH_LO, NULL) == -1 || addr != vaddr) {
+ addr = NULL;
+ }
+ }
+
+ if (addr == NULL) {
+ /*
+ * We either have a NULL p_vaddr (the common case, by
+ * many orders of magnitude) or we have a non-NULL
+ * p_vaddr and we were unable to obtain the specified
+ * VA range (presumably because it's an illegal
+ * address). Either way, obtain an address in which
+ * to map the interpreter.
+ */
+ map_addr(&addr, len, (offset_t)0, 1, 0);
+ if (addr == NULL)
+ return (ENOMEM);
+ }
+
+ /*
+ * Our voffset is the difference between where we landed and
+ * where we wanted to be.
+ */
+ *voffset = (uintptr_t)addr - (uintptr_t)vaddr;
} else {
*voffset = 0;
}
+
phdr = (Phdr *)phdrbase;
for (i = nphdrs; i > 0; i--) {
switch (phdr->p_type) {
case PT_LOAD:
- if ((*dyphdr != NULL) && (*uphdr == NULL))
- return (0);
-
ptload = 1;
prot = PROT_USER;
if (phdr->p_flags & PF_R)
@@ -1253,6 +1495,34 @@ mapelfexec(
addr = (caddr_t)((uintptr_t)phdr->p_vaddr + *voffset);
+ if ((*dyphdr != NULL) && uphdr != NULL &&
+ (*uphdr == NULL)) {
+ /*
+ * The PT_PHDR program header is, strictly
+ * speaking, optional. If we find that this
+ * is missing, we will determine the location
+ * of the program headers based on the address
+ * of the lowest PT_LOAD segment (namely, this
+ * one): we subtract the p_offset to get to
+ * the ELF header and then add back the program
+ * header offset to get to the program headers.
+ * We then cons up a Phdr that corresponds to
+ * the (missing) PT_PHDR, setting the flags
+ * to 0 to denote that this is artificial and
+ * should (must) be freed by the caller.
+ */
+ Phdr *cons;
+
+ cons = kmem_zalloc(sizeof (Phdr), KM_SLEEP);
+
+ cons->p_flags = 0;
+ cons->p_type = PT_PHDR;
+ cons->p_vaddr = ((uintptr_t)addr -
+ phdr->p_offset) + ehdr->e_phoff;
+
+ *uphdr = cons;
+ }
+
/*
* Keep track of the segment with the lowest starting
* address.
@@ -1260,6 +1530,41 @@ mapelfexec(
if (addr < mintmp)
mintmp = addr;
+ /*
+ * Segments need not correspond to page boundaries:
+ * they are permitted to share a page. If two PT_LOAD
+ * segments share the same page, and the permissions
+ * of the segments differ, the behavior is historically
+ * that the permissions of the latter segment are used
+ * for the page that the two segments share. This is
+ * also historically a non-issue: binaries generated
+ * by most anything will make sure that two PT_LOAD
+ * segments with differing permissions don't actually
+ * share any pages. However, there exist some crazy
+ * things out there (including at least an obscure
+ * Portuguese teaching language called G-Portugol) that
+ * actually do the wrong thing and expect it to work:
+ * they have a segment with execute permission share
+ * a page with a subsequent segment that does not
+ * have execute permissions and expect the resulting
+ * shared page to in fact be executable. To accommodate
+ * such broken link editors, we take advantage of a
+ * latitude explicitly granted to the loader: it is
+ * permitted to make _any_ PT_LOAD segment executable
+ * (provided that it is readable or writable). If we
+ * see that we're sharing a page and that the previous
+ * page was executable, we will add execute permissions
+ * to our segment.
+ */
+ if (btop(lastaddr) == btop((uintptr_t)addr) &&
+ (phdr->p_flags & (PF_R | PF_W)) &&
+ (lastprot & PROT_EXEC)) {
+ prot |= PROT_EXEC;
+ }
+
+ lastaddr = (uintptr_t)addr + phdr->p_filesz;
+ lastprot = prot;
+
zfodsz = (size_t)phdr->p_memsz - phdr->p_filesz;
offset = phdr->p_offset;
@@ -1324,8 +1629,22 @@ mapelfexec(
break;
case PT_INTERP:
- if (ptload)
- goto bad;
+ /*
+ * The ELF specification is unequivocal about the
+ * PT_INTERP program header with respect to any PT_LOAD
+ * program header: "If it is present, it must precede
+ * any loadable segment entry." Linux, however, makes
+ * no attempt to enforce this -- which has allowed some
+ * binary editing tools to get away with generating
+ * invalid ELF binaries in the respect that PT_INTERP
+ * occurs after the first PT_LOAD program header. This
+ * is unfortunate (and of course, disappointing) but
+ * it's no worse than that: there is no reason that we
+ * can't process the PT_INTERP entry (if present) after
+ * one or more PT_LOAD entries. We therefore
+ * deliberately do not check ptload here and always
+ * store dyphdr to be the PT_INTERP program header.
+ */
*dyphdr = phdr;
break;
@@ -1334,9 +1653,12 @@ mapelfexec(
break;
case PT_PHDR:
- if (ptload)
+ if (ptload || phdr->p_flags == 0)
goto bad;
- *uphdr = phdr;
+
+ if (uphdr != NULL)
+ *uphdr = phdr;
+
break;
case PT_NULL:
@@ -2185,7 +2507,7 @@ static struct modlexec modlexec = {
extern int elf32exec(vnode_t *vp, execa_t *uap, uarg_t *args,
intpdata_t *idatap, int level, long *execsz,
int setid, caddr_t exec_file, cred_t *cred,
- int brand_action);
+ int *brand_action);
extern int elf32core(vnode_t *vp, proc_t *p, cred_t *credp,
rlim64_t rlimit, int sig, core_content_t content);
diff --git a/usr/src/uts/common/exec/intp/intp.c b/usr/src/uts/common/exec/intp/intp.c
index 269ba86b1b..512cab2b66 100644
--- a/usr/src/uts/common/exec/intp/intp.c
+++ b/usr/src/uts/common/exec/intp/intp.c
@@ -22,6 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -47,6 +48,7 @@
#include <sys/kmem.h>
#include <sys/note.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
/*
* This is the loadable module wrapper.
@@ -54,7 +56,7 @@
#include <sys/modctl.h>
extern int intpexec(struct vnode *, struct execa *, struct uarg *,
- struct intpdata *, int, long *, int, caddr_t, struct cred *, int);
+ struct intpdata *, int, long *, int, caddr_t, struct cred *, int *);
static struct execsw esw = {
intpmagicstr,
@@ -126,13 +128,20 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
*cp = '\0';
/*
- * Locate the beginning and end of the interpreter name.
- * In addition to the name, one additional argument may
- * optionally be included here, to be prepended to the
- * arguments provided on the command line. Thus, for
- * example, you can say
+ * Locate the beginning and end of the interpreter name. Historically,
+ * for illumos and its predecessors, in addition to the name, one
+ * additional argument may optionally be included here, to be prepended
+ * to the arguments provided on the command line. Thus, for example,
+ * you can say
*
* #! /usr/bin/awk -f
+ *
+ * However, handling of interpreter arguments varies across operating
+ * systems and other systems allow more than one argument. In
+ * particular, Linux allows more than one and delivers all arguments
+ * as a single string (argv[1] is "-arg1 -arg2 ..."). We support this
+ * style of argument handling as a brand-specific option (setting
+ * b_intp_parse_arg to B_FALSE).
*/
for (cp = &linep[2]; *cp == ' '; cp++)
;
@@ -151,9 +160,12 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
idatap->intp_arg[0] = NULL;
else {
idatap->intp_arg[0] = cp;
- while (*cp && *cp != ' ')
- cp++;
- *cp = '\0';
+ if (!PROC_IS_BRANDED(curproc) ||
+ BROP(curproc)->b_intp_parse_arg) {
+ while (*cp && *cp != ' ')
+ cp++;
+ *cp = '\0';
+ }
}
}
return (0);
@@ -188,9 +200,8 @@ intpexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
- _NOTE(ARGUNUSED(brand_action))
vnode_t *nvp;
int error = 0;
struct intpdata idata;
@@ -281,7 +292,7 @@ intpexec(
}
error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
- EBA_NONE);
+ brand_action);
if (!error) {
/*
diff --git a/usr/src/uts/common/exec/java/java.c b/usr/src/uts/common/exec/java/java.c
index fdc327dcbb..5170fda5cb 100644
--- a/usr/src/uts/common/exec/java/java.c
+++ b/usr/src/uts/common/exec/java/java.c
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/*
@@ -85,7 +86,7 @@ char *jexec_arg = "-jar";
static int
javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
struct intpdata *idatap, int level, long *execsz, int setid,
- caddr_t execfile, cred_t *cred, int brand_action)
+ caddr_t execfile, cred_t *cred, int *brand_action)
{
struct intpdata idata;
int error;
diff --git a/usr/src/uts/common/exec/shbin/shbin.c b/usr/src/uts/common/exec/shbin/shbin.c
index ee5060a07e..016d87b9ef 100644
--- a/usr/src/uts/common/exec/shbin/shbin.c
+++ b/usr/src/uts/common/exec/shbin/shbin.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -58,7 +59,7 @@ shbinexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action);
+ int *brand_action);
#define SHBIN_CNTL(x) ((x)&037)
#define SHBINMAGIC_LEN 4
@@ -162,7 +163,7 @@ shbinexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
_NOTE(ARGUNUSED(brand_action))
vnode_t *nvp;
diff --git a/usr/src/uts/common/fs/dev/sdev_netops.c b/usr/src/uts/common/fs/dev/sdev_netops.c
index 4eaf38f484..41441ec52d 100644
--- a/usr/src/uts/common/fs/dev/sdev_netops.c
+++ b/usr/src/uts/common/fs/dev/sdev_netops.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
/*
@@ -41,8 +42,102 @@
#include <sys/zone.h>
#include <sys/dls.h>
+static const char *devnet_zpath = "/dev/net/zone/";
struct vnodeops *devnet_vnodeops;
+static zoneid_t
+devnet_nodetozone(sdev_node_t *dv)
+{
+ char *zname = NULL, *dup;
+ zone_t *zone;
+ int duplen;
+ zoneid_t zid;
+
+ /*
+ * If in a non-global zone, always return it's zid no matter what the
+ * node is.
+ */
+ zid = getzoneid();
+ if (zid != GLOBAL_ZONEID)
+ return (zid);
+
+ /*
+ * If it doesn't have /dev/net/zone/ then it can't be a specific zone
+ * we're targetting.
+ */
+ if (strncmp(devnet_zpath, dv->sdev_path, strlen(devnet_zpath)) != 0)
+ return (GLOBAL_ZONEID);
+
+ if (dv->sdev_vnode->v_type == VDIR) {
+ zone = zone_find_by_name(dv->sdev_name);
+ } else {
+ /* Non directories have the form /dev/net/zone/%z/%s */
+ dup = strdup(dv->sdev_path);
+ duplen = strlen(dup);
+ zname = strrchr(dup, '/');
+ *zname = '\0';
+ zname--;
+ zname = strrchr(dup, '/');
+ zname++;
+ zone = zone_find_by_name(zname);
+ kmem_free(dup, duplen + 1);
+ }
+ if (zone == NULL)
+ return (GLOBAL_ZONEID);
+ zid = zone->zone_id;
+ zone_rele(zone);
+ return (zid);
+}
+
+static int
+devnet_mkdir(struct sdev_node *ddv, char *name)
+{
+ sdev_node_t *dv;
+ struct vattr va;
+ int ret;
+
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ dv = sdev_cache_lookup(ddv, name);
+ if (dv != NULL) {
+ SDEV_SIMPLE_RELE(dv);
+ return (EEXIST);
+ }
+
+ va = *sdev_getdefault_attr(VDIR);
+ gethrestime(&va.va_atime);
+ va.va_mtime = va.va_atime;
+ va.va_ctime = va.va_atime;
+
+ ret = sdev_mknode(ddv, name, &dv, &va, NULL, NULL, kcred, SDEV_READY);
+ if (ret != 0)
+ return (ret);
+ SDEV_SIMPLE_RELE(dv);
+ return (0);
+}
+
+/*
+ * We basically need to walk down the directory path to determine what we should
+ * do. At the top level of /dev/net, only the directory /dev/net/zone is valid,
+ * and it is always valid. Following on that, /dev/net/zone/%zonename is valid
+ * if and only if we can look up that zone name. If it's not, or it's some other
+ * name, then it's SDEV_VTOR_INVALID.
+ */
+static int
+devnet_dirvalidate(struct sdev_node *dv)
+{
+ zone_t *zonep;
+ char *path = "/dev/net/zone";
+
+ if (strcmp(path, dv->sdev_path) == 0)
+ return (SDEV_VTOR_VALID);
+
+ zonep = zone_find_by_name(dv->sdev_name);
+ if (zonep == NULL)
+ return (SDEV_VTOR_INVALID);
+ zone_rele(zonep);
+ return (SDEV_VTOR_VALID);
+}
+
/*
* Check if a net sdev_node is still valid - i.e. it represents a current
* network link.
@@ -60,11 +155,20 @@ devnet_validate(struct sdev_node *dv)
ASSERT(dv->sdev_state == SDEV_READY);
- if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0)
+ if (dv->sdev_vnode->v_type == VDIR)
+ return (devnet_dirvalidate(dv));
+
+ if (strncmp(devnet_zpath, dv->sdev_path, strlen(devnet_zpath)) == 0) {
+ ASSERT(SDEV_IS_GLOBAL(dv));
+ zoneid = devnet_nodetozone(dv);
+ } else {
+ zoneid = getzoneid();
+ }
+
+ if (dls_mgmt_get_linkid_in_zone(dv->sdev_name, &linkid, zoneid) != 0)
return (SDEV_VTOR_INVALID);
- if (SDEV_IS_GLOBAL(dv))
+ if (zoneid == GLOBAL_ZONEID)
return (SDEV_VTOR_VALID);
- zoneid = getzoneid();
return (zone_check_datalink(&zoneid, linkid) == 0 ?
SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
}
@@ -74,13 +178,14 @@ devnet_validate(struct sdev_node *dv)
* a net entry when the node is not found in the cache.
*/
static int
-devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
+devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp,
+ zoneid_t zid)
{
timestruc_t now;
dev_t dev;
int error;
- if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
+ if ((error = dls_devnet_open_in_zone(nm, ddhp, &dev, zid)) != 0) {
sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
"network node: %s\n", nm));
return (error);
@@ -116,6 +221,7 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
struct sdev_node *ddv = VTOSDEV(dvp);
struct sdev_node *dv = NULL;
dls_dl_handle_t ddh = NULL;
+ zone_t *zone;
struct vattr vattr;
int nmlen;
int error = ENOENT;
@@ -123,6 +229,9 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
if (SDEVTOV(ddv)->v_type != VDIR)
return (ENOTDIR);
+ if (!SDEV_IS_GLOBAL(ddv) && crgetzoneid(cred) == GLOBAL_ZONEID)
+ return (EPERM);
+
/*
* Empty name or ., return node itself.
*/
@@ -145,6 +254,12 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
rw_enter(&ddv->sdev_contents, RW_WRITER);
/*
+ * ZOMBIED parent does not allow new node creation, bail out early.
+ */
+ if (ddv->sdev_state == SDEV_ZOMBIE)
+ goto failed;
+
+ /*
* directory cache lookup:
*/
if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
@@ -153,13 +268,42 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
goto found;
}
+ if (SDEV_IS_GLOBAL(ddv)) {
+ /*
+ * Check for /dev/net/zone
+ */
+ if (strcmp("zone", nm) == 0 && strcmp("/dev/net",
+ ddv->sdev_path) == 0) {
+ (void) devnet_mkdir(ddv, nm);
+ dv = sdev_cache_lookup(ddv, nm);
+ ASSERT(dv != NULL);
+ goto found;
+ }
+
+ /*
+ * Check for /dev/net/zone/%z. We can't use devnet_zpath due to
+ * its trailing slash.
+ */
+ if (strcmp("/dev/net/zone", ddv->sdev_path) == 0) {
+ zone = zone_find_by_name(nm);
+ if (zone == NULL)
+ goto failed;
+ (void) devnet_mkdir(ddv, nm);
+ zone_rele(zone);
+ dv = sdev_cache_lookup(ddv, nm);
+ ASSERT(dv != NULL);
+ goto found;
+ }
+ } else if (strcmp("/dev/net", ddv->sdev_path) != 0) {
+ goto failed;
+ }
+
/*
- * ZOMBIED parent does not allow new node creation, bail out early.
+ * We didn't find what we were looking for. What that is depends a lot
+ * on what directory we're in.
*/
- if (ddv->sdev_state == SDEV_ZOMBIE)
- goto failed;
- error = devnet_create_rvp(nm, &vattr, &ddh);
+ error = devnet_create_rvp(nm, &vattr, &ddh, devnet_nodetozone(ddv));
if (error != 0)
goto failed;
@@ -219,7 +363,7 @@ devnet_filldir_datalink(datalink_id_t linkid, void *arg)
if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
goto found;
- if (devnet_create_rvp(link, &vattr, &ddh) != 0)
+ if (devnet_create_rvp(link, &vattr, &ddh, devnet_nodetozone(arg)) != 0)
return (0);
ASSERT(ddh != NULL);
@@ -244,16 +388,77 @@ found:
return (0);
}
+/*
+ * Fill in all the entries for the current zone.
+ */
static void
-devnet_filldir(struct sdev_node *ddv)
+devnet_fillzone(struct sdev_node *ddv, zoneid_t zid)
{
- sdev_node_t *dv, *next;
datalink_id_t linkid;
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ if (zid == GLOBAL_ZONEID) {
+ ASSERT(SDEV_IS_GLOBAL(ddv));
+ linkid = DATALINK_INVALID_LINKID;
+ do {
+ linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
+ if (linkid != DATALINK_INVALID_LINKID)
+ (void) devnet_filldir_datalink(linkid, ddv);
+ } while (linkid != DATALINK_INVALID_LINKID);
+ } else {
+ (void) zone_datalink_walk(zid, devnet_filldir_datalink, ddv);
+ }
+}
+
+/*
+ * Callback for zone_walk when filling up /dev/net/zone/...
+ */
+static int
+devnet_fillzdir_cb(zone_t *zonep, void *arg)
+{
+ sdev_node_t *ddv = arg;
+
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ (void) devnet_mkdir(ddv, zonep->zone_name);
+ return (0);
+}
+
+/*
+ * Fill in a directory that isn't the top level /dev/net.
+ */
+static void
+devnet_fillzdir(struct sdev_node *ddv)
+{
+ zone_t *zonep;
+ char *path = "/dev/net/zone";
+
+ if (strcmp(path, ddv->sdev_path) == 0) {
+ (void) zone_walk(devnet_fillzdir_cb, ddv);
+ return;
+ }
+
+ zonep = zone_find_by_name(ddv->sdev_name);
+ if (zonep == NULL)
+ return;
+ devnet_fillzone(ddv, zonep->zone_id);
+ zone_rele(zonep);
+}
+
+static void
+devnet_filldir(struct sdev_node *ddv)
+{
+ int ret;
+ sdev_node_t *dv, *next;
+
ASSERT(RW_READ_HELD(&ddv->sdev_contents));
if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
rw_exit(&ddv->sdev_contents);
rw_enter(&ddv->sdev_contents, RW_WRITER);
+ if (ddv->sdev_state == SDEV_ZOMBIE) {
+ rw_exit(&ddv->sdev_contents);
+ return;
+ }
}
for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
@@ -276,31 +481,38 @@ devnet_filldir(struct sdev_node *ddv)
if (SDEVTOV(dv)->v_count > 0)
continue;
+
SDEV_HOLD(dv);
+
+ /*
+ * Clean out everything underneath before we remove ourselves.
+ */
+ if (SDEVTOV(ddv)->v_type == VDIR) {
+ ret = sdev_cleandir(dv, NULL, 0);
+ ASSERT(ret == 0);
+ }
/* remove the cache node */
(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
SDEV_CACHE_DELETE);
SDEV_RELE(dv);
}
+ if (strcmp(ddv->sdev_path, "/dev/net") != 0) {
+ devnet_fillzdir(ddv);
+ goto done;
+ }
+
if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
goto done;
if (SDEV_IS_GLOBAL(ddv)) {
- linkid = DATALINK_INVALID_LINKID;
- do {
- linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
- DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
- if (linkid != DATALINK_INVALID_LINKID)
- (void) devnet_filldir_datalink(linkid, ddv);
- } while (linkid != DATALINK_INVALID_LINKID);
+ devnet_fillzone(ddv, GLOBAL_ZONEID);
+ (void) devnet_mkdir(ddv, "zone");
} else {
- (void) zone_datalink_walk(getzoneid(),
- devnet_filldir_datalink, ddv);
+ devnet_fillzone(ddv, getzoneid());
}
ddv->sdev_flags &= ~SDEV_BUILD;
-
done:
rw_downgrade(&ddv->sdev_contents);
}
@@ -319,6 +531,9 @@ devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
ASSERT(sdvp);
+ if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
+ return (EPERM);
+
if (uiop->uio_offset == 0)
devnet_filldir(sdvp);
diff --git a/usr/src/uts/common/fs/dev/sdev_plugin.c b/usr/src/uts/common/fs/dev/sdev_plugin.c
new file mode 100644
index 0000000000..885191175f
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_plugin.c
@@ -0,0 +1,913 @@
+/*
+ * 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) 2014, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Dynamic directory plugin interface for sdev.
+ *
+ * The sdev plugin interfaces provides a means for a dynamic directory based on
+ * in-kernel state to be simply created. Traditionally, dynamic directories were
+ * built into sdev itself. While these legacy plugins are useful, it makes more
+ * sense for these pieces of functionality to live with the individual drivers.
+ *
+ * The plugin interface requires folks to implement three interfaces and
+ * provides a series of callbacks that can be made in the context of those
+ * interfaces to interrogate the sdev_node_t without having to leak
+ * implementation details of the sdev_node_t. These interfaces are:
+ *
+ * o spo_validate
+ *
+ * Given a particular node, answer the question as to whether or not this
+ * entry is still valid. Here, plugins should use the name and the dev_t
+ * associated with the node to verify that it matches something that still
+ * exists.
+ *
+ * o spo_filldir
+ *
+ * Fill all the entries inside of a directory. Note that some of these entries
+ * may already exist.
+ *
+ * o spo_inactive
+ *
+ * The given node is no longer being used. This allows the consumer to
+ * potentially tear down anything that was being held open related to this.
+ * Note that this only fires when the given sdev_node_t becomes a zombie.
+ *
+ * During these callbacks a consumer is not allowed to register or unregister a
+ * plugin, especially their own. They may call the sdev_ctx style functions. All
+ * callbacks fire in a context where blocking is allowed (eg. the spl is below
+ * LOCK_LEVEL).
+ *
+ * When a plugin is added, we create its directory in the global zone. By doing
+ * that, we ensure that something isn't already there and that nothing else can
+ * come along and try and create something without our knowledge. We only have
+ * to create it in the GZ and not for all other instances of sdev because an
+ * instance of sdev that isn't at /dev does not have dynamic directories, and
+ * second, any instance of sdev present in a non-global zone cannot create
+ * anything, therefore we know that by it not being in the global zone's
+ * instance of sdev that we're good to go.
+ *
+ * Lock Ordering
+ * -------------
+ *
+ * The global sdev_plugin_lock must be held before any of the individual
+ * sdev_plugin_t`sp_lock. Further, once any plugin related lock has been held,
+ * it is not legal to take any holds on any sdev_node_t or to grab the
+ * sdev_node_t`contents_lock in any way.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_plugin.h>
+#include <fs/fs_subr.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/list.h>
+#include <sys/ctype.h>
+
+kmutex_t sdev_plugin_lock;
+list_t sdev_plugin_list;
+kmem_cache_t *sdev_plugin_cache;
+struct vnodeops *sdev_plugin_vnops;
+
+#define SDEV_PLUGIN_NAMELEN 64
+
+typedef struct sdev_plugin {
+ list_node_t sp_link;
+ char sp_name[SDEV_PLUGIN_NAMELEN]; /* E */
+ int sp_nflags; /* E */
+ struct vnodeops *sp_vnops; /* E */
+ sdev_plugin_ops_t *sp_pops; /* E */
+ boolean_t sp_islegacy; /* E */
+ int (*sp_lvtor)(sdev_node_t *); /* E */
+ kmutex_t sp_lock; /* Protects everything below */
+ kcondvar_t sp_nodecv;
+ size_t sp_nnodes;
+} sdev_plugin_t;
+
+/* ARGSUSED */
+static int
+sdev_plugin_cache_constructor(void *buf, void *arg, int tags)
+{
+ sdev_plugin_t *spp = buf;
+ mutex_init(&spp->sp_lock, NULL, MUTEX_DRIVER, 0);
+ cv_init(&spp->sp_nodecv, NULL, CV_DRIVER, NULL);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+sdev_plugin_cache_destructor(void *buf, void *arg)
+{
+ sdev_plugin_t *spp = buf;
+ cv_destroy(&spp->sp_nodecv);
+ mutex_destroy(&spp->sp_lock);
+}
+
+enum vtype
+sdev_ctx_vtype(sdev_ctx_t ctx)
+{
+ sdev_node_t *sdp = (sdev_node_t *)ctx;
+
+ ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
+ return (sdp->sdev_vnode->v_type);
+}
+
+const char *
+sdev_ctx_path(sdev_ctx_t ctx)
+{
+ sdev_node_t *sdp = (sdev_node_t *)ctx;
+
+ ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
+ return (sdp->sdev_path);
+}
+
+const char *
+sdev_ctx_name(sdev_ctx_t ctx)
+{
+ sdev_node_t *sdp = (sdev_node_t *)ctx;
+
+ ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
+ return (sdp->sdev_name);
+}
+
+/*
+ * Currently we only support psasing through a single flag -- SDEV_IS_GLOBAL.
+ */
+sdev_ctx_flags_t
+sdev_ctx_flags(sdev_ctx_t ctx)
+{
+ sdev_node_t *sdp = (sdev_node_t *)ctx;
+
+ ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
+ return (sdp->sdev_flags & SDEV_GLOBAL);
+}
+
+/*
+ * Return some amount of private data specific to the vtype. In the case of a
+ * character or block device this is the device number.
+ */
+const void *
+sdev_ctx_vtype_data(sdev_ctx_t ctx)
+{
+ sdev_node_t *sdp = (sdev_node_t *)ctx;
+ void *ret;
+
+ ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
+ switch (sdp->sdev_vnode->v_type) {
+ case VCHR:
+ case VBLK:
+ ret = (void *)(uintptr_t)(sdp->sdev_vnode->v_rdev);
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Use the same rules as zones for a name. isalphanum + '-', '_', and '.'.
+ */
+static int
+sdev_plugin_name_isvalid(const char *c, int buflen)
+{
+ int i;
+
+ for (i = 0; i < buflen; i++, c++) {
+ if (*c == '\0')
+ return (1);
+
+ if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.')
+ return (0);
+ }
+ /* Never found a null terminator */
+ return (0);
+}
+
+static int
+sdev_plugin_mknode(sdev_plugin_t *spp, sdev_node_t *sdvp, char *name,
+ vattr_t *vap)
+{
+ int ret;
+ sdev_node_t *svp;
+
+ ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
+ ASSERT(spp != NULL);
+ svp = sdev_cache_lookup(sdvp, name);
+ if (svp != NULL) {
+ SDEV_SIMPLE_RELE(svp);
+ return (EEXIST);
+ }
+
+ ret = sdev_mknode(sdvp, name, &svp, vap, NULL, NULL, kcred,
+ SDEV_READY);
+ if (ret != 0)
+ return (ret);
+ SDEV_SIMPLE_RELE(svp);
+
+ return (0);
+}
+
+/*
+ * Plugin node creation callbacks
+ */
+int
+sdev_plugin_mkdir(sdev_ctx_t ctx, char *name)
+{
+ sdev_node_t *sdvp;
+ timestruc_t now;
+ struct vattr vap;
+
+ if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0)
+ return (EINVAL);
+
+ sdvp = (sdev_node_t *)ctx;
+ ASSERT(sdvp->sdev_private != NULL);
+ ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
+
+ vap = *sdev_getdefault_attr(VDIR);
+ gethrestime(&now);
+ vap.va_atime = now;
+ vap.va_mtime = now;
+ vap.va_ctime = now;
+
+ return (sdev_plugin_mknode(sdvp->sdev_private, sdvp, name, &vap));
+}
+
+int
+sdev_plugin_mknod(sdev_ctx_t ctx, char *name, mode_t mode, dev_t dev)
+{
+ sdev_node_t *sdvp;
+ timestruc_t now;
+ struct vattr vap;
+
+ if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0)
+ return (EINVAL);
+
+ sdvp = (sdev_node_t *)ctx;
+ ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
+ if (mode != S_IFCHR && mode != S_IFBLK)
+ return (EINVAL);
+
+ ASSERT(sdvp->sdev_private != NULL);
+
+ vap = *sdev_getdefault_attr(mode == S_IFCHR ? VCHR : VBLK);
+ gethrestime(&now);
+ vap.va_atime = now;
+ vap.va_mtime = now;
+ vap.va_ctime = now;
+ vap.va_rdev = dev;
+ vap.va_mode = mode | 0666;
+
+ /* Despite the similar name, this is in fact a different function */
+ return (sdev_plugin_mknode(sdvp->sdev_private, sdvp, name, &vap));
+
+}
+
+static int
+sdev_plugin_validate(sdev_node_t *sdp)
+{
+ int ret;
+ sdev_plugin_t *spp;
+
+ ASSERT(sdp->sdev_private != NULL);
+ spp = sdp->sdev_private;
+ ASSERT(spp->sp_islegacy == B_FALSE);
+ ASSERT(spp->sp_pops != NULL);
+ rw_enter(&sdp->sdev_contents, RW_READER);
+ ret = spp->sp_pops->spo_validate((uintptr_t)sdp);
+ rw_exit(&sdp->sdev_contents);
+ return (ret);
+}
+
+static void
+sdev_plugin_validate_dir(sdev_node_t *sdvp)
+{
+ int ret;
+ sdev_node_t *svp, *next;
+
+ ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
+
+ for (svp = SDEV_FIRST_ENTRY(sdvp); svp != NULL; svp = next) {
+
+ next = SDEV_NEXT_ENTRY(sdvp, svp);
+ ASSERT(svp->sdev_state != SDEV_ZOMBIE);
+ /* skip nodes that aren't ready */
+ if (svp->sdev_state == SDEV_INIT)
+ continue;
+
+ switch (sdev_plugin_validate(svp)) {
+ case SDEV_VTOR_VALID:
+ case SDEV_VTOR_SKIP:
+ continue;
+ case SDEV_VTOR_INVALID:
+ case SDEV_VTOR_STALE:
+ break;
+ }
+
+ SDEV_HOLD(svp);
+
+ /*
+ * Clean out everything underneath this node before we
+ * remove it.
+ */
+ if (svp->sdev_vnode->v_type == VDIR) {
+ ret = sdev_cleandir(svp, NULL, 0);
+ ASSERT(ret == 0);
+ }
+ /* remove the cache node */
+ (void) sdev_cache_update(sdvp, &svp, svp->sdev_name,
+ SDEV_CACHE_DELETE);
+ SDEV_RELE(svp);
+ }
+}
+
+/* ARGSUSED */
+static int
+sdev_plugin_vop_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
+ int *eofp, caller_context_t *ct_unused, int flags_unused)
+{
+ int ret;
+ sdev_node_t *sdvp = VTOSDEV(dvp);
+ sdev_plugin_t *spp;
+
+ ASSERT(RW_READ_HELD(&sdvp->sdev_contents));
+
+ /* Sanity check we're not a zombie before we do anyting else */
+ if (sdvp->sdev_state == SDEV_ZOMBIE)
+ return (ENOENT);
+
+ spp = sdvp->sdev_private;
+ ASSERT(spp != NULL);
+ ASSERT(spp->sp_islegacy == B_FALSE);
+ ASSERT(spp->sp_pops != NULL);
+
+ if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
+ return (EPERM);
+
+ if (uiop->uio_offset == 0) {
+ /*
+ * We upgrade to a write lock and grab the plugin's lock along
+ * the way. We're almost certainly going to get creation
+ * callbacks, so this is the only safe way to go.
+ */
+ if (rw_tryupgrade(&sdvp->sdev_contents) == 0) {
+ rw_exit(&sdvp->sdev_contents);
+ rw_enter(&sdvp->sdev_contents, RW_WRITER);
+ if (sdvp->sdev_state == SDEV_ZOMBIE) {
+ rw_downgrade(&sdvp->sdev_contents);
+ return (ENOENT);
+ }
+ }
+
+ sdev_plugin_validate_dir(sdvp);
+ ret = spp->sp_pops->spo_filldir((uintptr_t)sdvp);
+ rw_downgrade(&sdvp->sdev_contents);
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
+}
+
+/*
+ * If we don't have a callback function that returns a failure, then sdev will
+ * try to create a node for us which violates all of our basic assertions. To
+ * work around that we create our own callback for devname_lookup_func which
+ * always returns ENOENT as at this point either it was created with the filldir
+ * callback or it was not.
+ */
+/*ARGSUSED*/
+static int
+sdev_plugin_vop_lookup_cb(sdev_node_t *ddv, char *nm, void **arg, cred_t *cred,
+ void *unused, char *unused2)
+{
+ return (ENOENT);
+}
+
+/* ARGSUSED */
+static int
+sdev_plugin_vop_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+ struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
+ caller_context_t *ct, int *direntflags, pathname_t *realpnp)
+{
+ int ret;
+ sdev_node_t *sdvp;
+ sdev_plugin_t *spp;
+
+ /* execute access is required to search the directory */
+ if ((ret = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
+ return (ret);
+
+ sdvp = VTOSDEV(dvp);
+ spp = sdvp->sdev_private;
+ ASSERT(spp != NULL);
+ ASSERT(spp->sp_islegacy == B_FALSE);
+ ASSERT(spp->sp_pops != NULL);
+
+ if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
+ return (EPERM);
+
+ /*
+ * Go straight for the write lock.
+ */
+ rw_enter(&sdvp->sdev_contents, RW_WRITER);
+ if (sdvp->sdev_state == SDEV_ZOMBIE) {
+ rw_exit(&sdvp->sdev_contents);
+ return (ENOENT);
+ }
+ sdev_plugin_validate_dir(sdvp);
+ ret = spp->sp_pops->spo_filldir((uintptr_t)sdvp);
+ rw_exit(&sdvp->sdev_contents);
+ if (ret != 0)
+ return (ret);
+
+ return (devname_lookup_func(sdvp, nm, vpp, cred,
+ sdev_plugin_vop_lookup_cb, SDEV_VATTR));
+}
+
+/*
+ * sdev is not a good citizen. We get inactive callbacks whenever a vnode goes
+ * to zero, but isn't necessairily a zombie yet. As such, to make things easier
+ * for users, we only fire the inactive callback when the node becomes a zombie
+ * and thus will be torn down here.
+ */
+static void
+sdev_plugin_vop_inactive_cb(struct vnode *dvp)
+{
+ sdev_node_t *sdp = VTOSDEV(dvp);
+ sdev_plugin_t *spp = sdp->sdev_private;
+
+ rw_enter(&sdp->sdev_contents, RW_READER);
+ if (sdp->sdev_state != SDEV_ZOMBIE) {
+ rw_exit(&sdp->sdev_contents);
+ return;
+ }
+ spp->sp_pops->spo_inactive((uintptr_t)sdp);
+ mutex_enter(&spp->sp_lock);
+ VERIFY(spp->sp_nnodes > 0);
+ spp->sp_nnodes--;
+ cv_signal(&spp->sp_nodecv);
+ mutex_exit(&spp->sp_lock);
+ rw_exit(&sdp->sdev_contents);
+}
+
+/*ARGSUSED*/
+static void
+sdev_plugin_vop_inactive(struct vnode *dvp, struct cred *cred,
+ caller_context_t *ct)
+{
+ sdev_node_t *sdp = VTOSDEV(dvp);
+ sdev_plugin_t *spp = sdp->sdev_private;
+ ASSERT(sdp->sdev_private != NULL);
+ ASSERT(spp->sp_islegacy == B_FALSE);
+ devname_inactive_func(dvp, cred, sdev_plugin_vop_inactive_cb);
+}
+
+const fs_operation_def_t sdev_plugin_vnodeops_tbl[] = {
+ VOPNAME_READDIR, { .vop_readdir = sdev_plugin_vop_readdir },
+ VOPNAME_LOOKUP, { .vop_lookup = sdev_plugin_vop_lookup },
+ VOPNAME_INACTIVE, { .vop_inactive = sdev_plugin_vop_inactive },
+ VOPNAME_CREATE, { .error = fs_nosys },
+ VOPNAME_REMOVE, { .error = fs_nosys },
+ VOPNAME_MKDIR, { .error = fs_nosys },
+ VOPNAME_RMDIR, { .error = fs_nosys },
+ VOPNAME_SYMLINK, { .error = fs_nosys },
+ VOPNAME_SETSECATTR, { .error = fs_nosys },
+ NULL, NULL
+};
+
+/*
+ * construct a new template with overrides from vtab
+ */
+static fs_operation_def_t *
+sdev_merge_vtab(const fs_operation_def_t tab[])
+{
+ fs_operation_def_t *new;
+ const fs_operation_def_t *tab_entry;
+
+ /* make a copy of standard vnode ops table */
+ new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
+ bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
+
+ /* replace the overrides from tab */
+ for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
+ fs_operation_def_t *std_entry = new;
+ while (std_entry->name) {
+ if (strcmp(tab_entry->name, std_entry->name) == 0) {
+ std_entry->func = tab_entry->func;
+ break;
+ }
+ std_entry++;
+ }
+ }
+
+ return (new);
+}
+
+/* free memory allocated by sdev_merge_vtab */
+static void
+sdev_free_vtab(fs_operation_def_t *new)
+{
+ kmem_free(new, sdev_vnodeops_tbl_size);
+}
+
+/*
+ * Register a new plugin.
+ */
+sdev_plugin_hdl_t
+sdev_plugin_register(const char *name, sdev_plugin_ops_t *ops, int *errp)
+{
+ int ret, err;
+ sdev_plugin_t *spp, *iter;
+ vnode_t *vp, *nvp;
+ sdev_node_t *sdp, *slp;
+ timestruc_t now;
+ struct vattr vap;
+
+ /*
+ * Some consumers don't care about why they failed. To keep the code
+ * simple, we'll just pretend they gave us something.
+ */
+ if (errp == NULL)
+ errp = &err;
+
+ if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ if (ops->spo_version != 1) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ if (ops->spo_validate == NULL || ops->spo_filldir == NULL ||
+ ops->spo_inactive == NULL) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ if ((ops->spo_flags & ~SDEV_PLUGIN_FLAGS_MASK) != 0) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ spp = kmem_cache_alloc(sdev_plugin_cache, KM_SLEEP);
+ (void) strlcpy(spp->sp_name, name, SDEV_PLUGIN_NAMELEN);
+
+ spp->sp_pops = ops;
+ spp->sp_nflags = SDEV_DYNAMIC | SDEV_VTOR;
+ if (ops->spo_flags & SDEV_PLUGIN_NO_NCACHE)
+ spp->sp_nflags |= SDEV_NO_NCACHE;
+ if (ops->spo_flags & SDEV_PLUGIN_SUBDIR)
+ spp->sp_nflags |= SDEV_SUBDIR;
+ spp->sp_vnops = sdev_plugin_vnops;
+ spp->sp_islegacy = B_FALSE;
+ spp->sp_lvtor = NULL;
+ spp->sp_nnodes = 0;
+
+ /*
+ * Make sure it's unique, nothing exists with this name already, and add
+ * it to the list. We also need to go through and grab the sdev
+ * root node as we cannot grab any sdev node locks once we've grabbed
+ * the sdev_plugin_lock. We effectively assert that if a directory is
+ * not present in the GZ's /dev, then it doesn't exist in any of the
+ * local zones.
+ */
+ ret = vn_openat("/dev", UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir, -1);
+ if (ret != 0) {
+ *errp = ret;
+ kmem_cache_free(sdev_plugin_cache, spp);
+ return (NULL);
+ }
+ /* Make sure we have the real vnode */
+ if (VOP_REALVP(vp, &nvp, NULL) == 0) {
+ VN_HOLD(nvp);
+ VN_RELE(vp);
+ vp = nvp;
+ nvp = NULL;
+ }
+ VERIFY(vp->v_op == sdev_vnodeops);
+ sdp = VTOSDEV(vp);
+ rw_enter(&sdp->sdev_contents, RW_WRITER);
+ slp = sdev_cache_lookup(sdp, spp->sp_name);
+ if (slp != NULL) {
+ SDEV_RELE(slp);
+ rw_exit(&sdp->sdev_contents);
+ VN_RELE(vp);
+ *errp = EEXIST;
+ kmem_cache_free(sdev_plugin_cache, spp);
+ return (NULL);
+ }
+
+ mutex_enter(&sdev_plugin_lock);
+ for (iter = list_head(&sdev_plugin_list); iter != NULL;
+ iter = list_next(&sdev_plugin_list, iter)) {
+ if (strcmp(spp->sp_name, iter->sp_name) == 0) {
+ mutex_exit(&sdev_plugin_lock);
+ rw_exit(&sdp->sdev_contents);
+ VN_RELE(vp);
+ *errp = EEXIST;
+ kmem_cache_free(sdev_plugin_cache, spp);
+ return (NULL);
+ }
+ }
+
+ list_insert_tail(&sdev_plugin_list, spp);
+ mutex_exit(&sdev_plugin_lock);
+
+ /*
+ * Now go ahead and create the top level directory for the global zone.
+ */
+ vap = *sdev_getdefault_attr(VDIR);
+ gethrestime(&now);
+ vap.va_atime = now;
+ vap.va_mtime = now;
+ vap.va_ctime = now;
+
+ (void) sdev_plugin_mknode(spp, sdp, spp->sp_name, &vap);
+
+ rw_exit(&sdp->sdev_contents);
+ VN_RELE(vp);
+
+ return ((sdev_plugin_hdl_t)spp);
+}
+
+static void
+sdev_plugin_unregister_cb(sdev_node_t *rdp, void *arg)
+{
+ sdev_plugin_t *spp = arg;
+ sdev_node_t *sdp;
+
+ rw_enter(&rdp->sdev_contents, RW_WRITER);
+ sdp = sdev_cache_lookup(rdp, spp->sp_name);
+ /* If it doesn't exist, we're done here */
+ if (sdp == NULL) {
+ rw_exit(&rdp->sdev_contents);
+ return;
+ }
+
+ /*
+ * We first delete the directory before recursively marking everything
+ * else stale. This ordering should ensure that we don't accidentally
+ * miss anything.
+ */
+ sdev_cache_update(rdp, &sdp, spp->sp_name, SDEV_CACHE_DELETE);
+ sdev_stale(sdp);
+ SDEV_RELE(sdp);
+ rw_exit(&rdp->sdev_contents);
+}
+
+/*
+ * Remove a plugin. This will block until everything has become a zombie, thus
+ * guaranteeing the caller that nothing will call into them again once this call
+ * returns. While the call is ongoing, it could be called into. Note that while
+ * this is ongoing, it will block other mounts.
+ */
+int
+sdev_plugin_unregister(sdev_plugin_hdl_t hdl)
+{
+ sdev_plugin_t *spp = (sdev_plugin_t *)hdl;
+ if (spp->sp_islegacy)
+ return (EINVAL);
+
+ mutex_enter(&sdev_plugin_lock);
+ list_remove(&sdev_plugin_list, spp);
+ mutex_exit(&sdev_plugin_lock);
+
+ sdev_mnt_walk(sdev_plugin_unregister_cb, spp);
+ mutex_enter(&spp->sp_lock);
+ while (spp->sp_nnodes > 0)
+ cv_wait(&spp->sp_nodecv, &spp->sp_lock);
+ mutex_exit(&spp->sp_lock);
+ kmem_cache_free(sdev_plugin_cache, spp);
+ return (0);
+}
+
+/*
+ * Register an old sdev style plugin to deal with what used to be in the vtab.
+ */
+static int
+sdev_plugin_register_legacy(struct sdev_vop_table *vtp)
+{
+ sdev_plugin_t *spp;
+
+ spp = kmem_cache_alloc(sdev_plugin_cache, KM_SLEEP);
+ (void) strlcpy(spp->sp_name, vtp->vt_name, SDEV_PLUGIN_NAMELEN);
+ spp->sp_islegacy = B_TRUE;
+ spp->sp_pops = NULL;
+ spp->sp_nflags = vtp->vt_flags;
+ spp->sp_lvtor = vtp->vt_vtor;
+ spp->sp_nnodes = 0;
+
+ if (vtp->vt_service != NULL) {
+ fs_operation_def_t *templ;
+ templ = sdev_merge_vtab(vtp->vt_service);
+ if (vn_make_ops(vtp->vt_name,
+ (const fs_operation_def_t *)templ,
+ &spp->sp_vnops) != 0) {
+ cmn_err(CE_WARN, "%s: malformed vnode ops\n",
+ vtp->vt_name);
+ sdev_free_vtab(templ);
+ kmem_cache_free(sdev_plugin_cache, spp);
+ return (1);
+ }
+
+ if (vtp->vt_global_vops) {
+ *(vtp->vt_global_vops) = spp->sp_vnops;
+ }
+
+ sdev_free_vtab(templ);
+ } else {
+ spp->sp_vnops = sdev_vnodeops;
+ }
+
+ /*
+ * No need to check for EEXIST here. These are loaded as a part of the
+ * sdev's initialization function. Further, we don't have to create them
+ * as that's taken care of in sdev's mount for the GZ.
+ */
+ mutex_enter(&sdev_plugin_lock);
+ list_insert_tail(&sdev_plugin_list, spp);
+ mutex_exit(&sdev_plugin_lock);
+
+ return (0);
+}
+
+/*
+ * We need to match off of the sdev_path, not the sdev_name. We are only allowed
+ * to exist directly under /dev.
+ */
+static sdev_plugin_t *
+sdev_match(sdev_node_t *dv)
+{
+ int vlen;
+ const char *path;
+ sdev_plugin_t *spp;
+
+ if (strlen(dv->sdev_path) <= 5)
+ return (NULL);
+
+ if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
+ return (NULL);
+ path = dv->sdev_path + 5;
+
+ mutex_enter(&sdev_plugin_lock);
+
+ for (spp = list_head(&sdev_plugin_list); spp != NULL;
+ spp = list_next(&sdev_plugin_list, spp)) {
+ if (strcmp(spp->sp_name, path) == 0) {
+ mutex_exit(&sdev_plugin_lock);
+ return (spp);
+ }
+
+ if (spp->sp_nflags & SDEV_SUBDIR) {
+ vlen = strlen(spp->sp_name);
+ if ((strncmp(spp->sp_name, path,
+ vlen - 1) == 0) && path[vlen] == '/') {
+ mutex_exit(&sdev_plugin_lock);
+ return (spp);
+ }
+
+ }
+ }
+
+ mutex_exit(&sdev_plugin_lock);
+ return (NULL);
+}
+
+void
+sdev_set_no_negcache(sdev_node_t *dv)
+{
+ char *path;
+ sdev_plugin_t *spp;
+
+ ASSERT(dv->sdev_path);
+ path = dv->sdev_path + strlen("/dev/");
+
+ mutex_enter(&sdev_plugin_lock);
+ for (spp = list_head(&sdev_plugin_list); spp != NULL;
+ spp = list_next(&sdev_plugin_list, spp)) {
+ if (strcmp(spp->sp_name, path) == 0) {
+ if (spp->sp_nflags & SDEV_NO_NCACHE)
+ dv->sdev_flags |= SDEV_NO_NCACHE;
+ break;
+ }
+ }
+ mutex_exit(&sdev_plugin_lock);
+}
+
+struct vnodeops *
+sdev_get_vop(sdev_node_t *dv)
+{
+ char *path;
+ sdev_plugin_t *spp;
+
+ path = dv->sdev_path;
+ ASSERT(path);
+
+ /* gets the relative path to /dev/ */
+ path += 5;
+
+ if ((spp = sdev_match(dv)) != NULL) {
+ dv->sdev_flags |= spp->sp_nflags;
+ if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
+ (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
+ dv->sdev_flags |= SDEV_PERSIST;
+ return (spp->sp_vnops);
+ }
+
+ /* child inherits the persistence of the parent */
+ if (SDEV_IS_PERSIST(dv->sdev_dotdot))
+ dv->sdev_flags |= SDEV_PERSIST;
+ return (sdev_vnodeops);
+}
+
+void *
+sdev_get_vtor(sdev_node_t *dv)
+{
+ sdev_plugin_t *spp;
+
+ if (dv->sdev_private == NULL) {
+ spp = sdev_match(dv);
+ if (spp == NULL)
+ return (NULL);
+ } else {
+ spp = dv->sdev_private;
+ }
+
+ if (spp->sp_islegacy)
+ return ((void *)spp->sp_lvtor);
+ else
+ return ((void *)sdev_plugin_validate);
+}
+
+void
+sdev_plugin_nodeready(sdev_node_t *sdp)
+{
+ sdev_plugin_t *spp;
+
+ ASSERT(RW_WRITE_HELD(&sdp->sdev_contents));
+ ASSERT(sdp->sdev_private == NULL);
+
+ spp = sdev_match(sdp);
+ if (spp == NULL)
+ return;
+ if (spp->sp_islegacy)
+ return;
+ sdp->sdev_private = spp;
+ mutex_enter(&spp->sp_lock);
+ spp->sp_nnodes++;
+ mutex_exit(&spp->sp_lock);
+}
+
+int
+sdev_plugin_init(void)
+{
+ sdev_vop_table_t *vtp;
+ fs_operation_def_t *templ;
+
+ sdev_plugin_cache = kmem_cache_create("sdev_plugin",
+ sizeof (sdev_plugin_t), 0, sdev_plugin_cache_constructor,
+ sdev_plugin_cache_destructor, NULL, NULL, NULL, 0);
+ if (sdev_plugin_cache == NULL)
+ return (1);
+ mutex_init(&sdev_plugin_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&sdev_plugin_list, sizeof (sdev_plugin_t),
+ offsetof(sdev_plugin_t, sp_link));
+
+ /*
+ * Register all of the legacy vnops
+ */
+ for (vtp = &vtab[0]; vtp->vt_name != NULL; vtp++)
+ if (sdev_plugin_register_legacy(vtp) != 0)
+ return (1);
+
+ templ = sdev_merge_vtab(sdev_plugin_vnodeops_tbl);
+ if (vn_make_ops("sdev_plugin",
+ (const fs_operation_def_t *)templ,
+ &sdev_plugin_vnops) != 0) {
+ sdev_free_vtab(templ);
+ return (1);
+ }
+
+ sdev_free_vtab(templ);
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_subr.c b/usr/src/uts/common/fs/dev/sdev_subr.c
index 9234cc4a0c..511432453f 100644
--- a/usr/src/uts/common/fs/dev/sdev_subr.c
+++ b/usr/src/uts/common/fs/dev/sdev_subr.c
@@ -150,12 +150,6 @@ vattr_t sdev_vattr_chr = {
kmem_cache_t *sdev_node_cache; /* sdev_node cache */
int devtype; /* fstype */
-/* static */
-static struct vnodeops *sdev_get_vop(struct sdev_node *);
-static void sdev_set_no_negcache(struct sdev_node *);
-static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
-static void sdev_free_vtab(fs_operation_def_t *);
-
static void
sdev_prof_free(struct sdev_node *dv)
{
@@ -313,6 +307,7 @@ sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
/* overwritten for VLNK nodes */
dv->sdev_symlink = NULL;
+ list_link_init(&dv->sdev_plist);
vp = SDEVTOV(dv);
vn_reinit(vp);
@@ -401,6 +396,7 @@ sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
} else {
dv->sdev_nlink = 1;
}
+ sdev_plugin_nodeready(dv);
if (!(SDEV_IS_GLOBAL(dv))) {
dv->sdev_origin = (struct sdev_node *)args;
@@ -497,37 +493,22 @@ sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
return (dv);
}
-/* directory dependent vop table */
-struct sdev_vop_table {
- char *vt_name; /* subdirectory name */
- const fs_operation_def_t *vt_service; /* vnodeops table */
- struct vnodeops *vt_vops; /* constructed vop */
- struct vnodeops **vt_global_vops; /* global container for vop */
- int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */
- int vt_flags;
-};
-
-/*
- * A nice improvement would be to provide a plug-in mechanism
- * for this table instead of a const table.
- */
-static struct sdev_vop_table vtab[] =
-{
- { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
+struct sdev_vop_table vtab[] = {
+ { "pts", devpts_vnodeops_tbl, &devpts_vnodeops, devpts_validate,
SDEV_DYNAMIC | SDEV_VTOR },
- { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate,
+ { "vt", devvt_vnodeops_tbl, &devvt_vnodeops, devvt_validate,
SDEV_DYNAMIC | SDEV_VTOR },
- { "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops,
+ { "zvol", devzvol_vnodeops_tbl, &devzvol_vnodeops,
devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
- { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
+ { "zcons", NULL, NULL, NULL, SDEV_NO_NCACHE },
- { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate,
- SDEV_DYNAMIC | SDEV_VTOR },
+ { "net", devnet_vnodeops_tbl, &devnet_vnodeops, devnet_validate,
+ SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
- { "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops,
+ { "ipnet", devipnet_vnodeops_tbl, &devipnet_vnodeops,
devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE },
/*
@@ -542,132 +523,14 @@ static struct sdev_vop_table vtab[] =
* preventing a mkdir.
*/
- { "lofi", NULL, NULL, NULL, NULL,
+ { "lofi", NULL, NULL, NULL,
SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
- { "rlofi", NULL, NULL, NULL, NULL,
+ { "rlofi", NULL, NULL, NULL,
SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
- { NULL, NULL, NULL, NULL, NULL, 0}
+ { NULL, NULL, NULL, NULL, 0}
};
-/*
- * We need to match off of the sdev_path, not the sdev_name. We are only allowed
- * to exist directly under /dev.
- */
-struct sdev_vop_table *
-sdev_match(struct sdev_node *dv)
-{
- int vlen;
- int i;
- const char *path;
-
- if (strlen(dv->sdev_path) <= 5)
- return (NULL);
-
- if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
- return (NULL);
- path = dv->sdev_path + 5;
-
- for (i = 0; vtab[i].vt_name; i++) {
- if (strcmp(vtab[i].vt_name, path) == 0)
- return (&vtab[i]);
- if (vtab[i].vt_flags & SDEV_SUBDIR) {
- vlen = strlen(vtab[i].vt_name);
- if ((strncmp(vtab[i].vt_name, path,
- vlen - 1) == 0) && path[vlen] == '/')
- return (&vtab[i]);
- }
-
- }
- return (NULL);
-}
-
-/*
- * sets a directory's vnodeops if the directory is in the vtab;
- */
-static struct vnodeops *
-sdev_get_vop(struct sdev_node *dv)
-{
- struct sdev_vop_table *vtp;
- char *path;
-
- path = dv->sdev_path;
- ASSERT(path);
-
- /* gets the relative path to /dev/ */
- path += 5;
-
- /* gets the vtab entry it matches */
- if ((vtp = sdev_match(dv)) != NULL) {
- dv->sdev_flags |= vtp->vt_flags;
- if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
- (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
- dv->sdev_flags |= SDEV_PERSIST;
-
- if (vtp->vt_vops) {
- if (vtp->vt_global_vops)
- *(vtp->vt_global_vops) = vtp->vt_vops;
-
- return (vtp->vt_vops);
- }
-
- if (vtp->vt_service) {
- fs_operation_def_t *templ;
- templ = sdev_merge_vtab(vtp->vt_service);
- if (vn_make_ops(vtp->vt_name,
- (const fs_operation_def_t *)templ,
- &vtp->vt_vops) != 0) {
- cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
- vtp->vt_name);
- /*NOTREACHED*/
- }
- if (vtp->vt_global_vops) {
- *(vtp->vt_global_vops) = vtp->vt_vops;
- }
- sdev_free_vtab(templ);
-
- return (vtp->vt_vops);
- }
-
- return (sdev_vnodeops);
- }
-
- /* child inherits the persistence of the parent */
- if (SDEV_IS_PERSIST(dv->sdev_dotdot))
- dv->sdev_flags |= SDEV_PERSIST;
-
- return (sdev_vnodeops);
-}
-
-static void
-sdev_set_no_negcache(struct sdev_node *dv)
-{
- int i;
- char *path;
-
- ASSERT(dv->sdev_path);
- path = dv->sdev_path + strlen("/dev/");
-
- for (i = 0; vtab[i].vt_name; i++) {
- if (strcmp(vtab[i].vt_name, path) == 0) {
- if (vtab[i].vt_flags & SDEV_NO_NCACHE)
- dv->sdev_flags |= SDEV_NO_NCACHE;
- break;
- }
- }
-}
-
-void *
-sdev_get_vtor(struct sdev_node *dv)
-{
- struct sdev_vop_table *vtp;
-
- vtp = sdev_match(dv);
- if (vtp)
- return ((void *)vtp->vt_vtor);
- else
- return (NULL);
-}
/*
* Build the base root inode
@@ -947,8 +810,11 @@ sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
dv->sdev_path = NULL;
}
- if (!SDEV_IS_GLOBAL(dv))
+ if (!SDEV_IS_GLOBAL(dv)) {
sdev_prof_free(dv);
+ if (dv->sdev_vnode->v_type != VLNK && dv->sdev_origin != NULL)
+ SDEV_RELE(dv->sdev_origin);
+ }
if (SDEVTOV(dv)->v_type == VDIR) {
ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
@@ -962,6 +828,7 @@ sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
(void) memset((void *)&dv->sdev_instance_data, 0,
sizeof (dv->sdev_instance_data));
vn_invalid(SDEVTOV(dv));
+ dv->sdev_private = NULL;
kmem_cache_free(sdev_node_cache, dv);
}
@@ -2944,46 +2811,6 @@ sdev_modctl_devexists(const char *path)
return (error);
}
-extern int sdev_vnodeops_tbl_size;
-
-/*
- * construct a new template with overrides from vtab
- */
-static fs_operation_def_t *
-sdev_merge_vtab(const fs_operation_def_t tab[])
-{
- fs_operation_def_t *new;
- const fs_operation_def_t *tab_entry;
-
- /* make a copy of standard vnode ops table */
- new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
- bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
-
- /* replace the overrides from tab */
- for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
- fs_operation_def_t *std_entry = new;
- while (std_entry->name) {
- if (strcmp(tab_entry->name, std_entry->name) == 0) {
- std_entry->func = tab_entry->func;
- break;
- }
- std_entry++;
- }
- if (std_entry->name == NULL)
- cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
- tab_entry->name);
- }
-
- return (new);
-}
-
-/* free memory allocated by sdev_merge_vtab */
-static void
-sdev_free_vtab(fs_operation_def_t *new)
-{
- kmem_free(new, sdev_vnodeops_tbl_size);
-}
-
/*
* a generic setattr() function
*
diff --git a/usr/src/uts/common/fs/dev/sdev_vfsops.c b/usr/src/uts/common/fs/dev/sdev_vfsops.c
index 00e981ce9c..8de16926cd 100644
--- a/usr/src/uts/common/fs/dev/sdev_vfsops.c
+++ b/usr/src/uts/common/fs/dev/sdev_vfsops.c
@@ -172,7 +172,13 @@ devinit(int fstype, char *name)
if ((devmajor = getudev()) == (major_t)-1) {
cmn_err(CE_WARN, "%s: can't get unique dev", sdev_vfssw.name);
- return (1);
+ return (ENXIO);
+ }
+
+ if (sdev_plugin_init() != 0) {
+ cmn_err(CE_WARN, "%s: failed to set init plugin subsystem",
+ sdev_vfssw.name);
+ return (EIO);
}
/* initialize negative cache */
@@ -349,6 +355,7 @@ sdev_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
ASSERT(sdev_origins);
dv->sdev_flags &= ~SDEV_GLOBAL;
dv->sdev_origin = sdev_origins->sdev_root;
+ SDEV_HOLD(dv->sdev_origin);
} else {
sdev_ncache_setup();
rw_enter(&dv->sdev_contents, RW_WRITER);
@@ -521,3 +528,17 @@ sdev_mntinfo_rele(struct sdev_data *mntinfo)
SDEVTOV(mntinfo->sdev_root)->v_count--;
mutex_exit(&sdev_lock);
}
+
+void
+sdev_mnt_walk(void (*func)(struct sdev_node *, void *), void *arg)
+{
+ struct sdev_data *mntinfo;
+
+ mutex_enter(&sdev_lock);
+ mntinfo = sdev_mntinfo;
+ while (mntinfo != NULL) {
+ func(mntinfo->sdev_root, arg);
+ mntinfo = mntinfo->sdev_next;
+ }
+ mutex_exit(&sdev_lock);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_vnops.c b/usr/src/uts/common/fs/dev/sdev_vnops.c
index 59a3c9f17a..6ce4b0b174 100644
--- a/usr/src/uts/common/fs/dev/sdev_vnops.c
+++ b/usr/src/uts/common/fs/dev/sdev_vnops.c
@@ -22,7 +22,7 @@
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/*
@@ -864,6 +864,9 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred,
}
}
+ if (error == 0)
+ i_ddi_di_cache_invalidate();
+
return (error);
}
@@ -1188,6 +1191,7 @@ sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva,
sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
if (SDEV_IS_GLOBAL(parent))
atomic_inc_ulong(&parent->sdev_gdir_gen);
+ i_ddi_di_cache_invalidate();
/* wake up other threads blocked on looking up this node */
mutex_enter(&self->sdev_lookup_lock);
@@ -1260,6 +1264,7 @@ sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
if (SDEV_IS_GLOBAL(parent))
atomic_inc_ulong(&parent->sdev_gdir_gen);
+ i_ddi_di_cache_invalidate();
/* wake up other threads blocked on looking up this node */
mutex_enter(&self->sdev_lookup_lock);
@@ -1375,6 +1380,9 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
}
+ if (error == 0)
+ i_ddi_di_cache_invalidate();
+
return (error);
}
diff --git a/usr/src/uts/common/fs/dev/sdev_zvolops.c b/usr/src/uts/common/fs/dev/sdev_zvolops.c
index f75d5c3c4e..a52606aeda 100644
--- a/usr/src/uts/common/fs/dev/sdev_zvolops.c
+++ b/usr/src/uts/common/fs/dev/sdev_zvolops.c
@@ -472,8 +472,10 @@ devzvol_create_pool_dirs(struct vnode *dvp)
ASSERT(dvp->v_count > 0);
rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
NULL, kcred, NULL, 0, NULL);
- /* should either work, or not be visible from a zone */
- ASSERT(rc == 0 || rc == ENOENT);
+ /*
+ * should either work or we should get an error if this should
+ * not be visible from the zone, or disallowed in the zone
+ */
if (rc == 0)
VN_RELE(vp);
pools++;
diff --git a/usr/src/uts/common/fs/dnlc.c b/usr/src/uts/common/fs/dnlc.c
index 25327d2852..c949117da6 100644
--- a/usr/src/uts/common/fs/dnlc.c
+++ b/usr/src/uts/common/fs/dnlc.c
@@ -921,50 +921,6 @@ dnlc_fs_purge1(vnodeops_t *vop)
}
/*
- * Perform a reverse lookup in the DNLC. This will find the first occurrence of
- * the vnode. If successful, it will return the vnode of the parent, and the
- * name of the entry in the given buffer. If it cannot be found, or the buffer
- * is too small, then it will return NULL. Note that this is a highly
- * inefficient function, since the DNLC is constructed solely for forward
- * lookups.
- */
-vnode_t *
-dnlc_reverse_lookup(vnode_t *vp, char *buf, size_t buflen)
-{
- nc_hash_t *nch;
- ncache_t *ncp;
- vnode_t *pvp;
-
- if (!doingcache)
- return (NULL);
-
- for (nch = nc_hash; nch < &nc_hash[nc_hashsz]; nch++) {
- mutex_enter(&nch->hash_lock);
- ncp = nch->hash_next;
- while (ncp != (ncache_t *)nch) {
- /*
- * We ignore '..' entries since it can create
- * confusion and infinite loops.
- */
- if (ncp->vp == vp && !(ncp->namlen == 2 &&
- 0 == bcmp(ncp->name, "..", 2)) &&
- ncp->namlen < buflen) {
- bcopy(ncp->name, buf, ncp->namlen);
- buf[ncp->namlen] = '\0';
- pvp = ncp->dp;
- /* VN_HOLD 2 of 2 in this file */
- VN_HOLD_CALLER(pvp);
- mutex_exit(&nch->hash_lock);
- return (pvp);
- }
- ncp = ncp->hash_next;
- }
- mutex_exit(&nch->hash_lock);
- }
-
- return (NULL);
-}
-/*
* Utility routine to search for a cache entry. Return the
* ncache entry if found, NULL otherwise.
*/
diff --git a/usr/src/uts/common/fs/fem.c b/usr/src/uts/common/fs/fem.c
index b4e28cc860..5f524def30 100644
--- a/usr/src/uts/common/fs/fem.c
+++ b/usr/src/uts/common/fs/fem.c
@@ -23,6 +23,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/types.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
@@ -33,11 +37,12 @@
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
-
#include <sys/fem.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/vfs_opreg.h>
+#include <sys/stack.h>
+#include <sys/archsystm.h>
#define NNODES_DEFAULT 8 /* Default number of nodes in a fem_list */
/*
@@ -291,6 +296,536 @@ _op_find(femarg_t *ap, void **fp, int offs0, int offs1)
}
#endif
+/*
+ * File event monitoring handoffs
+ *
+ * File event monitoring relies on being able to inject stack frames between
+ * vnode consumers and the underlying file systems. This becomes problematic
+ * when there exist many monitors, as kernel stack depth is finite. The model
+ * very much encodes this injected frame: the flow of control deliberately
+ * lies with the monitor, not with the monitoring system. While we could
+ * conceivably address this by allowing each subsystem to install at most
+ * one monitor per vnode (and impose on subsystems that they handle any
+ * of their own consumer multiplexing internally), this in fact exports a
+ * substantial amount of run-time complexity to deal with an uncommon case
+ * (and, it must be said, assumes a small number of consuming subsystems).
+ * To allow our abstraction to remain clean, we instead check our remaining
+ * stack in every vnext_*() call; if the amount of stack remaining is lower
+ * than a threshold (fem_stack_needed), we call thread_splitstack() to carry
+ * on the execution of the monitors and the underlying vnode operation on a
+ * split stack. Because we can only pass a single argument to our split stack
+ * function, we must marshal our arguments, the mechanics of which are somewhat
+ * ornate in terms of the code: to marshal in a type-safe manner, we define a
+ * baton that is a union of payload structures for each kind of operation,
+ * loading the per-operation payload explicitly and calling into common handoff
+ * code that itself calls thread_splitstack(). The function passed to
+ * thread_splitstack() is a per-entry point function that continues monitor
+ * processing given the specified (marshalled) arguments. While this method
+ * is a little verbose to implement, it has the advantage of being relatively
+ * robust (that is, broadly type-safe) while imposing minimal burden on each
+ * vnext_*() entry point.
+ *
+ * In terms of the implementation:
+ *
+ * - The FEM_BATON_n macros define the per-entry point baton structures
+ * - The fem_baton_payload_t contains the union of these structures
+ * - The FEM_VNEXTn_DECL macros declare the post-handoff entry point
+ * - The FEM_VNEXTn macros constitute the per-handoff entry point
+ *
+ * Note that we don't use variadic macros -- we define a variant of these
+ * macros for each of our relevant argument counts. This may seem overly
+ * explicit, but it is deliberate: the object here is to minimize the
+ * future maintenance burden by minimizing the likelihood of introduced
+ * error -- not to minimize the number of characters in this source file.
+ */
+
+#ifndef STACK_GROWTH_DOWN
+#error Downward stack growth assumed.
+#endif
+
+int fem_stack_toodeep;
+uintptr_t fem_stack_needed = 8 * 1024;
+size_t fem_handoff_stacksize = 128 * 1024;
+
+#define FEM_TOODEEP() (STACK_BIAS + (uintptr_t)getfp() - \
+ (uintptr_t)curthread->t_stkbase < fem_stack_needed)
+
+#define FEM_BATON_1(what, t0, l0) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ } fb_##what
+
+#define FEM_BATON_2(what, t0, l0, t1, l1) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ } fb_##what
+
+#define FEM_BATON_3(what, t0, l0, t1, l1, t2, l2) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ } fb_##what
+
+#define FEM_BATON_4(what, t0, l0, t1, l1, t2, l2, t3, l3) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ } fb_##what
+
+#define FEM_BATON_5(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ } fb_##what
+
+#define FEM_BATON_6(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ } fb_##what
+
+#define FEM_BATON_8(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5, \
+ t6, l6, t7, l7) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ t6 fb_##what##_##l6; \
+ t7 fb_##what##_##l7; \
+ } fb_##what
+
+#define FEM_BATON_9(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5, \
+ t6, l6, t7, l7, t8, l8) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ t6 fb_##what##_##l6; \
+ t7 fb_##what##_##l7; \
+ t8 fb_##what##_##l8; \
+ } fb_##what
+
+typedef union {
+ FEM_BATON_2(open, int, mode, cred_t *, cr);
+ FEM_BATON_4(close, int, flag, int, count,
+ offset_t, offset, cred_t *, cr);
+ FEM_BATON_3(read, uio_t *, uiop, int, ioflag, cred_t *, cr);
+ FEM_BATON_3(write, uio_t *, uiop, int, ioflag, cred_t *, cr);
+ FEM_BATON_5(ioctl, int, cmd, intptr_t, arg,
+ int, flag, cred_t *, cr, int *, rvalp);
+ FEM_BATON_3(setfl, int, oflags, int, nflags, cred_t *, cr);
+ FEM_BATON_3(getattr, vattr_t *, vap, int, flags, cred_t *, cr);
+ FEM_BATON_3(setattr, vattr_t *, vap, int, flags, cred_t *, cr);
+ FEM_BATON_3(access, int, mode, int, flags, cred_t *, cr);
+ FEM_BATON_8(lookup, char *, nm, vnode_t **, vpp,
+ pathname_t *, pnp, int, flags, vnode_t *, rdir,
+ cred_t *, cr, int *, direntflags, pathname_t *, realpnp);
+ FEM_BATON_8(create, char *, name, vattr_t *, vap,
+ vcexcl_t, excl, int, mode, vnode_t **, vpp,
+ cred_t *, cr, int, flag, vsecattr_t *, vsecp);
+ FEM_BATON_3(remove, char *, nm, cred_t *, cr, int, flags);
+ FEM_BATON_4(link, vnode_t *, svp, char *, tnm,
+ cred_t *, cr, int, flags);
+ FEM_BATON_5(rename, char *, snm, vnode_t *, tdvp,
+ char *, tnm, cred_t *, cr, int, flags);
+ FEM_BATON_6(mkdir, char *, dirname, vattr_t *, vap,
+ vnode_t **, vpp, cred_t *, cr, int, flags,
+ vsecattr_t *, vsecp);
+ FEM_BATON_4(rmdir, char *, nm, vnode_t *, cdir,
+ cred_t *, cr, int, flags);
+ FEM_BATON_4(readdir, uio_t *, uiop, cred_t *, cr,
+ int *, eofp, int, flags);
+ FEM_BATON_5(symlink, char *, linkname, vattr_t *, vap,
+ char *, target, cred_t *, cr, int, flags);
+ FEM_BATON_2(readlink, uio_t *, uiop, cred_t *, cr);
+ FEM_BATON_2(fsync, int, syncflag, cred_t *, cr);
+ FEM_BATON_1(inactive, cred_t *, cr);
+ FEM_BATON_1(fid, fid_t *, fidp);
+ FEM_BATON_1(rwlock, int, write_lock);
+ FEM_BATON_1(rwunlock, int, write_lock);
+ FEM_BATON_2(seek, offset_t, ooff, offset_t *, noffp);
+ FEM_BATON_1(cmp, vnode_t *, vp2);
+ FEM_BATON_6(frlock, int, cmd, struct flock64 *, bfp,
+ int, flag, offset_t, offset, struct flk_callback *, flk_cbp,
+ cred_t *, cr);
+ FEM_BATON_5(space, int, cmd, struct flock64 *, bfp,
+ int, flag, offset_t, offset, cred_t *, cr);
+ FEM_BATON_1(realvp, vnode_t **, vpp);
+ FEM_BATON_9(getpage, offset_t, off, size_t, len,
+ uint_t *, protp, struct page **, plarr, size_t, plsz,
+ struct seg *, seg, caddr_t, addr, enum seg_rw, rw,
+ cred_t *, cr);
+ FEM_BATON_4(putpage, offset_t, off, size_t, len,
+ int, flags, cred_t *, cr);
+ FEM_BATON_8(map, offset_t, off, struct as *, as,
+ caddr_t *, addrp, size_t, len, uchar_t, prot,
+ uchar_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_8(addmap, offset_t, off, struct as *, as,
+ caddr_t, addr, size_t, len, uchar_t, prot,
+ uchar_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_8(delmap, offset_t, off, struct as *, as,
+ caddr_t, addr, size_t, len, uint_t, prot,
+ uint_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_4(poll, short, events, int, anyyet,
+ short *, reventsp, struct pollhead **, phpp);
+ FEM_BATON_3(dump, caddr_t, addr, offset_t, lbdn, offset_t, dblks);
+ FEM_BATON_3(pathconf, int, cmd, ulong_t *, valp, cred_t *, cr);
+ FEM_BATON_5(pageio, struct page *, pp, u_offset_t, io_off,
+ size_t, io_len, int, flags, cred_t *, cr);
+ FEM_BATON_2(dumpctl, int, action, offset_t *, blkp);
+ FEM_BATON_4(dispose, struct page *, pp, int, flag,
+ int, dn, cred_t *, cr);
+ FEM_BATON_3(setsecattr, vsecattr_t *, vsap, int, flag, cred_t *, cr);
+ FEM_BATON_3(getsecattr, vsecattr_t *, vsap, int, flag, cred_t *, cr);
+ FEM_BATON_4(shrlock, int, cmd, struct shrlock *, shr,
+ int, flag, cred_t *, cr);
+ FEM_BATON_3(vnevent, vnevent_t, vnevent, vnode_t *, dvp, char *, cname);
+ FEM_BATON_3(reqzcbuf, enum uio_rw, ioflag,
+ xuio_t *, xuiop, cred_t *, cr);
+ FEM_BATON_2(retzcbuf, xuio_t *, xuiop, cred_t *, cr);
+} fem_baton_payload_t;
+
+typedef struct {
+ fem_baton_payload_t fb_payload;
+ int (*fb_func)();
+ void (*fb_handoff)();
+ int fb_rval;
+} fem_baton_t;
+
+static int
+fem_handoff(fem_baton_t *bp)
+{
+ fem_stack_toodeep++;
+ thread_splitstack(bp->fb_handoff, bp, fem_handoff_stacksize);
+
+ return (bp->fb_rval);
+}
+
+#define FEM_VNEXT3_DECL(what, a0, a1, a2) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2); \
+}
+
+#define FEM_VNEXT4_DECL(what, a0, a1, a2, a3) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3); \
+}
+
+#define FEM_VNEXT5_DECL(what, a0, a1, a2, a3, a4) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4); \
+}
+
+#define FEM_VNEXT6_DECL(what, a0, a1, a2, a3, a4, a5) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5); \
+}
+
+#define FEM_VNEXT7_DECL(what, a0, a1, a2, a3, a4, a5, a6) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6); \
+}
+
+#define FEM_VNEXT8_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7); \
+}
+
+#define FEM_VNEXT10_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7, \
+ bp->fb_payload.fb_##what.fb_##what##_##a8, \
+ bp->fb_payload.fb_##what.fb_##what##_##a9); \
+}
+
+#define FEM_VNEXT11_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7, \
+ bp->fb_payload.fb_##what.fb_##what##_##a8, \
+ bp->fb_payload.fb_##what.fb_##what##_##a9, \
+ bp->fb_payload.fb_##what.fb_##what##_##a10); \
+}
+
+#define FEM_VNEXT3(what, func, a0, a1, a2) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2))
+
+#define FEM_VNEXT4(what, func, a0, a1, a2, a3) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3))
+
+#define FEM_VNEXT5(what, func, a0, a1, a2, a3, a4) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4))
+
+#define FEM_VNEXT6(what, func, a0, a1, a2, a3, a4, a5) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5))
+
+#define FEM_VNEXT7(what, func, a0, a1, a2, a3, a4, a5, a6) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6))
+
+#define FEM_VNEXT8(what, func, a0, a1, a2, a3, a4, a5, a6, a7) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7))
+
+#define FEM_VNEXT10(what, func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_payload.fb_##what.fb_##what##_##a8 = a8; \
+ baton->fb_payload.fb_##what.fb_##what##_##a9 = a9; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9))
+
+#define FEM_VNEXT11(what, func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_payload.fb_##what.fb_##what##_##a8 = a8; \
+ baton->fb_payload.fb_##what.fb_##what##_##a9 = a9; \
+ baton->fb_payload.fb_##what.fb_##what##_##a10 = a10; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10))
+
static fem_t *
fem_alloc()
{
@@ -2036,10 +2571,60 @@ static struct fs_operation_def fshead_vfs_spec[] = {
* 5. Return by invoking the base operation with the base object.
*
* for each classification, there needs to be at least one "next" operation
- * for each "head"operation.
- *
+ * for each "head" operation. Note that we also use the FEM_VNEXTn_DECL macros
+ * to define the function to run when the stack is split; see the discussion
+ * on "File event monitoring handoffs", above.
*/
+FEM_VNEXT4_DECL(open, arg0, mode, cr, ct)
+FEM_VNEXT6_DECL(close, arg0, flag, count, offset, cr, ct)
+FEM_VNEXT5_DECL(read, arg0, uiop, ioflag, cr, ct)
+FEM_VNEXT5_DECL(write, arg0, uiop, ioflag, cr, ct)
+FEM_VNEXT7_DECL(ioctl, arg0, cmd, arg, flag, cr, rvalp, ct)
+FEM_VNEXT5_DECL(setfl, arg0, oflags, nflags, cr, ct)
+FEM_VNEXT5_DECL(getattr, arg0, vap, flags, cr, ct)
+FEM_VNEXT5_DECL(setattr, arg0, vap, flags, cr, ct)
+FEM_VNEXT5_DECL(access, arg0, mode, flags, cr, ct)
+FEM_VNEXT10_DECL(lookup, arg0, nm, vpp, pnp, flags, rdir,
+ cr, ct, direntflags, realpnp)
+FEM_VNEXT10_DECL(create, arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp)
+FEM_VNEXT5_DECL(remove, arg0, nm, cr, ct, flags)
+FEM_VNEXT6_DECL(link, arg0, svp, tnm, cr, ct, flags)
+FEM_VNEXT7_DECL(rename, arg0, snm, tdvp, tnm, cr, ct, flags)
+FEM_VNEXT8_DECL(mkdir, arg0, dirname, vap, vpp, cr, ct, flags, vsecp)
+FEM_VNEXT6_DECL(rmdir, arg0, nm, cdir, cr, ct, flags)
+FEM_VNEXT6_DECL(readdir, arg0, uiop, cr, eofp, ct, flags)
+FEM_VNEXT7_DECL(symlink, arg0, linkname, vap, target, cr, ct, flags)
+FEM_VNEXT4_DECL(readlink, arg0, uiop, cr, ct)
+FEM_VNEXT4_DECL(fsync, arg0, syncflag, cr, ct)
+FEM_VNEXT3_DECL(fid, arg0, fidp, ct)
+FEM_VNEXT3_DECL(rwlock, arg0, write_lock, ct)
+FEM_VNEXT4_DECL(seek, arg0, ooff, noffp, ct)
+FEM_VNEXT3_DECL(cmp, arg0, vp2, ct)
+FEM_VNEXT8_DECL(frlock, arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct)
+FEM_VNEXT7_DECL(space, arg0, cmd, bfp, flag, offset, cr, ct)
+FEM_VNEXT3_DECL(realvp, arg0, vpp, ct)
+FEM_VNEXT11_DECL(getpage, arg0, off, len, protp, plarr, plsz,
+ seg, addr, rw, cr, ct)
+FEM_VNEXT6_DECL(putpage, arg0, off, len, flags, cr, ct)
+FEM_VNEXT10_DECL(map, arg0, off, as, addrp, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT10_DECL(addmap, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT10_DECL(delmap, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT6_DECL(poll, arg0, events, anyyet, reventsp, phpp, ct)
+FEM_VNEXT5_DECL(dump, arg0, addr, lbdn, dblks, ct)
+FEM_VNEXT5_DECL(pathconf, arg0, cmd, valp, cr, ct)
+FEM_VNEXT7_DECL(pageio, arg0, pp, io_off, io_len, flags, cr, ct)
+FEM_VNEXT4_DECL(dumpctl, arg0, action, blkp, ct)
+FEM_VNEXT5_DECL(setsecattr, arg0, vsap, flag, cr, ct)
+FEM_VNEXT5_DECL(getsecattr, arg0, vsap, flag, cr, ct)
+FEM_VNEXT6_DECL(shrlock, arg0, cmd, shr, flag, cr, ct)
+FEM_VNEXT5_DECL(vnevent, arg0, vnevent, dvp, cname, ct)
+FEM_VNEXT5_DECL(reqzcbuf, arg0, ioflag, xuiop, cr, ct)
+FEM_VNEXT4_DECL(retzcbuf, arg0, xuiop, cr, ct)
+
int
vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
{
@@ -2051,7 +2636,7 @@ vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_open, femop_open);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, mode, cr, ct));
+ FEM_VNEXT4(open, func, arg0, mode, cr, ct);
}
int
@@ -2066,7 +2651,7 @@ vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_close, femop_close);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, flag, count, offset, cr, ct));
+ FEM_VNEXT6(close, func, arg0, flag, count, offset, cr, ct);
}
int
@@ -2081,7 +2666,7 @@ vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_read, femop_read);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, ioflag, cr, ct));
+ FEM_VNEXT5(read, func, arg0, uiop, ioflag, cr, ct);
}
int
@@ -2096,7 +2681,7 @@ vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_write, femop_write);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, ioflag, cr, ct));
+ FEM_VNEXT5(write, func, arg0, uiop, ioflag, cr, ct);
}
int
@@ -2111,7 +2696,7 @@ vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_ioctl, femop_ioctl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, arg, flag, cr, rvalp, ct));
+ FEM_VNEXT7(ioctl, func, arg0, cmd, arg, flag, cr, rvalp, ct);
}
int
@@ -2126,7 +2711,7 @@ vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setfl, femop_setfl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, oflags, nflags, cr, ct));
+ FEM_VNEXT5(setfl, func, arg0, oflags, nflags, cr, ct);
}
int
@@ -2141,7 +2726,7 @@ vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_getattr, femop_getattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vap, flags, cr, ct));
+ FEM_VNEXT5(getattr, func, arg0, vap, flags, cr, ct);
}
int
@@ -2156,7 +2741,7 @@ vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setattr, femop_setattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vap, flags, cr, ct));
+ FEM_VNEXT5(setattr, func, arg0, vap, flags, cr, ct);
}
int
@@ -2171,7 +2756,7 @@ vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_access, femop_access);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, mode, flags, cr, ct));
+ FEM_VNEXT5(access, func, arg0, mode, flags, cr, ct);
}
int
@@ -2187,8 +2772,8 @@ vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp,
vsop_find(vf, &func, int, &arg0, vop_lookup, femop_lookup);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct,
- direntflags, realpnp));
+ FEM_VNEXT10(lookup, func, arg0, nm, vpp, pnp, flags, rdir, cr, ct,
+ direntflags, realpnp);
}
int
@@ -2204,7 +2789,8 @@ vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
vsop_find(vf, &func, int, &arg0, vop_create, femop_create);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp));
+ FEM_VNEXT10(create, func, arg0, name, vap, excl,
+ mode, vpp, cr, flag, ct, vsecp);
}
int
@@ -2219,7 +2805,7 @@ vnext_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
vsop_find(vf, &func, int, &arg0, vop_remove, femop_remove);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, cr, ct, flags));
+ FEM_VNEXT5(remove, func, arg0, nm, cr, ct, flags);
}
int
@@ -2234,7 +2820,7 @@ vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_link, femop_link);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, svp, tnm, cr, ct, flags));
+ FEM_VNEXT6(link, func, arg0, svp, tnm, cr, ct, flags);
}
int
@@ -2249,7 +2835,7 @@ vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_rename, femop_rename);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, snm, tdvp, tnm, cr, ct, flags));
+ FEM_VNEXT7(rename, func, arg0, snm, tdvp, tnm, cr, ct, flags);
}
int
@@ -2264,7 +2850,7 @@ vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
vsop_find(vf, &func, int, &arg0, vop_mkdir, femop_mkdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp));
+ FEM_VNEXT8(mkdir, func, arg0, dirname, vap, vpp, cr, ct, flags, vsecp);
}
int
@@ -2279,7 +2865,7 @@ vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_rmdir, femop_rmdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, cdir, cr, ct, flags));
+ FEM_VNEXT6(rmdir, func, arg0, nm, cdir, cr, ct, flags);
}
int
@@ -2294,7 +2880,7 @@ vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
vsop_find(vf, &func, int, &arg0, vop_readdir, femop_readdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, cr, eofp, ct, flags));
+ FEM_VNEXT6(readdir, func, arg0, uiop, cr, eofp, ct, flags);
}
int
@@ -2309,7 +2895,7 @@ vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
vsop_find(vf, &func, int, &arg0, vop_symlink, femop_symlink);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, linkname, vap, target, cr, ct, flags));
+ FEM_VNEXT7(symlink, func, arg0, linkname, vap, target, cr, ct, flags);
}
int
@@ -2323,7 +2909,7 @@ vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_readlink, femop_readlink);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, cr, ct));
+ FEM_VNEXT4(readlink, func, arg0, uiop, cr, ct);
}
int
@@ -2337,7 +2923,7 @@ vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_fsync, femop_fsync);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, syncflag, cr, ct));
+ FEM_VNEXT4(fsync, func, arg0, syncflag, cr, ct);
}
void
@@ -2365,7 +2951,7 @@ vnext_fid(femarg_t *vf, fid_t *fidp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_fid, femop_fid);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, fidp, ct));
+ FEM_VNEXT3(fid, func, arg0, fidp, ct);
}
int
@@ -2379,7 +2965,7 @@ vnext_rwlock(femarg_t *vf, int write_lock, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_rwlock, femop_rwlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, write_lock, ct));
+ FEM_VNEXT3(rwlock, func, arg0, write_lock, ct);
}
void
@@ -2407,7 +2993,7 @@ vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_seek, femop_seek);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, ooff, noffp, ct));
+ FEM_VNEXT4(seek, func, arg0, ooff, noffp, ct);
}
int
@@ -2421,7 +3007,7 @@ vnext_cmp(femarg_t *vf, vnode_t *vp2, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_cmp, femop_cmp);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vp2, ct));
+ FEM_VNEXT3(cmp, func, arg0, vp2, ct);
}
int
@@ -2437,7 +3023,7 @@ vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
vsop_find(vf, &func, int, &arg0, vop_frlock, femop_frlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+ FEM_VNEXT8(frlock, func, arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct);
}
int
@@ -2452,7 +3038,7 @@ vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
vsop_find(vf, &func, int, &arg0, vop_space, femop_space);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, bfp, flag, offset, cr, ct));
+ FEM_VNEXT7(space, func, arg0, cmd, bfp, flag, offset, cr, ct);
}
int
@@ -2466,7 +3052,7 @@ vnext_realvp(femarg_t *vf, vnode_t **vpp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_realvp, femop_realvp);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vpp, ct));
+ FEM_VNEXT3(realvp, func, arg0, vpp, ct);
}
int
@@ -2482,8 +3068,8 @@ vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp,
vsop_find(vf, &func, int, &arg0, vop_getpage, femop_getpage);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw,
- cr, ct));
+ FEM_VNEXT11(getpage, func, arg0, off, len, protp,
+ plarr, plsz, seg, addr, rw, cr, ct);
}
int
@@ -2498,7 +3084,7 @@ vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags,
vsop_find(vf, &func, int, &arg0, vop_putpage, femop_putpage);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, len, flags, cr, ct));
+ FEM_VNEXT6(putpage, func, arg0, off, len, flags, cr, ct);
}
int
@@ -2514,8 +3100,8 @@ vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
vsop_find(vf, &func, int, &arg0, vop_map, femop_map);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addrp, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(map, func, arg0, off, as, addrp, len, prot, maxprot, flags,
+ cr, ct);
}
int
@@ -2531,8 +3117,8 @@ vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
vsop_find(vf, &func, int, &arg0, vop_addmap, femop_addmap);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(addmap, func, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct);
}
int
@@ -2548,8 +3134,8 @@ vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
vsop_find(vf, &func, int, &arg0, vop_delmap, femop_delmap);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(delmap, func, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct);
}
int
@@ -2564,7 +3150,7 @@ vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp,
vsop_find(vf, &func, int, &arg0, vop_poll, femop_poll);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, events, anyyet, reventsp, phpp, ct));
+ FEM_VNEXT6(poll, func, arg0, events, anyyet, reventsp, phpp, ct);
}
int
@@ -2579,7 +3165,7 @@ vnext_dump(femarg_t *vf, caddr_t addr, offset_t lbdn, offset_t dblks,
vsop_find(vf, &func, int, &arg0, vop_dump, femop_dump);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, addr, lbdn, dblks, ct));
+ FEM_VNEXT5(dump, func, arg0, addr, lbdn, dblks, ct);
}
int
@@ -2594,7 +3180,7 @@ vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_pathconf, femop_pathconf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, valp, cr, ct));
+ FEM_VNEXT5(pathconf, func, arg0, cmd, valp, cr, ct);
}
int
@@ -2609,7 +3195,7 @@ vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off,
vsop_find(vf, &func, int, &arg0, vop_pageio, femop_pageio);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, pp, io_off, io_len, flags, cr, ct));
+ FEM_VNEXT7(pageio, func, arg0, pp, io_off, io_len, flags, cr, ct);
}
int
@@ -2623,7 +3209,7 @@ vnext_dumpctl(femarg_t *vf, int action, offset_t *blkp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_dumpctl, femop_dumpctl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, action, blkp, ct));
+ FEM_VNEXT4(dumpctl, func, arg0, action, blkp, ct);
}
void
@@ -2653,7 +3239,7 @@ vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setsecattr, femop_setsecattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vsap, flag, cr, ct));
+ FEM_VNEXT5(setsecattr, func, arg0, vsap, flag, cr, ct);
}
int
@@ -2668,7 +3254,7 @@ vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_getsecattr, femop_getsecattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vsap, flag, cr, ct));
+ FEM_VNEXT5(getsecattr, func, arg0, vsap, flag, cr, ct);
}
int
@@ -2683,7 +3269,7 @@ vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag,
vsop_find(vf, &func, int, &arg0, vop_shrlock, femop_shrlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, shr, flag, cr, ct));
+ FEM_VNEXT6(shrlock, func, arg0, cmd, shr, flag, cr, ct);
}
int
@@ -2698,7 +3284,7 @@ vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname,
vsop_find(vf, &func, int, &arg0, vop_vnevent, femop_vnevent);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vnevent, dvp, cname, ct));
+ FEM_VNEXT5(vnevent, func, arg0, vnevent, dvp, cname, ct);
}
int
@@ -2713,7 +3299,7 @@ vnext_reqzcbuf(femarg_t *vf, enum uio_rw ioflag, xuio_t *xuiop, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_reqzcbuf, femop_reqzcbuf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, ioflag, xuiop, cr, ct));
+ FEM_VNEXT5(reqzcbuf, func, arg0, ioflag, xuiop, cr, ct);
}
int
@@ -2727,7 +3313,7 @@ vnext_retzcbuf(femarg_t *vf, xuio_t *xuiop, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_retzcbuf, femop_retzcbuf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, xuiop, cr, ct));
+ FEM_VNEXT4(retzcbuf, func, arg0, xuiop, cr, ct);
}
int
diff --git a/usr/src/uts/common/fs/fifofs/fifosubr.c b/usr/src/uts/common/fs/fifofs/fifosubr.c
index 6e56000ffe..56204c6741 100644
--- a/usr/src/uts/common/fs/fifofs/fifosubr.c
+++ b/usr/src/uts/common/fs/fifofs/fifosubr.c
@@ -614,9 +614,12 @@ fifo_stropen(vnode_t **vpp, int flag, cred_t *crp, int dotwist, int lockheld)
/*
* The other end of the pipe is almost closed so
* reject any other open on this end of the pipe
- * This only happens with a pipe mounted under namefs
+ * This normally only happens with a pipe mounted under namefs, but
+ * we can also see an open via proc/fd, which should still succeed.
+ * To indicate the proc/fd case the FKLYR flag is passed.
*/
- if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE)) {
+ if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE) &&
+ (flag & FKLYR) == 0) {
fifo_cleanup(oldvp, flag);
cv_broadcast(&fnp->fn_wait_cv);
if (!lockheld)
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c
new file mode 100644
index 0000000000..05ee2c6e09
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c
@@ -0,0 +1,640 @@
+/*
+ * 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 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/policy.h>
+#include <sys/fs/hyprlofs_info.h>
+
+static int hldir_make_hlnode(hlnode_t *, hlfsmount_t *, vattr_t *, enum de_op,
+ vnode_t *, hlnode_t **, cred_t *);
+static int hldiraddentry(hlnode_t *, hlnode_t *, char *);
+
+
+#define HL_HASH_SIZE 8192 /* must be power of 2 */
+#define HL_MUTEX_SIZE 64
+
+static hldirent_t *hl_hashtable[HL_HASH_SIZE];
+static kmutex_t hl_hashmutex[HL_MUTEX_SIZE];
+
+#define HL_HASH_INDEX(a) ((a) & (HL_HASH_SIZE-1))
+#define HL_MUTEX_INDEX(a) ((a) & (HL_MUTEX_SIZE-1))
+
+#define HYPRLOFS_HASH(tp, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(tp) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ }
+
+void
+hyprlofs_hash_init(void)
+{
+ int ix;
+
+ for (ix = 0; ix < HL_MUTEX_SIZE; ix++)
+ mutex_init(&hl_hashmutex[ix], NULL, MUTEX_DEFAULT, NULL);
+}
+
+static void
+hyprlofs_hash_in(hldirent_t *h)
+{
+ uint_t hash;
+ hldirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ HYPRLOFS_HASH(h->hld_parent, h->hld_name, hash);
+ h->hld_hash = hash;
+ prevpp = &hl_hashtable[HL_HASH_INDEX(hash)];
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ h->hld_link = *prevpp;
+ *prevpp = h;
+ mutex_exit(hmtx);
+}
+
+/* Remove hldirent *h from the hash list. */
+static void
+hyprlofs_hash_out(hldirent_t *h)
+{
+ uint_t hash;
+ hldirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ hash = h->hld_hash;
+ prevpp = &hl_hashtable[HL_HASH_INDEX(hash)];
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ while (*prevpp != h)
+ prevpp = &(*prevpp)->hld_link;
+ *prevpp = h->hld_link;
+ mutex_exit(hmtx);
+}
+
+static hldirent_t *
+hyprlofs_hash_lookup(char *name, hlnode_t *parent, uint_t hold,
+ hlnode_t **found)
+{
+ hldirent_t *l;
+ uint_t hash;
+ kmutex_t *hmtx;
+ hlnode_t *hnp;
+
+ HYPRLOFS_HASH(parent, name, hash);
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ l = hl_hashtable[HL_HASH_INDEX(hash)];
+ while (l) {
+ if (l->hld_hash == hash && l->hld_parent == parent &&
+ strcmp(l->hld_name, name) == 0) {
+ /*
+ * Ensure that the hlnode that we put a hold on is the
+ * same one that we pass back. Thus the temp. var
+ * hnp is necessary.
+ */
+ hnp = l->hld_hlnode;
+ if (hold) {
+ ASSERT(hnp);
+ hlnode_hold(hnp);
+ }
+ if (found)
+ *found = hnp;
+ mutex_exit(hmtx);
+ return (l);
+ } else {
+ l = l->hld_link;
+ }
+ }
+ mutex_exit(hmtx);
+ return (NULL);
+}
+
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * The calling thread can't hold the write version of the rwlock for the
+ * directory being searched
+ *
+ * On success *foundtp points to the found hlnode with its vnode held.
+ */
+int
+hyprlofs_dirlookup(hlnode_t *parent, char *name, hlnode_t **foundtp, cred_t *cr)
+{
+ int error;
+
+ *foundtp = NULL;
+ if (parent->hln_type != VDIR)
+ return (ENOTDIR);
+
+ if ((error = hyprlofs_taccess(parent, VEXEC, cr)))
+ return (error);
+
+ if (*name == '\0') {
+ hlnode_hold(parent);
+ *foundtp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name. We need the lock
+ * protecting the hln_dir list so that it doesn't change out from
+ * underneath us. hyprlofs_hash_lookup() will pass back the hlnode
+ * with a hold on it.
+ */
+ if (hyprlofs_hash_lookup(name, parent, 1, foundtp) != NULL) {
+ ASSERT(*foundtp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Enter a directory entry (either a file or subdir, depending on op) for
+ * 'name' and 'hp' into directory 'dir'
+ */
+int
+hyprlofs_direnter(
+ hlfsmount_t *hm,
+ hlnode_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ vnode_t *realvp, /* real vnode */
+ vattr_t *va,
+ hlnode_t **hpp, /* return hlnode */
+ cred_t *cr)
+{
+ hldirent_t *hdp;
+ hlnode_t *found = NULL;
+ hlnode_t *hp;
+ int error = 0;
+ char *s;
+
+ /* hln_rwlock is held to serialize direnter and dirdeletes */
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ /* Don't allow '/' characters in pathname component */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("hyprlofs_direnter: NULL name");
+
+ /*
+ * This might be a "dangling detached directory". It could have been
+ * removed, but a reference to it kept in u_cwd. Don't bother searching
+ * it, and with any luck the user will get tired of dealing with us and
+ * cd to some absolute pathway. This is in ufs, too.
+ */
+ if (dir->hln_nlink == 0) {
+ return (ENOENT);
+ }
+
+ /* Search for the entry. Return "found" if it exists. */
+ hdp = hyprlofs_hash_lookup(name, dir, 1, &found);
+
+ if (hdp) {
+ ASSERT(found);
+ switch (op) {
+ case DE_CREATE:
+ case DE_MKDIR:
+ if (hpp) {
+ *hpp = found;
+ error = EEXIST;
+ } else {
+ hlnode_rele(found);
+ }
+ break;
+ }
+ } else {
+
+ /*
+ * The entry does not exist. Check write perms in dir to see if
+ * entry can be created.
+ */
+ if ((error = hyprlofs_taccess(dir, VWRITE, cr)))
+ return (error);
+
+ /* Make new hlnode and directory entry as required. */
+ if ((error = hldir_make_hlnode(dir, hm, va, op, realvp, &hp,
+ cr)))
+ return (error);
+
+ if ((error = hldiraddentry(dir, hp, name))) {
+ /* Unmake the inode we just made. */
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ if ((hp->hln_type) == VDIR) {
+ ASSERT(hdp == NULL);
+ /* cleanup allocs made by hyprlofs_dirinit() */
+ hyprlofs_dirtrunc(hp);
+ }
+ mutex_enter(&hp->hln_tlock);
+ hp->hln_nlink = 0;
+ mutex_exit(&hp->hln_tlock);
+ gethrestime(&hp->hln_ctime);
+ rw_exit(&hp->hln_rwlock);
+ hlnode_rele(hp);
+ hp = NULL;
+ } else if (hpp) {
+ *hpp = hp;
+ } else {
+ hlnode_rele(hp);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Delete entry hp of name "nm" from dir. Free dir entry space and decrement
+ * link count on hlnode(s).
+ */
+int
+hyprlofs_dirdelete(hlnode_t *dir, hlnode_t *hp, char *nm, enum dr_op op,
+ cred_t *cr)
+{
+ hldirent_t *hpdp;
+ int error;
+ size_t namelen;
+ hlnode_t *hnp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(RW_WRITE_HELD(&hp->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ if (nm[0] == '\0')
+ panic("hyprlofs_dirdelete: NULL name for %p", (void *)hp);
+
+ /* return error if removing . or .. */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = hyprlofs_taccess(dir, VEXEC|VWRITE, cr)) != 0)
+ return (error);
+
+ if (dir->hln_dir == NULL)
+ return (ENOENT);
+
+ hpdp = hyprlofs_hash_lookup(nm, dir, 0, &hnp);
+ if (hpdp == NULL) {
+ /*
+ * If it is gone, some other thread got here first!
+ * Return error ENOENT.
+ */
+ return (ENOENT);
+ }
+
+ /*
+ * If the hlnode in the hldirent changed (shouldn't happen since we
+ * don't support rename) then original is gone, so return that status
+ * (same as UFS).
+ */
+ if (hp != hnp)
+ return (ENOENT);
+
+ hyprlofs_hash_out(hpdp);
+
+ /* Take hpdp out of the directory list. */
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+ if (hpdp->hld_prev) {
+ hpdp->hld_prev->hld_next = hpdp->hld_next;
+ }
+ if (hpdp->hld_next) {
+ hpdp->hld_next->hld_prev = hpdp->hld_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match hpdp, point it at the
+ * previous dirent.
+ */
+ if (dir->hln_dir->hld_prev == hpdp) {
+ dir->hln_dir->hld_prev = hpdp->hld_prev;
+ }
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ /* hpdp points to the correct directory entry */
+ namelen = strlen(hpdp->hld_name) + 1;
+
+ kmem_free(hpdp, sizeof (hldirent_t) + namelen);
+ dir->hln_size -= (sizeof (hldirent_t) + namelen);
+ dir->hln_dirents--;
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+ hp->hln_ctime = now;
+
+ ASSERT(hp->hln_nlink > 0);
+ DECR_COUNT(&hp->hln_nlink, &hp->hln_tlock);
+ if (op == DR_RMDIR && hp->hln_type == VDIR) {
+ hyprlofs_dirtrunc(hp);
+ ASSERT(hp->hln_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * hyprlofs_dirinit initializes a dir with '.' and '..' entries without
+ * checking perms and locking
+ */
+void
+hyprlofs_dirinit(
+ hlnode_t *parent, /* parent of directory to initialize */
+ hlnode_t *dir) /* the new directory */
+{
+ hldirent_t *dot, *dotdot;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&parent->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ dot = kmem_zalloc(sizeof (hldirent_t) + 2, KM_SLEEP);
+ dotdot = kmem_zalloc(sizeof (hldirent_t) + 3, KM_SLEEP);
+
+ /* Initialize the entries */
+ dot->hld_hlnode = dir;
+ dot->hld_offset = 0;
+ dot->hld_name = (char *)dot + sizeof (hldirent_t);
+ dot->hld_name[0] = '.';
+ dot->hld_parent = dir;
+ hyprlofs_hash_in(dot);
+
+ dotdot->hld_hlnode = parent;
+ dotdot->hld_offset = 1;
+ dotdot->hld_name = (char *)dotdot + sizeof (hldirent_t);
+ dotdot->hld_name[0] = '.';
+ dotdot->hld_name[1] = '.';
+ dotdot->hld_parent = dir;
+ hyprlofs_hash_in(dotdot);
+
+ /* Initialize directory entry list. */
+ dot->hld_next = dotdot;
+ dot->hld_prev = dotdot;
+ dotdot->hld_next = NULL;
+ dotdot->hld_prev = dot;
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ /*
+ * Since hyprlofs_dirinit is called with both dir and parent being the
+ * same for the root vnode, we need to increment this before we set
+ * hln_nlink = 2 below.
+ */
+ INCR_COUNT(&parent->hln_nlink, &parent->hln_tlock);
+ parent->hln_ctime = now;
+
+ dir->hln_dir = dot;
+ dir->hln_size = 2 * sizeof (hldirent_t) + 5; /* dot and dotdot */
+ dir->hln_dirents = 2;
+ dir->hln_nlink = 2;
+}
+
+
+/*
+ * hyprlofs_dirtrunc removes all dir entries under this dir.
+ */
+void
+hyprlofs_dirtrunc(hlnode_t *dir)
+{
+ hldirent_t *hdp;
+ hlnode_t *tp;
+ size_t namelen;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ if (dir->hln_looped)
+ return;
+
+ for (hdp = dir->hln_dir; hdp; hdp = dir->hln_dir) {
+ ASSERT(hdp->hld_next != hdp);
+ ASSERT(hdp->hld_prev != hdp);
+ ASSERT(hdp->hld_hlnode);
+
+ dir->hln_dir = hdp->hld_next;
+ namelen = strlen(hdp->hld_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this dir entry removal.
+ */
+ tp = hdp->hld_hlnode;
+
+ ASSERT(tp->hln_nlink > 0);
+ DECR_COUNT(&tp->hln_nlink, &tp->hln_tlock);
+
+ hyprlofs_hash_out(hdp);
+
+ kmem_free(hdp, sizeof (hldirent_t) + namelen);
+ dir->hln_size -= (sizeof (hldirent_t) + namelen);
+ dir->hln_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ ASSERT(dir->hln_dir == NULL);
+ ASSERT(dir->hln_size == 0);
+ ASSERT(dir->hln_dirents == 0);
+}
+
+static int
+hldiraddentry(
+ hlnode_t *dir, /* target directory to make entry in */
+ hlnode_t *hp, /* new hlnode */
+ char *name)
+{
+ hldirent_t *hdp, *hpdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent dir wasn't removed from underneath the caller.
+ */
+ if (dir->hln_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same FS. */
+ if (hp->hln_vnode->v_vfsp != dir->hln_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Alloc and init dir entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (hldirent_t);
+ hdp = kmem_zalloc(alloc_size, KM_NORMALPRI | KM_NOSLEEP);
+ if (hdp == NULL)
+ return (ENOSPC);
+
+ dir->hln_size += alloc_size;
+ dir->hln_dirents++;
+ hdp->hld_hlnode = hp;
+ hdp->hld_parent = dir;
+
+ /* The dir entry and its name were allocated sequentially. */
+ hdp->hld_name = (char *)hdp + sizeof (hldirent_t);
+ (void) strcpy(hdp->hld_name, name);
+
+ hyprlofs_hash_in(hdp);
+
+ /*
+ * Some utilities expect the size of a directory to remain fairly
+ * static. For example, a routine which unlinks files between calls to
+ * readdir(); the size of the dir changes from underneath it and so the
+ * real dir offset in bytes is invalid. To circumvent this problem, we
+ * initialize a dir entry with a phony offset, and use this offset to
+ * determine end of file in hyprlofs_readdir.
+ */
+ hpdp = dir->hln_dir->hld_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (hpdp->hld_next != NULL && (hpdp->hld_next->hld_offset -
+ hpdp->hld_offset) <= 1) {
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+ ASSERT(hpdp->hld_next->hld_offset > hpdp->hld_offset);
+ hpdp = hpdp->hld_next;
+ }
+ hdp->hld_offset = hpdp->hld_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which is
+ * necessarily the largest offset in this dir) is more than twice the
+ * number of dirents, that means the dir is 50% holes. At this point
+ * we reset the slot pointer back to the beginning of the dir so we
+ * start using the holes. The idea is that if there are N dirents,
+ * there must also be N holes, so we can satisfy the next N creates by
+ * walking at most 2N entries; thus the average cost of a create is
+ * constant. Note that we use the first dirent's hld_prev as the roving
+ * slot pointer. This saves a word in every dirent.
+ */
+ if (hpdp->hld_next == NULL && hpdp->hld_offset > 2 * dir->hln_dirents)
+ dir->hln_dir->hld_prev = dir->hln_dir->hld_next;
+ else
+ dir->hln_dir->hld_prev = hdp;
+
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ hdp->hld_next = hpdp->hld_next;
+ if (hdp->hld_next) {
+ hdp->hld_next->hld_prev = hdp;
+ }
+ hdp->hld_prev = hpdp;
+ hpdp->hld_next = hdp;
+
+ ASSERT(hdp->hld_next != hdp);
+ ASSERT(hdp->hld_prev != hdp);
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ return (0);
+}
+
+static int
+hldir_make_hlnode(hlnode_t *dir, hlfsmount_t *hm, vattr_t *va, enum de_op op,
+ vnode_t *realvp, hlnode_t **newnode, cred_t *cr)
+{
+ hlnode_t *hp;
+ enum vtype type;
+
+ ASSERT(va != NULL);
+ ASSERT(op == DE_CREATE || op == DE_MKDIR);
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+ type = va->va_type;
+ hp = kmem_zalloc(sizeof (hlnode_t), KM_SLEEP);
+ hyprlofs_node_init(hm, hp, va, cr);
+
+ hp->hln_vnode->v_rdev = hp->hln_rdev = NODEV;
+ hp->hln_vnode->v_type = type;
+ hp->hln_uid = crgetuid(cr);
+
+ /*
+ * To determine the gid of the created file:
+ * If the directory's set-gid bit is set, set the gid to the gid
+ * of the parent dir, otherwise, use the process's gid.
+ */
+ if (dir->hln_mode & VSGID)
+ hp->hln_gid = dir->hln_gid;
+ else
+ hp->hln_gid = crgetgid(cr);
+
+ /*
+ * If we're creating a dir and the parent dir has the set-GID bit set,
+ * set it on the new dir. Otherwise, if the user is neither privileged
+ * nor a member of the file's new group, clear the file's set-GID bit.
+ */
+ if (dir->hln_mode & VSGID && type == VDIR)
+ hp->hln_mode |= VSGID;
+ else {
+ if ((hp->hln_mode & VSGID) &&
+ secpolicy_vnode_setids_setgids(cr, hp->hln_gid) != 0)
+ hp->hln_mode &= ~VSGID;
+ }
+
+ if (va->va_mask & AT_ATIME)
+ hp->hln_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ hp->hln_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ hyprlofs_dirinit(dir, hp);
+ hp->hln_looped = 0;
+ } else {
+ hp->hln_realvp = realvp;
+ hp->hln_size = va->va_size;
+ hp->hln_looped = 1;
+ }
+
+ *newnode = hp;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c
new file mode 100644
index 0000000000..1d857309f3
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c
@@ -0,0 +1,127 @@
+/*
+ * 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) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/debug.h>
+#include <sys/time.h>
+#include <sys/cmn_err.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/vfs.h>
+#include <sys/cred.h>
+#include <sys/kmem.h>
+#include <sys/atomic.h>
+#include <sys/policy.h>
+#include <sys/fs/hyprlofs_info.h>
+
+#define MODESHIFT 3
+
+/* Initialize a hlnode and add it to file list under mount point. */
+void
+hyprlofs_node_init(hlfsmount_t *hm, hlnode_t *h, vattr_t *vap, cred_t *cr)
+{
+ vnode_t *vp;
+ timestruc_t now;
+
+ ASSERT(vap != NULL);
+
+ rw_init(&h->hln_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&h->hln_tlock, NULL, MUTEX_DEFAULT, NULL);
+ h->hln_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ h->hln_mask = 0;
+ h->hln_type = vap->va_type;
+ h->hln_nodeid = (ino64_t)(uint32_t)((uintptr_t)h >> 3);
+ h->hln_nlink = 1;
+ h->hln_size = 0;
+
+ if (cr == NULL) {
+ h->hln_uid = vap->va_uid;
+ h->hln_gid = vap->va_gid;
+ } else {
+ h->hln_uid = crgetuid(cr);
+ h->hln_gid = crgetgid(cr);
+ }
+
+ h->hln_fsid = hm->hlm_dev;
+ h->hln_rdev = vap->va_rdev;
+ h->hln_blksize = PAGESIZE;
+ h->hln_nblocks = 0;
+ gethrestime(&now);
+ h->hln_atime = now;
+ h->hln_mtime = now;
+ h->hln_ctime = now;
+ h->hln_seq = 0;
+ h->hln_dir = NULL;
+
+ h->hln_vnode = vn_alloc(KM_SLEEP);
+ vp = HLNTOV(h);
+ vn_setops(vp, hyprlofs_vnodeops);
+ vp->v_vfsp = hm->hlm_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)h;
+ mutex_enter(&hm->hlm_contents);
+ /*
+ * Increment the pseudo generation number for this hlnode. Since
+ * hlnodes are allocated and freed, there really is no particular
+ * generation number for a new hlnode. Just fake it by using a
+ * counter in each file system.
+ */
+ h->hln_gen = hm->hlm_gen++;
+
+ /*
+ * Add new hlnode to end of linked list of hlnodes for this hyprlofs
+ * Root dir is handled specially in hyprlofs_mount.
+ */
+ if (hm->hlm_rootnode != (hlnode_t *)NULL) {
+ h->hln_forw = NULL;
+ h->hln_back = hm->hlm_rootnode->hln_back;
+ h->hln_back->hln_forw = hm->hlm_rootnode->hln_back = h;
+ }
+ mutex_exit(&hm->hlm_contents);
+ vn_exists(vp);
+}
+
+int
+hyprlofs_taccess(void *vtp, int mode, cred_t *cr)
+{
+ hlnode_t *hp = vtp;
+ int shift = 0;
+
+ /* Check access based on owner, group and public perms in hlnode. */
+ if (crgetuid(cr) != hp->hln_uid) {
+ shift += MODESHIFT;
+ if (groupmember(hp->hln_gid, cr) == 0)
+ shift += MODESHIFT;
+ }
+
+ return (secpolicy_vnode_access2(cr, HLNTOV(hp), hp->hln_uid,
+ hp->hln_mode << shift, mode));
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c
new file mode 100644
index 0000000000..c582a8cac2
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c
@@ -0,0 +1,614 @@
+/*
+ * 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) 2012, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Hyperlofs is a hybrid file system combining features of the tmpfs(7FS) and
+ * lofs(7FS) file systems. It is modeled on code from both of these file
+ * systems.
+ *
+ * The purpose is to create a high performance name space for files on which
+ * applications will compute. Given a large number of data files with various
+ * owners, we want to construct a view onto those files such that only a subset
+ * is visible to the applications and such that the view can be changed very
+ * quickly as compute progresses. Entries in the name space are not mounts and
+ * thus do not appear in the mnttab. Entries in the name space are allowed to
+ * refer to files on different backing file systems. Intermediate directories
+ * in the name space exist only in-memory, ala tmpfs. There are no leaf nodes
+ * in the name space except for entries that refer to backing files ala lofs.
+ *
+ * The name space is managed via ioctls issued on the mounted file system and
+ * is mostly read-only for the compute applications. That is, applications
+ * cannot create new files in the name space. If a file is unlinked by an
+ * application, that only removes the file from the name space, the backing
+ * file remains in place. It is possible for applications to write-through to
+ * the backing files if the file system is mounted read-write.
+ *
+ * The name space is managed via the HYPRLOFS_ADD_ENTRIES, HYPRLOFS_RM_ENTRIES,
+ * and HYPRLOFS_RM_ALL ioctls on the top-level mount.
+ *
+ * The HYPRLOFS_ADD_ENTRIES ioctl specifies path(s) to the backing file(s) and
+ * the name(s) for the file(s) in the name space. The name(s) may be path(s)
+ * which will be relative to the root of the mount and thus cannot begin with
+ * a /. If the name is a path, it does not have to correspond to any backing
+ * path. The intermediate directories will only exist in the name space. The
+ * entry(ies) will be added to the name space.
+ *
+ * The HYPRLOFS_RM_ENTRIES ioctl specifies the name(s) of the file(s) in the
+ * name space which should be removed. The name(s) may be path(s) which will
+ * be relative to the root of the mount and thus cannot begin with a /. The
+ * named entry(ies) will be removed.
+ *
+ * The HYPRLOFS_RM_ALL ioctl will remove all mappings from the name space.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/debug.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <fs/fs_subr.h>
+#include <vm/page.h>
+#include <vm/anon.h>
+#include <sys/model.h>
+#include <sys/policy.h>
+
+#include <sys/fs/swapnode.h>
+#include <sys/fs/hyprlofs_info.h>
+
+static int hyprlofsfstype;
+
+/*
+ * hyprlofs vfs operations.
+ */
+static int hyprlofsinit(int, char *);
+static int hyprlofs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int hyprlofs_unmount(vfs_t *, int, cred_t *);
+static int hyprlofs_root(vfs_t *, vnode_t **);
+static int hyprlofs_statvfs(vfs_t *, struct statvfs64 *);
+static int hyprlofs_vget(vfs_t *, vnode_t **, struct fid *);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static mntopts_t hyprlofs_mntopts;
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "hyprlofs",
+ hyprlofsinit,
+ VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_ZMOUNT,
+ &hyprlofs_mntopts
+};
+
+static mntopts_t hyprlofs_mntopts = {
+ 0, NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "filesystem for hyprlofs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+ if (error)
+ return (error);
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(hyprlofsfstype);
+ vn_freevnodeops(hyprlofs_vnodeops);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * The following are patchable variables limiting the amount of system
+ * resources hyprlofs can use.
+ *
+ * hyprlofs_maxkmem limits the amount of kernel kmem_alloc memory hyprlofs can
+ * use for it's data structures (e.g. hlnodes, directory entries). It is set
+ * as a percentage of physical memory which is determined when hyprlofs is
+ * first used in the system.
+ *
+ * hyprlofs_minfree is the minimum amount of swap space that hyprlofs leaves for
+ * the rest of the system. If the amount of free swap space in the system
+ * (i.e. anoninfo.ani_free) drops below hyprlofs_minfree, hyprlofs anon
+ * allocations will fail.
+ */
+size_t hyprlofs_maxkmem = 0;
+size_t hyprlofs_minfree = 0;
+size_t hyprlofs_kmemspace; /* bytes of kernel heap used by all hyprlofs */
+
+static major_t hyprlofs_major;
+static minor_t hyprlofs_minor;
+static kmutex_t hyprlofs_minor_lock;
+
+/*
+ * initialize global hyprlofs locks and hashes when loading hyprlofs module
+ */
+static int
+hyprlofsinit(int fstype, char *name)
+{
+ static const fs_operation_def_t hl_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = hyprlofs_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = hyprlofs_unmount },
+ VFSNAME_ROOT, { .vfs_root = hyprlofs_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = hyprlofs_statvfs },
+ VFSNAME_VGET, { .vfs_vget = hyprlofs_vget },
+ NULL, NULL
+ };
+ int error;
+ extern void hyprlofs_hash_init();
+
+ hyprlofs_hash_init();
+ hyprlofsfstype = fstype;
+ ASSERT(hyprlofsfstype != 0);
+
+ error = vfs_setfsops(fstype, hl_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "hyprlofsinit: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, hyprlofs_vnodeops_template,
+ &hyprlofs_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "hyprlofsinit: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * hyprlofs_minfree is an absolute limit of swap space which still
+ * allows other processes to execute. Set it if its not patched.
+ */
+ if (hyprlofs_minfree == 0)
+ hyprlofs_minfree = btopr(HYPRLOFSMINFREE);
+
+ if ((hyprlofs_major = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN,
+ "hyprlofsinit: Can't get unique device number.");
+ hyprlofs_major = 0;
+ }
+ mutex_init(&hyprlofs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+ return (0);
+}
+
+static int
+hyprlofs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ hlfsmount_t *hm = NULL;
+ hlnode_t *hp;
+ struct pathname dpn;
+ int error;
+ vattr_t rattr;
+ int got_attrs;
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (uap->flags & MS_REMOUNT)
+ return (EBUSY);
+
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /* Having the resource be anything but "swap" doesn't make sense. */
+ vfs_setresource(vfsp, "swap", 0);
+
+ if ((error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
+ &dpn)) != 0)
+ goto out;
+
+ if ((hm = kmem_zalloc(sizeof (hlfsmount_t),
+ KM_NORMALPRI | KM_NOSLEEP)) == NULL) {
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Get an available minor device number for this mount */
+ mutex_enter(&hyprlofs_minor_lock);
+ do {
+ hyprlofs_minor = (hyprlofs_minor + 1) & L_MAXMIN32;
+ hm->hlm_dev = makedevice(hyprlofs_major, hyprlofs_minor);
+ } while (vfs_devismounted(hm->hlm_dev));
+ mutex_exit(&hyprlofs_minor_lock);
+
+ /*
+ * Set but don't bother entering the mutex since hlfsmount is not on
+ * the mount list yet.
+ */
+ mutex_init(&hm->hlm_contents, NULL, MUTEX_DEFAULT, NULL);
+
+ hm->hlm_vfsp = vfsp;
+
+ vfsp->vfs_data = (caddr_t)hm;
+ vfsp->vfs_fstype = hyprlofsfstype;
+ vfsp->vfs_dev = hm->hlm_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, hm->hlm_dev, hyprlofsfstype);
+ hm->hlm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(hm->hlm_mntpath, dpn.pn_path);
+
+ /* allocate and initialize root hlnode structure */
+ bzero(&rattr, sizeof (vattr_t));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0777);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+ hp = kmem_zalloc(sizeof (hlnode_t), KM_SLEEP);
+ hyprlofs_node_init(hm, hp, &rattr, cr);
+
+ /* Get the mode, uid, and gid from the underlying mount point. */
+ rattr.va_mask = AT_MODE|AT_UID|AT_GID;
+ got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr, NULL);
+
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ HLNTOV(hp)->v_flag |= VROOT;
+
+ /*
+ * If the getattr succeeded, use its results, otherwise allow the
+ * previously set defaults to prevail.
+ */
+ if (got_attrs == 0) {
+ hp->hln_mode = rattr.va_mode;
+ hp->hln_uid = rattr.va_uid;
+ hp->hln_gid = rattr.va_gid;
+ }
+
+ /*
+ * Initialize linked list of hlnodes so that the back pointer of the
+ * root hlnode always points to the last one on the list and the
+ * forward pointer of the last node is null
+ */
+ hp->hln_back = hp;
+ hp->hln_forw = NULL;
+ hp->hln_nlink = 0;
+ hm->hlm_rootnode = hp;
+
+ hyprlofs_dirinit(hp, hp);
+
+ rw_exit(&hp->hln_rwlock);
+
+ pn_free(&dpn);
+ error = 0;
+
+out:
+ return (error);
+}
+
+static int
+hyprlofs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hnp, *cancel;
+ vnode_t *vp;
+ int error;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ /*
+ * forced unmount is not supported by this file system
+ * and thus, ENOTSUP, is being returned.
+ */
+ if (flag & MS_FORCE)
+ return (ENOTSUP);
+
+ mutex_enter(&hm->hlm_contents);
+
+ /*
+ * If there are no open files, only the root node should have a ref cnt.
+ * With hlm_contents held, nothing can be added or removed. There may
+ * be some dirty pages. To prevent fsflush from disrupting the unmount,
+ * put a hold on each node while scanning. If we find a previously
+ * referenced node, undo the holds we have placed and fail EBUSY.
+ */
+ hnp = hm->hlm_rootnode;
+ if (HLNTOV(hnp)->v_count > 1) {
+ mutex_exit(&hm->hlm_contents);
+ return (EBUSY);
+ }
+
+ for (hnp = hnp->hln_forw; hnp; hnp = hnp->hln_forw) {
+ if ((vp = HLNTOV(hnp))->v_count > 0) {
+ cancel = hm->hlm_rootnode->hln_forw;
+ while (cancel != hnp) {
+ vp = HLNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->hln_forw;
+ }
+ mutex_exit(&hm->hlm_contents);
+ return (EBUSY);
+ }
+ VN_HOLD(vp);
+ }
+
+ /* We can drop the mutex now because no one can find this mount */
+ mutex_exit(&hm->hlm_contents);
+
+ /*
+ * Free all alloc'd memory associated with this FS. To do this, we go
+ * through the file list twice, once to remove all the dir entries, and
+ * then to remove all the files.
+ */
+
+ /* Remove all directory entries */
+ for (hnp = hm->hlm_rootnode; hnp; hnp = hnp->hln_forw) {
+ rw_enter(&hnp->hln_rwlock, RW_WRITER);
+ if (hnp->hln_type == VDIR)
+ hyprlofs_dirtrunc(hnp);
+ rw_exit(&hnp->hln_rwlock);
+ }
+
+ ASSERT(hm->hlm_rootnode);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place. VN_RELE
+ * should make the node disappear, unless somebody is holding pages
+ * against it. Wait and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on a hlnode
+ * from blowing it away (in hyprlofs_inactive) while we're trying to
+ * get to it here. Once we have a HOLD on it we know it'll stick around.
+ */
+ mutex_enter(&hm->hlm_contents);
+
+ /* Remove all the files (except the rootnode) backwards. */
+ while ((hnp = hm->hlm_rootnode->hln_back) != hm->hlm_rootnode) {
+ mutex_exit(&hm->hlm_contents);
+ /* Note we handled the link count in pass 2 above. */
+ vp = HLNTOV(hnp);
+ VN_RELE(vp);
+ mutex_enter(&hm->hlm_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again.
+ */
+ if (hnp == hm->hlm_rootnode->hln_back) {
+ VN_HOLD(vp);
+ mutex_exit(&hm->hlm_contents);
+ delay(hz / 4);
+ mutex_enter(&hm->hlm_contents);
+ }
+ }
+ mutex_exit(&hm->hlm_contents);
+
+ VN_RELE(HLNTOV(hm->hlm_rootnode));
+
+ ASSERT(hm->hlm_mntpath);
+
+ kmem_free(hm->hlm_mntpath, strlen(hm->hlm_mntpath) + 1);
+
+ mutex_destroy(&hm->hlm_contents);
+ kmem_free(hm, sizeof (hlfsmount_t));
+
+ return (0);
+}
+
+/* Return root hlnode for given vnode */
+static int
+hyprlofs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hp = hm->hlm_rootnode;
+ vnode_t *vp;
+
+ ASSERT(hp);
+
+ vp = HLNTOV(hp);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+hyprlofs_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ /*
+ * The FS may have been mounted by the GZ on behalf of the NGZ. In
+ * that case, the hlfsmount zone_id will be the global zone. We want
+ * to show the swap cap inside the zone in this case, even though the
+ * FS was mounted by the GZ.
+ */
+ if (curproc->p_zone->zone_id != GLOBAL_ZONEUNIQID)
+ zp = curproc->p_zone;
+ else
+ zp = hm->hlm_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > hyprlofs_minfree)
+ sbp->f_bfree = blocks - hyprlofs_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is what's available plus what's been used
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a NGZ with a swap cap, then report the
+ * capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * This is fairly inaccurate since it doesn't take into account the
+ * names stored in the directory entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (hlnode_t) + sizeof (hldirent_t));
+
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[hyprlofsfstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, hm->hlm_mntpath, sizeof (sbp->f_fstr));
+ /*
+ * ensure null termination
+ */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static int
+hyprlofs_vget(vfs_t *vfsp, vnode_t **vpp, struct fid *fidp)
+{
+ hlfid_t *hfid;
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hp = NULL;
+
+ hfid = (hlfid_t *)fidp;
+ *vpp = NULL;
+
+ mutex_enter(&hm->hlm_contents);
+ for (hp = hm->hlm_rootnode; hp; hp = hp->hln_forw) {
+ mutex_enter(&hp->hln_tlock);
+ if (hp->hln_nodeid == hfid->hlfid_ino) {
+ /*
+ * If the gen numbers don't match we know the file
+ * won't be found since only one hlnode can have this
+ * number at a time.
+ */
+ if (hp->hln_gen != hfid->hlfid_gen ||
+ hp->hln_nlink == 0) {
+ mutex_exit(&hp->hln_tlock);
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+ }
+ *vpp = (vnode_t *)HLNTOV(hp);
+
+ VN_HOLD(*vpp);
+
+ if ((hp->hln_mode & S_ISVTX) &&
+ !(hp->hln_mode & (S_IXUSR | S_IFDIR))) {
+ mutex_enter(&(*vpp)->v_lock);
+ (*vpp)->v_flag |= VISSWAP;
+ mutex_exit(&(*vpp)->v_lock);
+ }
+ mutex_exit(&hp->hln_tlock);
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+ }
+ mutex_exit(&hp->hln_tlock);
+ }
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c
new file mode 100644
index 0000000000..a2064dfa1f
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c
@@ -0,0 +1,1441 @@
+/*
+ * 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 2015 Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/fs/hyprlofs.h>
+#include <sys/fs/hyprlofs_info.h>
+#include <sys/mman.h>
+#include <vm/pvn.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+static int hyprlofs_add_entry(vnode_t *, char *, char *, cred_t *,
+ caller_context_t *);
+static int hyprlofs_rm_entry(vnode_t *, char *, cred_t *, caller_context_t *,
+ int);
+static int hyprlofs_rm_all(vnode_t *, cred_t *, caller_context_t *, int);
+static int hyprlofs_remove(vnode_t *, char *, cred_t *, caller_context_t *,
+ int);
+static int hyprlofs_get_all(vnode_t *, intptr_t, cred_t *, caller_context_t *,
+ int);
+
+/*
+ * This is a somewhat arbitrary upper limit on the number of entries we can
+ * pass in on a single add/rm ioctl call. This is only used to validate that
+ * the input list looks sane.
+ */
+#define MAX_IOCTL_PARAMS 100000
+
+static int
+hyprlofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *rvp;
+ int error;
+
+ rvp = REALVP(*vpp);
+
+ if (VTOHLN(*vpp)->hln_looped == 0)
+ return (0);
+
+ /*
+ * looped back, pass through to real vnode. Need to hold new reference
+ * to vp since VOP_OPEN() may decide to release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ ASSERT(rvp->v_count > 1);
+ VN_RELE(rvp);
+
+ return (error);
+}
+
+static int
+hyprlofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 0) {
+ cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+ cleanshares(vp, ttoproc(curthread)->p_pid);
+ return (0);
+ }
+
+ return (VOP_CLOSE(REALVP(vp), flag, count, offset, cr, ct));
+}
+
+static int
+hyprlofs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ return (VOP_READ(REALVP(vp), uiop, ioflag, cr, ct));
+}
+
+static int
+hyprlofs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* We don't support writing to non-regular files */
+ if (vp->v_type != VREG)
+ return (EINVAL);
+
+ if (vn_is_readonly(vp))
+ return (EROFS);
+
+ return (VOP_WRITE(REALVP(vp), uiop, ioflag, cr, ct));
+}
+
+/* ARGSUSED */
+static int
+hyprlofs_ioctl(vnode_t *vp, int cmd, intptr_t data, int flag,
+ cred_t *cr, int *rvalp, caller_context_t *ct)
+{
+ int len, cnt, error;
+ int i;
+ model_t model;
+ char path[MAXPATHLEN];
+ char nm[MAXPATHLEN];
+
+ /* We only support the hyprlofs ioctls on the root vnode */
+ if (!(vp->v_flag & VROOT))
+ return (ENOTTY);
+
+ /*
+ * Check if managing hyprlofs is allowed.
+ */
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES || cmd == HYPRLOFS_RM_ENTRIES) {
+ model = get_udatamodel();
+
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_entries_t ebuf;
+ hyprlofs_entry_t *e;
+
+ if (copyin((void *)data, &ebuf, sizeof (ebuf)))
+ return (EFAULT);
+ cnt = ebuf.hle_len;
+ if (cnt > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ len = sizeof (hyprlofs_entry_t) * cnt;
+
+ e = kmem_alloc(len, KM_SLEEP);
+ if (copyin((void *)(ebuf.hle_entries), e, len)) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (e[i].hle_nlen == 0 ||
+ e[i].hle_nlen > MAXPATHLEN)
+ return (EINVAL);
+
+ if (copyin(e[i].hle_name, nm, e[i].hle_nlen)
+ != 0) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+ nm[e[i].hle_nlen] = '\0';
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES) {
+ if (e[i].hle_plen == 0 ||
+ e[i].hle_plen > MAXPATHLEN)
+ return (EINVAL);
+
+ if (copyin(e[i].hle_path, path,
+ e[i].hle_plen) != 0) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+ path[e[i].hle_plen] = '\0';
+
+ if ((error = hyprlofs_add_entry(vp,
+ path, nm, cr, ct)) != 0) {
+ kmem_free(e, len);
+ return (error);
+ }
+ } else {
+ if ((error = hyprlofs_rm_entry(vp, nm,
+ cr, ct, flag)) != 0) {
+ kmem_free(e, len);
+ return (error);
+ }
+ }
+ }
+
+ kmem_free(e, len);
+ return (0);
+
+ } else {
+ hyprlofs_entries32_t ebuf32;
+ hyprlofs_entry32_t *e32;
+
+ if (copyin((void *)data, &ebuf32, sizeof (ebuf32)))
+ return (EFAULT);
+
+ cnt = ebuf32.hle_len;
+ if (cnt > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ len = sizeof (hyprlofs_entry32_t) * cnt;
+
+ e32 = kmem_alloc(len, KM_SLEEP);
+ if (copyin((void *)(unsigned long)(ebuf32.hle_entries),
+ e32, len)) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (e32[i].hle_nlen == 0 ||
+ e32[i].hle_nlen > MAXPATHLEN)
+ return (EINVAL);
+
+ if (copyin((void *)(unsigned long)
+ e32[i].hle_name, nm,
+ e32[i].hle_nlen) != 0) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+ nm[e32[i].hle_nlen] = '\0';
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES) {
+ if (e32[i].hle_plen == 0 ||
+ e32[i].hle_plen > MAXPATHLEN)
+ return (EINVAL);
+
+ if (copyin((void *)(unsigned long)
+ e32[i].hle_path, path,
+ e32[i].hle_plen) != 0) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+ path[e32[i].hle_plen] = '\0';
+
+ if ((error = hyprlofs_add_entry(vp,
+ path, nm, cr, ct)) != 0) {
+ kmem_free(e32, len);
+ return (error);
+ }
+ } else {
+ if ((error = hyprlofs_rm_entry(vp, nm,
+ cr, ct, flag)) != 0) {
+ kmem_free(e32, len);
+ return (error);
+ }
+ }
+ }
+
+ kmem_free(e32, len);
+ return (0);
+ }
+ }
+
+ if (cmd == HYPRLOFS_RM_ALL) {
+ return (hyprlofs_rm_all(vp, cr, ct, flag));
+ }
+
+ if (cmd == HYPRLOFS_GET_ENTRIES) {
+ return (hyprlofs_get_all(vp, data, cr, ct, flag));
+ }
+
+ return (ENOTTY);
+}
+
+static int
+hyprlofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ vattr_t tmp_va;
+
+ if (tp->hln_looped == 1) {
+ int error;
+
+ if ((error = VOP_GETATTR(REALVP(vp), &tmp_va, flags, cr,
+ ct)) != 0)
+ return (error);
+ }
+
+ mutex_enter(&tp->hln_tlock);
+ vap->va_type = vp->v_type;
+ vap->va_mode = tp->hln_mode & MODEMASK;
+ vap->va_uid = tp->hln_uid;
+ vap->va_gid = tp->hln_gid;
+ vap->va_fsid = tp->hln_fsid;
+ vap->va_nodeid = (ino64_t)tp->hln_nodeid;
+ vap->va_nlink = tp->hln_nlink;
+ vap->va_size = (u_offset_t)tp->hln_size;
+ vap->va_atime = tp->hln_atime;
+ vap->va_mtime = tp->hln_mtime;
+ vap->va_ctime = tp->hln_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = tp->hln_rdev;
+ vap->va_seq = tp->hln_seq;
+
+ if (tp->hln_looped == 1) {
+ vap->va_nblocks = tmp_va.va_nblocks;
+ } else {
+ vap->va_nblocks =
+ (fsblkcnt64_t)btodb(ptob(btopr(vap->va_size)));
+ }
+ mutex_exit(&tp->hln_tlock);
+ return (0);
+}
+
+/*ARGSUSED4*/
+static int
+hyprlofs_setattr(vnode_t *vp, vattr_t *vap, int flags,
+ cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ int error = 0;
+ vattr_t *get;
+ long mask;
+
+ /*
+ * Cannot set these attributes
+ */
+ if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR))
+ return (EINVAL);
+
+ mutex_enter(&tp->hln_tlock);
+
+ get = &tp->hln_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cr, vp, vap, get, flags,
+ hyprlofs_taccess, tp);
+
+ if (error)
+ goto out;
+
+ mask = vap->va_mask;
+
+ if (mask & AT_MODE) {
+ get->va_mode &= S_IFMT;
+ get->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ get->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ get->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ get->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ get->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&tp->hln_ctime);
+
+out:
+ mutex_exit(&tp->hln_tlock);
+ return (error);
+}
+
+static int
+hyprlofs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ int error;
+
+ if (mode & VWRITE) {
+ if (vp->v_type == VREG && vn_is_readonly(vp))
+ return (EROFS);
+ }
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_ACCESS(REALVP(vp), mode, flags, cr, ct));
+
+ mutex_enter(&tp->hln_tlock);
+ error = hyprlofs_taccess(tp, mode, cr);
+ mutex_exit(&tp->hln_tlock);
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+hyprlofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *ntp = NULL;
+ int error;
+
+ if (VTOHLN(dvp)->hln_looped == 1)
+ return (VOP_LOOKUP(REALVP(dvp), nm, vpp, pnp, flags, rdir,
+ cr, ct, direntflags, realpnp));
+
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /* Null component name is a synonym for directory being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+ ASSERT(tp);
+
+ if ((error = hyprlofs_dirlookup(tp, nm, &ntp, cr)) == 0) {
+ ASSERT(ntp);
+ *vpp = HLNTOV(ntp);
+ }
+ return (error);
+}
+
+/*
+ * Create the loopback from the hyprlofs vnode to the real vnode.
+ */
+static int
+hyprlofs_loopback(vnode_t *dvp, vnode_t *rvp, char *nm, vattr_t *vap,
+ int mode, cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *parent;
+ hlfsmount_t *tm;
+ int error;
+ hlnode_t *oldtp;
+ vnode_t *vp;
+
+ parent = (hlnode_t *)VTOHLN(dvp);
+ tm = (hlfsmount_t *)VTOHLM(dvp);
+ error = 0;
+ oldtp = NULL;
+
+ if (vap->va_type == VREG && (vap->va_mode & VSVTX)) {
+ /* we don't support the sticky bit */
+ vap->va_mode &= ~VSVTX;
+ } else if (vap->va_type == VNON) {
+ return (EINVAL);
+ }
+
+ /* Null component name is a synonym for directory being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ oldtp = parent;
+ } else {
+ error = hyprlofs_dirlookup(parent, nm, &oldtp, cr);
+ }
+
+ if (error == 0) { /* name found */
+ ASSERT(oldtp);
+
+ rw_enter(&oldtp->hln_rwlock, RW_WRITER);
+
+ /*
+ * if create/read-only an existing directory, allow it
+ */
+ if ((oldtp->hln_type == VDIR) && (mode & VWRITE))
+ error = EISDIR;
+ else {
+ error = hyprlofs_taccess(oldtp, mode, cr);
+ }
+
+ if (error) {
+ rw_exit(&oldtp->hln_rwlock);
+ hlnode_rele(oldtp);
+ return (error);
+ }
+
+ vp = HLNTOV(oldtp);
+ rw_exit(&oldtp->hln_rwlock);
+
+ if (vp->v_type == VREG) {
+ hlnode_rele(oldtp);
+ return (EEXIST);
+ }
+
+ vnevent_create(vp, ct);
+ return (0);
+ }
+
+ if (error != ENOENT)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ error = hyprlofs_direnter(tm, parent, nm, DE_CREATE, rvp, vap, NULL,
+ cr);
+ rw_exit(&parent->hln_rwlock);
+
+ return (error);
+}
+
+/*
+ * Create an in-memory directory based on the add-entry ioctl name.
+ * If the dir exists, return EEXIST but still also return node in vpp.
+ */
+static int
+hyprlofs_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cr)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *self = NULL;
+ hlfsmount_t *tm = (hlfsmount_t *)VTOHLM(dvp);
+ int error;
+
+ /*
+ * Might be dangling directory. Catch it here, because a ENOENT return
+ * from hyprlofs_dirlookup() is a valid return.
+ */
+ if (parent->hln_nlink == 0)
+ return (ENOENT);
+
+ error = hyprlofs_dirlookup(parent, nm, &self, cr);
+ if (error == 0) {
+ ASSERT(self);
+ hlnode_rele(self);
+ /* We can't loop in under a looped in directory */
+ if (self->hln_looped)
+ return (EACCES);
+ *vpp = HLNTOV(self);
+ return (EEXIST);
+ }
+ if (error != ENOENT)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ error = hyprlofs_direnter(tm, parent, nm, DE_MKDIR, (vnode_t *)NULL,
+ va, &self, cr);
+ rw_exit(&parent->hln_rwlock);
+
+ if (error == 0 || error == EEXIST) {
+ hlnode_rele(self);
+ *vpp = HLNTOV(self);
+ }
+
+ return (error);
+}
+
+/*
+ * Loop in a file or directory into the namespace.
+ */
+static int
+hyprlofs_add_entry(vnode_t *vp, char *fspath, char *fsname,
+ cred_t *cr, caller_context_t *ct)
+{
+ int error;
+ char *p, *pnm;
+ vnode_t *realvp, *dvp;
+ vattr_t va;
+
+ /*
+ * Get vnode for the real file/dir. We'll have a hold on realvp which
+ * we won't vn_rele until hyprlofs_inactive.
+ */
+ if ((error = lookupname(fspath, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &realvp)) != 0)
+ return (error);
+
+ /* no devices allowed */
+ if (IS_DEVVP(realvp)) {
+ VN_RELE(realvp);
+ return (ENODEV);
+ }
+
+ /*
+ * realvp may be an AUTOFS node, in which case we perform a VOP_ACCESS
+ * to trigger the mount of the intended filesystem. This causes a
+ * loopback mount of the intended filesystem instead of the AUTOFS
+ * filesystem.
+ */
+ if ((error = VOP_ACCESS(realvp, 0, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ /*
+ * We're interested in the top most filesystem. This is specially
+ * important when fspath is a trigger AUTOFS node, since we're really
+ * interested in mounting the filesystem AUTOFS mounted as result of
+ * the VOP_ACCESS() call not the AUTOFS node itself.
+ */
+ if (vn_mountedvfs(realvp) != NULL) {
+ if ((error = traverse(&realvp)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+ }
+
+ va.va_type = VNON;
+ /*
+ * If the target name is a path, make sure we have all of the
+ * intermediate directories, creating them if necessary.
+ */
+ dvp = vp;
+ pnm = p = fsname;
+
+ /* path cannot be absolute */
+ if (*p == '/') {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ for (p = strchr(pnm, '/'); p != NULL; p = strchr(pnm, '/')) {
+ if (va.va_type == VNON)
+ /* use the top-level dir as the template va for mkdir */
+ if ((error = VOP_GETATTR(vp, &va, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ *p = '\0';
+
+ /* Path component cannot be empty or relative */
+ if (pnm[0] == '\0' ||
+ (pnm[0] == '.' && pnm[1] == '.' && pnm[2] == '\0')) {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ if ((error = hyprlofs_mkdir(dvp, pnm, &va, &dvp, cr)) != 0 &&
+ error != EEXIST) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ *p = '/';
+ pnm = p + 1;
+ }
+
+ /* The file name is required */
+ if (pnm[0] == '\0') {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ /* Now use the real file's va as the template va */
+ if ((error = VOP_GETATTR(realvp, &va, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ /* Make the vnode */
+ error = hyprlofs_loopback(dvp, realvp, pnm, &va, va.va_mode, cr, ct);
+ if (error != 0)
+ VN_RELE(realvp);
+ return (error);
+}
+
+/*
+ * Remove a looped in file from the namespace.
+ */
+static int
+hyprlofs_rm_entry(vnode_t *dvp, char *fsname, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int error;
+ char *p, *pnm;
+ hlnode_t *parent;
+ hlnode_t *fndtp;
+
+ pnm = p = fsname;
+
+ /* path cannot be absolute */
+ if (*p == '/')
+ return (EINVAL);
+
+ /*
+ * If the target name is a path, get the containing dir and simple
+ * file name.
+ */
+ parent = (hlnode_t *)VTOHLN(dvp);
+ for (p = strchr(pnm, '/'); p != NULL; p = strchr(pnm, '/')) {
+ *p = '\0';
+
+ /* Path component cannot be empty or relative */
+ if (pnm[0] == '\0' ||
+ (pnm[0] == '.' && pnm[1] == '.' && pnm[2] == '\0'))
+ return (EINVAL);
+
+ if ((error = hyprlofs_dirlookup(parent, pnm, &fndtp, cr)) != 0)
+ return (error);
+
+ dvp = HLNTOV(fndtp);
+ parent = fndtp;
+ pnm = p + 1;
+ }
+
+ /* The file name is required */
+ if (pnm[0] == '\0')
+ return (EINVAL);
+
+ /* Remove the entry from the parent dir */
+ return (hyprlofs_remove(dvp, pnm, cr, ct, flags));
+}
+
+/*
+ * Remove all looped in files from the namespace.
+ */
+static int
+hyprlofs_rm_all(vnode_t *dvp, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int error = 0;
+ hlnode_t *hp = (hlnode_t *)VTOHLN(dvp);
+ hldirent_t *hdp;
+
+ hlnode_hold(hp);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ goto done;
+ }
+
+ hdp = hp->hln_dir;
+ while (hdp) {
+ hlnode_t *fndhp;
+
+ if (strcmp(hdp->hld_name, ".") == 0 ||
+ strcmp(hdp->hld_name, "..") == 0) {
+ hdp = hdp->hld_next;
+ continue;
+ }
+
+ /* This holds the fndhp vnode */
+ error = hyprlofs_dirlookup(hp, hdp->hld_name, &fndhp, cr);
+ if (error != 0)
+ goto done;
+ hlnode_rele(fndhp);
+
+ if (fndhp->hln_looped == 0) {
+ /* recursively remove contents of this subdir */
+ if (fndhp->hln_type == VDIR) {
+ vnode_t *tvp = HLNTOV(fndhp);
+
+ error = hyprlofs_rm_all(tvp, cr, ct, flags);
+ if (error != 0)
+ goto done;
+ }
+ }
+
+ /* remove the entry */
+ error = hyprlofs_remove(dvp, hdp->hld_name, cr, ct, flags);
+ if (error != 0)
+ goto done;
+
+ hdp = hp->hln_dir;
+ }
+
+done:
+ hlnode_rele(hp);
+ return (error);
+}
+
+/*
+ * Get a list of all looped in files in the namespace.
+ */
+static int
+hyprlofs_get_all_entries(vnode_t *dvp, hyprlofs_curr_entry_t *hcp,
+ char *prefix, int *pcnt, int n_max,
+ cred_t *cr, caller_context_t *ct, int flags)
+{
+ int error = 0;
+ int too_big = 0;
+ int cnt;
+ int len;
+ hlnode_t *hp = (hlnode_t *)VTOHLN(dvp);
+ hldirent_t *hdp;
+ char *path;
+
+ cnt = *pcnt;
+ path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ hlnode_hold(hp);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ goto done;
+ }
+
+ hdp = hp->hln_dir;
+ while (hdp) {
+ hlnode_t *fndhp;
+ vnode_t *tvp;
+
+ if (strcmp(hdp->hld_name, ".") == 0 ||
+ strcmp(hdp->hld_name, "..") == 0) {
+ hdp = hdp->hld_next;
+ continue;
+ }
+
+ /* This holds the fndhp vnode */
+ error = hyprlofs_dirlookup(hp, hdp->hld_name, &fndhp, cr);
+ if (error != 0)
+ goto done;
+ hlnode_rele(fndhp);
+
+ if (fndhp->hln_looped == 0) {
+ /* recursively get contents of this subdir */
+ VERIFY(fndhp->hln_type == VDIR);
+ tvp = HLNTOV(fndhp);
+
+ if (*prefix == '\0')
+ (void) strlcpy(path, hdp->hld_name, MAXPATHLEN);
+ else
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ prefix, hdp->hld_name);
+
+ error = hyprlofs_get_all_entries(tvp, hcp, path,
+ &cnt, n_max, cr, ct, flags);
+
+ if (error == E2BIG) {
+ too_big = 1;
+ error = 0;
+ }
+ if (error != 0)
+ goto done;
+ } else {
+ if (cnt < n_max) {
+ char *p;
+
+ if (*prefix == '\0')
+ (void) strlcpy(path, hdp->hld_name,
+ MAXPATHLEN);
+ else
+ (void) snprintf(path, MAXPATHLEN,
+ "%s/%s", prefix, hdp->hld_name);
+
+ len = strlen(path);
+ ASSERT(len <= MAXPATHLEN);
+ if (copyout(path, (void *)(hcp[cnt].hce_name),
+ len)) {
+ error = EFAULT;
+ goto done;
+ }
+
+ tvp = REALVP(HLNTOV(fndhp));
+ if (tvp->v_path == vn_vpath_empty) {
+ p = "<unknown>";
+ } else {
+ p = tvp->v_path;
+ }
+ len = strlen(p);
+ ASSERT(len <= MAXPATHLEN);
+ if (copyout(p, (void *)(hcp[cnt].hce_path),
+ len)) {
+ error = EFAULT;
+ goto done;
+ }
+ }
+
+ cnt++;
+ if (cnt > n_max)
+ too_big = 1;
+ }
+
+ hdp = hdp->hld_next;
+ }
+
+done:
+ hlnode_rele(hp);
+ kmem_free(path, MAXPATHLEN);
+
+ *pcnt = cnt;
+ if (error == 0 && too_big == 1)
+ error = E2BIG;
+
+ return (error);
+}
+
+/*
+ * Return a list of all looped in files in the namespace.
+ */
+static int
+hyprlofs_get_all(vnode_t *dvp, intptr_t data, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int limit, cnt, error;
+ model_t model;
+ hyprlofs_curr_entry_t *e;
+
+ model = get_udatamodel();
+
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_curr_entries_t ebuf;
+
+ if (copyin((void *)data, &ebuf, sizeof (ebuf)))
+ return (EFAULT);
+ limit = ebuf.hce_cnt;
+ e = ebuf.hce_entries;
+ if (limit > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+
+ } else {
+ hyprlofs_curr_entries32_t ebuf32;
+
+ if (copyin((void *)data, &ebuf32, sizeof (ebuf32)))
+ return (EFAULT);
+
+ limit = ebuf32.hce_cnt;
+ e = (hyprlofs_curr_entry_t *)(unsigned long)
+ (ebuf32.hce_entries);
+ if (limit > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ }
+
+ cnt = 0;
+ error = hyprlofs_get_all_entries(dvp, e, "", &cnt, limit, cr, ct,
+ flags);
+
+ if (error == 0 || error == E2BIG) {
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_curr_entries_t ebuf;
+
+ ebuf.hce_cnt = cnt;
+ if (copyout(&ebuf, (void *)data, sizeof (ebuf)))
+ return (EFAULT);
+
+ } else {
+ hyprlofs_curr_entries32_t ebuf32;
+
+ ebuf32.hce_cnt = cnt;
+ if (copyout(&ebuf32, (void *)data, sizeof (ebuf32)))
+ return (EFAULT);
+ }
+ }
+
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+hyprlofs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ int error;
+ hlnode_t *hp = NULL;
+
+ /* This holds the hp vnode */
+ error = hyprlofs_dirlookup(parent, nm, &hp, cr);
+ if (error)
+ return (error);
+
+ ASSERT(hp);
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+
+ error = hyprlofs_dirdelete(parent, hp, nm, DR_REMOVE, cr);
+
+ rw_exit(&hp->hln_rwlock);
+ rw_exit(&parent->hln_rwlock);
+ vnevent_remove(HLNTOV(hp), dvp, nm, ct);
+
+ /*
+ * We've now dropped the dir link so by rele-ing our vnode we should
+ * clean up in hyprlofs_inactive.
+ */
+ hlnode_rele(hp);
+
+ return (error);
+}
+
+/* ARGSUSED4 */
+static int
+hyprlofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *self = NULL;
+ vnode_t *vp;
+ int error = 0;
+
+ /* Return error if removing . or .. */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST); /* Should be ENOTEMPTY */
+ error = hyprlofs_dirlookup(parent, nm, &self, cr);
+ if (error)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ rw_enter(&self->hln_rwlock, RW_WRITER);
+
+ vp = HLNTOV(self);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto done1;
+ }
+ if (self->hln_type != VDIR) {
+ error = ENOTDIR;
+ goto done1;
+ }
+
+ /*
+ * When a dir is looped in, we only remove the in-memory dir, not the
+ * backing dir.
+ */
+ if (self->hln_looped == 0) {
+ mutex_enter(&self->hln_tlock);
+ if (self->hln_nlink > 2) {
+ mutex_exit(&self->hln_tlock);
+ error = EEXIST;
+ goto done1;
+ }
+ mutex_exit(&self->hln_tlock);
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto done1;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ goto done;
+ }
+
+ /*
+ * Check for an empty directory, i.e. only includes entries for
+ * "." and ".."
+ */
+ if (self->hln_dirents > 2) {
+ error = EEXIST; /* SIGH should be ENOTEMPTY */
+ /*
+ * Update atime because checking hln_dirents is
+ * equivalent to reading the directory
+ */
+ gethrestime(&self->hln_atime);
+ goto done;
+ }
+
+ error = hyprlofs_dirdelete(parent, self, nm, DR_RMDIR, cr);
+ } else {
+ error = hyprlofs_dirdelete(parent, self, nm, DR_REMOVE, cr);
+ }
+
+done:
+ if (self->hln_looped == 0)
+ vn_vfsunlock(vp);
+done1:
+ rw_exit(&self->hln_rwlock);
+ rw_exit(&parent->hln_rwlock);
+ vnevent_rmdir(HLNTOV(self), dvp, nm, ct);
+
+ /*
+ * We've now dropped the dir link so by rele-ing our vnode we should
+ * clean up in hyprlofs_inactive.
+ */
+ hlnode_rele(self);
+
+ return (error);
+}
+
+static int
+hyprlofs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hldirent_t *hdp;
+ int error = 0;
+ size_t namelen;
+ struct dirent64 *dp;
+ ulong_t offset;
+ ulong_t total_bytes_wanted;
+ long outcount = 0;
+ long bufsize;
+ int reclen;
+ caddr_t outbuf;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_READDIR(REALVP(vp), uiop, cr, eofp, ct, flags));
+
+ if (uiop->uio_loffset >= MAXOFF_T) {
+ if (eofp)
+ *eofp = 1;
+ return (0);
+ }
+ /* assuming syscall has already called hln_rwlock */
+ ASSERT(RW_READ_HELD(&hp->hln_rwlock));
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ return (0);
+ }
+
+ /* Get space for multiple dir entries */
+ total_bytes_wanted = uiop->uio_iov->iov_len;
+ bufsize = total_bytes_wanted + sizeof (struct dirent64);
+ outbuf = kmem_alloc(bufsize, KM_SLEEP);
+
+ dp = (struct dirent64 *)((uintptr_t)outbuf);
+
+ offset = 0;
+ hdp = hp->hln_dir;
+ while (hdp) {
+ namelen = strlen(hdp->hld_name); /* no +1 needed */
+ offset = hdp->hld_offset;
+ if (offset >= uiop->uio_offset) {
+ reclen = (int)DIRENT64_RECLEN(namelen);
+ if (outcount + reclen > total_bytes_wanted) {
+ if (!outcount)
+ /* Buffer too small for any entries. */
+ error = EINVAL;
+ break;
+ }
+ ASSERT(hdp->hld_hlnode != NULL);
+
+ /* zero out uninitialized bytes */
+ (void) strncpy(dp->d_name, hdp->hld_name,
+ DIRENT64_NAMELEN(reclen));
+ dp->d_reclen = (ushort_t)reclen;
+ dp->d_ino = (ino64_t)hdp->hld_hlnode->hln_nodeid;
+ dp->d_off = (offset_t)hdp->hld_offset + 1;
+ dp = (struct dirent64 *)
+ ((uintptr_t)dp + dp->d_reclen);
+ outcount += reclen;
+ ASSERT(outcount <= bufsize);
+ }
+ hdp = hdp->hld_next;
+ }
+
+ if (!error)
+ error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+ if (!error) {
+ /*
+ * If we reached the end of the list our offset should now be
+ * just past the end.
+ */
+ if (!hdp) {
+ offset += 1;
+ if (eofp)
+ *eofp = 1;
+ } else if (eofp)
+ *eofp = 0;
+ uiop->uio_offset = offset;
+ }
+ gethrestime(&hp->hln_atime);
+ kmem_free(outbuf, bufsize);
+ return (error);
+}
+
+static int
+hyprlofs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_FSYNC(REALVP(vp), syncflag, cr, ct));
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+hyprlofs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vp->v_vfsp);
+
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+
+ mutex_enter(&hp->hln_tlock);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's nothing to do except drop our hold.
+ */
+ if (vp->v_count > 1 || hp->hln_nlink != 0) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&hp->hln_tlock);
+ rw_exit(&hp->hln_rwlock);
+ return;
+ }
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&hp->hln_tlock);
+
+ /* release hold on the real vnode now */
+ if (hp->hln_looped == 1 && hp->hln_realvp != NULL)
+ VN_RELE(hp->hln_realvp);
+
+ /* Here's our chance to send invalid event while we're between locks */
+ vn_invalid(HLNTOV(hp));
+
+ mutex_enter(&hm->hlm_contents);
+ if (hp->hln_forw == NULL)
+ hm->hlm_rootnode->hln_back = hp->hln_back;
+ else
+ hp->hln_forw->hln_back = hp->hln_back;
+ hp->hln_back->hln_forw = hp->hln_forw;
+ mutex_exit(&hm->hlm_contents);
+ rw_exit(&hp->hln_rwlock);
+ rw_destroy(&hp->hln_rwlock);
+ mutex_destroy(&hp->hln_tlock);
+ vn_free(HLNTOV(hp));
+ kmem_free(hp, sizeof (hlnode_t));
+}
+
+static int
+hyprlofs_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hlfid_t *hfid;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_FID(REALVP(vp), fidp, ct));
+
+ if (fidp->fid_len < (sizeof (hlfid_t) - sizeof (ushort_t))) {
+ fidp->fid_len = sizeof (hlfid_t) - sizeof (ushort_t);
+ return (ENOSPC);
+ }
+
+ hfid = (hlfid_t *)fidp;
+ bzero(hfid, sizeof (hlfid_t));
+ hfid->hlfid_len = (int)sizeof (hlfid_t) - sizeof (ushort_t);
+
+ hfid->hlfid_ino = hp->hln_nodeid;
+ hfid->hlfid_gen = hp->hln_gen;
+
+ return (0);
+}
+
+static int
+hyprlofs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
+ page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw,
+ cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_GETPAGE(REALVP(vp), off, len, protp, pl, plsz, seg, addr,
+ rw, cr, ct));
+}
+
+int
+hyprlofs_putpage(vnode_t *vp, offset_t off, size_t len, int flags,
+ cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_PUTPAGE(REALVP(vp), off, len, flags, cr, ct));
+}
+
+static int
+hyprlofs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_MAP(REALVP(vp), off, as, addrp, len, prot, maxprot, flags,
+ cr, ct));
+}
+
+static int
+hyprlofs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_ADDMAP(REALVP(vp), off, as, addr, len, prot, maxprot,
+ flags, cr, ct));
+}
+
+static int
+hyprlofs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_DELMAP(REALVP(vp), off, as, addr, len, prot, maxprot,
+ flags, cr, ct));
+}
+
+static int
+hyprlofs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_SPACE(REALVP(vp), cmd, bfp, flag, offset, cr, ct));
+}
+
+static int
+hyprlofs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp,
+ caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 0)
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+
+ return (VOP_SEEK(REALVP(vp), ooff, noffp, ct));
+}
+
+static int
+hyprlofs_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ hlnode_t *hp = VTOHLN(vp);
+
+ if (hp->hln_looped == 1)
+ return (VOP_RWLOCK(REALVP(vp), write_lock, ct));
+
+ if (write_lock) {
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ } else {
+ rw_enter(&hp->hln_rwlock, RW_READER);
+ }
+ return (write_lock);
+}
+
+static void
+hyprlofs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ hlnode_t *hp = VTOHLN(vp);
+
+ if (hp->hln_looped == 1) {
+ VOP_RWUNLOCK(REALVP(vp), write_lock, ct);
+ return;
+ }
+
+ rw_exit(&hp->hln_rwlock);
+}
+
+static int
+hyprlofs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_PATHCONF(REALVP(vp), cmd, valp, cr, ct));
+
+ switch (cmd) {
+ case _PC_XATTR_ENABLED:
+ case _PC_XATTR_EXISTS:
+ case _PC_SATTR_ENABLED:
+ case _PC_SATTR_EXISTS:
+ error = EINVAL;
+ break;
+ case _PC_TIMESTAMP_RESOLUTION:
+ /* nanosecond timestamp resolution */
+ *valp = 1L;
+ error = 0;
+ break;
+ default:
+ error = fs_pathconf(vp, cmd, valp, cr, ct);
+ }
+ return (error);
+}
+
+
+struct vnodeops *hyprlofs_vnodeops;
+
+const fs_operation_def_t hyprlofs_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = hyprlofs_open },
+ VOPNAME_CLOSE, { .vop_close = hyprlofs_close },
+ VOPNAME_READ, { .vop_read = hyprlofs_read },
+ VOPNAME_WRITE, { .vop_write = hyprlofs_write },
+ VOPNAME_IOCTL, { .vop_ioctl = hyprlofs_ioctl },
+ VOPNAME_GETATTR, { .vop_getattr = hyprlofs_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = hyprlofs_setattr },
+ VOPNAME_ACCESS, { .vop_access = hyprlofs_access },
+ VOPNAME_LOOKUP, { .vop_lookup = hyprlofs_lookup },
+ VOPNAME_CREATE, { .error = fs_error },
+ VOPNAME_REMOVE, { .vop_remove = hyprlofs_remove },
+ VOPNAME_LINK, { .error = fs_error },
+ VOPNAME_RENAME, { .error = fs_error },
+ VOPNAME_MKDIR, { .error = fs_error },
+ VOPNAME_RMDIR, { .vop_rmdir = hyprlofs_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = hyprlofs_readdir },
+ VOPNAME_SYMLINK, { .error = fs_error },
+ VOPNAME_READLINK, { .error = fs_error },
+ VOPNAME_FSYNC, { .vop_fsync = hyprlofs_fsync },
+ VOPNAME_INACTIVE, { .vop_inactive = hyprlofs_inactive },
+ VOPNAME_FID, { .vop_fid = hyprlofs_fid },
+ VOPNAME_RWLOCK, { .vop_rwlock = hyprlofs_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = hyprlofs_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = hyprlofs_seek },
+ VOPNAME_SPACE, { .vop_space = hyprlofs_space },
+ VOPNAME_GETPAGE, { .vop_getpage = hyprlofs_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = hyprlofs_putpage },
+ VOPNAME_MAP, { .vop_map = hyprlofs_map },
+ VOPNAME_ADDMAP, { .vop_addmap = hyprlofs_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = hyprlofs_delmap },
+ VOPNAME_PATHCONF, { .vop_pathconf = hyprlofs_pathconf },
+ VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/fs/lookup.c b/usr/src/uts/common/fs/lookup.c
index 55ffb94805..7dc83ff8d5 100644
--- a/usr/src/uts/common/fs/lookup.c
+++ b/usr/src/uts/common/fs/lookup.c
@@ -21,6 +21,7 @@
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -57,6 +58,7 @@
#include <sys/zone.h>
#include <sys/dnlc.h>
#include <sys/fs/snode.h>
+#include <sys/brand.h>
/* Controls whether paths are stored with vnodes. */
int vfs_vnode_path = 1;
@@ -977,6 +979,109 @@ localpath(char *path, struct vnode *vrootp, cred_t *cr)
}
/*
+ * Clean a stale v_path from a vnode. This is only performed if the v_path has
+ * not been altered since it was found to be stale
+ */
+static void
+vnode_clear_vpath(vnode_t *vp, char *vpath_old)
+{
+ mutex_enter(&vp->v_lock);
+ if (vp->v_path != vn_vpath_empty && vp->v_path == vpath_old) {
+ vp->v_path = vn_vpath_empty;
+ mutex_exit(&vp->v_lock);
+ kmem_free(vpath_old, strlen(vpath_old) + 1);
+ } else {
+ mutex_exit(&vp->v_lock);
+ }
+}
+
+/*
+ * Validate that a pathname refers to a given vnode.
+ */
+static int
+vnode_valid_pn(vnode_t *vp, vnode_t *vrootp, pathname_t *pn, pathname_t *rpn,
+ int flags, cred_t *cr)
+{
+ vnode_t *compvp;
+ /*
+ * If we are in a zone or a chroot environment, then we have to
+ * take additional steps, since the path to the root might not
+ * be readable with the current credentials, even though the
+ * process can legitmately access the file. In this case, we
+ * do the following:
+ *
+ * lookuppnvp() with all privileges to get the resolved path.
+ * call localpath() to get the local portion of the path, and
+ * continue as normal.
+ *
+ * If the the conversion to a local path fails, then we continue
+ * as normal. This is a heuristic to make process object file
+ * paths available from within a zone. Because lofs doesn't
+ * support page operations, the vnode stored in the seg_t is
+ * actually the underlying real vnode, not the lofs node itself.
+ * Most of the time, the lofs path is the same as the underlying
+ * vnode (for example, /usr/lib/libc.so.1).
+ */
+ if (vrootp != rootdir) {
+ char *local = NULL;
+
+ VN_HOLD(rootdir);
+ if (lookuppnvp(pn, rpn, FOLLOW, NULL, &compvp, rootdir,
+ rootdir, kcred) == 0) {
+ local = localpath(rpn->pn_path, vrootp, kcred);
+ VN_RELE(compvp);
+ }
+
+ /*
+ * The original pn was changed through lookuppnvp().
+ * Set it to local for next validation attempt.
+ */
+ if (local) {
+ (void) pn_set(pn, local);
+ } else {
+ return (1);
+ }
+ }
+
+ /*
+ * We should have a local path at this point, so start the search from
+ * the root of the current process.
+ */
+ VN_HOLD(vrootp);
+ if (vrootp != rootdir)
+ VN_HOLD(vrootp);
+ if (lookuppnvp(pn, rpn, FOLLOW | flags, NULL, &compvp, vrootp, vrootp,
+ cr) == 0) {
+ /*
+ * Check to see if the returned vnode is the same as the one we
+ * expect.
+ */
+ if (vn_compare(vp, compvp) ||
+ vnode_match(vp, compvp, cr)) {
+ VN_RELE(compvp);
+ return (0);
+ } else {
+ VN_RELE(compvp);
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * Struct for tracking vnodes with invalidated v_path entries during a
+ * dirtopath reverse lookup. By keepeing adequate state, those vnode can be
+ * revisted to populate v_path.
+ */
+struct dirpath_walk {
+ struct dirpath_walk *dw_next;
+ vnode_t *dw_vnode;
+ vnode_t *dw_pvnode;
+ size_t dw_len;
+ char *dw_name;
+};
+
+/*
* Given a directory, return the full, resolved path. This looks up "..",
* searches for the given vnode in the parent, appends the component, etc. It
* is used to implement vnodetopath() and getcwd() when the cached path fails.
@@ -985,16 +1090,14 @@ static int
dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
cred_t *cr)
{
- pathname_t pn, rpn, emptypn;
- vnode_t *cmpvp, *pvp = NULL;
- vnode_t *startvp = vp;
- int err = 0, vprivs;
- size_t complen;
- char *dbuf;
- dirent64_t *dp;
- char *bufloc;
- size_t dlen = DIRENT64_RECLEN(MAXPATHLEN);
- refstr_t *mntpt;
+ pathname_t pn, rpn, emptypn;
+ vnode_t *pvp = NULL, *startvp = vp;
+ int err = 0;
+ size_t complen;
+ dirent64_t *dp;
+ char *bufloc, *dbuf;
+ const size_t dlen = DIRENT64_RECLEN(MAXPATHLEN);
+ struct dirpath_walk *dw_chain = NULL, *dw_entry;
/* Operation only allowed on directories */
ASSERT(vp->v_type == VDIR);
@@ -1019,6 +1122,9 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
VN_HOLD(vp);
for (;;) {
+ int vprivs;
+ hrtime_t cached_stamp;
+
/*
* Return if we've reached the root. If the buffer is empty,
* return '/'. We explicitly don't use vn_compare(), since it
@@ -1043,85 +1149,33 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
}
/*
- * Shortcut: see if this vnode is a mountpoint. If so,
- * grab the path information from the vfs_t.
- */
- if (vp->v_flag & VROOT) {
-
- mntpt = vfs_getmntpoint(vp->v_vfsp);
- if ((err = pn_set(&pn, (char *)refstr_value(mntpt)))
- == 0) {
- refstr_rele(mntpt);
- rpn.pn_path = rpn.pn_buf;
-
- /*
- * Ensure the mountpoint still exists.
- */
- VN_HOLD(vrootp);
- if (vrootp != rootdir)
- VN_HOLD(vrootp);
- if (lookuppnvp(&pn, &rpn, flags, NULL,
- &cmpvp, vrootp, vrootp, cr) == 0) {
-
- if (VN_CMP(vp, cmpvp)) {
- VN_RELE(cmpvp);
-
- complen = strlen(rpn.pn_path);
- bufloc -= complen;
- if (bufloc < buf) {
- err = ERANGE;
- goto out;
- }
- bcopy(rpn.pn_path, bufloc,
- complen);
- break;
- } else {
- VN_RELE(cmpvp);
- }
- }
- } else {
- refstr_rele(mntpt);
- }
- }
-
- /*
* Shortcut: see if this vnode has correct v_path. If so,
* we have the work done.
*/
mutex_enter(&vp->v_lock);
- if (vp->v_path != NULL) {
-
- if ((err = pn_set(&pn, vp->v_path)) == 0) {
- mutex_exit(&vp->v_lock);
- rpn.pn_path = rpn.pn_buf;
+ if (vp->v_path != vn_vpath_empty &&
+ pn_set(&pn, vp->v_path) == 0) {
+ cached_stamp = vp->v_path_stamp;
+ mutex_exit(&vp->v_lock);
+ rpn.pn_path = rpn.pn_buf;
- /*
- * Ensure the v_path pointing to correct vnode
- */
- VN_HOLD(vrootp);
- if (vrootp != rootdir)
- VN_HOLD(vrootp);
- if (lookuppnvp(&pn, &rpn, flags, NULL,
- &cmpvp, vrootp, vrootp, cr) == 0) {
-
- if (VN_CMP(vp, cmpvp)) {
- VN_RELE(cmpvp);
-
- complen = strlen(rpn.pn_path);
- bufloc -= complen;
- if (bufloc < buf) {
- err = ERANGE;
- goto out;
- }
- bcopy(rpn.pn_path, bufloc,
- complen);
- break;
- } else {
- VN_RELE(cmpvp);
- }
+ /* Ensure the v_path pointing to correct vnode */
+ if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags,
+ cr) == 0) {
+ complen = strlen(rpn.pn_path);
+ bufloc -= complen;
+ if (bufloc < buf) {
+ err = ERANGE;
+ goto out;
}
+ bcopy(rpn.pn_path, bufloc, complen);
+ break;
} else {
- mutex_exit(&vp->v_lock);
+ /*
+ * Immediately nuke cached v_path entries known
+ * to be invalid.
+ */
+ vn_clearpath(vp, cached_stamp);
}
} else {
mutex_exit(&vp->v_lock);
@@ -1166,38 +1220,6 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
}
/*
- * Try to obtain the path component from dnlc cache
- * before searching through the directory.
- */
- if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) {
- /*
- * If we got parent vnode as a result,
- * then the answered path is correct.
- */
- if (VN_CMP(cmpvp, pvp)) {
- VN_RELE(cmpvp);
- complen = strlen(dbuf);
- bufloc -= complen;
- if (bufloc <= buf) {
- err = ENAMETOOLONG;
- goto out;
- }
- bcopy(dbuf, bufloc, complen);
-
- /* Prepend a slash to the current path */
- *--bufloc = '/';
-
- /* And continue with the next component */
- VN_RELE(vp);
- vp = pvp;
- pvp = NULL;
- continue;
- } else {
- VN_RELE(cmpvp);
- }
- }
-
- /*
* Search the parent directory for the entry corresponding to
* this vnode.
*/
@@ -1215,6 +1237,19 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
/* Prepend a slash to the current path. */
*--bufloc = '/';
+ /*
+ * Record the name and directory for later reconstruction and
+ * link it up with the others.
+ */
+ dw_entry = kmem_alloc(sizeof (*dw_entry), KM_SLEEP);
+ dw_entry->dw_name = kmem_alloc(complen + 1, KM_SLEEP);
+ VN_HOLD(dw_entry->dw_vnode = vp);
+ VN_HOLD(dw_entry->dw_pvnode = pvp);
+ bcopy(dp->d_name, dw_entry->dw_name, complen + 1);
+ dw_entry->dw_len = complen;
+ dw_entry->dw_next = dw_chain;
+ dw_chain = dw_entry;
+
/* And continue with the next component */
VN_RELE(vp);
vp = pvp;
@@ -1229,6 +1264,37 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags,
out:
/*
+ * Walk over encountered directory entries which were afflicted with a
+ * stale or absent v_path. If the dirtopath was successful, we should
+ * possess the necessary information to populate all of them with a
+ * valid v_path.
+ *
+ * While processing this list, it is safe to call vn_setpath despite
+ * the fact that racing vnode actions may have altered v_path entries
+ * while the above loopwas still executing. Any updated entries will
+ * have a newer v_path_stamp value which prevents an invalid overwrite.
+ *
+ * If an error was encountered during the search, freeing the chain is
+ * still required.
+ */
+ dw_entry = dw_chain;
+ while (dw_entry != NULL) {
+ struct dirpath_walk *next = dw_entry->dw_next;
+
+ if (err == 0) {
+ vn_setpath(NULL, dw_entry->dw_pvnode,
+ dw_entry->dw_vnode, dw_entry->dw_name,
+ dw_entry->dw_len);
+ }
+
+ VN_RELE(dw_entry->dw_vnode);
+ VN_RELE(dw_entry->dw_pvnode);
+ kmem_free(dw_entry->dw_name, dw_entry->dw_len + 1);
+ kmem_free(dw_entry, sizeof (*dw_entry));
+ dw_entry = next;
+ }
+
+ /*
* If the error was ESTALE and the current directory to look in
* was the root for this lookup, the root for a mounted file
* system, or the starting directory for lookups, then
@@ -1268,18 +1334,18 @@ static int
vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
cred_t *cr, int flags)
{
- pathname_t pn, rpn;
- int ret, len;
- vnode_t *compvp, *pvp, *realvp;
- proc_t *p = curproc;
- char path[MAXNAMELEN];
- int doclose = 0;
+ pathname_t pn;
+ int ret = 0;
+ vnode_t *realvp;
+ boolean_t doclose = B_FALSE;
/*
* If vrootp is NULL, get the root for curproc. Callers with any other
* requirements should pass in a different vrootp.
*/
if (vrootp == NULL) {
+ proc_t *p = curproc;
+
mutex_enter(&p->p_lock);
if ((vrootp = PTOU(p)->u_rdir) == NULL)
vrootp = rootdir;
@@ -1301,150 +1367,59 @@ vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
realvp != vp) {
VN_HOLD(vp);
if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0)
- doclose = 1;
+ doclose = B_TRUE;
else
VN_RELE(vp);
}
- pn_alloc(&pn);
-
/*
- * Check to see if we have a cached path in the vnode.
+ * Check to see if we have a valid cached path in the vnode.
*/
+ pn_alloc(&pn);
mutex_enter(&vp->v_lock);
- if (vp->v_path != NULL) {
+ if (vp->v_path != vn_vpath_empty) {
+ hrtime_t cached_stamp;
+ pathname_t rpn;
+
+ cached_stamp = vp->v_path_stamp;
(void) pn_set(&pn, vp->v_path);
mutex_exit(&vp->v_lock);
- pn_alloc(&rpn);
-
/* We should only cache absolute paths */
ASSERT(pn.pn_buf[0] == '/');
- /*
- * If we are in a zone or a chroot environment, then we have to
- * take additional steps, since the path to the root might not
- * be readable with the current credentials, even though the
- * process can legitmately access the file. In this case, we
- * do the following:
- *
- * lookuppnvp() with all privileges to get the resolved path.
- * call localpath() to get the local portion of the path, and
- * continue as normal.
- *
- * If the the conversion to a local path fails, then we continue
- * as normal. This is a heuristic to make process object file
- * paths available from within a zone. Because lofs doesn't
- * support page operations, the vnode stored in the seg_t is
- * actually the underlying real vnode, not the lofs node itself.
- * Most of the time, the lofs path is the same as the underlying
- * vnode (for example, /usr/lib/libc.so.1).
- */
- if (vrootp != rootdir) {
- char *local = NULL;
- VN_HOLD(rootdir);
- if (lookuppnvp(&pn, &rpn, FOLLOW,
- NULL, &compvp, rootdir, rootdir, kcred) == 0) {
- local = localpath(rpn.pn_path, vrootp,
- kcred);
- VN_RELE(compvp);
- }
-
- /*
- * The original pn was changed through lookuppnvp().
- * Set it to local for next validation attempt.
- */
- if (local) {
- (void) pn_set(&pn, local);
+ pn_alloc(&rpn);
+ if (vnode_valid_pn(vp, vrootp, &pn, &rpn, flags, cr) == 0) {
+ /* Return the result, if we're able. */
+ if (buflen > rpn.pn_pathlen) {
+ bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
} else {
- goto notcached;
- }
- }
-
- /*
- * We should have a local path at this point, so start the
- * search from the root of the current process.
- */
- VN_HOLD(vrootp);
- if (vrootp != rootdir)
- VN_HOLD(vrootp);
- ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL,
- &compvp, vrootp, vrootp, cr);
- if (ret == 0) {
- /*
- * Check to see if the returned vnode is the same as
- * the one we expect. If not, give up.
- */
- if (!vn_compare(vp, compvp) &&
- !vnode_match(vp, compvp, cr)) {
- VN_RELE(compvp);
- goto notcached;
+ ret = ENAMETOOLONG;
}
-
- VN_RELE(compvp);
-
- /*
- * Return the result.
- */
- if (buflen <= rpn.pn_pathlen)
- goto notcached;
-
- bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
pn_free(&pn);
pn_free(&rpn);
- VN_RELE(vrootp);
- if (doclose) {
- (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
- VN_RELE(vp);
- }
- return (0);
+ goto out;
}
-
-notcached:
pn_free(&rpn);
+ vn_clearpath(vp, cached_stamp);
} else {
mutex_exit(&vp->v_lock);
}
-
pn_free(&pn);
if (vp->v_type != VDIR) {
/*
- * If we don't have a directory, try to find it in the dnlc via
- * reverse lookup. Once this is found, we can use the regular
- * directory search to find the full path.
+ * The reverse lookup tricks used by dirtopath aren't possible
+ * for non-directory entries. The best which can be done is
+ * clearing any stale v_path so later lookups can potentially
+ * repopulate it with a valid path.
*/
- if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) {
- /*
- * Check if we have read privilege so, that
- * we can lookup the path in the directory
- */
- ret = 0;
- if ((flags & LOOKUP_CHECKREAD)) {
- ret = VOP_ACCESS(pvp, VREAD, 0, cr, NULL);
- }
- if (ret == 0) {
- ret = dirtopath(vrootp, pvp, buf, buflen,
- flags, cr);
- }
- if (ret == 0) {
- len = strlen(buf);
- if (len + strlen(path) + 1 >= buflen) {
- ret = ENAMETOOLONG;
- } else {
- if (buf[len - 1] != '/')
- buf[len++] = '/';
- bcopy(path, buf + len,
- strlen(path) + 1);
- }
- }
-
- VN_RELE(pvp);
- } else
- ret = ENOENT;
- } else
+ ret = ENOENT;
+ } else {
ret = dirtopath(vrootp, vp, buf, buflen, flags, cr);
+ }
+out:
VN_RELE(vrootp);
if (doclose) {
(void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_subr.c b/usr/src/uts/common/fs/lxproc/lxpr_subr.c
new file mode 100644
index 0000000000..3c1405d4af
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_subr.c
@@ -0,0 +1,524 @@
+/*
+ * 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) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/varargs.h>
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include "lxproc.h"
+
+#define LXPRCACHE_NAME "lxpr_cache"
+
+static int lxpr_node_constructor(void *, void *, int);
+static void lxpr_node_destructor(void *, void *);
+
+static kmem_cache_t *lxpr_node_cache;
+
+struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t buffsize;
+ char *pos;
+ size_t beg;
+ int error;
+};
+
+int lxpr_bufsize = 4000;
+
+struct lxpr_uiobuf *
+lxpr_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxpr_uiobuf and output buffer */
+ int bufsize = lxpr_bufsize;
+ struct lxpr_uiobuf *uiobuf =
+ kmem_alloc(sizeof (struct lxpr_uiobuf) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->buffsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize);
+}
+
+void
+lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset)
+{
+ uiobuf->uiop->uio_offset = (off_t)offset;
+}
+
+void
+lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->buffsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxpr_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxpr_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxpr_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
+
+/*
+ * lxpr_lock():
+ *
+ * Lookup process from pid and return with p_plock and P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock(pid_t pid)
+{
+ proc_t *p;
+ kmutex_t *mp;
+
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ for (;;) {
+ mutex_enter(&pidlock);
+
+ /*
+ * If the pid is 1, we really want the zone's init process
+ */
+ p = prfind((pid == 1) ?
+ curproc->p_zone->zone_proc_initpid : pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+
+ /*
+ * p_lock is persistent, but p itself is not -- it could
+ * vanish during cv_wait(). Load p->p_lock now so we can
+ * drop it after cv_wait() without referencing p.
+ */
+ mp = &p->p_lock;
+ mutex_enter(mp);
+
+ mutex_exit(&pidlock);
+
+ if (p->p_flag & SEXITING) {
+ /*
+ * This process is exiting -- let it go.
+ */
+ mutex_exit(mp);
+ return (NULL);
+ }
+
+ if (!(p->p_proc_flag & P_PR_LOCK))
+ break;
+
+ cv_wait(&pr_pid_cv[p->p_slot], mp);
+ mutex_exit(mp);
+ }
+
+ p->p_proc_flag |= P_PR_LOCK;
+ THREAD_KPRI_REQUEST();
+ return (p);
+}
+
+/*
+ * lxpr_unlock()
+ *
+ * Unlock locked process
+ */
+void
+lxpr_unlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+ mutex_exit(&p->p_lock);
+ THREAD_KPRI_RELEASE();
+}
+
+void
+lxpr_initnodecache()
+{
+ lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME,
+ sizeof (lxpr_node_t), 0,
+ lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxpr_fininodecache()
+{
+ kmem_cache_destroy(lxpr_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxpr_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxpr_node_t *lxpnp = buf;
+ vnode_t *vp;
+
+ vp = lxpnp->lxpr_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxpr_vnodeops);
+ vp->v_data = lxpnp;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_node_destructor(void *buf, void *un)
+{
+ lxpr_node_t *lxpnp = buf;
+
+ vn_free(LXPTOV(lxpnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxproc node
+ */
+ino_t
+lxpr_inode(lxpr_nodetype_t type, pid_t pid, int fd)
+{
+ if (pid == 1)
+ pid = curproc->p_zone->zone_proc_initpid;
+
+ switch (type) {
+ case LXPR_PIDDIR:
+ return (pid + 1);
+ case LXPR_PROCDIR:
+ return (maxpid + 2);
+ case LXPR_PID_FD_FD:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ LXPR_NFILES + fd);
+ default:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ type);
+ }
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxpr_parentinode(lxpr_node_t *lxpnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxpnp->lxpr_type != LXPR_PROCDIR)
+ return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino);
+ else
+ return (lxpnp->lxpr_ino);
+}
+
+/*
+ * Allocate a new lxproc node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxpr_node_t *
+lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int fd)
+{
+ lxpr_node_t *lxpnp;
+ vnode_t *vp;
+ user_t *up;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_innactive
+ */
+ lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxpnp->lxpr_type = type;
+ lxpnp->lxpr_realvp = NULL;
+ lxpnp->lxpr_parent = dp;
+ VN_HOLD(dp);
+ if (p != NULL) {
+ lxpnp->lxpr_pid = ((p->p_pid ==
+ curproc->p_zone->zone_proc_initpid) ? 1 : p->p_pid);
+
+ lxpnp->lxpr_time = PTOU(p)->u_start;
+ lxpnp->lxpr_uid = crgetruid(p->p_cred);
+ lxpnp->lxpr_gid = crgetrgid(p->p_cred);
+ lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, fd);
+ } else {
+ /* Pretend files without a proc belong to sched */
+ lxpnp->lxpr_pid = 0;
+ lxpnp->lxpr_time = now;
+ lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0;
+ lxpnp->lxpr_ino = lxpr_inode(type, 0, 0);
+ }
+
+ /* initialize the vnode data */
+ vp = lxpnp->lxpr_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Do node specific stuff
+ */
+ switch (type) {
+ case LXPR_PROCDIR:
+ vp->v_flag |= VROOT;
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_CURDIR:
+ ASSERT(p != NULL);
+
+ /*
+ * Zombie check. p_stat is officially protected by pidlock,
+ * but we can't grab pidlock here because we already hold
+ * p_lock. Luckily if we look at the process exit code
+ * we see that p_stat only transisions from SRUN to SZOMB
+ * while p_lock is held. Aside from this, the only other
+ * p_stat transition that we need to be aware about is
+ * SIDL to SRUN, but that's not a problem since lxpr_lock()
+ * ignores nodes in the SIDL state so we'll never get a node
+ * that isn't already in the SRUN state.
+ */
+ if (p->p_stat == SZOMB) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ up = PTOU(p);
+ lxpnp->lxpr_realvp = up->u_cdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_ROOTDIR:
+ ASSERT(p != NULL);
+ /* Zombie check. see locking comment above */
+ if (p->p_stat == SZOMB) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ up = PTOU(p);
+ lxpnp->lxpr_realvp =
+ up->u_rdir != NULL ? up->u_rdir : rootdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_EXE:
+ ASSERT(p != NULL);
+ lxpnp->lxpr_realvp = p->p_exec;
+ if (lxpnp->lxpr_realvp != NULL) {
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777;
+ break;
+
+ case LXPR_SELF:
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_FD_FD:
+ ASSERT(p != NULL);
+ /* lxpr_realvp is set after we return */
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0700; /* read-write-exe owner only */
+ break;
+
+ case LXPR_PID_FDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0500; /* read-search by owner only */
+ break;
+
+ case LXPR_PIDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0511;
+ break;
+
+ case LXPR_NETDIR:
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by all */
+ break;
+
+ case LXPR_PID_ENV:
+ case LXPR_PID_MEM:
+ ASSERT(p != NULL);
+ /*FALLTHRU*/
+ case LXPR_KCORE:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0400; /* read-only by owner only */
+ break;
+
+ default:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0444; /* read-only by all */
+ break;
+ }
+
+ return (lxpnp);
+}
+
+
+/*
+ * Free the storage obtained from lxpr_getnode().
+ */
+void
+lxpr_freenode(lxpr_node_t *lxpnp)
+{
+ ASSERT(lxpnp != NULL);
+ ASSERT(LXPTOV(lxpnp) != NULL);
+
+ /*
+ * delete any association with realvp
+ */
+ if (lxpnp->lxpr_realvp != NULL)
+ VN_RELE(lxpnp->lxpr_realvp);
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxpnp->lxpr_parent != NULL)
+ VN_RELE(lxpnp->lxpr_parent);
+
+ /*
+ * Release the lxprnode.
+ */
+ kmem_cache_free(lxpr_node_cache, lxpnp);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c b/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c
new file mode 100644
index 0000000000..1bb7bd3823
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c
@@ -0,0 +1,367 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+
+#include "lxproc.h"
+
+/* Module level parameters */
+static int lxprocfstype;
+static dev_t lxprocdev;
+static kmutex_t lxpr_mount_lock;
+
+int nproc_highbit; /* highbit(v.v_nproc) */
+
+static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxpr_unmount(vfs_t *, int, cred_t *);
+static int lxpr_root(vfs_t *, vnode_t **);
+static int lxpr_statvfs(vfs_t *, statvfs64_t *);
+static int lxpr_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lxproc",
+ lxpr_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "generic linux procfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ /*
+ * destroy lxpr_node cache
+ */
+ lxpr_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxprocfstype);
+ vn_freevnodeops(lxpr_vnodeops);
+
+ mutex_destroy(&lxpr_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxpr_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxpr_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxpr_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxpr_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxpr_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxpr_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxpr_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ nproc_highbit = highbit(v.v_proc);
+ lxprocfstype = fstype;
+ ASSERT(lxprocfstype != 0);
+
+ mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxprocdev = makedevice(dev, 0);
+
+ /*
+ * Initialize cache for lxpr_nodes
+ */
+ lxpr_initnodecache();
+
+ return (0);
+}
+
+static int
+lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt;
+ zone_t *zone = curproc->p_zone;
+ ldi_ident_t li;
+ int err;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (zone == global_zone) {
+ zone_t *mntzone;
+
+ mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+ zone_rele(mntzone);
+ if (zone != mntzone)
+ return (EBUSY);
+ }
+
+ /*
+ * Having the resource be anything but "lxproc" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxproc", 0);
+
+ lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
+
+ if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+ return (err);
+ }
+
+ lxpr_mnt->lxprm_li = li;
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxpr_mount_lock);
+ kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * allocate the first vnode
+ */
+ zone_hold(lxpr_mnt->lxprm_zone = zone);
+
+ /* Arbitrarily set the parent vnode to the mounted over directory */
+ lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
+
+ /* Correctly set the fs for the root node */
+ lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxprocfstype;
+ vfsp->vfs_data = (caddr_t)lxpr_mnt;
+ vfsp->vfs_dev = lxprocdev;
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+ int count;
+
+ ASSERT(lxpr_mnt != NULL);
+ vp = LXPTOV(lxpr_mnt->lxprm_node);
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * must be root to unmount
+ */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EPERM);
+ }
+
+ /*
+ * forced unmount is not supported by this file system
+ */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxpr_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Ensure that no vnodes are in use on this mount point.
+ */
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * purge the dnlc cache for vnode entries
+ * associated with this file system
+ */
+ count = dnlc_purge_vfsp(vfsp, 0);
+
+ /*
+ * free up the lxprnode
+ */
+ lxpr_freenode(lxpr_mnt->lxprm_node);
+ zone_rele(lxpr_mnt->lxprm_zone);
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
+ vnode_t *vp = LXPTOV(lxpnp);
+
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ int n;
+ dev32_t d32;
+ extern uint_t nproc;
+
+ n = v.v_proc - nproc;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)v.v_proc + 2;
+ sp->f_ffree = (fsfilcnt64_t)n;
+ sp->f_favail = (fsfilcnt64_t)n;
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+
+ (void) strcpy(sp->f_fstr, "lxproc");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_vnops.c b/usr/src/uts/common/fs/lxproc/lxpr_vnops.c
new file mode 100644
index 0000000000..9c996891f3
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_vnops.c
@@ -0,0 +1,3099 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lxproc -- a loosely Linux-compatible /proc
+ *
+ * We have -- confusingly -- two implementations of Linux /proc. One is to
+ * support the LX brand with a Linux /proc entirely compatible with the Linux
+ * world view; the other -- this one -- is to support native (but Linux-borne)
+ * programs that wish to view the native system via the Linux /proc model. So
+ * the aspiration here is to provide something that sufficiently approximates
+ * the Linux /proc implementation for purposes of offering some compatibility
+ * for simple Linux /proc readers (e.g., ps/top/htop). However, it is not
+ * intended to exactly mimic Linux semantics; when choosing between offering
+ * compatibility and telling the truth, we emphatically pick the truth. A
+ * particular glaring example of this is the Linux notion of "tasks" (that is,
+ * threads), which -- due to historical misadventures on Linux -- allocate their
+ * identifiers from the process identifier space. (That is, each thread has in
+ * effect a pid.) Some Linux /proc readers have come to depend on this
+ * attribute, and become confused when threads appear with proper identifiers,
+ * so we simply opt for the pre-2.6 behavior, and do not present the tasks
+ * directory at all. Similarly, when choosing between offering compatibility
+ * and remaining consistent with our broader security model, we (obviously)
+ * choose security over compatibility. In short, this is meant to be a best
+ * effort -- no more -- and as such, it should not be unified with the much
+ * more complete Linux /proc implementation found in the LX brand.
+ */
+
+#include <sys/cpupart.h>
+#include <sys/cpuvar.h>
+#include <sys/session.h>
+#include <sys/vmparam.h>
+#include <sys/mman.h>
+#include <vm/rm.h>
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+
+/* Dependent on procfs */
+extern kthread_t *prchoose(proc_t *);
+
+#include "lxproc.h"
+
+extern pgcnt_t swapfs_minfree;
+extern time_t boot_time;
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxprinit() in lxpr_vfsops.c
+ */
+vnodeops_t *lxpr_vnodeops;
+
+static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxpr_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *);
+static int lxpr_sync(void);
+static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxpr_lookup_procdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_piddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_not_a_dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_netdir(vnode_t *, char *);
+
+static int lxpr_readdir_procdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_piddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_not_a_dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_netdir(lxpr_node_t *, uio_t *, int *);
+
+static void lxpr_read_invalid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_empty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cpuinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_isdir(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fd(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_kmsg(lxpr_node_t *, lxpr_uiobuf_t *, ldi_handle_t);
+static void lxpr_read_loadavg(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_meminfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_mounts(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_partitions(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_uptime(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_version(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_net_arp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev_mcast(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_igmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_vif(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_mcfilter(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_netstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_raw(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rpc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rt_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_sockstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_snmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_unix(lxpr_node_t *, lxpr_uiobuf_t *);
+
+/*
+ * Simple conversion
+ */
+#define btok(x) ((x) >> 10) /* bytes to kbytes */
+#define ptok(x) ((x) << (PAGESHIFT - 10)) /* pages to kbytes */
+
+/*
+ * The lxproc vnode operations vector
+ */
+const fs_operation_def_t lxpr_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxpr_open },
+ VOPNAME_CLOSE, { .vop_close = lxpr_close },
+ VOPNAME_READ, { .vop_read = lxpr_read },
+ VOPNAME_GETATTR, { .vop_getattr = lxpr_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxpr_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxpr_lookup },
+ VOPNAME_READDIR, { .vop_readdir = lxpr_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxpr_readlink },
+ VOPNAME_FSYNC, { .error = lxpr_sync },
+ VOPNAME_SEEK, { .error = lxpr_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxpr_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxpr_cmp },
+ VOPNAME_REALVP, { .vop_realvp = lxpr_realvp },
+ NULL, NULL
+};
+
+/*
+ * file contents of an lxproc directory.
+ */
+static lxpr_dirent_t lxpr_dir[] = {
+ { LXPR_CMDLINE, "cmdline" },
+ { LXPR_CPUINFO, "cpuinfo" },
+ { LXPR_DEVICES, "devices" },
+ { LXPR_DMA, "dma" },
+ { LXPR_FILESYSTEMS, "filesystems" },
+ { LXPR_INTERRUPTS, "interrupts" },
+ { LXPR_IOPORTS, "ioports" },
+ { LXPR_KCORE, "kcore" },
+ { LXPR_KMSG, "kmsg" },
+ { LXPR_LOADAVG, "loadavg" },
+ { LXPR_MEMINFO, "meminfo" },
+ { LXPR_MOUNTS, "mounts" },
+ { LXPR_NETDIR, "net" },
+ { LXPR_PARTITIONS, "partitions" },
+ { LXPR_SELF, "self" },
+ { LXPR_STAT, "stat" },
+ { LXPR_UPTIME, "uptime" },
+ { LXPR_VERSION, "version" }
+};
+
+#define PROCDIRFILES (sizeof (lxpr_dir) / sizeof (lxpr_dir[0]))
+
+/*
+ * Contents of an /lxproc/<pid> directory.
+ */
+static lxpr_dirent_t piddir[] = {
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_STATUS, "status" },
+ { LXPR_PID_FDDIR, "fd" }
+};
+
+#define PIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]))
+
+/*
+ * contents of /lxproc/net directory
+ */
+static lxpr_dirent_t netdir[] = {
+ { LXPR_NET_ARP, "arp" },
+ { LXPR_NET_DEV, "dev" },
+ { LXPR_NET_DEV_MCAST, "dev_mcast" },
+ { LXPR_NET_IGMP, "igmp" },
+ { LXPR_NET_IP_MR_CACHE, "ip_mr_cache" },
+ { LXPR_NET_IP_MR_VIF, "ip_mr_vif" },
+ { LXPR_NET_MCFILTER, "mcfilter" },
+ { LXPR_NET_NETSTAT, "netstat" },
+ { LXPR_NET_RAW, "raw" },
+ { LXPR_NET_ROUTE, "route" },
+ { LXPR_NET_RPC, "rpc" },
+ { LXPR_NET_RT_CACHE, "rt_cache" },
+ { LXPR_NET_SOCKSTAT, "sockstat" },
+ { LXPR_NET_SNMP, "snmp" },
+ { LXPR_NET_STAT, "stat" },
+ { LXPR_NET_TCP, "tcp" },
+ { LXPR_NET_UDP, "udp" },
+ { LXPR_NET_UNIX, "unix" }
+};
+
+#define NETDIRFILES (sizeof (netdir) / sizeof (netdir[0]))
+
+/*
+ * These are the major signal number differences between Linux and native:
+ *
+ * ====================================
+ * | Number | Linux | Native |
+ * | ====== | ========= | ========== |
+ * | 7 | SIGBUS | SIGEMT |
+ * | 10 | SIGUSR1 | SIGBUS |
+ * | 12 | SIGUSR2 | SIGSYS |
+ * | 16 | SIGSTKFLT | SIGUSR1 |
+ * | 17 | SIGCHLD | SIGUSR2 |
+ * | 18 | SIGCONT | SIGCHLD |
+ * | 19 | SIGSTOP | SIGPWR |
+ * | 20 | SIGTSTP | SIGWINCH |
+ * | 21 | SIGTTIN | SIGURG |
+ * | 22 | SIGTTOU | SIGPOLL |
+ * | 23 | SIGURG | SIGSTOP |
+ * | 24 | SIGXCPU | SIGTSTP |
+ * | 25 | SIGXFSZ | SIGCONT |
+ * | 26 | SIGVTALARM | SIGTTIN |
+ * | 27 | SIGPROF | SIGTTOU |
+ * | 28 | SIGWINCH | SIGVTALARM |
+ * | 29 | SIGPOLL | SIGPROF |
+ * | 30 | SIGPWR | SIGXCPU |
+ * | 31 | SIGSYS | SIGXFSZ |
+ * ====================================
+ *
+ * Not every Linux signal maps to a native signal, nor does every native
+ * signal map to a Linux counterpart. However, when signals do map, the
+ * mapping is unique.
+ */
+static int
+lxpr_sigmap[NSIG] = {
+ 0,
+ LX_SIGHUP,
+ LX_SIGINT,
+ LX_SIGQUIT,
+ LX_SIGILL,
+ LX_SIGTRAP,
+ LX_SIGABRT,
+ LX_SIGSTKFLT,
+ LX_SIGFPE,
+ LX_SIGKILL,
+ LX_SIGBUS,
+ LX_SIGSEGV,
+ LX_SIGSYS,
+ LX_SIGPIPE,
+ LX_SIGALRM,
+ LX_SIGTERM,
+ LX_SIGUSR1,
+ LX_SIGUSR2,
+ LX_SIGCHLD,
+ LX_SIGPWR,
+ LX_SIGWINCH,
+ LX_SIGURG,
+ LX_SIGPOLL,
+ LX_SIGSTOP,
+ LX_SIGTSTP,
+ LX_SIGCONT,
+ LX_SIGTTIN,
+ LX_SIGTTOU,
+ LX_SIGVTALRM,
+ LX_SIGPROF,
+ LX_SIGXCPU,
+ LX_SIGXFSZ,
+ -1, /* 32: illumos SIGWAITING */
+ -1, /* 33: illumos SIGLWP */
+ -1, /* 34: illumos SIGFREEZE */
+ -1, /* 35: illumos SIGTHAW */
+ -1, /* 36: illumos SIGCANCEL */
+ -1, /* 37: illumos SIGLOST */
+ -1, /* 38: illumos SIGXRES */
+ -1, /* 39: illumos SIGJVM1 */
+ -1, /* 40: illumos SIGJVM2 */
+ -1, /* 41: illumos SIGINFO */
+ LX_SIGRTMIN, /* 42: illumos _SIGRTMIN */
+ LX_SIGRTMIN + 1,
+ LX_SIGRTMIN + 2,
+ LX_SIGRTMIN + 3,
+ LX_SIGRTMIN + 4,
+ LX_SIGRTMIN + 5,
+ LX_SIGRTMIN + 6,
+ LX_SIGRTMIN + 7,
+ LX_SIGRTMIN + 8,
+ LX_SIGRTMIN + 9,
+ LX_SIGRTMIN + 10,
+ LX_SIGRTMIN + 11,
+ LX_SIGRTMIN + 12,
+ LX_SIGRTMIN + 13,
+ LX_SIGRTMIN + 14,
+ LX_SIGRTMIN + 15,
+ LX_SIGRTMIN + 16,
+ LX_SIGRTMIN + 17,
+ LX_SIGRTMIN + 18,
+ LX_SIGRTMIN + 19,
+ LX_SIGRTMIN + 20,
+ LX_SIGRTMIN + 21,
+ LX_SIGRTMIN + 22,
+ LX_SIGRTMIN + 23,
+ LX_SIGRTMIN + 24,
+ LX_SIGRTMIN + 25,
+ LX_SIGRTMIN + 26,
+ LX_SIGRTMIN + 27,
+ LX_SIGRTMIN + 28,
+ LX_SIGRTMIN + 29,
+ LX_SIGRTMIN + 30,
+ LX_SIGRTMAX
+};
+
+/*
+ * lxpr_open(): Vnode operation for VOP_OPEN()
+ */
+static int
+lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *vp = *vpp;
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *rvp;
+ int error = 0;
+
+ /*
+ * We only allow reading in this file systrem
+ */
+ if (flag & FWRITE)
+ return (EROFS);
+
+ /*
+ * If we are opening an underlying file only allow regular files
+ * reject the open for anything but a regular file.
+ * Just do it if we are opening the current or root directory.
+ */
+ if (lxpnp->lxpr_realvp != NULL) {
+ rvp = lxpnp->lxpr_realvp;
+
+ if (type == LXPR_PID_FD_FD && rvp->v_type != VREG)
+ error = EACCES;
+ else {
+ /*
+ * Need to hold rvp since VOP_OPEN() may release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ if (error) {
+ VN_RELE(rvp);
+ } else {
+ *vpp = rvp;
+ VN_RELE(vp);
+ }
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * lxpr_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpr = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpr->lxpr_type;
+
+ /*
+ * we should never get here because the close is done on the realvp
+ * for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR &&
+ type != LXPR_PID_EXE);
+
+ return (0);
+}
+
+static void (*lxpr_read_function[LXPR_NFILES])() = {
+ lxpr_read_isdir, /* /proc */
+ lxpr_read_isdir, /* /proc/<pid> */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/cmdline */
+ lxpr_read_empty, /* /proc/<pid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/cwd */
+ lxpr_read_empty, /* /proc/<pid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/exe */
+ lxpr_read_pid_maps, /* /proc/<pid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/mem */
+ lxpr_read_invalid, /* /proc/<pid>/root */
+ lxpr_read_pid_stat, /* /proc/<pid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/statm */
+ lxpr_read_pid_status, /* /proc/<pid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/fd/nn */
+ lxpr_read_empty, /* /proc/cmdline */
+ lxpr_read_cpuinfo, /* /proc/cpuinfo */
+ lxpr_read_empty, /* /proc/devices */
+ lxpr_read_empty, /* /proc/dma */
+ lxpr_read_empty, /* /proc/filesystems */
+ lxpr_read_empty, /* /proc/interrupts */
+ lxpr_read_empty, /* /proc/ioports */
+ lxpr_read_empty, /* /proc/kcore */
+ lxpr_read_invalid, /* /proc/kmsg -- see lxpr_read() */
+ lxpr_read_loadavg, /* /proc/loadavg */
+ lxpr_read_meminfo, /* /proc/meminfo */
+ lxpr_read_mounts, /* /proc/mounts */
+ lxpr_read_isdir, /* /proc/net */
+ lxpr_read_net_arp, /* /proc/net/arp */
+ lxpr_read_net_dev, /* /proc/net/dev */
+ lxpr_read_net_dev_mcast, /* /proc/net/dev_mcast */
+ lxpr_read_net_igmp, /* /proc/net/igmp */
+ lxpr_read_net_ip_mr_cache, /* /proc/net/ip_mr_cache */
+ lxpr_read_net_ip_mr_vif, /* /proc/net/ip_mr_vif */
+ lxpr_read_net_mcfilter, /* /proc/net/mcfilter */
+ lxpr_read_net_netstat, /* /proc/net/netstat */
+ lxpr_read_net_raw, /* /proc/net/raw */
+ lxpr_read_net_route, /* /proc/net/route */
+ lxpr_read_net_rpc, /* /proc/net/rpc */
+ lxpr_read_net_rt_cache, /* /proc/net/rt_cache */
+ lxpr_read_net_sockstat, /* /proc/net/sockstat */
+ lxpr_read_net_snmp, /* /proc/net/snmp */
+ lxpr_read_net_stat, /* /proc/net/stat */
+ lxpr_read_net_tcp, /* /proc/net/tcp */
+ lxpr_read_net_udp, /* /proc/net/udp */
+ lxpr_read_net_unix, /* /proc/net/unix */
+ lxpr_read_partitions, /* /proc/partitions */
+ lxpr_read_invalid, /* /proc/self */
+ lxpr_read_stat, /* /proc/stat */
+ lxpr_read_uptime, /* /proc/uptime */
+ lxpr_read_version, /* /proc/version */
+};
+
+/*
+ * Array of lookup functions, indexed by /lxproc file type.
+ */
+static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
+ lxpr_lookup_procdir, /* /proc */
+ lxpr_lookup_piddir, /* /proc/<pid> */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/status */
+ lxpr_lookup_fddir, /* /proc/<pid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_lookup_not_a_dir, /* /proc/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/cpuinfo */
+ lxpr_lookup_not_a_dir, /* /proc/devices */
+ lxpr_lookup_not_a_dir, /* /proc/dma */
+ lxpr_lookup_not_a_dir, /* /proc/filesystems */
+ lxpr_lookup_not_a_dir, /* /proc/interrupts */
+ lxpr_lookup_not_a_dir, /* /proc/ioports */
+ lxpr_lookup_not_a_dir, /* /proc/kcore */
+ lxpr_lookup_not_a_dir, /* /proc/kmsg */
+ lxpr_lookup_not_a_dir, /* /proc/loadavg */
+ lxpr_lookup_not_a_dir, /* /proc/meminfo */
+ lxpr_lookup_not_a_dir, /* /proc/mounts */
+ lxpr_lookup_netdir, /* /proc/net */
+ lxpr_lookup_not_a_dir, /* /proc/net/arp */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_lookup_not_a_dir, /* /proc/net/igmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_lookup_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_lookup_not_a_dir, /* /proc/net/netstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/raw */
+ lxpr_lookup_not_a_dir, /* /proc/net/route */
+ lxpr_lookup_not_a_dir, /* /proc/net/rpc */
+ lxpr_lookup_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/sockstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/snmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/stat */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp */
+ lxpr_lookup_not_a_dir, /* /proc/net/unix */
+ lxpr_lookup_not_a_dir, /* /proc/partitions */
+ lxpr_lookup_not_a_dir, /* /proc/self */
+ lxpr_lookup_not_a_dir, /* /proc/stat */
+ lxpr_lookup_not_a_dir, /* /proc/uptime */
+ lxpr_lookup_not_a_dir, /* /proc/version */
+};
+
+/*
+ * Array of readdir functions, indexed by /proc file type.
+ */
+static int (*lxpr_readdir_function[LXPR_NFILES])() = {
+ lxpr_readdir_procdir, /* /proc */
+ lxpr_readdir_piddir, /* /proc/<pid> */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/status */
+ lxpr_readdir_fddir, /* /proc/<pid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_readdir_not_a_dir, /* /proc/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/cpuinfo */
+ lxpr_readdir_not_a_dir, /* /proc/devices */
+ lxpr_readdir_not_a_dir, /* /proc/dma */
+ lxpr_readdir_not_a_dir, /* /proc/filesystems */
+ lxpr_readdir_not_a_dir, /* /proc/interrupts */
+ lxpr_readdir_not_a_dir, /* /proc/ioports */
+ lxpr_readdir_not_a_dir, /* /proc/kcore */
+ lxpr_readdir_not_a_dir, /* /proc/kmsg */
+ lxpr_readdir_not_a_dir, /* /proc/loadavg */
+ lxpr_readdir_not_a_dir, /* /proc/meminfo */
+ lxpr_readdir_not_a_dir, /* /proc/mounts */
+ lxpr_readdir_netdir, /* /proc/net */
+ lxpr_readdir_not_a_dir, /* /proc/net/arp */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_readdir_not_a_dir, /* /proc/net/igmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_readdir_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_readdir_not_a_dir, /* /proc/net/netstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/raw */
+ lxpr_readdir_not_a_dir, /* /proc/net/route */
+ lxpr_readdir_not_a_dir, /* /proc/net/rpc */
+ lxpr_readdir_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/sockstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/snmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/stat */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp */
+ lxpr_readdir_not_a_dir, /* /proc/net/unix */
+ lxpr_readdir_not_a_dir, /* /proc/partitions */
+ lxpr_readdir_not_a_dir, /* /proc/self */
+ lxpr_readdir_not_a_dir, /* /proc/stat */
+ lxpr_readdir_not_a_dir, /* /proc/uptime */
+ lxpr_readdir_not_a_dir, /* /proc/version */
+};
+
+
+/*
+ * lxpr_read(): Vnode operation for VOP_READ()
+ *
+ * As the format of all the files that can be read in lxproc is human readable
+ * and not binary structures there do not have to be different read variants
+ * depending on whether the reading process model is 32- or 64-bit.
+ */
+/* ARGSUSED */
+static int
+lxpr_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ lxpr_uiobuf_t *uiobuf = lxpr_uiobuf_new(uiop);
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ if (type == LXPR_KMSG) {
+ ldi_ident_t li = VTOLXPM(vp)->lxprm_li;
+ ldi_handle_t ldih;
+ struct strioctl str;
+ int rv;
+
+ /*
+ * Open the zone's console device using the layered driver
+ * interface.
+ */
+ if ((error =
+ ldi_open_by_name("/dev/log", FREAD, cr, &ldih, li)) != 0)
+ return (error);
+
+ /*
+ * Send an ioctl to the underlying console device, letting it
+ * know we're interested in getting console messages.
+ */
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if ((error = ldi_ioctl(ldih, I_STR,
+ (intptr_t)&str, FKIOCTL, cr, &rv)) != 0)
+ return (error);
+
+ lxpr_read_kmsg(lxpnp, uiobuf, ldih);
+
+ if ((error = ldi_close(ldih, FREAD, cr)) != 0)
+ return (error);
+ } else {
+ lxpr_read_function[type](lxpnp, uiobuf);
+ }
+
+ error = lxpr_uiobuf_flush(uiobuf);
+ lxpr_uiobuf_free(uiobuf);
+
+ return (error);
+}
+
+/*
+ * lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
+ *
+ * Various special case reads:
+ * - trying to read a directory
+ * - invalid file (used to mean a file that should be implemented,
+ * but isn't yet)
+ * - empty file
+ * - wait to be able to read a file that will never have anything to read
+ */
+/* ARGSUSED */
+static void
+lxpr_read_isdir(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EISDIR);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_invalid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_empty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_pid_cmdline():
+ *
+ * This is not precisely compatible with Linux: the Linux cmdline returns argv
+ * with the correct separation using \0 between the arguments, but we cannot do
+ * that without copying the real argv from the correct process context. This
+ * is too difficult to attempt so we pretend that the entire cmdline is just
+ * argv[0]. This is good enough for ps and htop to display correctly, but might
+ * cause some other things not to work correctly.
+ */
+static void
+lxpr_read_pid_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CMDLINE);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ buf = PTOU(p)->u_argv != 0 ? PTOU(p)->u_psargs : PTOU(p)->u_comm;
+
+ lxpr_uiobuf_write(uiobuf, buf, strlen(buf) + 1);
+ lxpr_unlock(p);
+}
+
+/*
+ * lxpr_read_pid_maps(): memory map file
+ */
+static void
+lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ struct seg *seg;
+ char *buf;
+ int buflen = MAXPATHLEN;
+ struct print_data {
+ caddr_t saddr;
+ caddr_t eaddr;
+ int type;
+ char prot[5];
+ uint32_t offset;
+ vnode_t *vp;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *pbuf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MAPS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+
+ if (as == &kas) {
+ lxpr_unlock(p);
+ return;
+ }
+
+ mutex_exit(&p->p_lock);
+
+ /* Iterate over all segments in the address space */
+ AS_LOCK_ENTER(as, RW_READER);
+ for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
+ vnode_t *vp;
+ uint_t protbits;
+
+ pbuf = kmem_alloc(sizeof (*pbuf), KM_SLEEP);
+
+ pbuf->saddr = seg->s_base;
+ pbuf->eaddr = seg->s_base+seg->s_size;
+ pbuf->type = SEGOP_GETTYPE(seg, seg->s_base);
+
+ /*
+ * Cheat and only use the protection bits of the first page
+ * in the segment
+ */
+ (void) strncpy(pbuf->prot, "----", sizeof (pbuf->prot));
+ (void) SEGOP_GETPROT(seg, seg->s_base, 0, &protbits);
+
+ if (protbits & PROT_READ) pbuf->prot[0] = 'r';
+ if (protbits & PROT_WRITE) pbuf->prot[1] = 'w';
+ if (protbits & PROT_EXEC) pbuf->prot[2] = 'x';
+ if (pbuf->type & MAP_SHARED) pbuf->prot[3] = 's';
+ else if (pbuf->type & MAP_PRIVATE) pbuf->prot[3] = 'p';
+
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG) {
+ VN_HOLD(vp);
+ pbuf->vp = vp;
+ } else {
+ pbuf->vp = NULL;
+ }
+
+ pbuf->offset = (uint32_t)SEGOP_GETOFFSET(seg, pbuf->saddr);
+
+ pbuf->next = NULL;
+ *print_tail = pbuf;
+ print_tail = &pbuf->next;
+ }
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ buf = kmem_alloc(buflen, KM_SLEEP);
+
+ /* print the data we've extracted */
+ pbuf = print_head;
+ while (pbuf != NULL) {
+ struct print_data *pbuf_next;
+ vattr_t vattr;
+
+ int maj = 0;
+ int min = 0;
+ u_longlong_t inode = 0;
+
+ *buf = '\0';
+ if (pbuf->vp != NULL) {
+ vattr.va_mask = AT_FSID | AT_NODEID;
+ if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(),
+ NULL) == 0) {
+ maj = getmajor(vattr.va_fsid);
+ min = getminor(vattr.va_fsid);
+ inode = vattr.va_nodeid;
+ }
+ (void) vnodetopath(NULL, pbuf->vp, buf, buflen, CRED());
+ VN_RELE(pbuf->vp);
+ }
+
+ if (*buf != '\0') {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02d:%03d %lld %s\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode, buf);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02d:%03d %lld\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode);
+ }
+
+ pbuf_next = pbuf->next;
+ kmem_free(pbuf, sizeof (*pbuf));
+ pbuf = pbuf_next;
+ }
+
+ kmem_free(buf, buflen);
+}
+
+/*
+ * lxpr_read_pid_statm(): memory status file
+ */
+static void
+lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ size_t vsize;
+ size_t rss;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATM);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+
+ mutex_exit(&p->p_lock);
+
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = btopr(as->a_resvsize);
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%lu %lu %lu %lu %lu %lu %lu\n",
+ vsize, rss, 0l, rss, 0l, 0l, 0l);
+}
+
+/*
+ * lxpr_read_pid_status(): status file
+ */
+static void
+lxpr_read_pid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ user_t *up;
+ cred_t *cr;
+ const gid_t *groups;
+ int ngroups;
+ struct as *as;
+ char *status;
+ pid_t pid, ppid;
+ size_t vsize;
+ size_t rss;
+ k_sigset_t current, ignore, handle;
+ int i, lx_sig;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATUS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ pid = p->p_pid;
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's init
+ * process
+ */
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1;
+ ppid = 0; /* parent pid for init is 0 */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP)
+ ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+ }
+
+ t = prchoose(p);
+ if (t != NULL) {
+ switch (t->t_state) {
+ case TS_SLEEP:
+ status = "S (sleeping)";
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ status = "R (running)";
+ break;
+ case TS_ZOMB:
+ status = "Z (zombie)";
+ break;
+ case TS_STOPPED:
+ status = "T (stopped)";
+ break;
+ default:
+ status = "! (unknown)";
+ break;
+ }
+ thread_unlock(t);
+ } else {
+ /*
+ * there is a hole in the exit code, where a proc can have
+ * no threads but it is yet to be flagged SZOMB. We will
+ * assume we are about to become a zombie
+ */
+ status = "Z (zombie)";
+ }
+
+ up = PTOU(p);
+ mutex_enter(&p->p_crlock);
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Name:\t%s\n"
+ "State:\t%s\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "TracerPid:\t%d\n"
+ "Uid:\t%u\t%u\t%u\t%u\n"
+ "Gid:\t%u\t%u\t%u\t%u\n"
+ "FDSize:\t%d\n"
+ "Groups:\t",
+ up->u_comm,
+ status,
+ pid, /* thread group id - same as pid */
+ pid,
+ ppid,
+ 0,
+ crgetruid(cr), crgetuid(cr), crgetsuid(cr), crgetuid(cr),
+ crgetrgid(cr), crgetgid(cr), crgetsgid(cr), crgetgid(cr),
+ p->p_fno_ctl);
+
+ ngroups = crgetngroups(cr);
+ groups = crgetgroups(cr);
+ for (i = 0; i < ngroups; i++) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%u ",
+ groups[i]);
+ }
+ crfree(cr);
+
+ as = p->p_as;
+ if ((p->p_stat != SZOMB) && !(p->p_flag & SSYS) && (as != &kas)) {
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB",
+ btok(vsize),
+ 0l,
+ ptok(rss),
+ 0l,
+ btok(p->p_stksize),
+ ptok(rss),
+ 0l);
+ }
+
+ sigemptyset(&current);
+ sigemptyset(&ignore);
+ sigemptyset(&handle);
+
+ for (i = 1; i < NSIG; i++) {
+ lx_sig = lxpr_sigmap[i];
+
+ if ((lx_sig > 0) && (lx_sig <= LX_NSIG)) {
+ if (sigismember(&p->p_sig, i))
+ sigaddset(&current, lx_sig);
+
+ if (up->u_signal[i - 1] == SIG_IGN)
+ sigaddset(&ignore, lx_sig);
+ else if (up->u_signal[i - 1] != SIG_DFL)
+ sigaddset(&handle, lx_sig);
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "SigPnd:\t%08x%08x\n"
+ "SigBlk:\t%08x%08x\n"
+ "SigIgn:\t%08x%08x\n"
+ "SigCgt:\t%08x%08x\n"
+ "CapInh:\t%016x\n"
+ "CapPrm:\t%016x\n"
+ "CapEff:\t%016x\n",
+ current.__sigbits[1], current.__sigbits[0],
+ 0, 0, /* signals blocked on per thread basis */
+ ignore.__sigbits[1], ignore.__sigbits[0],
+ handle.__sigbits[1], handle.__sigbits[0],
+ /* Can't do anything with linux capabilities */
+ 0,
+ 0,
+ 0);
+
+ lxpr_unlock(p);
+}
+
+
+/*
+ * lxpr_read_pid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ struct as *as;
+ char stat;
+ pid_t pid, ppid, pgpid, spid;
+ gid_t psgid;
+ dev_t psdev;
+ size_t rss, vsize;
+ int nice, pri;
+ caddr_t wchan;
+ processorid_t cpu;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STAT);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ pid = p->p_pid;
+
+ /*
+ * Set Linux defaults if we're the zone's init process
+ */
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1; /* PID for init */
+ ppid = 0; /* parent PID for init is 0 */
+ pgpid = 0; /* process group for init is 0 */
+ psgid = (gid_t)-1; /* credential GID for init is -1 */
+ spid = 0; /* session id for init is 0 */
+ psdev = 0; /* session device for init is 0 */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP) ?
+ curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+
+ pgpid = p->p_pgrp;
+
+ mutex_enter(&p->p_splock);
+ mutex_enter(&p->p_sessp->s_lock);
+ spid = p->p_sessp->s_sid;
+ psdev = p->p_sessp->s_dev;
+ if (p->p_sessp->s_cred)
+ psgid = crgetgid(p->p_sessp->s_cred);
+ else
+ psgid = crgetgid(p->p_cred);
+
+ mutex_exit(&p->p_sessp->s_lock);
+ mutex_exit(&p->p_splock);
+ }
+
+ t = prchoose(p);
+ if (t != NULL) {
+ switch (t->t_state) {
+ case TS_SLEEP:
+ stat = 'S'; break;
+ case TS_RUN:
+ case TS_ONPROC:
+ stat = 'R'; break;
+ case TS_ZOMB:
+ stat = 'Z'; break;
+ case TS_STOPPED:
+ stat = 'T'; break;
+ default:
+ stat = '!'; break;
+ }
+
+ if (CL_DONICE(t, NULL, 0, &nice) != 0)
+ nice = 0;
+
+ pri = t->t_pri;
+ wchan = t->t_wchan;
+ cpu = t->t_cpu->cpu_id;
+ thread_unlock(t);
+ } else {
+ /* Only zombies have no threads */
+ stat = 'Z';
+ nice = 0;
+ pri = 0;
+ wchan = 0;
+ cpu = 0;
+ }
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%d (%s) %c %d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%lu %lu %ld %ld "
+ "%d %d %d "
+ "%lu "
+ "%lu "
+ "%lu %ld %llu "
+ "%lu %lu %u "
+ "%lu %lu "
+ "%lu %lu %lu %lu "
+ "%lu "
+ "%lu %lu "
+ "%d "
+ "%d"
+ "\n",
+ pid, PTOU(p)->u_comm, stat, ppid, pgpid, spid, psdev, psgid,
+ 0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
+ p->p_utime, p->p_stime, p->p_cutime, p->p_cstime,
+ pri, nice, p->p_lwpcnt,
+ 0l, /* itrealvalue (time before next SIGALRM) */
+ PTOU(p)->u_ticks,
+ vsize, rss, p->p_vmem_ctl,
+ 0l, 0l, USRSTACK, /* startcode, endcode, startstack */
+ 0l, 0l, /* kstkesp, kstkeip */
+ 0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch */
+ wchan,
+ 0l, 0l, /* nswap, cnswap */
+ 0, /* exit_signal */
+ cpu);
+
+ lxpr_unlock(p);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_arp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "Inter-| Receive "
+ " | Transmit\n");
+ lxpr_uiobuf_printf(uiobuf, " face |bytes packets errs drop fifo"
+ " frame compressed multicast|bytes packets errs drop fifo"
+ " colls carrier compressed\n");
+
+ /*
+ * Data about each interface should go here, but that shouldn't be added
+ * unless there is an lxproc reader that actually makes use of it (and
+ * doesn't need anything else that we refuse to provide)...
+ */
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_igmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_vif(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_netstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_raw(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rpc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rt_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_sockstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_snmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_kmsg(): read the contents of the kernel message queue. We
+ * translate this into the reception of console messages for this zone; each
+ * read copies out a single zone console message, or blocks until the next one
+ * is produced.
+ */
+
+#define LX_KMSG_PRI "<0>"
+
+static void
+lxpr_read_kmsg(lxpr_node_t *lxpnp, struct lxpr_uiobuf *uiobuf, ldi_handle_t lh)
+{
+ mblk_t *mp;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_KMSG);
+
+ if (ldi_getmsg(lh, &mp, NULL) == 0) {
+ /*
+ * lxproc doesn't like successive reads to the same file
+ * descriptor unless we do an explicit rewind each time.
+ */
+ lxpr_uiobuf_seek(uiobuf, 0);
+
+ lxpr_uiobuf_printf(uiobuf, "%s%s", LX_KMSG_PRI,
+ mp->b_cont->b_rptr);
+
+ freemsg(mp);
+ }
+}
+
+/*
+ * lxpr_read_loadavg(): read the contents of the "loadavg" file. We do just
+ * enough for uptime and other simple lxproc readers to work
+ */
+extern int nthread;
+
+static void
+lxpr_read_loadavg(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ulong_t avenrun1;
+ ulong_t avenrun5;
+ ulong_t avenrun15;
+ ulong_t avenrun1_cs;
+ ulong_t avenrun5_cs;
+ ulong_t avenrun15_cs;
+ int loadavg[3];
+ int *loadbuf;
+ cpupart_t *cp;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ uint_t nrunnable = 0;
+ rctl_qty_t nlwps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_LOADAVG);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Need to add up values over all CPU partitions. If pools are active,
+ * only report the values of the zone's partition, which by definition
+ * includes the current CPU.
+ */
+ if (pool_pset_enabled()) {
+ psetid_t psetid = zone_pset_get(curproc->p_zone);
+
+ ASSERT(curproc->p_zone != &zone0);
+ cp = CPU->cpu_part;
+
+ nrunnable = cp->cp_nrunning + cp->cp_nrunnable;
+ (void) cpupart_get_loadavg(psetid, &loadavg[0], 3);
+ loadbuf = &loadavg[0];
+ } else {
+ cp = cp_list_head;
+ do {
+ nrunnable += cp->cp_nrunning + cp->cp_nrunnable;
+ } while ((cp = cp->cp_next) != cp_list_head);
+
+ loadbuf = zone == global_zone ?
+ &avenrun[0] : zone->zone_avenrun;
+ }
+
+ /*
+ * If we're in the non-global zone, we'll report the total number of
+ * LWPs in the zone for the "nproc" parameter of /proc/loadavg,
+ * otherwise will just use nthread (which will include kernel threads,
+ * but should be good enough for lxproc).
+ */
+ nlwps = zone == global_zone ? nthread : zone->zone_nlwps;
+
+ mutex_exit(&cpu_lock);
+
+ avenrun1 = loadbuf[0] >> FSHIFT;
+ avenrun1_cs = ((loadbuf[0] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun5 = loadbuf[1] >> FSHIFT;
+ avenrun5_cs = ((loadbuf[1] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun15 = loadbuf[2] >> FSHIFT;
+ avenrun15_cs = ((loadbuf[2] & (FSCALE-1)) * 100) >> FSHIFT;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
+ avenrun1, avenrun1_cs,
+ avenrun5, avenrun5_cs,
+ avenrun15, avenrun15_cs,
+ nrunnable, nlwps, 0);
+}
+
+/*
+ * lxpr_read_meminfo(): read the contents of the "meminfo" file.
+ */
+static void
+lxpr_read_meminfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ int global = zone == global_zone;
+ long total_mem, free_mem, total_swap, used_swap;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_MEMINFO);
+
+ if (global || zone->zone_phys_mem_ctl == UINT64_MAX) {
+ total_mem = physmem * PAGESIZE;
+ free_mem = freemem * PAGESIZE;
+ } else {
+ total_mem = zone->zone_phys_mem_ctl;
+ free_mem = zone->zone_phys_mem_ctl - zone->zone_phys_mem;
+ }
+
+ if (global || zone->zone_max_swap_ctl == UINT64_MAX) {
+ total_swap = k_anoninfo.ani_max * PAGESIZE;
+ used_swap = k_anoninfo.ani_phys_resv * PAGESIZE;
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ total_swap = zone->zone_max_swap_ctl;
+ used_swap = zone->zone_max_swap;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ " total: used: free: shared: buffers: cached:\n"
+ "Mem: %8lu %8lu %8lu %8u %8u %8u\n"
+ "Swap: %8lu %8lu %8lu\n"
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemShared: %8u kB\n"
+ "Buffers: %8u kB\n"
+ "Cached: %8u kB\n"
+ "SwapCached:%8u kB\n"
+ "Active: %8u kB\n"
+ "Inactive: %8u kB\n"
+ "HighTotal: %8u kB\n"
+ "HighFree: %8u kB\n"
+ "LowTotal: %8u kB\n"
+ "LowFree: %8u kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ total_mem, total_mem - free_mem, free_mem, 0, 0, 0,
+ total_swap, used_swap, total_swap - used_swap,
+ btok(total_mem), /* MemTotal */
+ btok(free_mem), /* MemFree */
+ 0, /* MemShared */
+ 0, /* Buffers */
+ 0, /* Cached */
+ 0, /* SwapCached */
+ 0, /* Active */
+ 0, /* Inactive */
+ 0, /* HighTotal */
+ 0, /* HighFree */
+ btok(total_mem), /* LowTotal */
+ btok(free_mem), /* LowFree */
+ btok(total_swap), /* SwapTotal */
+ btok(total_swap - used_swap)); /* SwapFree */
+}
+
+/*
+ * lxpr_read_mounts():
+ */
+/* ARGSUSED */
+static void
+lxpr_read_mounts(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ zone_t *zone = LXPTOZ(lxpnp);
+ struct print_data {
+ refstr_t *vfs_mntpt;
+ refstr_t *vfs_resource;
+ uint_t vfs_flag;
+ int vfs_fstype;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *printp;
+
+ vfs_list_read_lock();
+
+ if (zone == global_zone) {
+ vfsp = vfslist = rootvfs;
+ } else {
+ vfsp = vfslist = zone->zone_vfslist;
+ /*
+ * If the zone has a root entry, it will be the first in
+ * the list. If it doesn't, we conjure one up.
+ */
+ if (vfslist == NULL || strcmp(refstr_value(vfsp->vfs_mntpt),
+ zone->zone_rootpath) != 0) {
+ struct vfs *tvfsp;
+ /*
+ * The root of the zone is not a mount point. The vfs
+ * we want to report is that of the zone's root vnode.
+ */
+ tvfsp = zone->zone_rootvp->v_vfsp;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "/ / %s %s 0 0\n",
+ vfssw[tvfsp->vfs_fstype].vsw_name,
+ tvfsp->vfs_flag & VFS_RDONLY ? "ro" : "rw");
+
+ }
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return;
+ }
+ }
+
+ /*
+ * Later on we have to do a lookupname, which can end up causing
+ * another vfs_list_read_lock() to be called. Which can lead to a
+ * deadlock. To avoid this, we extract the data we need into a local
+ * list, then we can run this list without holding vfs_list_read_lock()
+ * We keep the list in the same order as the vfs_list
+ */
+ do {
+ /* Skip mounts we shouldn't show */
+ if (vfsp->vfs_flag & VFS_NOMNTTAB) {
+ goto nextfs;
+ }
+
+ printp = kmem_alloc(sizeof (*printp), KM_SLEEP);
+ refstr_hold(vfsp->vfs_mntpt);
+ printp->vfs_mntpt = vfsp->vfs_mntpt;
+ refstr_hold(vfsp->vfs_resource);
+ printp->vfs_resource = vfsp->vfs_resource;
+ printp->vfs_flag = vfsp->vfs_flag;
+ printp->vfs_fstype = vfsp->vfs_fstype;
+ printp->next = NULL;
+
+ *print_tail = printp;
+ print_tail = &printp->next;
+
+nextfs:
+ vfsp = (zone == global_zone) ?
+ vfsp->vfs_next : vfsp->vfs_zone_next;
+
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ printp = print_head;
+ while (printp != NULL) {
+ struct print_data *printp_next;
+ const char *resource;
+ char *mntpt;
+ struct vnode *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(printp->vfs_mntpt);
+ resource = refstr_value(printp->vfs_resource);
+
+ if (mntpt != NULL && mntpt[0] != '\0')
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ else
+ mntpt = "-";
+
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+
+ if (error != 0)
+ goto nextp;
+
+ if (!(vp->v_flag & VROOT)) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) :
+ mntpt;
+ }
+ } else {
+ resource = "-";
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%s %s %s %s 0 0\n",
+ resource, mntpt, vfssw[printp->vfs_fstype].vsw_name,
+ printp->vfs_flag & VFS_RDONLY ? "ro" : "rw");
+
+nextp:
+ printp_next = printp->next;
+ refstr_rele(printp->vfs_mntpt);
+ refstr_rele(printp->vfs_resource);
+ kmem_free(printp, sizeof (*printp));
+ printp = printp_next;
+
+ }
+}
+
+/*
+ * lxpr_read_partitions():
+ *
+ * We don't support partitions in a local zone because it requires access to
+ * physical devices. But we need to fake up enough of the file to show that we
+ * have no partitions.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_partitions(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf,
+ "major minor #blocks name rio rmerge rsect ruse "
+ "wio wmerge wsect wuse running use aveq\n\n");
+}
+
+/*
+ * lxpr_read_version(): read the contents of the "version" file. Note that
+ * we don't lie here -- we don't pretend that we're Linux. If lxproc is to
+ * be used in a Linux-branded zone, there will need to be a mount option to
+ * indicate that Linux should be more fully mimicked.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_version(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf,
+ "%s version %s (%s version %d.%d.%d) "
+ "#%s SMP %s\n",
+ utsname.sysname, utsname.release,
+#if defined(__GNUC__)
+ "gcc",
+ __GNUC__,
+ __GNUC_MINOR__,
+ __GNUC_PATCHLEVEL__,
+#else
+ "Sun C",
+ __SUNPRO_C / 0x100,
+ (__SUNPRO_C & 0xff) / 0x10,
+ __SUNPRO_C & 0xf,
+#endif
+ utsname.version,
+ "00:00:00 00/00/00");
+}
+
+/*
+ * lxpr_read_stat(): read the contents of the "stat" file.
+ *
+ */
+/* ARGSUSED */
+static void
+lxpr_read_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t sys_cum = 0;
+ ulong_t user_cum = 0;
+ ulong_t irq_cum = 0;
+ ulong_t cpu_nrunnable_cum = 0;
+ ulong_t w_io_cum = 0;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+ ulong_t intr_cum = 0;
+ ulong_t pswitch_cum = 0;
+ ulong_t forks_cum = 0;
+ hrtime_t msnsecs[NCMSTATES];
+
+ /* temporary variable since scalehrtime modifies data in place */
+ hrtime_t tmptime;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_STAT);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ int i;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_cum += NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_cum += NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_cum += NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+ cpu_nrunnable_cum += cp->cpu_disp->disp_nrunnable;
+ w_io_cum += CPU_STATS(cp, sys.iowait);
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_cum += NSEC_TO_TICK(tmptime);
+ }
+
+ for (i = 0; i < PIL_MAX; i++)
+ intr_cum += CPU_STATS(cp, sys.intr[i]);
+
+ pswitch_cum += CPU_STATS(cp, sys.pswitch);
+ forks_cum += CPU_STATS(cp, sys.sysfork);
+ forks_cum += CPU_STATS(cp, sys.sysvfork);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ lxpr_uiobuf_printf(uiobuf, "cpu %lu %lu %lu %lu %lu %lu %lu\n",
+ user_cum, 0L, sys_cum, idle_cum, 0L, irq_cum, 0L);
+
+ /* Do per processor stats */
+ do {
+ int i;
+
+ ulong_t idle_ticks;
+ ulong_t sys_ticks;
+ ulong_t user_ticks;
+ ulong_t irq_ticks = 0;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_ticks = NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_ticks = NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_ticks = NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_ticks += NSEC_TO_TICK(tmptime);
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "cpu%d %lu %lu %lu %lu %lu %lu %lu\n",
+ cp->cpu_id, user_ticks, 0L, sys_ticks, idle_ticks,
+ 0L, irq_ticks, 0L);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n"
+ "procs_running %lu\n"
+ "procs_blocked %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ boot_time,
+ forks_cum,
+ cpu_nrunnable_cum,
+ w_io_cum);
+}
+
+/*
+ * lxpr_read_uptime(): read the contents of the "uptime" file.
+ *
+ * format is: "%.2lf, %.2lf",uptime_secs, idle_secs
+ * Use fixed point arithmetic to get 2 decimal places
+ */
+/* ARGSUSED */
+static void
+lxpr_read_uptime(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t cpu_count = 0;
+ ulong_t idle_s;
+ ulong_t idle_cs;
+ ulong_t up_s;
+ ulong_t up_cs;
+ hrtime_t birthtime;
+ hrtime_t centi_sec = 10000000; /* 10^7 */
+
+ ASSERT(lxpnp->lxpr_type == LXPR_UPTIME);
+
+ /* Calculate cumulative stats */
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_idle);
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_wait);
+ cpu_count += 1;
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /* Getting the Zone zsched process startup time */
+ birthtime = LXPTOZ(lxpnp)->zone_zsched->p_mstart;
+ up_cs = (gethrtime() - birthtime) / centi_sec;
+ up_s = up_cs / 100;
+ up_cs %= 100;
+
+ ASSERT(cpu_count > 0);
+ idle_cum /= cpu_count;
+ idle_s = idle_cum / hz;
+ idle_cs = idle_cum % hz;
+ idle_cs *= 100;
+ idle_cs /= hz;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d\n", up_s, up_cs, idle_s, idle_cs);
+}
+
+static const char *amd_x_edx[] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "syscall",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "mp",
+ "nx", NULL, "mmxext", NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, "lm", "3dnowext", "3dnow"
+};
+
+static const char *amd_x_ecx[] = {
+ "lahf_lm", NULL, "svm", NULL,
+ "altmovcr8"
+};
+
+static const char *tm_x_edx[] = {
+ "recovery", "longrun", NULL, "lrti"
+};
+
+/*
+ * Intel calls no-execute "xd" in its docs, but Linux still reports it as "nx."
+ */
+static const char *intc_x_edx[] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "syscall",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ "nx", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, "lm", NULL, NULL
+};
+
+static const char *intc_edx[] = {
+ "fpu", "vme", "de", "pse",
+ "tsc", "msr", "pae", "mce",
+ "cx8", "apic", NULL, "sep",
+ "mtrr", "pge", "mca", "cmov",
+ "pat", "pse36", "pn", "clflush",
+ NULL, "dts", "acpi", "mmx",
+ "fxsr", "sse", "sse2", "ss",
+ "ht", "tm", "ia64", "pbe"
+};
+
+/*
+ * "sse3" on linux is called "pni" (Prescott New Instructions).
+ */
+static const char *intc_ecx[] = {
+ "pni", NULL, NULL, "monitor",
+ "ds_cpl", NULL, NULL, "est",
+ "tm2", NULL, "cid", NULL,
+ NULL, "cx16", "xtpr"
+};
+
+static void
+lxpr_read_cpuinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ int i;
+ uint32_t bits;
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ const char **fp;
+ char brandstr[CPU_IDSTRLEN];
+ struct cpuid_regs cpr;
+ int maxeax;
+ int std_ecx, std_edx, ext_ecx, ext_edx;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_CPUINFO);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * This returns the maximum eax value for standard cpuid
+ * functions in eax.
+ */
+ cpr.cp_eax = 0;
+ (void) cpuid_insn(cp, &cpr);
+ maxeax = cpr.cp_eax;
+
+ /*
+ * Get standard x86 feature flags.
+ */
+ cpr.cp_eax = 1;
+ (void) cpuid_insn(cp, &cpr);
+ std_ecx = cpr.cp_ecx;
+ std_edx = cpr.cp_edx;
+
+ /*
+ * Now get extended feature flags.
+ */
+ cpr.cp_eax = 0x80000001;
+ (void) cpuid_insn(cp, &cpr);
+ ext_ecx = cpr.cp_ecx;
+ ext_edx = cpr.cp_edx;
+
+ (void) cpuid_getbrandstr(cp, brandstr, CPU_IDSTRLEN);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "processor\t: %d\n"
+ "vendor_id\t: %s\n"
+ "cpu family\t: %d\n"
+ "model\t\t: %d\n"
+ "model name\t: %s\n"
+ "stepping\t: %d\n"
+ "cpu MHz\t\t: %u.%03u\n",
+ cp->cpu_id, cpuid_getvendorstr(cp), cpuid_getfamily(cp),
+ cpuid_getmodel(cp), brandstr, cpuid_getstep(cp),
+ (uint32_t)(cpu_freq_hz / 1000000),
+ ((uint32_t)(cpu_freq_hz / 1000)) % 1000);
+
+ lxpr_uiobuf_printf(uiobuf, "cache size\t: %u KB\n",
+ getl2cacheinfo(cp, NULL, NULL, NULL) / 1024);
+
+ if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
+ /*
+ * 'siblings' is used for HT-style threads
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "physical id\t: %lu\n"
+ "siblings\t: %u\n",
+ pg_plat_hw_instance_id(cp, PGHW_CHIP),
+ cpuid_get_ncpu_per_chip(cp));
+ }
+
+ /*
+ * Since we're relatively picky about running on older hardware,
+ * we can be somewhat cavalier about the answers to these ones.
+ *
+ * In fact, given the hardware we support, we just say:
+ *
+ * fdiv_bug : no (if we're on a 64-bit kernel)
+ * hlt_bug : no
+ * f00f_bug : no
+ * coma_bug : no
+ * wp : yes (write protect in supervsr mode)
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "fdiv_bug\t: %s\n"
+ "hlt_bug \t: no\n"
+ "f00f_bug\t: no\n"
+ "coma_bug\t: no\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
+ "flags\t\t:",
+#if defined(__i386)
+ fpu_pentium_fdivbug ? "yes" : "no",
+#else
+ "no",
+#endif /* __i386 */
+ fpu_exists ? "yes" : "no", fpu_exists ? "yes" : "no",
+ maxeax);
+
+ for (bits = std_edx, fp = intc_edx, i = 0;
+ i < sizeof (intc_edx) / sizeof (intc_edx[0]); fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ /*
+ * name additional features where appropriate
+ */
+ switch (x86_vendor) {
+ case X86_VENDOR_Intel:
+ for (bits = ext_edx, fp = intc_x_edx, i = 0;
+ i < sizeof (intc_x_edx) / sizeof (intc_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+
+ case X86_VENDOR_AMD:
+ for (bits = ext_edx, fp = amd_x_edx, i = 0;
+ i < sizeof (amd_x_edx) / sizeof (amd_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ for (bits = ext_ecx, fp = amd_x_ecx, i = 0;
+ i < sizeof (amd_x_ecx) / sizeof (amd_x_ecx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+
+ case X86_VENDOR_TM:
+ for (bits = ext_edx, fp = tm_x_edx, i = 0;
+ i < sizeof (tm_x_edx) / sizeof (tm_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+ default:
+ break;
+ }
+
+ for (bits = std_ecx, fp = intc_ecx, i = 0;
+ i < sizeof (intc_ecx) / sizeof (intc_ecx[0]); fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ lxpr_uiobuf_printf(uiobuf, "\n\n");
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FD_FD);
+ lxpr_uiobuf_seterr(uiobuf, EFAULT);
+}
+
+/*
+ * lxpr_getattr(): Vnode operation for VOP_GETATTR()
+ */
+static int
+lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ extern uint_t nproc;
+ int error;
+
+ /*
+ * Return attributes of underlying vnode if ATTR_REAL
+ *
+ * but keep fd files with the symlink permissions
+ */
+ if (lxpnp->lxpr_realvp != NULL && (flags & ATTR_REAL)) {
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+
+ /*
+ * withold attribute information to owner or root
+ */
+ if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * now its attributes
+ */
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * if it's a file in lx /proc/pid/fd/xx then set its
+ * mode and keep it looking like a symlink
+ */
+ if (type == LXPR_PID_FD_FD) {
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_type = vp->v_type;
+ vap->va_size = 0;
+ vap->va_nlink = 1;
+ }
+ return (0);
+ }
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxpnp->lxpr_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxpnp->lxpr_uid;
+ vap->va_gid = lxpnp->lxpr_gid;
+ vap->va_nodeid = lxpnp->lxpr_ino;
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vap->va_nlink = nproc + 2 + PROCDIRFILES;
+ vap->va_size = (nproc + 2 + PROCDIRFILES) * LXPR_SDSIZE;
+ break;
+ case LXPR_PIDDIR:
+ vap->va_nlink = PIDDIRFILES;
+ vap->va_size = PIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_SELF:
+ vap->va_uid = crgetruid(curproc->p_cred);
+ vap->va_gid = crgetrgid(curproc->p_cred);
+ break;
+ default:
+ break;
+ }
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxpr_access(): Vnode operation for VOP_ACCESS()
+ */
+static int
+lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ int shift = 0;
+ proc_t *tp;
+
+ /* lx /proc is a read only file system */
+ if (mode & VWRITE)
+ return (EROFS);
+
+ /*
+ * If this is a restricted file, check access permissions.
+ */
+ switch (lxpnp->lxpr_type) {
+ case LXPR_PIDDIR:
+ return (0);
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ENV:
+ case LXPR_PID_EXE:
+ case LXPR_PID_MAPS:
+ case LXPR_PID_MEM:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_FDDIR:
+ case LXPR_PID_FD_FD:
+ if ((tp = lxpr_lock(lxpnp->lxpr_pid)) == NULL)
+ return (ENOENT);
+ if (tp != curproc && secpolicy_proc_access(cr) != 0 &&
+ priv_proc_cred_perm(cr, tp, NULL, mode) != 0) {
+ lxpr_unlock(tp);
+ return (EACCES);
+ }
+ lxpr_unlock(tp);
+ default:
+ break;
+ }
+
+ if (lxpnp->lxpr_realvp != NULL) {
+ /*
+ * For these we use the underlying vnode's accessibility.
+ */
+ return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct));
+ }
+
+ /* If user is root allow access regardless of permission bits */
+ if (secpolicy_proc_access(cr) == 0)
+ return (0);
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxpnp->lxpr_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxpnp->lxpr_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxpnp->lxpr_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxpr_lookup_not_a_dir(vnode_t *dp, char *comp)
+{
+ return (NULL);
+}
+
+/*
+ * lxpr_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the lookup
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxpnp->lxpr_parent);
+ *vpp = lxpnp->lxpr_parent;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxpr_lookup_function[type](dp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+/*
+ * Do a sequential search on the given directory table
+ */
+static vnode_t *
+lxpr_lookup_common(vnode_t *dp, char *comp, proc_t *p,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ lxpr_node_t *lxpnp;
+ int count;
+
+ for (count = 0; count < dirtablen; count++) {
+ if (strcmp(dirtab[count].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, dirtab[count].d_type, p, 0);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ return (dp);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxpr_lookup_piddir(vnode_t *dp, char *comp)
+{
+ proc_t *p;
+
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PIDDIR);
+
+ p = lxpr_lock(VTOLXP(dp)->lxpr_pid);
+ if (p == NULL)
+ return (NULL);
+
+ dp = lxpr_lookup_common(dp, comp, p, piddir, PIDDIRFILES);
+
+ lxpr_unlock(p);
+
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's open files.
+ */
+static vnode_t *
+lxpr_lookup_fddir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ vnode_t *vp = NULL;
+ proc_t *p;
+ file_t *fp;
+ uint_t fd;
+ int c;
+ uf_entry_t *ufp;
+ uf_info_t *fip;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_FDDIR);
+
+ /*
+ * convert the string rendition of the filename
+ * to a file descriptor
+ */
+ fd = 0;
+ while ((c = *comp++) != '\0') {
+ int ofd;
+ if (c < '0' || c > '9')
+ return (NULL);
+
+ ofd = fd;
+ fd = 10*fd + c - '0';
+ /* integer overflow */
+ if (fd / 10 != ofd)
+ return (NULL);
+ }
+
+ /*
+ * get the proc to work with and lock it
+ */
+ p = lxpr_lock(dlxpnp->lxpr_pid);
+ if ((p == NULL))
+ return (NULL);
+
+ /*
+ * If the process is a zombie or system process
+ * it can't have any open files.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & SSYS) || (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * get us a fresh node/vnode
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PID_FD_FD, p, fd);
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we dereference into fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * get open file info
+ */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+
+ if (fd < fip->fi_nfiles) {
+ UF_ENTER(ufp, fip, fd);
+ /*
+ * ensure the fd is still kosher.
+ * it may have gone between the readdir and
+ * the lookup
+ */
+ if (fip->fi_list[fd].uf_file == NULL) {
+ mutex_exit(&fip->fi_lock);
+ UF_EXIT(ufp);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ lxpr_freenode(lxpnp);
+ return (NULL);
+ }
+
+ if ((fp = ufp->uf_file) != NULL)
+ vp = fp->f_vnode;
+ UF_EXIT(ufp);
+ }
+ mutex_exit(&fip->fi_lock);
+
+ if (vp == NULL) {
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ lxpr_freenode(lxpnp);
+ return (NULL);
+ } else {
+ /*
+ * Fill in the lxpr_node so future references will be able to
+ * find the underlying vnode. The vnode is held on the realvp.
+ */
+ lxpnp->lxpr_realvp = vp;
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_NETDIR);
+
+ dp = lxpr_lookup_common(dp, comp, NULL, netdir, NETDIRFILES);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_procdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PROCDIR);
+
+ /*
+ * We know all the names of files & dirs in our file system structure
+ * except those that are pid names. These change as pids are created/
+ * deleted etc., so we just look for a number as the first char to see
+ * if we are we doing pid lookups.
+ *
+ * Don't need to check for "self" as it is implemented as a symlink
+ */
+ if (*comp >= '0' && *comp <= '9') {
+ pid_t pid = 0;
+ lxpr_node_t *lxpnp = NULL;
+ proc_t *p;
+ int c;
+
+ while ((c = *comp++) != '\0')
+ pid = 10 * pid + c - '0';
+
+ /*
+ * Can't continue if the process is still loading or it doesn't
+ * really exist yet (or maybe it just died!)
+ */
+ p = lxpr_lock(pid);
+ if (p == NULL)
+ return (NULL);
+
+ if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * allocate and fill in a new lxpr node
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PIDDIR, p, 0);
+
+ lxpr_unlock(p);
+
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+
+ return (dp);
+ }
+
+ /* Lookup fixed names */
+ return (lxpr_lookup_common(dp, comp, NULL, lxpr_dir, PROCDIRFILES));
+}
+
+/*
+ * lxpr_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the readdir
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXPR_SDSIZE)
+ return (ENOENT);
+
+ return (lxpr_readdir_function[lxpnp->lxpr_type](lxpnp, uiop, eofp));
+}
+
+/* ARGSUSED */
+static int
+lxpr_readdir_not_a_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (ENOTDIR);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxpr_readdir_common(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Satisfy user request
+ */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXPR_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxpnp->lxpr_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXPR_SDSIZE) {
+
+ dirent->d_ino = lxpr_parentinode(lxpnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxpr_inode(dirtab[dirindex].d_type,
+ lxpnp->lxpr_pid, 0);
+
+ VERIFY(slen < LXPNSIZ);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ if (eofp) {
+ *eofp =
+ (uiop->uio_offset >= ((dirtablen+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+ return (0);
+}
+
+
+static int
+lxpr_readdir_procdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ zoneid_t zoneid;
+ pid_t pid;
+ int error;
+ int ceof;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PROCDIR);
+
+ oresid = uiop->uio_resid;
+ zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ /*
+ * We return directory entries in the order: "." and ".." then the
+ * unique lxproc files, then the directories corresponding to the
+ * running processes. We have defined this as the ordering because
+ * it allows us to more easily keep track of where we are betwen calls
+ * to getdents(). If the number of processes changes between calls
+ * then we can't lose track of where we are in the lxproc files.
+ */
+
+ /* Do the fixed entries */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, lxpr_dir,
+ PROCDIRFILES);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ return (error);
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Do the process entries */
+ while ((uresid = uiop->uio_resid) > 0) {
+ proc_t *p;
+ int len;
+ int reclen;
+ int i;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop when entire proc table has been examined.
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2 - PROCDIRFILES;
+ if (i < 0 || i >= v.v_proc) {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+ mutex_enter(&pidlock);
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, a PID of 0,
+ * and anything the security policy doesn't allow
+ * us to look at.
+ */
+ if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+ p->p_pid == 0 ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ mutex_exit(&pidlock);
+ goto next;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's
+ * init process, otherwise use the value from the proc
+ * structure
+ */
+ pid = ((p->p_pid != curproc->p_zone->zone_proc_initpid) ?
+ p->p_pid : 1);
+
+ /*
+ * If this /proc was mounted in the global zone, view
+ * all procs; otherwise, only view zone member procs.
+ */
+ if (zoneid != GLOBAL_ZONEID && p->p_zone->zone_id != zoneid) {
+ goto next;
+ }
+
+ ASSERT(p->p_stat != 0);
+
+ dirent->d_ino = lxpr_inode(LXPR_PIDDIR, pid, 0);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", pid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ return (EINVAL);
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ if (eofp != NULL) {
+ *eofp = (uiop->uio_offset >=
+ ((v.v_proc + PROCDIRFILES + 2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+ return (0);
+}
+
+static int
+lxpr_readdir_piddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PIDDIR);
+
+ /* can't read its contents if it died */
+ mutex_enter(&pidlock);
+
+ p = prfind((lxpnp->lxpr_pid == 1) ?
+ curproc->p_zone->zone_proc_initpid : lxpnp->lxpr_pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, piddir, PIDDIRFILES));
+}
+
+static int
+lxpr_readdir_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, netdir, NETDIRFILES));
+}
+
+static int
+lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+ int ceof;
+ proc_t *p;
+ int fddirsize = -1;
+ uf_info_t *fip;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FDDIR);
+
+ oresid = uiop->uio_resid;
+
+ /* can't read its contents if it died */
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL)
+ return (ENOENT);
+
+ if ((p->p_stat == SZOMB) || (p->p_flag & SSYS) || (p->p_as == &kas))
+ fddirsize = 0;
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /* Get open file info */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+
+ if (fddirsize == -1)
+ fddirsize = fip->fi_nfiles;
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until
+ * all file descriptors have been examined.
+ */
+ for (; (uresid = uiop->uio_resid) > 0;
+ uiop->uio_offset = uoffset + LXPR_SDSIZE) {
+ int reclen;
+ int fd;
+ int len;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the fd list
+ */
+ fd = (uoffset / LXPR_SDSIZE) - 2;
+ if (fd < 0 || fd >= fddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (fip->fi_list[fd].uf_file == NULL)
+ continue;
+
+ dirent->d_ino = lxpr_inode(LXPR_PID_FD_FD, lxpnp->lxpr_pid, fd);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", fd);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+ }
+
+ if (eofp != NULL) {
+ *eofp =
+ (uiop->uio_offset >= ((fddirsize+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+out:
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+
+/*
+ * lxpr_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char bp[MAXPATHLEN + 1];
+ size_t buflen = sizeof (bp);
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ pid_t pid;
+ int error = 0;
+
+ /* must be a symbolic link file */
+ if (vp->v_type != VLNK)
+ return (EINVAL);
+
+ /* Try to produce a symlink name for anything that has a realvp */
+ if (rvp != NULL) {
+ if ((error = lxpr_access(vp, VREAD, 0, CRED(), ct)) != 0)
+ return (error);
+ if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 0)
+ return (error);
+ } else {
+ switch (lxpnp->lxpr_type) {
+ case LXPR_SELF:
+ /*
+ * Convert pid to the Linux default of 1 if we're the
+ * zone's init process
+ */
+ pid = ((curproc->p_pid !=
+ curproc->p_zone->zone_proc_initpid)
+ ? curproc->p_pid : 1);
+
+ /*
+ * Don't need to check result as every possible int
+ * will fit within MAXPATHLEN bytes.
+ */
+ (void) snprintf(bp, buflen, "%d", pid);
+ break;
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_EXE:
+ return (EACCES);
+ default:
+ /*
+ * Need to return error so that nothing thinks
+ * that the symlink is empty and hence "."
+ */
+ return (EINVAL);
+ }
+ }
+
+ /* copy the link data to user space */
+ return (uiomove(bp, strlen(bp), UIO_READ, uiop));
+}
+
+/*
+ * lxpr_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_freenode(VTOLXP(vp));
+}
+
+/*
+ * lxpr_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxpr_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxpr_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ while (vn_matchops(vp1, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp1)->lxpr_realvp) != NULL) {
+ vp1 = rvp;
+ }
+
+ while (vn_matchops(vp2, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp2)->lxpr_realvp) != NULL) {
+ vp2 = rvp;
+ }
+
+ if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops))
+ return (vp1 == vp2);
+
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+/*
+ * lxpr_realvp(): Vnode operation for VOP_REALVP()
+ */
+static int
+lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) {
+ vp = rvp;
+ if (VOP_REALVP(vp, &rvp, ct) == 0)
+ vp = rvp;
+ }
+
+ *vpp = vp;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxproc.h b/usr/src/uts/common/fs/lxproc/lxproc.h
new file mode 100644
index 0000000000..eadb2ccd27
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxproc.h
@@ -0,0 +1,278 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LXPROC_H
+#define _LXPROC_H
+
+#ifdef _LXPROC_BRANDED_H
+#error Attempted to include native lxproc.h after branded lx_proc.h
+#endif
+
+#define _LXPROC_NATIVE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxproc.h: declarations, data structures and macros for lxprocfs
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+
+#define LX_SIGHUP 1
+#define LX_SIGINT 2
+#define LX_SIGQUIT 3
+#define LX_SIGILL 4
+#define LX_SIGTRAP 5
+#define LX_SIGABRT 6
+#define LX_SIGIOT 6
+#define LX_SIGBUS 7
+#define LX_SIGFPE 8
+#define LX_SIGKILL 9
+#define LX_SIGUSR1 10
+#define LX_SIGSEGV 11
+#define LX_SIGUSR2 12
+#define LX_SIGPIPE 13
+#define LX_SIGALRM 14
+#define LX_SIGTERM 15
+#define LX_SIGSTKFLT 16
+#define LX_SIGCHLD 17
+#define LX_SIGCONT 18
+#define LX_SIGSTOP 19
+#define LX_SIGTSTP 20
+#define LX_SIGTTIN 21
+#define LX_SIGTTOU 22
+#define LX_SIGURG 23
+#define LX_SIGXCPU 24
+#define LX_SIGXFSZ 25
+#define LX_SIGVTALRM 26
+#define LX_SIGPROF 27
+#define LX_SIGWINCH 28
+#define LX_SIGIO 29
+#define LX_SIGPOLL LX_SIGIO
+#define LX_SIGPWR 30
+#define LX_SIGSYS 31
+#define LX_SIGUNUSED 31
+
+#define LX_NSIG 64 /* Linux _NSIG */
+
+#define LX_SIGRTMIN 32
+#define LX_SIGRTMAX LX_NSIG
+
+/*
+ * Convert a vnode into an lxpr_mnt_t
+ */
+#define VTOLXPM(vp) ((lxpr_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxpr_node
+ */
+#define VTOLXP(vp) ((lxpr_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxprnode into a vnode
+ */
+#define LXPTOV(lxpnp) ((lxpnp)->lxpr_vnode)
+
+/*
+ * convert a lxpr_node into zone for fs
+ */
+#define LXPTOZ(lxpnp) \
+ (((lxpr_mnt_t *)(lxpnp)->lxpr_vnode->v_vfsp->vfs_data)->lxprm_zone)
+
+#define LXPNSIZ 256 /* max size of lx /proc file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXPR_SDSIZE 16
+
+/*
+ * Node/file types for lx /proc files
+ * (directories and files contained therein).
+ */
+typedef enum lxpr_nodetype {
+ LXPR_PROCDIR, /* /proc */
+ LXPR_PIDDIR, /* /proc/<pid> */
+ LXPR_PID_CMDLINE, /* /proc/<pid>/cmdline */
+ LXPR_PID_CPU, /* /proc/<pid>/cpu */
+ LXPR_PID_CURDIR, /* /proc/<pid>/cwd */
+ LXPR_PID_ENV, /* /proc/<pid>/environ */
+ LXPR_PID_EXE, /* /proc/<pid>/exe */
+ LXPR_PID_MAPS, /* /proc/<pid>/maps */
+ LXPR_PID_MEM, /* /proc/<pid>/mem */
+ LXPR_PID_ROOTDIR, /* /proc/<pid>/root */
+ LXPR_PID_STAT, /* /proc/<pid>/stat */
+ LXPR_PID_STATM, /* /proc/<pid>/statm */
+ LXPR_PID_STATUS, /* /proc/<pid>/status */
+ LXPR_PID_FDDIR, /* /proc/<pid>/fd */
+ LXPR_PID_FD_FD, /* /proc/<pid>/fd/nn */
+ LXPR_CMDLINE, /* /proc/cmdline */
+ LXPR_CPUINFO, /* /proc/cpuinfo */
+ LXPR_DEVICES, /* /proc/devices */
+ LXPR_DMA, /* /proc/dma */
+ LXPR_FILESYSTEMS, /* /proc/filesystems */
+ LXPR_INTERRUPTS, /* /proc/interrupts */
+ LXPR_IOPORTS, /* /proc/ioports */
+ LXPR_KCORE, /* /proc/kcore */
+ LXPR_KMSG, /* /proc/kmsg */
+ LXPR_LOADAVG, /* /proc/loadavg */
+ LXPR_MEMINFO, /* /proc/meminfo */
+ LXPR_MOUNTS, /* /proc/mounts */
+ LXPR_NETDIR, /* /proc/net */
+ LXPR_NET_ARP, /* /proc/net/arp */
+ LXPR_NET_DEV, /* /proc/net/dev */
+ LXPR_NET_DEV_MCAST, /* /proc/net/dev_mcast */
+ LXPR_NET_IGMP, /* /proc/net/igmp */
+ LXPR_NET_IP_MR_CACHE, /* /proc/net/ip_mr_cache */
+ LXPR_NET_IP_MR_VIF, /* /proc/net/ip_mr_vif */
+ LXPR_NET_MCFILTER, /* /proc/net/mcfilter */
+ LXPR_NET_NETSTAT, /* /proc/net/netstat */
+ LXPR_NET_RAW, /* /proc/net/raw */
+ LXPR_NET_ROUTE, /* /proc/net/route */
+ LXPR_NET_RPC, /* /proc/net/rpc */
+ LXPR_NET_RT_CACHE, /* /proc/net/rt_cache */
+ LXPR_NET_SOCKSTAT, /* /proc/net/sockstat */
+ LXPR_NET_SNMP, /* /proc/net/snmp */
+ LXPR_NET_STAT, /* /proc/net/stat */
+ LXPR_NET_TCP, /* /proc/net/tcp */
+ LXPR_NET_UDP, /* /proc/net/udp */
+ LXPR_NET_UNIX, /* /proc/net/unix */
+ LXPR_PARTITIONS, /* /proc/partitions */
+ LXPR_SELF, /* /proc/self */
+ LXPR_STAT, /* /proc/stat */
+ LXPR_UPTIME, /* /proc/uptime */
+ LXPR_VERSION, /* /proc/version */
+ LXPR_NFILES /* number of lx /proc file types */
+} lxpr_nodetype_t;
+
+/*
+ * Number of fds allowed for in the inode number calculation
+ * per process (if a process has more fds then inode numbers
+ * may be duplicated)
+ */
+#define LXPR_FD_PERPROC 2000
+
+/*
+ * external dirent characteristics
+ */
+#define LXPRMAXNAMELEN 14
+typedef struct {
+ lxpr_nodetype_t d_type;
+ char d_name[LXPRMAXNAMELEN];
+} lxpr_dirent_t;
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+typedef struct lxpr_node {
+ lxpr_nodetype_t lxpr_type; /* type of this node */
+ vnode_t *lxpr_vnode; /* vnode for the node */
+ vnode_t *lxpr_parent; /* parent directory */
+ vnode_t *lxpr_realvp; /* real vnode, file in dirs */
+ timestruc_t lxpr_time; /* creation etc time for file */
+ mode_t lxpr_mode; /* file mode bits */
+ uid_t lxpr_uid; /* file owner */
+ gid_t lxpr_gid; /* file group owner */
+ pid_t lxpr_pid; /* pid of proc referred to */
+ ino_t lxpr_ino; /* node id */
+} lxpr_node_t;
+
+struct zone; /* forward declaration */
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxpr_mnt {
+ lxpr_node_t *lxprm_node; /* node at root of proc mount */
+ struct zone *lxprm_zone; /* zone for this mount */
+ ldi_ident_t lxprm_li; /* ident for ldi */
+} lxpr_mnt_t;
+
+extern vnodeops_t *lxpr_vnodeops;
+extern int nproc_highbit; /* highbit(v.v_nproc) */
+
+typedef struct mounta mounta_t;
+
+extern void lxpr_initnodecache();
+extern void lxpr_fininodecache();
+extern void lxpr_initrootnode(lxpr_node_t **, vfs_t *);
+extern ino_t lxpr_inode(lxpr_nodetype_t, pid_t, int);
+extern ino_t lxpr_parentinode(lxpr_node_t *);
+extern lxpr_node_t *lxpr_getnode(vnode_t *, lxpr_nodetype_t, proc_t *, int);
+extern void lxpr_freenode(lxpr_node_t *);
+
+typedef struct lxpr_uiobuf lxpr_uiobuf_t;
+extern lxpr_uiobuf_t *lxpr_uiobuf_new(uio_t *);
+extern void lxpr_uiobuf_free(lxpr_uiobuf_t *);
+extern int lxpr_uiobuf_flush(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_seek(lxpr_uiobuf_t *, offset_t);
+extern void lxpr_uiobuf_write(lxpr_uiobuf_t *, const char *, size_t);
+extern void lxpr_uiobuf_printf(lxpr_uiobuf_t *, const char *, ...);
+extern void lxpr_uiobuf_seterr(lxpr_uiobuf_t *, int);
+
+proc_t *lxpr_lock(pid_t);
+void lxpr_unlock(proc_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXPROC_H */
diff --git a/usr/src/uts/common/fs/nfs/nfs3_vfsops.c b/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
index 207a708771..2176dcb9de 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*/
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs3_vnops.c b/usr/src/uts/common/fs/nfs/nfs3_vnops.c
index b7354c168a..d3b12817ba 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_vnops.c
@@ -29,7 +29,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -3353,10 +3353,9 @@ nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
if (nvp)
vnevent_rename_dest(nvp, ndvp, nnm, ct);
- if (odvp != ndvp)
- vnevent_rename_dest_dir(ndvp, ct);
ASSERT(ovp != NULL);
vnevent_rename_src(ovp, odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
}
if (nvp) {
@@ -5523,8 +5522,13 @@ nfs3_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfs3setattr(vp, &va, 0, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
index ce0c9485a6..3ee41939ac 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
@@ -22,6 +22,7 @@
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <sys/systm.h>
@@ -178,12 +179,12 @@ pseudo_exportfs(vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
kex = &exi->exi_export;
kex->ex_flags = EX_PSEUDO;
- vpathlen = vp->v_path ? strlen(vp->v_path) : 0;
+ vpathlen = strlen(vp->v_path);
kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX);
kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP);
if (vpathlen)
- (void) strcpy(kex->ex_path, vp->v_path);
+ (void) strncpy(kex->ex_path, vp->v_path, vpathlen);
(void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX);
/* Transfer the secinfo data from exdata to this new pseudo node */
diff --git a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
index 151cb62403..55f6c95289 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
@@ -22,6 +22,7 @@
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*/
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_vnops.c
index 1c8a3bc235..1c5b8fb39d 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c
@@ -34,7 +34,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/param.h>
@@ -3739,8 +3739,13 @@ nfs4_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
*/
error = nfs4setattr(vp, vap, flags, cr, NULL);
- if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0 && (vap->va_mask & AT_SIZE)) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
return (error);
}
@@ -8063,8 +8068,9 @@ link_call:
* vnode if it already existed.
*/
if (error == 0) {
- vnode_t *tvp;
+ vnode_t *tvp, *tovp;
rnode4_t *trp;
+
/*
* Notify the vnode. Each links is represented by
* a different vnode, in nfsv4.
@@ -8077,23 +8083,20 @@ link_call:
vnevent_rename_dest(tvp, ndvp, nnm, ct);
}
- /*
- * if the source and destination directory are not the
- * same notify the destination directory.
- */
- if (VTOR4(odvp) != VTOR4(ndvp)) {
- trp = VTOR4(ndvp);
- tvp = ndvp;
- if (IS_SHADOW(ndvp, trp))
- tvp = RTOV4(trp);
- vnevent_rename_dest_dir(tvp, ct);
- }
-
trp = VTOR4(ovp);
- tvp = ovp;
+ tovp = ovp;
if (IS_SHADOW(ovp, trp))
+ tovp = RTOV4(trp);
+
+ vnevent_rename_src(tovp, odvp, onm, ct);
+
+ trp = VTOR4(ndvp);
+ tvp = ndvp;
+
+ if (IS_SHADOW(ndvp, trp))
tvp = RTOV4(trp);
- vnevent_rename_src(tvp, odvp, onm, ct);
+
+ vnevent_rename_dest_dir(tvp, tovp, nnm, ct);
}
if (nvp) {
@@ -11002,8 +11005,13 @@ nfs4_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfs4setattr(vp, &va, 0, cr, NULL);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c
index 2851f8bef9..5fa0e6414f 100644
--- a/usr/src/uts/common/fs/nfs/nfs_auth.c
+++ b/usr/src/uts/common/fs/nfs/nfs_auth.c
@@ -22,6 +22,7 @@
/*
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
* Copyright (c) 2015 by Delphix. All rights reserved.
*/
@@ -561,11 +562,16 @@ retry:
*access = res.ares.auth_perm;
*srv_uid = res.ares.auth_srv_uid;
*srv_gid = res.ares.auth_srv_gid;
- *srv_gids_cnt = res.ares.auth_srv_gids.len;
- *srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
- KM_SLEEP);
- bcopy(res.ares.auth_srv_gids.val, *srv_gids,
- *srv_gids_cnt * sizeof (gid_t));
+
+ if ((*srv_gids_cnt = res.ares.auth_srv_gids.len) != 0) {
+ *srv_gids = kmem_alloc(*srv_gids_cnt *
+ sizeof (gid_t), KM_SLEEP);
+ bcopy(res.ares.auth_srv_gids.val, *srv_gids,
+ *srv_gids_cnt * sizeof (gid_t));
+ } else {
+ *srv_gids = NULL;
+ }
+
break;
case NFSAUTH_DR_EFAIL:
@@ -1054,9 +1060,13 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
if (gid != NULL)
*gid = p->auth_srv_gid;
if (ngids != NULL && gids != NULL) {
- *ngids = p->auth_srv_ngids;
- *gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
- bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
+ if ((*ngids = p->auth_srv_ngids) != 0) {
+ size_t sz = *ngids * sizeof (gid_t);
+ *gids = kmem_alloc(sz, KM_SLEEP);
+ bcopy(p->auth_srv_gids, *gids, sz);
+ } else {
+ *gids = NULL;
+ }
}
access = p->auth_access;
diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c
index 7e94c62734..dcc64def59 100644
--- a/usr/src/uts/common/fs/nfs/nfs_server.c
+++ b/usr/src/uts/common/fs/nfs/nfs_server.c
@@ -23,6 +23,7 @@
* Copyright (c) 2011 Bayard G. Bell. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
/*
@@ -2570,6 +2571,9 @@ nfs_srvinit(void)
{
int error;
+ if (getzoneid() != GLOBAL_ZONEID)
+ return (EACCES);
+
error = nfs_exportinit();
if (error != 0)
return (error);
@@ -3208,7 +3212,7 @@ nfs_getflabel(vnode_t *vp, struct exportinfo *exi)
char *path;
mutex_enter(&vp->v_lock);
- if (vp->v_path != NULL) {
+ if (vp->v_path != vn_vpath_empty) {
zone = zone_find_by_any_path(vp->v_path, B_FALSE);
mutex_exit(&vp->v_lock);
} else {
diff --git a/usr/src/uts/common/fs/nfs/nfs_vfsops.c b/usr/src/uts/common/fs/nfs/nfs_vfsops.c
index 57b21778b4..ffd5380a86 100644
--- a/usr/src/uts/common/fs/nfs/nfs_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs_vfsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
* All rights reserved.
diff --git a/usr/src/uts/common/fs/nfs/nfs_vnops.c b/usr/src/uts/common/fs/nfs/nfs_vnops.c
index 1a1082bcb8..ee3bac484f 100644
--- a/usr/src/uts/common/fs/nfs/nfs_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs_vnops.c
@@ -26,7 +26,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -1174,8 +1174,13 @@ nfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
error = nfssetattr(vp, vap, flags, cr);
- if (error == 0 && (mask & AT_SIZE) && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0 && (mask & AT_SIZE)) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
return (error);
}
@@ -2688,11 +2693,9 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
if (nvp)
vnevent_rename_dest(nvp, ndvp, nnm, ct);
- if (odvp != ndvp)
- vnevent_rename_dest_dir(ndvp, ct);
-
ASSERT(ovp != NULL);
vnevent_rename_src(ovp, odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
}
if (nvp) {
@@ -4620,8 +4623,13 @@ nfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfssetattr(vp, &va, 0, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/pcfs/pc_dir.c b/usr/src/uts/common/fs/pcfs/pc_dir.c
index 976715e346..275330a0ae 100644
--- a/usr/src/uts/common/fs/pcfs/pc_dir.c
+++ b/usr/src/uts/common/fs/pcfs/pc_dir.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/param.h>
@@ -826,8 +826,7 @@ top:
if (error == 0) {
vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
- if (dp != tdp)
- vnevent_rename_dest_dir(PCTOV(tdp), ctp);
+ vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
}
done:
diff --git a/usr/src/uts/common/fs/pcfs/pc_vnops.c b/usr/src/uts/common/fs/pcfs/pc_vnops.c
index a8743b245a..ae72cada7a 100644
--- a/usr/src/uts/common/fs/pcfs/pc_vnops.c
+++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c
@@ -781,8 +781,11 @@ pcfs_setattr(
if (error)
goto out;
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
}
/*
* Change file modified times.
diff --git a/usr/src/uts/common/fs/portfs/port.c b/usr/src/uts/common/fs/portfs/port.c
index 14be8cbbae..11b7386269 100644
--- a/usr/src/uts/common/fs/portfs/port.c
+++ b/usr/src/uts/common/fs/portfs/port.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
#include <sys/types.h>
#include <sys/systm.h>
@@ -1381,12 +1383,18 @@ portnowait:
if (model == DATAMODEL_NATIVE) {
eventsz = sizeof (port_event_t);
- kevp = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
- if (kevp == NULL) {
- if (nmax > pp->port_max_list)
- nmax = pp->port_max_list;
- kevp = kmem_alloc(eventsz * nmax, KM_SLEEP);
+
+ if (nmax == 0) {
+ kevp = NULL;
+ } else {
+ kevp = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
+ if (kevp == NULL) {
+ if (nmax > pp->port_max_list)
+ nmax = pp->port_max_list;
+ kevp = kmem_alloc(eventsz * nmax, KM_SLEEP);
+ }
}
+
results = kevp;
lev = NULL; /* start with first event in the queue */
for (nevents = 0; nevents < nmax; ) {
@@ -1423,12 +1431,18 @@ portnowait:
port_event32_t *kevp32;
eventsz = sizeof (port_event32_t);
- kevp32 = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
- if (kevp32 == NULL) {
- if (nmax > pp->port_max_list)
- nmax = pp->port_max_list;
- kevp32 = kmem_alloc(eventsz * nmax, KM_SLEEP);
+
+ if (nmax == 0) {
+ kevp32 = NULL;
+ } else {
+ kevp32 = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
+ if (kevp32 == NULL) {
+ if (nmax > pp->port_max_list)
+ nmax = pp->port_max_list;
+ kevp32 = kmem_alloc(eventsz * nmax, KM_SLEEP);
+ }
}
+
results = kevp32;
lev = NULL; /* start with first event in the queue */
for (nevents = 0; nevents < nmax; ) {
diff --git a/usr/src/uts/common/fs/proc/prargv.c b/usr/src/uts/common/fs/proc/prargv.c
new file mode 100644
index 0000000000..b09a9c8afc
--- /dev/null
+++ b/usr/src/uts/common/fs/proc/prargv.c
@@ -0,0 +1,441 @@
+/*
+ * 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 2015, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <sys/sysmacros.h>
+#include <vm/as.h>
+
+/*
+ * Safely read a contiguous region of memory from 'addr' in the address space
+ * of a particular process into the supplied kernel buffer (*buf, sz).
+ * Partially mapped regions will result in a partial read terminating at the
+ * first hole in the address space. The number of bytes actually read is
+ * returned to the caller via 'rdsz'.
+ */
+int
+prreadbuf(proc_t *p, uintptr_t ustart, uint8_t *buf, size_t sz, size_t *rdsz)
+{
+ int error = 0;
+ size_t rem = sz;
+ off_t pos = 0;
+
+ if (rdsz != NULL)
+ *rdsz = 0;
+
+ while (rem != 0) {
+ uintptr_t addr = ustart + pos;
+ size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+ if ((error = uread(p, buf + pos, len, addr)) != 0) {
+ if (error == ENXIO) {
+ /*
+ * ENXIO from uread() indicates that the page
+ * does not exist. This will simply be a
+ * partial read.
+ */
+ error = 0;
+ }
+ break;
+ }
+
+ rem -= len;
+ pos += len;
+ }
+
+ if (rdsz != NULL)
+ *rdsz = pos;
+
+ return (error);
+}
+
+/*
+ * Attempt to read the argument vector (argv) from this process. The caller
+ * must hold the p_lock mutex, and have marked the process P_PR_LOCK (e.g. via
+ * prlock or lx_prlock).
+ *
+ * The caller must provide a buffer (buf, buflen). We will concatenate each
+ * argument string (including the NUL terminator) into this buffer. The number
+ * of characters written to this buffer (including the final NUL terminator)
+ * will be stored in 'slen'.
+ */
+int
+prreadargv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+ int error;
+ user_t *up;
+ struct as *as;
+ size_t pos = 0;
+ caddr_t *argv = NULL;
+ size_t argvsz = 0;
+ int i;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+ up = PTOU(p);
+ as = p->p_as;
+
+ if ((p->p_flag & SSYS) || as == &kas || up->u_argv == NULL) {
+ /*
+ * Return the regular psargs string to the caller.
+ */
+ bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
+ buf[bufsz - 1] = '\0';
+ *slen = strlen(buf) + 1;
+
+ return (0);
+ }
+
+ /*
+ * Allocate space to store argv array.
+ */
+ argvsz = up->u_argc * (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ argv = kmem_alloc(argvsz, KM_SLEEP);
+
+ /*
+ * Extract the argv array from the target process. Drop p_lock
+ * while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+ if ((error = prreadbuf(p, up->u_argv, (uint8_t *)argv, argvsz,
+ NULL)) != 0) {
+ kmem_free(argv, argvsz);
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ /*
+ * Read each argument string from the pointers in the argv array.
+ */
+ pos = 0;
+ for (i = 0; i < up->u_argc; i++) {
+ size_t rdsz, trysz;
+ uintptr_t arg;
+ off_t j;
+ boolean_t found_nul;
+ boolean_t do_retry = B_TRUE;
+
+#ifdef _SYSCALL32_IMPL
+ if (p->p_model == DATAMODEL_ILP32) {
+ arg = (uintptr_t)((caddr32_t *)argv)[i];
+ } else {
+ arg = (uintptr_t)argv[i];
+ }
+#else
+ arg = (uintptr_t)argv[i];
+#endif
+
+ /*
+ * Stop trying to read arguments if we reach a NULL
+ * pointer in the vector.
+ */
+ if (arg == NULL)
+ break;
+
+ /*
+ * Stop reading if we have read the maximum length
+ * we can return to the user.
+ */
+ if (pos >= bufsz)
+ break;
+
+ /*
+ * Initially we try a short read, on the assumption that
+ * most individual argument strings are less than 80
+ * characters long.
+ */
+ if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+ /*
+ * We don't have room in the target buffer for even
+ * an entire short read, so there is no need to retry
+ * with a longer read.
+ */
+ do_retry = B_FALSE;
+ }
+
+retry:
+ /*
+ * Read string data for this argument. Leave room
+ * in the buffer for a final NUL terminator.
+ */
+ if ((error = prreadbuf(p, arg, (uint8_t *)&buf[pos], trysz,
+ &rdsz)) != 0) {
+ /*
+ * There was a problem reading this string
+ * from the process. Give up.
+ */
+ break;
+ }
+
+ /*
+ * Find the NUL terminator.
+ */
+ found_nul = B_FALSE;
+ for (j = 0; j < rdsz; j++) {
+ if (buf[pos + j] == '\0') {
+ found_nul = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found_nul && do_retry) {
+ /*
+ * We did not find a NUL terminator, but this
+ * was a first pass short read. Try once more
+ * with feeling.
+ */
+ trysz = bufsz - pos - 1;
+ do_retry = B_FALSE;
+ goto retry;
+ }
+
+ /*
+ * Commit the string we read to the buffer.
+ */
+ pos += j + 1;
+ if (!found_nul && pos < bufsz) {
+ /*
+ * A NUL terminator was not found; add one.
+ */
+ buf[pos++] = '\0';
+ }
+ }
+
+ /*
+ * Ensure the entire string is NUL-terminated.
+ */
+ buf[bufsz - 1] = '\0';
+
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ kmem_free(argv, argvsz);
+
+ /*
+ * If the operation was a success, return the copied string length
+ * to the caller.
+ */
+ *slen = (error == 0) ? pos : 0;
+
+ return (error);
+}
+
+/*
+ * Similar to prreadargv except reads the env vector. This is slightly more
+ * complex because there is no count for the env vector that corresponds to
+ * u_argc.
+ */
+int
+prreadenvv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+ int error;
+ user_t *up;
+ struct as *as;
+ size_t pos = 0;
+ caddr_t *envp = NULL;
+ uintptr_t tmpp = NULL;
+ size_t envpsz = 0, rdsz = 0;
+ int i;
+ int cnt, bound;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+ up = PTOU(p);
+ as = p->p_as;
+
+ if ((p->p_flag & SSYS) || as == &kas || up->u_envp == NULL) {
+ /*
+ * Return empty string.
+ */
+ buf[0] = '\0';
+ *slen = 1;
+
+ return (0);
+ }
+
+ /*
+ * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * We first have to count how many env entries we have. This is
+ * somewhat painful. We extract the env entries from the target process
+ * one entry at a time. Stop trying to read env entries if we reach a
+ * NULL pointer in the vector or hit our upper bound (which we take
+ * as the bufsz/4) to ensure we don't run off.
+ */
+ rdsz = (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ bound = (int)(bufsz / 4);
+ for (cnt = 0, tmpp = up->u_envp; cnt < bound; cnt++, tmpp += rdsz) {
+ caddr_t tmp = NULL;
+
+ if ((error = prreadbuf(p, tmpp, (uint8_t *)&tmp, rdsz,
+ NULL)) != 0) {
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ if (tmp == NULL)
+ break;
+ }
+ if (cnt == 0) {
+ /* Return empty string. */
+ buf[0] = '\0';
+ *slen = 1;
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (0);
+ }
+
+ /*
+ * Allocate space to store env array.
+ */
+ envpsz = cnt * (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ envp = kmem_alloc(envpsz, KM_SLEEP);
+
+ /*
+ * Extract the env array from the target process.
+ */
+ if ((error = prreadbuf(p, up->u_envp, (uint8_t *)envp, envpsz,
+ NULL)) != 0) {
+ kmem_free(envp, envpsz);
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ /*
+ * Read each env string from the pointers in the env array.
+ */
+ pos = 0;
+ for (i = 0; i < cnt; i++) {
+ size_t rdsz, trysz;
+ uintptr_t ev;
+ off_t j;
+ boolean_t found_nul;
+ boolean_t do_retry = B_TRUE;
+
+#ifdef _SYSCALL32_IMPL
+ if (p->p_model == DATAMODEL_ILP32) {
+ ev = (uintptr_t)((caddr32_t *)envp)[i];
+ } else {
+ ev = (uintptr_t)envp[i];
+ }
+#else
+ ev = (uintptr_t)envp[i];
+#endif
+
+ /*
+ * Stop trying to read env entries if we reach a NULL
+ * pointer in the vector.
+ */
+ if (ev == NULL)
+ break;
+
+ /*
+ * Stop reading if we have read the maximum length
+ * we can return to the user.
+ */
+ if (pos >= bufsz)
+ break;
+
+ /*
+ * Initially we try a short read, on the assumption that
+ * most individual env strings are less than 80
+ * characters long.
+ */
+ if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+ /*
+ * We don't have room in the target buffer for even
+ * an entire short read, so there is no need to retry
+ * with a longer read.
+ */
+ do_retry = B_FALSE;
+ }
+
+retry:
+ /*
+ * Read string data for this env var. Leave room
+ * in the buffer for a final NUL terminator.
+ */
+ if ((error = prreadbuf(p, ev, (uint8_t *)&buf[pos], trysz,
+ &rdsz)) != 0) {
+ /*
+ * There was a problem reading this string
+ * from the process. Give up.
+ */
+ break;
+ }
+
+ /*
+ * Find the NUL terminator.
+ */
+ found_nul = B_FALSE;
+ for (j = 0; j < rdsz; j++) {
+ if (buf[pos + j] == '\0') {
+ found_nul = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found_nul && do_retry) {
+ /*
+ * We did not find a NUL terminator, but this
+ * was a first pass short read. Try once more
+ * with feeling.
+ */
+ trysz = bufsz - pos - 1;
+ do_retry = B_FALSE;
+ goto retry;
+ }
+
+ /*
+ * Commit the string we read to the buffer.
+ */
+ pos += j + 1;
+ if (!found_nul && pos < bufsz) {
+ /*
+ * A NUL terminator was not found; add one.
+ */
+ buf[pos++] = '\0';
+ }
+ }
+
+ /*
+ * Ensure the entire string is NUL-terminated.
+ */
+ buf[bufsz - 1] = '\0';
+
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ kmem_free(envp, envpsz);
+
+ /*
+ * If the operation was a success, return the copied string length
+ * to the caller.
+ */
+ *slen = (error == 0) ? pos : 0;
+
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/proc/prcontrol.c b/usr/src/uts/common/fs/proc/prcontrol.c
index 6b151a6369..07dcb1e7db 100644
--- a/usr/src/uts/common/fs/proc/prcontrol.c
+++ b/usr/src/uts/common/fs/proc/prcontrol.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -1481,7 +1481,7 @@ pr_setsig(prnode_t *pnp, siginfo_t *sip)
} else if (t->t_state == TS_STOPPED && sig == SIGKILL) {
/* If SIGKILL, set stopped lwp running */
p->p_stopsig = 0;
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
t->t_dtrace_stop = 0;
setrun_locked(t);
}
@@ -2276,9 +2276,17 @@ pr_szoneid(proc_t *p, zoneid_t zoneid, cred_t *cr)
return (EPERM);
if (zoneid != GLOBAL_ZONEID && zoneid != p->p_zone->zone_id)
return (EINVAL);
- if ((zptr = zone_find_by_id(zoneid)) == NULL)
- return (EINVAL);
+ /*
+ * We cannot hold p_lock when we call zone_find_by_id since that can
+ * lead to a deadlock. zone_find_by_id() takes zonehash_lock.
+ * zone_enter() can hold the zonehash_lock and needs p_lock when it
+ * calls task_join.
+ */
mutex_exit(&p->p_lock);
+ if ((zptr = zone_find_by_id(zoneid)) == NULL) {
+ mutex_enter(&p->p_lock);
+ return (EINVAL);
+ }
mutex_enter(&p->p_crlock);
oldcred = p->p_cred;
crhold(oldcred);
diff --git a/usr/src/uts/common/fs/proc/prdata.h b/usr/src/uts/common/fs/proc/prdata.h
index 8ea516bf82..72f26b3c05 100644
--- a/usr/src/uts/common/fs/proc/prdata.h
+++ b/usr/src/uts/common/fs/proc/prdata.h
@@ -27,7 +27,7 @@
/* All Rights Reserved */
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_PROC_PRDATA_H
@@ -123,6 +123,7 @@ typedef enum prnodetype {
#if defined(__i386) || defined(__amd64)
PR_LDT, /* /proc/<pid>/ldt */
#endif
+ PR_ARGV, /* /proc/<pid>/argv */
PR_USAGE, /* /proc/<pid>/usage */
PR_LUSAGE, /* /proc/<pid>/lusage */
PR_PAGEDATA, /* /proc/<pid>/pagedata */
@@ -347,6 +348,8 @@ extern int pr_unset(proc_t *, long);
extern void pr_sethold(prnode_t *, sigset_t *);
extern void pr_setfault(proc_t *, fltset_t *);
extern int prusrio(proc_t *, enum uio_rw, struct uio *, int);
+extern int prreadargv(proc_t *, char *, size_t, size_t *);
+extern int prreadenvv(proc_t *, char *, size_t, size_t *);
extern int prwritectl(vnode_t *, struct uio *, cred_t *);
extern int prlock(prnode_t *, int);
extern void prunmark(proc_t *);
diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c
index 28950bf972..21c25a01e3 100644
--- a/usr/src/uts/common/fs/proc/prsubr.c
+++ b/usr/src/uts/common/fs/proc/prsubr.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -201,6 +201,7 @@ prchoose(proc_t *p)
case PR_SYSEXIT:
case PR_SIGNALLED:
case PR_FAULTED:
+ case PR_BRAND:
/*
* Make an lwp calling exit() be the
* last lwp seen in the process.
@@ -534,6 +535,12 @@ prexecend(void)
pcp->prc_tslot = tslot;
}
}
+
+ /*
+ * There may be threads waiting for the flag change blocked behind the
+ * pr_pid_cv as well.
+ */
+ cv_signal(&pr_pid_cv[p->p_slot]);
}
/*
diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c
index 39f2abbc32..245133abf4 100644
--- a/usr/src/uts/common/fs/proc/prvnops.c
+++ b/usr/src/uts/common/fs/proc/prvnops.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -96,6 +96,11 @@ struct prdirect {
#define PRSDSIZE (sizeof (struct prdirect))
/*
+ * Maximum length of the /proc/$$/argv file:
+ */
+int prmaxargvlen = 4096;
+
+/*
* Directory characteristics.
*/
typedef struct prdirent {
@@ -166,6 +171,8 @@ static prdirent_t piddir[] = {
{ PR_LDT, 27 * sizeof (prdirent_t), sizeof (prdirent_t),
"ldt" },
#endif
+ { PR_ARGV, 28 * sizeof (prdirent_t), sizeof (prdirent_t),
+ "argv" },
};
#define NPIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]) - 2)
@@ -582,6 +589,7 @@ static int pr_read_inval(), pr_read_as(), pr_read_status(),
#if defined(__x86)
pr_read_ldt(),
#endif
+ pr_read_argv(),
pr_read_usage(), pr_read_lusage(), pr_read_pagedata(),
pr_read_watch(), pr_read_lwpstatus(), pr_read_lwpsinfo(),
pr_read_lwpusage(), pr_read_xregs(), pr_read_priv(),
@@ -610,6 +618,7 @@ static int (*pr_read_function[PR_NFILES])() = {
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
pr_read_usage, /* /proc/<pid>/usage */
pr_read_lusage, /* /proc/<pid>/lusage */
pr_read_pagedata, /* /proc/<pid>/pagedata */
@@ -672,6 +681,41 @@ pr_uioread(void *base, long count, uio_t *uiop)
}
static int
+pr_read_argv(prnode_t *pnp, uio_t *uiop)
+{
+ char *args;
+ int error;
+ size_t asz = prmaxargvlen, sz;
+
+ /*
+ * Allocate a scratch buffer for collection of the process arguments.
+ */
+ args = kmem_alloc(asz, KM_SLEEP);
+
+ ASSERT(pnp->pr_type == PR_ARGV);
+
+ if ((error = prlock(pnp, ZNO)) != 0) {
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ if ((error = prreadargv(pnp->pr_common->prc_proc, args, asz,
+ &sz)) != 0) {
+ prunlock(pnp);
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ prunlock(pnp);
+
+ error = pr_uioread(args, sz, uiop);
+
+ kmem_free(args, asz);
+
+ return (error);
+}
+
+static int
pr_read_as(prnode_t *pnp, uio_t *uiop)
{
int error;
@@ -1767,6 +1811,7 @@ static int (*pr_read_function_32[PR_NFILES])() = {
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
pr_read_usage_32, /* /proc/<pid>/usage */
pr_read_lusage_32, /* /proc/<pid>/lusage */
pr_read_pagedata_32, /* /proc/<pid>/pagedata */
@@ -2686,6 +2731,103 @@ prread(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
#endif
}
+/*
+ * We make pr_write_psinfo_fname() somewhat simpler by asserting at compile
+ * time that PRFNSZ has the same definition as MAXCOMLEN.
+ */
+#if PRFNSZ != MAXCOMLEN
+#error PRFNSZ/MAXCOMLEN mismatch
+#endif
+
+static int
+pr_write_psinfo_fname(prnode_t *pnp, uio_t *uiop)
+{
+ char fname[PRFNSZ];
+ int offset = offsetof(psinfo_t, pr_fname), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_fname);
+#endif
+
+ /*
+ * If this isn't a write to pr_fname (or if the size doesn't match
+ * PRFNSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRFNSZ)
+ return (0);
+
+ if ((error = uiomove(fname, PRFNSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ fname[PRFNSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(fname, pnp->pr_common->prc_proc->p_user.u_comm, PRFNSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+/*
+ * We make pr_write_psinfo_psargs() somewhat simpler by asserting at compile
+ * time that PRARGSZ has the same definition as PSARGSZ.
+ */
+#if PRARGSZ != PSARGSZ
+#error PRARGSZ/PSARGSZ mismatch
+#endif
+
+static int
+pr_write_psinfo_psargs(prnode_t *pnp, uio_t *uiop)
+{
+ char psargs[PRARGSZ];
+ int offset = offsetof(psinfo_t, pr_psargs), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_psargs);
+#endif
+
+ /*
+ * If this isn't a write to pr_psargs (or if the size doesn't match
+ * PRARGSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRARGSZ)
+ return (0);
+
+ if ((error = uiomove(psargs, PRARGSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ psargs[PRARGSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(psargs, pnp->pr_common->prc_proc->p_user.u_psargs, PRARGSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+int
+pr_write_psinfo(prnode_t *pnp, uio_t *uiop)
+{
+ int error;
+
+ if ((error = pr_write_psinfo_fname(pnp, uiop)) != 0)
+ return (error);
+
+ if ((error = pr_write_psinfo_psargs(pnp, uiop)) != 0)
+ return (error);
+
+ return (0);
+}
+
+
/* ARGSUSED */
static int
prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
@@ -2764,6 +2906,9 @@ prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
uiop->uio_resid = resid;
return (error);
+ case PR_PSINFO:
+ return (pr_write_psinfo(pnp, uiop));
+
default:
return ((vp->v_type == VDIR)? EISDIR : EBADF);
}
@@ -3047,6 +3192,13 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
case PR_AUXV:
vap->va_size = __KERN_NAUXV_IMPL * PR_OBJSIZE(auxv32_t, auxv_t);
break;
+ case PR_ARGV:
+ if ((p->p_flag & SSYS) || p->p_as == &kas) {
+ vap->va_size = PSARGSZ;
+ } else {
+ vap->va_size = prmaxargvlen;
+ }
+ break;
#if defined(__x86)
case PR_LDT:
mutex_exit(&p->p_lock);
@@ -3222,6 +3374,7 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
p = pr_p_lock(pnp);
mutex_exit(&pr_pidlock);
if (p == NULL)
@@ -3307,6 +3460,7 @@ static vnode_t *(*pr_lookup_function[PR_NFILES])() = {
#if defined(__x86)
pr_lookup_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_lookup_notdir, /* /proc/<pid>/argv */
pr_lookup_notdir, /* /proc/<pid>/usage */
pr_lookup_notdir, /* /proc/<pid>/lusage */
pr_lookup_notdir, /* /proc/<pid>/pagedata */
@@ -4546,11 +4700,15 @@ prgetnode(vnode_t *dp, prnodetype_t type)
break;
case PR_PSINFO:
+ pnp->pr_mode = 0644; /* readable by all + owner can write */
+ break;
+
case PR_LPSINFO:
case PR_LWPSINFO:
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
pnp->pr_mode = 0444; /* read-only by all */
break;
@@ -4656,6 +4814,7 @@ static int (*pr_readdir_function[PR_NFILES])() = {
#if defined(__x86)
pr_readdir_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_readdir_notdir, /* /proc/<pid>/argv */
pr_readdir_notdir, /* /proc/<pid>/usage */
pr_readdir_notdir, /* /proc/<pid>/lusage */
pr_readdir_notdir, /* /proc/<pid>/pagedata */
@@ -4805,6 +4964,7 @@ pr_readdir_piddir(prnode_t *pnp, uio_t *uiop, int *eofp)
case PR_PROCDIR:
case PR_PSINFO:
case PR_USAGE:
+ case PR_ARGV:
break;
default:
continue;
diff --git a/usr/src/uts/common/fs/sockfs/sockcommon.c b/usr/src/uts/common/fs/sockfs/sockcommon.c
index 703e26ea61..682f1d867b 100644
--- a/usr/src/uts/common/fs/sockfs/sockcommon.c
+++ b/usr/src/uts/common/fs/sockfs/sockcommon.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -501,6 +502,9 @@ sonode_constructor(void *buf, void *cdrarg, int kmflags)
cv_init(&so->so_copy_cv, NULL, CV_DEFAULT, NULL);
cv_init(&so->so_closing_cv, NULL, CV_DEFAULT, NULL);
+ so->so_krecv_cb = NULL;
+ so->so_krecv_arg = NULL;
+
return (0);
}
@@ -654,6 +658,10 @@ sonode_fini(struct sonode *so)
if (so->so_filter_top != NULL)
sof_sonode_cleanup(so);
+ /* Clean up any remnants of krecv callbacks */
+ so->so_krecv_cb = NULL;
+ so->so_krecv_arg = NULL;
+
ASSERT(list_is_empty(&so->so_acceptq_list));
ASSERT(list_is_empty(&so->so_acceptq_defer));
ASSERT(!list_link_active(&so->so_acceptq_node));
diff --git a/usr/src/uts/common/fs/sockfs/sockcommon_sops.c b/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
index e5bc6dc845..9b8186a8a0 100644
--- a/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
+++ b/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -128,7 +128,7 @@ so_bind(struct sonode *so, struct sockaddr *name, socklen_t namelen,
{
int error;
- SO_BLOCK_FALLBACK(so, SOP_BIND(so, name, namelen, flags, cr));
+ SO_BLOCK_FALLBACK_SAFE(so, SOP_BIND(so, name, namelen, flags, cr));
ASSERT(flags == _SOBIND_XPG4_2 || flags == _SOBIND_SOCKBSD);
@@ -305,7 +305,7 @@ so_connect(struct sonode *so, struct sockaddr *name,
* This can happen if a non blocking operation caused an error.
*/
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -404,7 +404,7 @@ so_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
break;
}
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -513,7 +513,7 @@ so_sendmblk_impl(struct sonode *so, struct nmsghdr *msg, int fflag,
error = EPIPE;
break;
}
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -586,11 +586,6 @@ so_sendmblk(struct sonode *so, struct nmsghdr *msg, int fflag,
SO_BLOCK_FALLBACK(so, SOP_SENDMBLK(so, msg, fflag, cr, mpp));
- if ((so->so_mode & SM_SENDFILESUPP) == 0) {
- SO_UNBLOCK_FALLBACK(so);
- return (EOPNOTSUPP);
- }
-
error = so_sendmblk_impl(so, msg, fflag, cr, mpp, so->so_filter_top,
B_FALSE);
@@ -653,7 +648,7 @@ so_getsockname(struct sonode *so, struct sockaddr *addr,
{
int error;
- SO_BLOCK_FALLBACK(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
+ SO_BLOCK_FALLBACK_SAFE(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
if (so->so_filter_active == 0 ||
(error = sof_filter_getsockname(so, addr, addrlen, cr)) < 0)
@@ -702,7 +697,7 @@ so_getsockopt(struct sonode *so, int level, int option_name,
if (level == SOL_FILTER)
return (sof_getsockopt(so, option_name, optval, optlenp, cr));
- SO_BLOCK_FALLBACK(so,
+ SO_BLOCK_FALLBACK_SAFE(so,
SOP_GETSOCKOPT(so, level, option_name, optval, optlenp, flags, cr));
if ((so->so_filter_active == 0 ||
@@ -791,7 +786,7 @@ so_setsockopt(struct sonode *so, int level, int option_name,
if (level == SOL_FILTER)
return (sof_setsockopt(so, option_name, optval, optlen, cr));
- SO_BLOCK_FALLBACK(so,
+ SO_BLOCK_FALLBACK_SAFE(so,
SOP_SETSOCKOPT(so, level, option_name, optval, optlen, cr));
/* X/Open requires this check */
@@ -876,7 +871,7 @@ so_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
* If there is a pending error, return error
* This can happen if a non blocking operation caused an error.
*/
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -1329,6 +1324,26 @@ so_queue_msg_impl(struct sonode *so, mblk_t *mp,
}
}
+ mutex_enter(&so->so_lock);
+ if (so->so_krecv_cb != NULL) {
+ boolean_t cont;
+ so_krecv_f func = so->so_krecv_cb;
+ void *arg = so->so_krecv_arg;
+
+ mutex_exit(&so->so_lock);
+ cont = func(so, mp, msg_size, flags & MSG_OOB, arg);
+ mutex_enter(&so->so_lock);
+ if (cont == B_TRUE) {
+ space_left = so->so_rcvbuf;
+ } else {
+ so->so_rcv_queued = so->so_rcvlowat;
+ *errorp = ENOSPC;
+ space_left = -1;
+ }
+ goto done_unlock;
+ }
+ mutex_exit(&so->so_lock);
+
if (flags & MSG_OOB) {
so_queue_oob(so, mp, msg_size);
mutex_enter(&so->so_lock);
@@ -1607,6 +1622,13 @@ so_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
return (ENOTCONN);
}
+ mutex_enter(&so->so_lock);
+ if (so->so_krecv_cb != NULL) {
+ mutex_exit(&so->so_lock);
+ return (EOPNOTSUPP);
+ }
+ mutex_exit(&so->so_lock);
+
if (msg->msg_flags & MSG_PEEK)
msg->msg_flags &= ~MSG_WAITALL;
diff --git a/usr/src/uts/common/fs/sockfs/sockcommon_subr.c b/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
index 957c8f93b4..7bdd64393b 100644
--- a/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
+++ b/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
@@ -24,6 +24,7 @@
*/
/*
* Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/types.h>
@@ -670,10 +671,15 @@ so_dequeue_msg(struct sonode *so, mblk_t **mctlp, struct uio *uiop,
int more = 0;
int error;
ssize_t oobmark;
+ ssize_t copied = 0;
sodirect_t *sodp = so->so_direct;
+ xuio_t *xuio = NULL;
partial_read = B_FALSE;
*mctlp = NULL;
+ if ((uiop->uio_extflg & UIO_XUIO) != 0) {
+ xuio = (xuio_t *)uiop;
+ }
again:
mutex_enter(&so->so_lock);
again1:
@@ -784,8 +790,6 @@ again1:
* enabled socket, uio_resid can be 0.
*/
if (uiop->uio_resid >= 0) {
- ssize_t copied = 0;
-
if (sodp != NULL && (DB_FLAGS(mp) & DBLK_UIOA)) {
mutex_enter(&so->so_lock);
ASSERT(uiop == (uio_t *)&sodp->sod_uioa);
@@ -843,6 +847,18 @@ again1:
}
if (mp != NULL) { /* more data blocks in msg */
more |= MOREDATA;
+
+ /*
+ * If requested, tally up remaining data along with the
+ * amount already copied.
+ */
+ if (xuio != NULL &&
+ xuio->xu_type == UIOTYPE_PEEKSIZE) {
+ xuio->xu_ext.xu_ps.xu_ps_set = B_TRUE;
+ xuio->xu_ext.xu_ps.xu_ps_size =
+ copied + msgdsize(mp);
+ }
+
if ((flags & (MSG_PEEK|MSG_TRUNC))) {
if (flags & MSG_PEEK) {
freemsg(mp);
@@ -2276,9 +2292,9 @@ so_tpi_fallback(struct sonode *so, struct cred *cr)
fbfunc = sp->sp_smod_info->smod_proto_fallback_func;
/*
- * Cannot fallback if the socket has active filters
+ * Cannot fallback if the socket has active filters or a krecv callback.
*/
- if (so->so_filter_active > 0)
+ if (so->so_filter_active > 0 || so->so_krecv_cb != NULL)
return (EINVAL);
switch (so->so_family) {
@@ -2456,3 +2472,50 @@ out:
return (error);
}
+
+int
+so_krecv_set(sonode_t *so, so_krecv_f cb, void *arg)
+{
+ int ret;
+
+ if (cb == NULL && arg != NULL)
+ return (EINVAL);
+
+ SO_BLOCK_FALLBACK(so, so_krecv_set(so, cb, arg));
+
+ mutex_enter(&so->so_lock);
+ if (so->so_state & SS_FALLBACK_COMP) {
+ mutex_exit(&so->so_lock);
+ SO_UNBLOCK_FALLBACK(so);
+ return (ENOTSUP);
+ }
+
+ ret = so_lock_read(so, 0);
+ VERIFY(ret == 0);
+ /*
+ * Other consumers may actually care about getting extant data delivered
+ * to them, when they come along, they should figure out the best API
+ * for that.
+ */
+ so_rcv_flush(so);
+
+ so->so_krecv_cb = cb;
+ so->so_krecv_arg = arg;
+
+ so_unlock_read(so);
+ mutex_exit(&so->so_lock);
+ SO_UNBLOCK_FALLBACK(so);
+
+ return (0);
+}
+
+void
+so_krecv_unblock(sonode_t *so)
+{
+ mutex_enter(&so->so_lock);
+ VERIFY(so->so_krecv_cb != NULL);
+
+ so->so_rcv_queued = 0;
+ (void) so_check_flow_control(so);
+ mutex_exit(&so->so_lock);
+}
diff --git a/usr/src/uts/common/fs/sockfs/sockfilter.c b/usr/src/uts/common/fs/sockfs/sockfilter.c
index 971523945e..7dca6ae6fc 100644
--- a/usr/src/uts/common/fs/sockfs/sockfilter.c
+++ b/usr/src/uts/common/fs/sockfs/sockfilter.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/systm.h>
@@ -246,6 +247,18 @@ sof_setsockopt_impl(struct sonode *so, int option_name,
/* Module loaded OK, so there must be an ops vector */
ASSERT(ent->sofe_mod != NULL);
+
+ /*
+ * Check again to confirm ATTACH is ok. See if the the module
+ * is not SOF_ATT_SAFE after an unsafe operation has taken
+ * place.
+ */
+ if ((ent->sofe_mod->sofm_flags & SOF_ATT_SAFE) == 0 &&
+ so->so_state & SS_FILOP_UNSF) {
+ sof_instance_destroy(inst);
+ return (EINVAL);
+ }
+
inst->sofi_ops = &ent->sofe_mod->sofm_ops;
SOF_STAT_ADD(inst, tot_active_attach, 1);
@@ -1444,7 +1457,13 @@ sof_filter_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
* sof_register(version, name, ops, flags)
*
* Register a socket filter identified by name `name' and which should use
- * the ops vector `ops' for event notification. `flags' should be set to 0.
+ * the ops vector `ops' for event notification. `flags' should be set to 0
+ * by default for "unsafe" modules or SOF_ATT_SAFE for "safe" modules. An
+ * unsafe filter is one that cannot be attached after any socket operation has
+ * occured. This is the legacy default. A "safe" filter can be attached even
+ * after some basic initial socket operations have taken place. This set is
+ * currently bind, getsockname, getsockopt and setsockopt. The order in which
+ * a "safe" filter can be attached is more relaxed, and thus more flexible.
* On success 0 is returned, otherwise an errno is returned.
*/
int
@@ -1452,14 +1471,13 @@ sof_register(int version, const char *name, const sof_ops_t *ops, int flags)
{
sof_module_t *mod;
- _NOTE(ARGUNUSED(flags));
-
if (version != SOF_VERSION)
return (EINVAL);
mod = kmem_zalloc(sizeof (sof_module_t), KM_SLEEP);
mod->sofm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
(void) strcpy(mod->sofm_name, name);
+ mod->sofm_flags = flags;
mod->sofm_ops = *ops;
mutex_enter(&sof_module_lock);
diff --git a/usr/src/uts/common/fs/sockfs/sockfilter_impl.h b/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
index 7f7aece1f1..cf2ad8b20d 100644
--- a/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
+++ b/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SOCKFS_SOCKFILTER_H
@@ -51,6 +52,7 @@ typedef struct sof_kstat sof_kstat_t;
struct sof_module {
char *sofm_name;
+ int sofm_flags;
sof_ops_t sofm_ops;
uint_t sofm_refcnt;
list_node_t sofm_node;
diff --git a/usr/src/uts/common/fs/sockfs/socksubr.c b/usr/src/uts/common/fs/sockfs/socksubr.c
index 30027200b6..eea86672b8 100644
--- a/usr/src/uts/common/fs/sockfs/socksubr.c
+++ b/usr/src/uts/common/fs/sockfs/socksubr.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -436,10 +437,12 @@ sogetoff(mblk_t *mp, t_uscalar_t offset,
*
* The underlying filesystem VSOCK vnode has a v_stream pointer that
* references the actual stream head (hence indirectly the actual sonode).
+ *
+ * This function is non-static so it can be used by brand emulation.
*/
-static int
+int
so_ux_lookup(struct sonode *so, struct sockaddr_un *soun, int checkaccess,
- vnode_t **vpp)
+ vnode_t **vpp)
{
vnode_t *vp; /* Underlying filesystem vnode */
vnode_t *rvp; /* real vnode */
@@ -1879,7 +1882,7 @@ ssize_t
soreadfile(file_t *fp, uchar_t *buf, u_offset_t fileoff, int *err, size_t size)
{
struct uio auio;
- struct iovec aiov[MSG_MAXIOVLEN];
+ struct iovec aiov[1];
register vnode_t *vp;
int ioflag, rwflag;
ssize_t cnt;
diff --git a/usr/src/uts/common/fs/sockfs/socksyscalls.c b/usr/src/uts/common/fs/sockfs/socksyscalls.c
index 21f3744895..854dd040b5 100644
--- a/usr/src/uts/common/fs/sockfs/socksyscalls.c
+++ b/usr/src/uts/common/fs/sockfs/socksyscalls.c
@@ -21,6 +21,8 @@
/*
* Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
@@ -54,6 +56,7 @@
#include <sys/cmn_err.h>
#include <sys/vmsystm.h>
#include <sys/policy.h>
+#include <sys/limits.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
@@ -86,12 +89,6 @@ extern void nl7c_init(void);
extern int sockfs_defer_nl7c_init;
/*
- * Note: DEF_IOV_MAX is defined and used as it is in "fs/vncalls.c"
- * as there isn't a formal definition of IOV_MAX ???
- */
-#define MSG_MAXIOVLEN 16
-
-/*
* Kernel component of socket creation.
*
* The socket library determines which version number to use.
@@ -1026,9 +1023,10 @@ recvmsg(int sock, struct nmsghdr *msg, int flags)
STRUCT_HANDLE(nmsghdr, umsgptr);
struct nmsghdr lmsg;
struct uio auio;
- struct iovec aiov[MSG_MAXIOVLEN];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ ssize_t iovsize = 0;
int iovcnt;
- ssize_t len;
+ ssize_t len, rval;
int i;
int *flagsp;
model_t model;
@@ -1071,22 +1069,37 @@ recvmsg(int sock, struct nmsghdr *msg, int flags)
iovcnt = lmsg.msg_iovlen;
- if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
+ if (iovcnt <= 0 || iovcnt > IOV_MAX) {
return (set_errno(EMSGSIZE));
}
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ aiov = kmem_alloc(iovsize, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded, while ensuring
* that they can't move more than 2Gbytes of data in a single call.
*/
if (model == DATAMODEL_ILP32) {
- struct iovec32 aiov32[MSG_MAXIOVLEN];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ ssize_t iov32size;
ssize32_t count32;
- if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
- iovcnt * sizeof (struct iovec32)))
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0)
+ aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+
+ if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
@@ -1094,15 +1107,28 @@ recvmsg(int sock, struct nmsghdr *msg, int flags)
iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
- if (iovlen32 < 0 || count32 < 0)
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
return (set_errno(EINVAL));
+ }
+
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+
+ if (iovsize != 0)
+ kmem_free(aiov32, iov32size);
} else
#endif /* _SYSCALL32_IMPL */
if (copyin(lmsg.msg_iov, aiov, iovcnt * sizeof (struct iovec))) {
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
return (set_errno(EFAULT));
}
len = 0;
@@ -1110,6 +1136,9 @@ recvmsg(int sock, struct nmsghdr *msg, int flags)
ssize_t iovlen = aiov[i].iov_len;
len += iovlen;
if (iovlen < 0 || len < 0) {
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
return (set_errno(EINVAL));
}
}
@@ -1124,12 +1153,20 @@ recvmsg(int sock, struct nmsghdr *msg, int flags)
(do_useracc == 0 ||
useracc(lmsg.msg_control, lmsg.msg_controllen,
B_WRITE) != 0)) {
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
return (set_errno(EFAULT));
}
- return (recvit(sock, &lmsg, &auio, flags,
+ rval = recvit(sock, &lmsg, &auio, flags,
STRUCT_FADDR(umsgptr, msg_namelen),
- STRUCT_FADDR(umsgptr, msg_controllen), flagsp));
+ STRUCT_FADDR(umsgptr, msg_controllen), flagsp);
+
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
+ return (rval);
}
/*
@@ -1267,9 +1304,10 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
struct nmsghdr lmsg;
STRUCT_DECL(nmsghdr, u_lmsg);
struct uio auio;
- struct iovec aiov[MSG_MAXIOVLEN];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ ssize_t iovsize = 0;
int iovcnt;
- ssize_t len;
+ ssize_t len, rval;
int i;
model_t model;
@@ -1312,7 +1350,7 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
iovcnt = lmsg.msg_iovlen;
- if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
+ if (iovcnt <= 0 || iovcnt > IOV_MAX) {
/*
* Unless this is XPG 4.2 we allow iovcnt == 0 to
* be compatible with SunOS 4.X and 4.4BSD.
@@ -1321,19 +1359,34 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
return (set_errno(EMSGSIZE));
}
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ aiov = kmem_alloc(iovsize, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded, while ensuring
* that they can't move more than 2Gbytes of data in a single call.
*/
if (model == DATAMODEL_ILP32) {
- struct iovec32 aiov32[MSG_MAXIOVLEN];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ ssize_t iov32size;
ssize32_t count32;
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0)
+ aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+
if (iovcnt != 0 &&
- copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
- iovcnt * sizeof (struct iovec32)))
+ copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
@@ -1341,17 +1394,30 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
- if (iovlen32 < 0 || count32 < 0)
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
return (set_errno(EINVAL));
+ }
+
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+
+ if (iovsize != 0)
+ kmem_free(aiov32, iov32size);
} else
#endif /* _SYSCALL32_IMPL */
if (iovcnt != 0 &&
copyin(lmsg.msg_iov, aiov,
(unsigned)iovcnt * sizeof (struct iovec))) {
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
return (set_errno(EFAULT));
}
len = 0;
@@ -1359,6 +1425,9 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
ssize_t iovlen = aiov[i].iov_len;
len += iovlen;
if (iovlen < 0 || len < 0) {
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
return (set_errno(EINVAL));
}
}
@@ -1369,7 +1438,12 @@ sendmsg(int sock, struct nmsghdr *msg, int flags)
auio.uio_segflg = UIO_USERSPACE;
auio.uio_limit = 0;
- return (sendit(sock, &lmsg, &auio, flags));
+ rval = sendit(sock, &lmsg, &auio, flags);
+
+ if (iovsize != 0)
+ kmem_free(aiov, iovsize);
+
+ return (rval);
}
ssize_t
diff --git a/usr/src/uts/common/fs/sockfs/socktpi_impl.h b/usr/src/uts/common/fs/sockfs/socktpi_impl.h
index 6a515be122..24acb81a0a 100644
--- a/usr/src/uts/common/fs/sockfs/socktpi_impl.h
+++ b/usr/src/uts/common/fs/sockfs/socktpi_impl.h
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SOCKFS_SOCKTPI_IMPL_H
@@ -56,6 +57,8 @@ extern int sogetrderr(vnode_t *, int, int *);
extern int sogetwrerr(vnode_t *, int, int *);
extern int so_addr_verify(struct sonode *, const struct sockaddr *,
socklen_t);
+extern int so_ux_lookup(struct sonode *, struct sockaddr_un *, int,
+ vnode_t **);
extern int so_ux_addr_xlate(struct sonode *, struct sockaddr *,
socklen_t, int, void **, socklen_t *);
extern void so_unix_close(struct sonode *);
diff --git a/usr/src/uts/common/fs/swapfs/swap_subr.c b/usr/src/uts/common/fs/swapfs/swap_subr.c
index 74c4302da9..a4d983665b 100644
--- a/usr/src/uts/common/fs/swapfs/swap_subr.c
+++ b/usr/src/uts/common/fs/swapfs/swap_subr.c
@@ -110,9 +110,11 @@ swapfs_recalc(pgcnt_t pgs)
* memory that can be used as swap space should do so by
* setting swapfs_desfree at boot time, not swapfs_minfree.
* However, swapfs_minfree is tunable by install as a
- * workaround for bugid 1147463.
+ * workaround for bugid 1147463. Note swapfs_minfree is set
+ * to 1/8th of memory, but clamped at the limit of 256 MB.
*/
- new_swapfs_minfree = MAX(btopr(2 * 1024 * 1024), pgs >> 3);
+ new_swapfs_minfree = MIN(MAX(btopr(2 * 1024 * 1024), pgs >> 3),
+ btopr(256 * 1024 * 1024));
}
/*
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_dir.c b/usr/src/uts/common/fs/tmpfs/tmp_dir.c
index f6621c8097..1a620642cc 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_dir.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_dir.c
@@ -21,10 +21,9 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
@@ -383,20 +382,7 @@ tdirenter(
/*
* Unmake the inode we just made.
*/
- rw_enter(&tp->tn_rwlock, RW_WRITER);
- if ((tp->tn_type) == VDIR) {
- ASSERT(tdp == NULL);
- /*
- * cleanup allocs made by tdirinit()
- */
- tdirtrunc(tp);
- }
- mutex_enter(&tp->tn_tlock);
- tp->tn_nlink = 0;
- mutex_exit(&tp->tn_tlock);
- gethrestime(&tp->tn_ctime);
- rw_exit(&tp->tn_rwlock);
- tmpnode_rele(tp);
+ tmpnode_cleanup(tp);
tp = NULL;
}
} else if (tpp) {
@@ -431,6 +417,7 @@ tdirdelete(
enum dr_op op,
struct cred *cred)
{
+ struct tmount *tm;
struct tdirent *tpdp;
int error;
size_t namelen;
@@ -516,7 +503,8 @@ tdirdelete(
*/
namelen = strlen(tpdp->td_name) + 1;
- tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
+ tm = TNTOTM(dir);
+ tmp_kmem_free(tm, tpdp, sizeof (struct tdirent) + namelen);
dir->tn_size -= (sizeof (struct tdirent) + namelen);
dir->tn_dirents--;
@@ -538,19 +526,27 @@ tdirdelete(
* tdirinit is used internally to initialize a directory (dir)
* with '.' and '..' entries without checking permissions and locking
*/
-void
+int
tdirinit(
struct tmpnode *parent, /* parent of directory to initialize */
struct tmpnode *dir) /* the new directory */
{
+ struct tmount *tm;
struct tdirent *dot, *dotdot;
timestruc_t now;
ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
ASSERT(dir->tn_type == VDIR);
- dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
- dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
+ tm = TNTOTM(parent);
+ dot = tmp_kmem_zalloc(tm, sizeof (struct tdirent) + 2, KM_SLEEP);
+ if (dot == NULL)
+ return (ENOSPC);
+ dotdot = tmp_kmem_zalloc(tm, sizeof (struct tdirent) + 3, KM_SLEEP);
+ if (dotdot == NULL) {
+ tmp_kmem_free(tm, dot, sizeof (struct tdirent) + 2);
+ return (ENOSPC);
+ }
/*
* Initialize the entries
@@ -601,6 +597,8 @@ tdirinit(
dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */
dir->tn_dirents = 2;
dir->tn_nlink = 2;
+
+ return (0);
}
@@ -612,6 +610,7 @@ tdirtrunc(struct tmpnode *dir)
{
struct tdirent *tdp;
struct tmpnode *tp;
+ struct tmount *tm;
size_t namelen;
timestruc_t now;
int isvattrdir, isdotdot, skip_decr;
@@ -619,6 +618,8 @@ tdirtrunc(struct tmpnode *dir)
ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
ASSERT(dir->tn_type == VDIR);
+ tm = TNTOTM(dir);
+
isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
ASSERT(tdp->td_next != tdp);
@@ -650,7 +651,7 @@ tdirtrunc(struct tmpnode *dir)
tmpfs_hash_out(tdp);
- tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
+ tmp_kmem_free(tm, tdp, sizeof (struct tdirent) + namelen);
dir->tn_size -= (sizeof (struct tdirent) + namelen);
dir->tn_dirents--;
}
@@ -903,6 +904,7 @@ tdiraddentry(
enum de_op op,
struct tmpnode *fromtp)
{
+ struct tmount *tm;
struct tdirent *tdp, *tpdp;
size_t namelen, alloc_size;
timestruc_t now;
@@ -923,9 +925,10 @@ tdiraddentry(
/*
* Allocate and initialize directory entry
*/
+ tm = TNTOTM(dir);
namelen = strlen(name) + 1;
alloc_size = namelen + sizeof (struct tdirent);
- tdp = tmp_memalloc(alloc_size, 0);
+ tdp = tmp_kmem_zalloc(tm, alloc_size, KM_NOSLEEP | KM_NORMALPRI);
if (tdp == NULL)
return (ENOSPC);
@@ -1025,7 +1028,10 @@ tdirmaketnode(
((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
return (EOVERFLOW);
type = va->va_type;
- tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
+ tp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode), KM_SLEEP);
+ if (tp == NULL) {
+ return (ENOSPC);
+ }
tmpnode_init(tm, tp, va, cred);
/* setup normal file/dir's extended attribute directory */
@@ -1087,8 +1093,13 @@ tdirmaketnode(
if (va->va_mask & AT_MTIME)
tp->tn_mtime = va->va_mtime;
- if (op == DE_MKDIR)
- tdirinit(dir, tp);
+ if (op == DE_MKDIR) {
+ int ret;
+ if ((ret = tdirinit(dir, tp)) != 0) {
+ tmpnode_cleanup(tp);
+ return (ret);
+ }
+ }
*newnode = tp;
return (0);
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_subr.c b/usr/src/uts/common/fs/tmpfs/tmp_subr.c
index 2e59d28d80..6b752b3f5b 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_subr.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_subr.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -40,9 +41,94 @@
#include <sys/policy.h>
#include <sys/fs/tmp.h>
#include <sys/fs/tmpnode.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <vm/anon.h>
+
+#define KILOBYTE 1024
+#define MEGABYTE (1024 * KILOBYTE)
+#define GIGABYTE (1024 * MEGABYTE)
#define MODESHIFT 3
+#define VALIDMODEBITS 07777
+
+extern pgcnt_t swapfs_minfree;
+
+void *
+tmp_kmem_zalloc(struct tmount *tm, size_t size, int flag)
+{
+ void *buf;
+ zone_t *zone;
+ size_t pages;
+
+ mutex_enter(&tm->tm_contents);
+ zone = tm->tm_vfsp->vfs_zone;
+ if (tm->tm_anonmem + size > tm->tm_anonmax ||
+ tm->tm_anonmem + size < tm->tm_anonmem ||
+ size + ptob(tmpfs_minfree) <= size ||
+ !anon_checkspace(size + ptob(tmpfs_minfree), zone)) {
+ mutex_exit(&tm->tm_contents);
+ return (NULL);
+ }
+
+ /*
+ * Only make anonymous memory reservations when a page boundary is
+ * crossed. This is necessary since the anon_resv functions rounds up
+ * to PAGESIZE internally.
+ */
+ pages = btopr(tm->tm_allocmem + size);
+ pages -= btopr(tm->tm_allocmem);
+ if (pages > 0 && anon_try_resv_zone(ptob(pages), zone) == 0) {
+ mutex_exit(&tm->tm_contents);
+ return (NULL);
+ }
+
+ tm->tm_allocmem += size;
+ tm->tm_anonmem += size;
+ mutex_exit(&tm->tm_contents);
+
+ buf = kmem_zalloc(size, flag);
+ if (buf == NULL) {
+ mutex_enter(&tm->tm_contents);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
+ tm->tm_anonmem -= size;
+ if (pages > 0) {
+ /*
+ * Re-chasing the zone pointer is necessary since a
+ * forced umount could have been performed while the
+ * tm_contents lock was dropped during allocation.
+ */
+ anon_unresv_zone(ptob(pages), tm->tm_vfsp->vfs_zone);
+ }
+ mutex_exit(&tm->tm_contents);
+ }
+
+ return (buf);
+}
+
+void
+tmp_kmem_free(struct tmount *tm, void *buf, size_t size)
+{
+ size_t pages;
+
+ kmem_free(buf, size);
+ mutex_enter(&tm->tm_contents);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
+ tm->tm_anonmem -= size;
+ pages = btopr(tm->tm_allocmem);
+ tm->tm_allocmem -= size;
+ pages -= btopr(tm->tm_allocmem);
+ /*
+ * Like the tmp_kmem_zalloc case, only unreserve anonymous memory when
+ * a page boundary has been crossed.
+ */
+ if (pages > 0) {
+ anon_unresv_zone(size, tm->tm_vfsp->vfs_zone);
+ }
+ mutex_exit(&tm->tm_contents);
+}
+
int
tmp_taccess(void *vtp, int mode, struct cred *cred)
{
@@ -71,10 +157,9 @@ tmp_taccess(void *vtp, int mode, struct cred *cred)
* a plain file and you have write access to that file.
* Function returns 0 if remove access is granted.
*/
-
int
tmp_sticky_remove_access(struct tmpnode *dir, struct tmpnode *entry,
- struct cred *cr)
+ struct cred *cr)
{
uid_t uid = crgetuid(cr);
@@ -89,118 +174,162 @@ tmp_sticky_remove_access(struct tmpnode *dir, struct tmpnode *entry,
}
/*
- * Allocate zeroed memory if tmpfs_maxkmem has not been exceeded
- * or the 'musthave' flag is set. 'musthave' allocations should
- * always be subordinate to normal allocations so that tmpfs_maxkmem
- * can't be exceeded by more than a few KB. Example: when creating
- * a new directory, the tmpnode is a normal allocation; if that
- * succeeds, the dirents for "." and ".." are 'musthave' allocations.
- */
-void *
-tmp_memalloc(size_t size, int musthave)
-{
- static time_t last_warning;
- time_t now;
-
- if (atomic_add_long_nv(&tmp_kmemspace, size) < tmpfs_maxkmem ||
- musthave)
- return (kmem_zalloc(size, KM_SLEEP));
-
- atomic_add_long(&tmp_kmemspace, -size);
- now = gethrestime_sec();
- if (last_warning != now) {
- last_warning = now;
- cmn_err(CE_WARN, "tmp_memalloc: tmpfs over memory limit");
- }
- return (NULL);
-}
-
-void
-tmp_memfree(void *cp, size_t size)
-{
- kmem_free(cp, size);
- atomic_add_long(&tmp_kmemspace, -size);
-}
-
-/*
- * Convert a string containing a number (number of bytes) to a pgcnt_t,
- * containing the corresponding number of pages. On 32-bit kernels, the
+ * Convert a string containing a number (number of bytes) to a size_t,
+ * containing the corresponding number of bytes. On 32-bit kernels, the
* maximum value encoded in 'str' is PAGESIZE * ULONG_MAX, while the value
* returned in 'maxpg' is at most ULONG_MAX.
*
- * If the number is followed by a "k" or "K", the value is converted from
- * kilobytes to bytes. If it is followed by an "m" or "M" it is converted
- * from megabytes to bytes. If it is not followed by a character it is
- * assumed to be in bytes. Multiple letter options are allowed, so for instance
- * '2mk' is interpreted as 2gb.
+ * The number may be followed by a magnitude suffix: "k" or "K" for kilobytes;
+ * "m" or "M" for megabytes; "g" or "G" for gigabytes. This interface allows
+ * for an arguably esoteric interpretation of multiple suffix characters:
+ * namely, they cascade. For example, the caller may specify "2mk", which is
+ * interpreted as 2 gigabytes. It would seem, at this late stage, that the
+ * horse has left not only the barn but indeed the country, and possibly the
+ * entire planetary system. Alternatively, the number may be followed by a
+ * single '%' sign, indicating the size is a percentage of either the zone's
+ * swap limit or the system's overall swap size.
*
* Parse and overflow errors are detected and a non-zero number returned on
* error.
*/
-
int
-tmp_convnum(char *str, pgcnt_t *maxpg)
+tmp_convnum(char *str, size_t *maxbytes)
{
- uint64_t num = 0, oldnum;
-#ifdef _LP64
- uint64_t max_bytes = ULONG_MAX;
-#else
- uint64_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
-#endif
+ u_longlong_t num = 0;
+ u_longlong_t max_bytes = (uint64_t)SIZE_MAX;
+ size_t pages;
+
char *c;
+ const struct convchar {
+ char *cc_char;
+ uint64_t cc_factor;
+ } convchars[] = {
+ { "kK", KILOBYTE },
+ { "mM", MEGABYTE },
+ { "gG", GIGABYTE },
+ { NULL, 0 }
+ };
- if (str == NULL)
+ if (str == NULL) {
return (EINVAL);
+ }
c = str;
/*
- * Convert str to number
+ * Convert the initial numeric portion of the input string.
*/
- while ((*c >= '0') && (*c <= '9')) {
- oldnum = num;
- num = num * 10 + (*c++ - '0');
- if (oldnum > num) /* overflow */
+ if (ddi_strtoull(str, &c, 10, &num) != 0) {
+ return (EINVAL);
+ }
+
+ /*
+ * Handle a size in percent. Anything other than a single percent
+ * modifier is invalid. We use either the zone's swap limit or the
+ * system's total available swap size as the initial value. Perform the
+ * intermediate calculation in pages to avoid overflow.
+ */
+ if (*c == '%') {
+ u_longlong_t cap;
+
+ if (*(c + 1) != '\0')
return (EINVAL);
+
+ if (num > 100)
+ return (EINVAL);
+
+ cap = (u_longlong_t)curproc->p_zone->zone_max_swap_ctl;
+ if (cap == UINT64_MAX) {
+ /*
+ * Use the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ cap = TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+ } else {
+ cap = btop(cap);
+ }
+
+ num = ptob(cap * num / 100);
+ goto done;
}
/*
- * Terminate on null
+ * Apply the (potentially cascading) magnitude suffixes until an
+ * invalid character is found, or the string comes to an end.
*/
- while (*c != '\0') {
- switch (*c++) {
+ for (; *c != '\0'; c++) {
+ int i;
- /*
- * convert from kilobytes
- */
- case 'k':
- case 'K':
- if (num > max_bytes / 1024) /* will overflow */
- return (EINVAL);
- num *= 1024;
- break;
+ for (i = 0; convchars[i].cc_char != NULL; i++) {
+ /*
+ * Check if this character matches this multiplier
+ * class:
+ */
+ if (strchr(convchars[i].cc_char, *c) != NULL) {
+ /*
+ * Check for overflow:
+ */
+ if (num > max_bytes / convchars[i].cc_factor) {
+ return (EINVAL);
+ }
+
+ num *= convchars[i].cc_factor;
+ goto valid_char;
+ }
+ }
/*
- * convert from megabytes
+ * This was not a valid multiplier suffix character.
*/
- case 'm':
- case 'M':
- if (num > max_bytes / (1024 * 1024)) /* will overflow */
- return (EINVAL);
- num *= 1024 * 1024;
- break;
-
- default:
- return (EINVAL);
- }
+ return (EINVAL);
+
+valid_char:
+ continue;
}
+done:
+
/*
- * Since btopr() rounds up to page granularity, this round-up can
- * cause an overflow only if 'num' is between (max_bytes - PAGESIZE)
- * and (max_bytes). In this case the resulting number is zero, which
- * is what we check for below.
+ * We've been given a size in bytes; however, we want to make sure that
+ * we have at least one page worth no matter what. Therefore we use
+ * btopr to round up. However, this may cause an overflow only if 'num'
+ * is between (max_bytes - PAGESIZE) and (max_bytes). In this case the
+ * resulting number is zero, which is what we check for below. Note, we
+ * require at least one page, so if pages is zero, well, it wasn't going
+ * to work anyways.
*/
- if ((*maxpg = (pgcnt_t)btopr(num)) == 0 && num != 0)
+ pages = btopr(num);
+ if (pages == 0) {
return (EINVAL);
+ }
+
+ *maxbytes = ptob(pages);
+
+ return (0);
+}
+
+/*
+ * Parse an octal mode string for use as the permissions set for the root
+ * of the tmpfs mount.
+ */
+int
+tmp_convmode(char *str, mode_t *mode)
+{
+ ulong_t num;
+ char *c;
+
+ if (str == NULL) {
+ return (EINVAL);
+ }
+
+ if (ddi_strtoul(str, &c, 8, &num) != 0) {
+ return (EINVAL);
+ }
+
+ if ((num & ~VALIDMODEBITS) != 0) {
+ return (EINVAL);
+ }
+
+ *mode = VALIDMODEBITS & num;
return (0);
}
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_tnode.c b/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
index 51e57b2611..13ea356924 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -64,21 +65,35 @@ tmp_resv(
int pagecreate) /* call anon_resv if set */
{
pgcnt_t pages = btopr(delta);
+ size_t pbytes = ptob(pages);
zone_t *zone;
ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
ASSERT(tp->tn_type == VREG);
+
/*
- * pagecreate is set only if we actually need to call anon_resv
- * to reserve an additional page of anonymous memory.
- * Since anon_resv always reserves a page at a time,
- * it should only get called when we know we're growing the
- * file into a new page or filling a hole.
+ * pagecreate is set only if we actually need to call anon_resv to
+ * reserve an additional page of anonymous memory. Since anon_resv
+ * always reserves a page at a time, it should only get called when we
+ * know we're growing the file into a new page or filling a hole. This
+ * is why we transform delta into a number of pages. However, because we
+ * track bytes and not pages, we convert that back to a number of bytes
+ * that we allocate against.
*
- * Deny if trying to reserve more than tmpfs can allocate
+ * Deny if trying to reserve more than tmpfs can allocate, the
+ * allocation causes an overflow, or the delta round up overflowed.
+ * Note, that btopr rounds up, so we need to catch the unsigned
+ * overflow. Note, rounding up when we are within a page of SIZE_MAX is
+ * done by adding a page, overflowing, which will then be rounded back
+ * to zero. Hence the following check.
*/
+ if (pages == 0 && delta != 0)
+ return (1);
+
zone = tm->tm_vfsp->vfs_zone;
- if (pagecreate && ((tm->tm_anonmem + pages > tm->tm_anonmax) ||
+ if (pagecreate && ((tm->tm_anonmem + pbytes > tm->tm_anonmax) ||
+ (tm->tm_anonmem + pbytes < tm->tm_anonmem) ||
+ (ptob(pages + tmpfs_minfree) <= pbytes) ||
(!anon_checkspace(ptob(pages + tmpfs_minfree), zone)) ||
(anon_try_resv_zone(delta, zone) == 0))) {
return (1);
@@ -89,7 +104,7 @@ tmp_resv(
*/
if (pagecreate) {
mutex_enter(&tm->tm_contents);
- tm->tm_anonmem += pages;
+ tm->tm_anonmem += pbytes;
mutex_exit(&tm->tm_contents);
TRACE_2(TR_FAC_VM, TR_ANON_TMPFS, "anon tmpfs:%p %lu",
@@ -110,13 +125,27 @@ tmp_unresv(
struct tmpnode *tp,
size_t delta)
{
+ size_t pages, pbytes;
+
ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
ASSERT(tp->tn_type == VREG);
+ /*
+ * If this is true, we have a grevious overflow bug and some size
+ * accounting has been messed with as having an amount to truncate at
+ * this size would imply that all of memory was used for this file. No
+ * matter how small the kernel, it will always need at least one page.
+ */
+ pages = btopr(delta);
+ if (pages == 0 && delta != 0)
+ panic("tmpfs unsigned overflow detected");
+ pbytes = ptob(pages);
+
anon_unresv_zone(delta, tm->tm_vfsp->vfs_zone);
mutex_enter(&tm->tm_contents);
- tm->tm_anonmem -= btopr(delta);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - pbytes);
+ tm->tm_anonmem -= pbytes;
mutex_exit(&tm->tm_contents);
TRACE_2(TR_FAC_VM, TR_ANON_TMPFS, "anon tmpfs:%p %lu", tp, delta);
@@ -154,6 +183,26 @@ tmpnode_growmap(struct tmpnode *tp, ulong_t newsize)
}
/*
+ * This is used to clean up a tmpnode that hasn't made it out the door. In other
+ * words, we allocated it and did a tmpnode_init; however, before it could get
+ * fully inserted into a directory, bad things happened and it failed.
+ */
+void
+tmpnode_cleanup(struct tmpnode *tp)
+{
+ rw_enter(&tp->tn_rwlock, RW_WRITER);
+ if ((tp->tn_type) == VDIR) {
+ tdirtrunc(tp);
+ }
+ mutex_enter(&tp->tn_tlock);
+ tp->tn_nlink = 0;
+ mutex_exit(&tp->tn_tlock);
+ gethrestime(&tp->tn_ctime);
+ rw_exit(&tp->tn_rwlock);
+ tmpnode_rele(tp);
+}
+
+/*
* Initialize a tmpnode and add it to file list under mount point.
*/
void
@@ -232,7 +281,6 @@ tmpnode_trunc(
{
size_t oldsize = tp->tn_size;
size_t delta;
- struct vnode *vp = TNTOV(tp);
timestruc_t now;
int error = 0;
@@ -316,7 +364,7 @@ tmpnode_trunc(
/* Delete anon array for tmpnode */
ASSERT(tp->tn_nblocks == 0);
ASSERT(anon_get_ptr(tp->tn_anon, 0) == NULL);
- ASSERT(!vn_has_cached_data(vp));
+ ASSERT(!vn_has_cached_data(TNTOV(tp)));
anon_release(tp->tn_anon, tp->tn_asize);
tp->tn_anon = NULL;
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
index f22cc3ecf0..6013134be2 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -56,6 +56,15 @@
static int tmpfsfstype;
/*
+ * tmpfs_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. With forced umount support, the
+ * filesystem module must not be allowed to go away before the last
+ * VFS_FREEVFS() call has been made. Since this is just an atomic counter,
+ * there's no need for locking.
+ */
+static uint32_t tmpfs_mountcount;
+
+/*
* tmpfs vfs operations.
*/
static int tmpfsinit(int, char *);
@@ -65,6 +74,7 @@ static int tmp_unmount(struct vfs *, int, struct cred *);
static int tmp_root(struct vfs *, struct vnode **);
static int tmp_statvfs(struct vfs *, struct statvfs64 *);
static int tmp_vget(struct vfs *, struct vnode **, struct fid *);
+static void tmp_freevfs(vfs_t *vfsp);
/*
* Loadable module wrapper
@@ -91,7 +101,8 @@ static mntopt_t tmpfs_options[] = {
/* Option name Cancel Opt Arg Flags Data */
{ MNTOPT_XATTR, xattr_cancel, NULL, MO_DEFAULT, NULL},
{ MNTOPT_NOXATTR, noxattr_cancel, NULL, NULL, NULL},
- { "size", NULL, "0", MO_HASVALUE, NULL}
+ { "size", NULL, "0", MO_HASVALUE, NULL},
+ { "mode", NULL, NULL, MO_HASVALUE, NULL}
};
@@ -122,6 +133,14 @@ _fini()
{
int error;
+ /*
+ * If a forceably unmounted instance is still hanging around, we cannot
+ * allow the module to be unloaded because that would cause panics once
+ * the VFS framework decides it's time to call into VFS_FREEVFS().
+ */
+ if (tmpfs_mountcount)
+ return (EBUSY);
+
error = mod_remove(&modlinkage);
if (error)
return (error);
@@ -140,14 +159,6 @@ _info(struct modinfo *modinfop)
}
/*
- * The following are patchable variables limiting the amount of system
- * resources tmpfs can use.
- *
- * tmpfs_maxkmem limits the amount of kernel kmem_alloc memory
- * tmpfs can use for it's data structures (e.g. tmpnodes, directory entries)
- * It is not determined by setting a hard limit but rather as a percentage of
- * physical memory which is determined when tmpfs is first used in the system.
- *
* tmpfs_minfree is the minimum amount of swap space that tmpfs leaves for
* the rest of the system. In other words, if the amount of free swap space
* in the system (i.e. anoninfo.ani_free) drops below tmpfs_minfree, tmpfs
@@ -156,9 +167,7 @@ _info(struct modinfo *modinfop)
* There is also a per mount limit on the amount of swap space
* (tmount.tm_anonmax) settable via a mount option.
*/
-size_t tmpfs_maxkmem = 0;
size_t tmpfs_minfree = 0;
-size_t tmp_kmemspace; /* bytes of kernel heap used by all tmpfs */
static major_t tmpfs_major;
static minor_t tmpfs_minor;
@@ -177,6 +186,7 @@ tmpfsinit(int fstype, char *name)
VFSNAME_ROOT, { .vfs_root = tmp_root },
VFSNAME_STATVFS, { .vfs_statvfs = tmp_statvfs },
VFSNAME_VGET, { .vfs_vget = tmp_vget },
+ VFSNAME_FREEVFS, { .vfs_freevfs = tmp_freevfs },
NULL, NULL
};
int error;
@@ -211,37 +221,28 @@ tmpfsinit(int fstype, char *name)
tmpfs_minfree = btopr(TMPMINFREE);
}
- /*
- * The maximum amount of space tmpfs can allocate is
- * TMPMAXPROCKMEM percent of kernel memory
- */
- if (tmpfs_maxkmem == 0)
- tmpfs_maxkmem = MAX(PAGESIZE, kmem_maxavail() / TMPMAXFRACKMEM);
-
if ((tmpfs_major = getudev()) == (major_t)-1) {
cmn_err(CE_WARN, "tmpfsinit: Can't get unique device number.");
tmpfs_major = 0;
}
mutex_init(&tmpfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+ tmpfs_mountcount = 0;
return (0);
}
static int
-tmp_mount(
- struct vfs *vfsp,
- struct vnode *mvp,
- struct mounta *uap,
- struct cred *cr)
+tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
struct tmount *tm = NULL;
struct tmpnode *tp;
struct pathname dpn;
int error;
- pgcnt_t anonmax;
+ size_t anonmax;
struct vattr rattr;
int got_attrs;
-
- char *sizestr;
+ boolean_t mode_arg = B_FALSE;
+ mode_t root_mode = 0777;
+ char *argstr;
if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
return (error);
@@ -276,11 +277,22 @@ tmp_mount(
* tm_anonmax is set according to the mount arguments
* if any. Otherwise, it is set to a maximum value.
*/
- if (vfs_optionisset(vfsp, "size", &sizestr)) {
- if ((error = tmp_convnum(sizestr, &anonmax)) != 0)
+ if (vfs_optionisset(vfsp, "size", &argstr)) {
+ if ((error = tmp_convnum(argstr, &anonmax)) != 0)
goto out;
} else {
- anonmax = ULONG_MAX;
+ anonmax = SIZE_MAX;
+ }
+
+ /*
+ * The "mode" mount argument allows the operator to override the
+ * permissions of the root of the tmpfs mount.
+ */
+ if (vfs_optionisset(vfsp, "mode", &argstr)) {
+ if ((error = tmp_convmode(argstr, &root_mode)) != 0) {
+ goto out;
+ }
+ mode_arg = B_TRUE;
}
if (error = pn_get(uap->dir,
@@ -302,7 +314,8 @@ tmp_mount(
goto out;
}
- if ((tm = tmp_memalloc(sizeof (struct tmount), 0)) == NULL) {
+ if ((tm = kmem_zalloc(sizeof (struct tmount),
+ KM_NOSLEEP | KM_NORMALPRI)) == NULL) {
pn_free(&dpn);
error = ENOMEM;
goto out;
@@ -334,17 +347,37 @@ tmp_mount(
vfsp->vfs_bsize = PAGESIZE;
vfsp->vfs_flag |= VFS_NOTRUNC;
vfs_make_fsid(&vfsp->vfs_fsid, tm->tm_dev, tmpfsfstype);
- tm->tm_mntpath = tmp_memalloc(dpn.pn_pathlen + 1, TMP_MUSTHAVE);
+ tm->tm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
(void) strcpy(tm->tm_mntpath, dpn.pn_path);
/*
+ * Preemptively set vfs_zone before any of the tmp_kmem_* functions are
+ * called. That field is not populated until after a successful
+ * VFS_MOUNT when domount() sets vfsp metadata via vfs_add(). An
+ * accurate value is required for proper swap usage accounting.
+ */
+ ASSERT0(uap->flags & MS_REMOUNT);
+ ASSERT(vfsp->vfs_zone == NULL);
+ vfsp->vfs_zone = curproc->p_zone;
+
+ /*
* allocate and initialize root tmpnode structure
*/
bzero(&rattr, sizeof (struct vattr));
- rattr.va_mode = (mode_t)(S_IFDIR | 0777); /* XXX modes */
+ rattr.va_mode = (mode_t)(S_IFDIR | root_mode);
rattr.va_type = VDIR;
rattr.va_rdev = 0;
- tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
+ tp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode), KM_SLEEP);
+ if (tp == NULL) {
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ mutex_destroy(&tm->tm_contents);
+ mutex_destroy(&tm->tm_renamelck);
+ kmem_free(tm, sizeof (struct tmount));
+
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
tmpnode_init(tm, tp, &rattr, cr);
/*
@@ -361,7 +394,14 @@ tmp_mount(
* the previously set hardwired defaults to prevail.
*/
if (got_attrs == 0) {
- tp->tn_mode = rattr.va_mode;
+ if (!mode_arg) {
+ /*
+ * Only use the underlying mount point for the
+ * mode if the "mode" mount argument was not
+ * provided.
+ */
+ tp->tn_mode = rattr.va_mode;
+ }
tp->tn_uid = rattr.va_uid;
tp->tn_gid = rattr.va_gid;
}
@@ -376,12 +416,34 @@ tmp_mount(
tp->tn_nlink = 0;
tm->tm_rootnode = tp;
- tdirinit(tp, tp);
+ if (tdirinit(tp, tp) != 0) {
+ /*
+ * While we would normally let our VOP_INACTIVE function take
+ * care of cleaning up here, we're in a bit of a delicate
+ * situation, so we do so manually. While it's tempting to try
+ * and rely upon tmpfs_freevfs() and others, it's probably safer
+ * for the time to do this manually at the cost of duplication.
+ */
+ vn_invalid(TNTOV(tp));
+ rw_destroy(&tp->tn_rwlock);
+ mutex_destroy(&tp->tn_tlock);
+ vn_free(TNTOV(tp));
+ tmp_kmem_free(tm, tp, sizeof (struct tmpnode));
+
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ mutex_destroy(&tm->tm_contents);
+ mutex_destroy(&tm->tm_renamelck);
+ kmem_free(tm, sizeof (struct tmount));
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
rw_exit(&tp->tn_rwlock);
pn_free(&dpn);
error = 0;
+ atomic_inc_32(&tmpfs_mountcount);
out:
if (error == 0)
@@ -397,36 +459,107 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
struct tmpnode *tnp, *cancel;
struct vnode *vp;
int error;
+ uint_t cnt;
+ int i;
if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
return (error);
- /*
- * forced unmount is not supported by this file system
- * and thus, ENOTSUP, is being returned.
- */
- if (flag & MS_FORCE)
- return (ENOTSUP);
-
mutex_enter(&tm->tm_contents);
/*
- * If there are no open files, only the root node should have
- * a reference count.
+ * In the normal unmount case (non-forced unmount), if there are no
+ * open files, only the root node should have a reference count.
+ *
* With tm_contents held, nothing can be added or removed.
* There may be some dirty pages. To prevent fsflush from
* disrupting the unmount, put a hold on each node while scanning.
* If we find a previously referenced node, undo the holds we have
* placed and fail EBUSY.
+ *
+ * However, in the case of a forced umount, things are a bit different.
+ * An additional VFS_HOLD is added for each outstanding VN_HOLD to
+ * ensure that the file system is not cleaned up (tmp_freevfs) until
+ * the last vfs hold is dropped. This happens in tmp_inactive as the
+ * vnodes are released. Also, we can't add an additional VN_HOLD in
+ * this case since that would prevent tmp_inactive from ever being
+ * called. Finally, we do need to drop the zone ref now (zone_rele_ref)
+ * so that the zone is not blocked waiting for the final file system
+ * cleanup.
*/
tnp = tm->tm_rootnode;
- if (TNTOV(tnp)->v_count > 1) {
+
+ vp = TNTOV(tnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (flag & MS_FORCE) {
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ /* Extra hold which we rele below when we drop the zone ref */
+ VFS_HOLD(vfsp);
+
+ for (i = 1; i < cnt; i++)
+ VFS_HOLD(vfsp);
+
+ /* drop the mutex now because no one can find this mount */
+ mutex_exit(&tm->tm_contents);
+ } else if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
mutex_exit(&tm->tm_contents);
return (EBUSY);
}
+ mutex_exit(&vp->v_lock);
+ /*
+ * Check for open files. An open file causes everything to unwind
+ * unless this is a forced umount.
+ */
for (tnp = tnp->tn_forw; tnp; tnp = tnp->tn_forw) {
- if ((vp = TNTOV(tnp))->v_count > 0) {
+ vp = TNTOV(tnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (flag & MS_FORCE) {
+ for (i = 0; i < cnt; i++)
+ VFS_HOLD(vfsp);
+
+ /*
+ * In the case of a forced umount don't add an
+ * additional VN_HOLD on the already held vnodes, like
+ * we do in the non-forced unmount case. If the
+ * cnt > 0, then the vnode already has at least one
+ * hold and we need tmp_inactive to get called when the
+ * last pre-existing hold on the node is released so
+ * that we can VFS_RELE the VFS holds we just added.
+ */
+ if (cnt == 0) {
+ /* directly add VN_HOLD since have the lock */
+ vp->v_count++;
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * If the tmpnode has any pages associated with it
+ * (i.e. if it's a normal file with non-zero size), the
+ * tmpnode could still be discovered by pageout or
+ * fsflush via the page vnode pointers. To prevent this
+ * from interfering with the tmp_freevfs, truncate the
+ * tmpnode now.
+ */
+ if (tnp->tn_size != 0 && tnp->tn_type == VREG) {
+ rw_enter(&tnp->tn_rwlock, RW_WRITER);
+ rw_enter(&tnp->tn_contents, RW_WRITER);
+
+ (void) tmpnode_trunc(tm, tnp, 0);
+
+ rw_exit(&tnp->tn_contents);
+ rw_exit(&tnp->tn_rwlock);
+
+ ASSERT(tnp->tn_size == 0);
+ ASSERT(tnp->tn_nblocks == 0);
+ }
+ } else if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
cancel = tm->tm_rootnode->tn_forw;
while (cancel != tnp) {
vp = TNTOV(cancel);
@@ -436,14 +569,50 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
}
mutex_exit(&tm->tm_contents);
return (EBUSY);
+ } else {
+ /* directly add a VN_HOLD since we have the lock */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
}
- VN_HOLD(vp);
}
- /*
- * We can drop the mutex now because no one can find this mount
- */
- mutex_exit(&tm->tm_contents);
+ if (flag & MS_FORCE) {
+ /*
+ * Drop the zone ref now since we don't know how long it will
+ * be until the final vfs_rele is called by tmp_inactive.
+ */
+ if (vfsp->vfs_zone) {
+ zone_rele_ref(&vfsp->vfs_implp->vi_zone_ref,
+ ZONE_REF_VFS);
+ vfsp->vfs_zone = 0;
+ }
+ /* We can now drop the extra hold we added above. */
+ VFS_RELE(vfsp);
+ } else {
+ /*
+ * For the non-forced case, we can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&tm->tm_contents);
+ }
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS() to support forced umounts. This is called by
+ * the vfs framework after umount and the last VFS_RELE, to trigger the release
+ * of any resources still associated with the given vfs_t. We only add
+ * additional VFS_HOLDs during the forced umount case, so this is normally
+ * called immediately after tmp_umount.
+ */
+void
+tmp_freevfs(vfs_t *vfsp)
+{
+ struct tmount *tm = (struct tmount *)VFSTOTM(vfsp);
+ struct tmpnode *tnp;
+ struct vnode *vp;
/*
* Free all kmemalloc'd and anonalloc'd memory associated with
@@ -453,6 +622,16 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
* tmpnode_free which assumes that the directory entry has been
* removed before the file.
*/
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the tmount that says
+ * we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
/*
* Remove all directory entries
*/
@@ -519,15 +698,16 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
ASSERT(tm->tm_mntpath);
- tmp_memfree(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
ASSERT(tm->tm_anonmem == 0);
mutex_destroy(&tm->tm_contents);
mutex_destroy(&tm->tm_renamelck);
- tmp_memfree(tm, sizeof (struct tmount));
+ kmem_free(tm, sizeof (struct tmount));
- return (0);
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&tmpfs_mountcount);
}
/*
@@ -589,18 +769,19 @@ tmp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
* If tm_anonmax for this mount is less than the available swap space
* (minus the amount tmpfs can't use), use that instead
*/
- if (blocks > tmpfs_minfree)
+ if (blocks > tmpfs_minfree && tm->tm_anonmax > tm->tm_anonmem) {
sbp->f_bfree = MIN(blocks - tmpfs_minfree,
- tm->tm_anonmax - tm->tm_anonmem);
- else
+ btop(tm->tm_anonmax) - btopr(tm->tm_anonmem));
+ } else {
sbp->f_bfree = 0;
+ }
sbp->f_bavail = sbp->f_bfree;
/*
* Total number of blocks is what's available plus what's been used
*/
- sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree + tm->tm_anonmem);
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree + btopr(tm->tm_anonmem));
if (eff_zid != GLOBAL_ZONEUNIQID &&
zp->zone_max_swap_ctl != UINT64_MAX) {
@@ -630,13 +811,7 @@ tmp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
* available to tmpfs. This is fairly inaccurate since it doesn't
* take into account the names stored in the directory entries.
*/
- if (tmpfs_maxkmem > tmp_kmemspace)
- sbp->f_ffree = (tmpfs_maxkmem - tmp_kmemspace) /
- (sizeof (struct tmpnode) + sizeof (struct tdirent));
- else
- sbp->f_ffree = 0;
-
- sbp->f_files = tmpfs_maxkmem /
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
(sizeof (struct tmpnode) + sizeof (struct tdirent));
sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
(void) cmpldev(&d32, vfsp->vfs_dev);
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
index 82195e6742..9087454e32 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 RackTop Systems.
*/
@@ -585,6 +585,10 @@ tmp_read(struct vnode *vp, struct uio *uiop, int ioflag, cred_t *cred,
struct tmount *tm = (struct tmount *)VTOTM(vp);
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
/*
* We don't currently support reading non-regular files
*/
@@ -614,6 +618,10 @@ tmp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
struct tmount *tm = (struct tmount *)VTOTM(vp);
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
/*
* We don't currently support writing to non-regular files
*/
@@ -787,8 +795,13 @@ tmp_setattr(
rw_exit(&tp->tn_contents);
rw_exit(&tp->tn_rwlock);
- if (error == 0 && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
goto out1;
}
@@ -834,6 +847,9 @@ tmp_lookup(
struct tmpnode *ntp = NULL;
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
/* allow cd into @ dir */
if (flags & LOOKUP_XATTR) {
@@ -852,6 +868,8 @@ tmp_lookup(
rw_enter(&tp->tn_rwlock, RW_WRITER);
if (tp->tn_xattrdp == NULL) {
+ int err;
+
if (!(flags & CREATE_XATTR_DIR)) {
rw_exit(&tp->tn_rwlock);
return (ENOENT);
@@ -872,9 +890,13 @@ tmp_lookup(
return (error);
}
- xdp = tmp_memalloc(sizeof (struct tmpnode),
- TMP_MUSTHAVE);
tm = VTOTM(dvp);
+ xdp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode),
+ KM_SLEEP);
+ if (xdp == NULL) {
+ rw_exit(&tp->tn_rwlock);
+ return (ENOSPC);
+ }
tmpnode_init(tm, xdp, &tp->tn_attr, NULL);
/*
* Fix-up fields unique to attribute directories.
@@ -892,7 +914,16 @@ tmp_lookup(
}
xdp->tn_vnode->v_type = VDIR;
xdp->tn_vnode->v_flag |= V_XATTRDIR;
- tdirinit(tp, xdp);
+ if ((err = tdirinit(tp, xdp)) != 0) {
+ rw_exit(&tp->tn_rwlock);
+ /*
+ * This never got properly initialized so we can
+ * just clean it up.
+ */
+ xdp->tn_vnode->v_flag &= V_XATTRDIR;
+ tmpnode_cleanup(tp);
+ return (err);
+ }
tp->tn_xattrdp = xdp;
} else {
VN_HOLD(tp->tn_xattrdp->tn_vnode);
@@ -1304,10 +1335,8 @@ tmp_rename(
vnevent_rename_src(TNTOV(fromtp), odvp, onm, ct);
/*
* vnevent_rename_dest is called in tdirenter().
- * Notify the target dir if not same as source dir.
*/
- if (ndvp != odvp)
- vnevent_rename_dest_dir(ndvp, ct);
+ vnevent_rename_dest_dir(ndvp, TNTOV(fromtp), nnm, ct);
}
done:
@@ -1476,6 +1505,10 @@ tmp_readdir(
int reclen;
caddr_t outbuf;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
if (uiop->uio_loffset >= MAXOFF_T) {
if (eofp)
*eofp = 1;
@@ -1609,12 +1642,12 @@ tmp_symlink(
rw_exit(&parent->tn_rwlock);
if (error) {
- if (self)
+ if (self != NULL)
tmpnode_rele(self);
return (error);
}
len = strlen(tnm) + 1;
- cp = tmp_memalloc(len, 0);
+ cp = tmp_kmem_zalloc(tm, len, KM_NOSLEEP | KM_NORMALPRI);
if (cp == NULL) {
tmpnode_rele(self);
return (ENOSPC);
@@ -1679,10 +1712,27 @@ top:
* there's little to do -- just drop our hold.
*/
if (vp->v_count > 1 || tp->tn_nlink != 0) {
- vp->v_count--;
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
+ /*
+ * Since the file system was forcibly unmounted, we can
+ * have a case (v_count == 1, tn_nlink != 0) where this
+ * file was open so we didn't add an extra hold on the
+ * file in tmp_unmount. We are counting on the
+ * interaction of the hold made in tmp_unmount and
+ * rele-ed in tmp_vfsfree so we need to be sure we
+ * don't decrement in this case.
+ */
+ if (vp->v_count > 1)
+ vp->v_count--;
+ } else {
+ vp->v_count--;
+ }
mutex_exit(&vp->v_lock);
mutex_exit(&tp->tn_tlock);
rw_exit(&tp->tn_rwlock);
+ /* If the filesystem was umounted by force, rele the vfs ref */
+ if (tm->tm_vfsp->vfs_flag & VFS_UNMOUNTED)
+ VFS_RELE(tm->tm_vfsp);
return;
}
@@ -1707,7 +1757,7 @@ top:
goto top;
}
if (tp->tn_type == VLNK)
- tmp_memfree(tp->tn_symlink, tp->tn_size + 1);
+ tmp_kmem_free(tm, tp->tn_symlink, tp->tn_size + 1);
}
/*
@@ -1741,7 +1791,11 @@ top:
rw_destroy(&tp->tn_rwlock);
mutex_destroy(&tp->tn_tlock);
vn_free(TNTOV(tp));
- tmp_memfree(tp, sizeof (struct tmpnode));
+ tmp_kmem_free(tm, tp, sizeof (struct tmpnode));
+
+ /* If the filesystem was umounted by force, rele the vfs ref */
+ if (tm->tm_vfsp->vfs_flag & VFS_UNMOUNTED)
+ VFS_RELE(tm->tm_vfsp);
}
/* ARGSUSED2 */
@@ -1863,6 +1917,10 @@ tmp_getapage(
struct vnode *pvp;
u_offset_t poff;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
if (protp != NULL)
*protp = PROT_ALL;
again:
@@ -2084,6 +2142,10 @@ tmp_putapage(
u_offset_t offset;
u_offset_t tmpoff;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
ASSERT(PAGE_LOCKED(pp));
/* Kluster in tmp_klustsize chunks */
@@ -2344,8 +2406,13 @@ tmp_space(
return (EFBIG);
error = tmp_freesp(vp, bfp, flag);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
return (error);
}
diff --git a/usr/src/uts/common/fs/udfs/udf_dir.c b/usr/src/uts/common/fs/udfs/udf_dir.c
index c1e2c74a87..def046a0bf 100644
--- a/usr/src/uts/common/fs/udfs/udf_dir.c
+++ b/usr/src/uts/common/fs/udfs/udf_dir.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -562,9 +563,8 @@ out:
namep, ctp);
}
- if (sdp != tdp) {
- vnevent_rename_dest_dir(ITOV(tdp), ctp);
- }
+ vnevent_rename_dest_dir(ITOV(tdp), ITOV(tip),
+ namep, ctp);
}
/*
diff --git a/usr/src/uts/common/fs/udfs/udf_vnops.c b/usr/src/uts/common/fs/udfs/udf_vnops.c
index 93cc4d49e8..7e17f16ce2 100644
--- a/usr/src/uts/common/fs/udfs/udf_vnops.c
+++ b/usr/src/uts/common/fs/udfs/udf_vnops.c
@@ -569,8 +569,11 @@ udf_setattr(
goto update_inode;
}
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
}
/*
* Change file access or modified times.
@@ -1649,8 +1652,13 @@ udf_space(
} else if ((error = convoff(vp, bfp, 0, offset)) == 0) {
error = ud_freesp(vp, bfp, flag, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
return (error);
diff --git a/usr/src/uts/common/fs/ufs/ufs_vnops.c b/usr/src/uts/common/fs/ufs/ufs_vnops.c
index cf45b48e3c..d689a8173b 100644
--- a/usr/src/uts/common/fs/ufs/ufs_vnops.c
+++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -2193,8 +2193,13 @@ again:
goto update_inode;
}
- if (error == 0 && vap->va_size)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (vap->va_size) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
if (ulp) {
@@ -3726,12 +3731,7 @@ retry_firstlock:
if (error == 0) {
vnevent_rename_src(ITOV(sip), sdvp, snm, ct);
- /*
- * Notify the target directory of the rename event
- * if source and target directories are not the same.
- */
- if (sdvp != tdvp)
- vnevent_rename_dest_dir(tdvp, ct);
+ vnevent_rename_dest_dir(tdvp, ITOV(sip), tnm, ct);
}
errout:
@@ -4478,8 +4478,13 @@ ufs_space(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
return (error);
error = ufs_freesp(vp, bfp, flag, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else if (cmd == F_ALLOCSP) {
error = ufs_lockfs_begin(ufsvfsp, &ulp,
ULOCKFS_FALLOCATE_MASK);
diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c
index 64ed4386a4..c1b5c12542 100644
--- a/usr/src/uts/common/fs/vfs.c
+++ b/usr/src/uts/common/fs/vfs.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 Toomas Soome <tsoome@me.com>
*/
@@ -237,10 +237,13 @@ fsop_root(vfs_t *vfsp, vnode_t **vpp)
* Make sure this root has a path. With lofs, it is possible to have
* a NULL mountpoint.
*/
- if (ret == 0 && vfsp->vfs_mntpt != NULL && (*vpp)->v_path == NULL) {
+ if (ret == 0 && vfsp->vfs_mntpt != NULL &&
+ (*vpp)->v_path == vn_vpath_empty) {
+ const char *path;
+
mntpt = vfs_getmntpoint(vfsp);
- vn_setpath_str(*vpp, refstr_value(mntpt),
- strlen(refstr_value(mntpt)));
+ path = refstr_value(mntpt);
+ vn_setpath_str(*vpp, path, strlen(path));
refstr_rele(mntpt);
}
@@ -3902,6 +3905,8 @@ vfs_to_modname(const char *vfstype)
vfstype = "fdfs";
} else if (strncmp(vfstype, "nfs", 3) == 0) {
vfstype = "nfs";
+ } else if (strcmp(vfstype, "lxproc") == 0) {
+ vfstype = "lxprocfs";
}
return (vfstype);
diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c
index e6b6adf56b..5a54ef8eee 100644
--- a/usr/src/uts/common/fs/vnode.c
+++ b/usr/src/uts/common/fs/vnode.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -66,6 +66,8 @@
#include <fs/fs_subr.h>
#include <sys/taskq.h>
#include <fs/fs_reparse.h>
+#include <sys/time.h>
+#include <sys/sdt.h>
/* Determine if this vnode is a file that is read-only */
#define ISROFILE(vp) \
@@ -102,6 +104,9 @@ kmutex_t vskstat_tree_lock;
/* Global variable which enables/disables the vopstats collection */
int vopstats_enabled = 1;
+/* Global used for empty/invalid v_path */
+char *vn_vpath_empty = "";
+
/*
* forward declarations for internal vnode specific data (vsd)
*/
@@ -200,6 +205,11 @@ static void (**vsd_destructor)(void *);
cr = crgetmapped(cr); \
}
+#define VOP_LATENCY_10MS 10000000
+#define VOP_LATENCY_100MS 100000000
+#define VOP_LATENCY_1S 1000000000
+#define VOP_LATENCY_10S 10000000000
+
/*
* Convert stat(2) formats to vnode types and vice versa. (Knows about
* numerical order of S_IFMT and vnode types.)
@@ -1643,7 +1653,7 @@ vn_rename(char *from, char *to, enum uio_seg seg)
int
vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp,
- char *tname, enum uio_seg seg)
+ char *tname, enum uio_seg seg)
{
int error;
struct vattr vattr;
@@ -2284,7 +2294,8 @@ vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
cv_init(&vp->v_cv, NULL, CV_DEFAULT, NULL);
rw_init(&vp->v_nbllock, NULL, RW_DEFAULT, NULL);
vp->v_femhead = NULL; /* Must be done before vn_reinit() */
- vp->v_path = NULL;
+ vp->v_path = vn_vpath_empty;
+ vp->v_path_stamp = 0;
vp->v_mpssdata = NULL;
vp->v_vsd = NULL;
vp->v_fopdata = NULL;
@@ -2331,6 +2342,7 @@ void
vn_recycle(vnode_t *vp)
{
ASSERT(vp->v_pages == NULL);
+ VERIFY(vp->v_path != NULL);
/*
* XXX - This really belongs in vn_reinit(), but we have some issues
@@ -2353,10 +2365,11 @@ vn_recycle(vnode_t *vp)
kmem_free(vp->v_femhead, sizeof (*(vp->v_femhead)));
vp->v_femhead = NULL;
}
- if (vp->v_path) {
+ if (vp->v_path != vn_vpath_empty) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
- vp->v_path = NULL;
+ vp->v_path = vn_vpath_empty;
}
+ vp->v_path_stamp = 0;
if (vp->v_fopdata != NULL) {
free_fopdata(vp);
@@ -2427,9 +2440,10 @@ vn_free(vnode_t *vp)
*/
ASSERT((vp->v_count == 0) || (vp->v_count == 1));
ASSERT(vp->v_count_dnlc == 0);
- if (vp->v_path != NULL) {
+ VERIFY(vp->v_path != NULL);
+ if (vp->v_path != vn_vpath_empty) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
- vp->v_path = NULL;
+ vp->v_path = vn_vpath_empty;
}
/* If FEM was in use, make sure everything gets cleaned up */
@@ -2516,6 +2530,7 @@ vnevent_rename_src(vnode_t *vp, vnode_t *dvp, char *name, caller_context_t *ct)
if (vp == NULL || vp->v_femhead == NULL) {
return;
}
+ (void) VOP_VNEVENT(dvp, VE_RENAME_SRC_DIR, vp, name, ct);
(void) VOP_VNEVENT(vp, VE_RENAME_SRC, dvp, name, ct);
}
@@ -2530,12 +2545,13 @@ vnevent_rename_dest(vnode_t *vp, vnode_t *dvp, char *name,
}
void
-vnevent_rename_dest_dir(vnode_t *vp, caller_context_t *ct)
+vnevent_rename_dest_dir(vnode_t *vp, vnode_t *nvp, char *name,
+ caller_context_t *ct)
{
if (vp == NULL || vp->v_femhead == NULL) {
return;
}
- (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, NULL, NULL, ct);
+ (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, nvp, name, ct);
}
void
@@ -2622,6 +2638,15 @@ vnevent_truncate(vnode_t *vp, caller_context_t *ct)
(void) VOP_VNEVENT(vp, VE_TRUNCATE, NULL, NULL, ct);
}
+void
+vnevent_resize(vnode_t *vp, caller_context_t *ct)
+{
+ if (vp == NULL || vp->v_femhead == NULL) {
+ return;
+ }
+ (void) VOP_VNEVENT(vp, VE_RESIZE, NULL, NULL, ct);
+}
+
/*
* Vnode accessors.
*/
@@ -2952,107 +2977,229 @@ fs_new_caller_id()
}
/*
- * Given a starting vnode and a path, updates the path in the target vnode in
- * a safe manner. If the vnode already has path information embedded, then the
- * cached path is left untouched.
+ * The value stored in v_path is relative to rootdir, located in the global
+ * zone. Zones or chroot environments which reside deeper inside the VFS
+ * hierarchy will have a relative view of MAXPATHLEN since they are unaware of
+ * what lies below their perceived root. In order to keep v_path usable for
+ * these child environments, its allocations are allowed to exceed MAXPATHLEN.
+ *
+ * An upper bound of max_vnode_path is placed upon v_path allocations to
+ * prevent the system from going too wild at the behest of pathological
+ * behavior from the operator.
*/
-
size_t max_vnode_path = 4 * MAXPATHLEN;
+
void
-vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp,
- const char *path, size_t plen)
-{
- char *rpath;
- vnode_t *base;
- size_t rpathlen, rpathalloc;
- int doslash = 1;
-
- if (*path == '/') {
- base = rootvp;
- path++;
- plen--;
- } else {
- base = startvp;
- }
+vn_clearpath(vnode_t *vp, hrtime_t compare_stamp)
+{
+ char *buf;
+ mutex_enter(&vp->v_lock);
/*
- * We cannot grab base->v_lock while we hold vp->v_lock because of
- * the potential for deadlock.
+ * If the snapshot of v_path_stamp passed in via compare_stamp does not
+ * match the present value on the vnode, it indicates that subsequent
+ * changes have occurred. The v_path value is not cleared in this case
+ * since the new value may be valid.
*/
- mutex_enter(&base->v_lock);
- if (base->v_path == NULL) {
- mutex_exit(&base->v_lock);
+ if (compare_stamp != 0 && vp->v_path_stamp != compare_stamp) {
+ mutex_exit(&vp->v_lock);
return;
}
+ buf = vp->v_path;
+ vp->v_path = vn_vpath_empty;
+ vp->v_path_stamp = 0;
+ mutex_exit(&vp->v_lock);
+ if (buf != vn_vpath_empty) {
+ kmem_free(buf, strlen(buf) + 1);
+ }
+}
- rpathlen = strlen(base->v_path);
- rpathalloc = rpathlen + plen + 1;
- /* Avoid adding a slash if there's already one there */
- if (base->v_path[rpathlen-1] == '/')
- doslash = 0;
- else
- rpathalloc++;
-
- /*
- * We don't want to call kmem_alloc(KM_SLEEP) with kernel locks held,
- * so we must do this dance. If, by chance, something changes the path,
- * just give up since there is no real harm.
- */
- mutex_exit(&base->v_lock);
+static void
+vn_setpath_common(vnode_t *pvp, vnode_t *vp, const char *name, size_t len,
+ boolean_t is_rename)
+{
+ char *buf, *oldbuf;
+ hrtime_t pstamp;
+ size_t baselen, buflen = 0;
+
+ /* Handle the vn_setpath_str case. */
+ if (pvp == NULL) {
+ if (len + 1 > max_vnode_path) {
+ DTRACE_PROBE4(vn__setpath__too__long, vnode_t *, pvp,
+ vnode_t *, vp, char *, name, size_t, len + 1);
+ return;
+ }
+ buf = kmem_alloc(len + 1, KM_SLEEP);
+ bcopy(name, buf, len);
+ buf[len] = '\0';
- /* Paths should stay within reason */
- if (rpathalloc > max_vnode_path)
+ mutex_enter(&vp->v_lock);
+ oldbuf = vp->v_path;
+ vp->v_path = buf;
+ vp->v_path_stamp = gethrtime();
+ mutex_exit(&vp->v_lock);
+ if (oldbuf != vn_vpath_empty) {
+ kmem_free(oldbuf, strlen(oldbuf) + 1);
+ }
return;
+ }
+
+ /* Take snapshot of parent dir */
+ mutex_enter(&pvp->v_lock);
+retrybuf:
+ if (pvp->v_path == vn_vpath_empty) {
+ /*
+ * Without v_path from the parent directory, generating a child
+ * path from the name is impossible.
+ */
+ if (len > 0) {
+ pstamp = pvp->v_path_stamp;
+ mutex_exit(&pvp->v_lock);
+ vn_clearpath(vp, pstamp);
+ return;
+ }
- rpath = kmem_alloc(rpathalloc, KM_SLEEP);
+ /*
+ * The only feasible case here is where a NUL lookup is being
+ * performed on rootdir prior to its v_path being populated.
+ */
+ ASSERT(pvp->v_path_stamp = 0);
+ baselen = 0;
+ pstamp = 0;
+ } else {
+ pstamp = pvp->v_path_stamp;
+ baselen = strlen(pvp->v_path);
+ /* ignore a trailing slash if present */
+ if (pvp->v_path[baselen - 1] == '/') {
+ /* This should only the be case for rootdir */
+ ASSERT(baselen == 1 && pvp == rootdir);
+ baselen--;
+ }
+ }
+ mutex_exit(&pvp->v_lock);
- mutex_enter(&base->v_lock);
- if (base->v_path == NULL || strlen(base->v_path) != rpathlen) {
- mutex_exit(&base->v_lock);
- kmem_free(rpath, rpathalloc);
+ if (buflen != 0) {
+ /* Free the existing (mis-sized) buffer in case of retry */
+ kmem_free(buf, buflen);
+ }
+ /* base, '/', name and trailing NUL */
+ buflen = baselen + len + 2;
+ if (buflen > max_vnode_path) {
+ DTRACE_PROBE4(vn__setpath_too__long, vnode_t *, pvp,
+ vnode_t *, vp, char *, name, size_t, buflen);
return;
}
- bcopy(base->v_path, rpath, rpathlen);
- mutex_exit(&base->v_lock);
+ buf = kmem_alloc(buflen, KM_SLEEP);
- if (doslash)
- rpath[rpathlen++] = '/';
- bcopy(path, rpath + rpathlen, plen);
- rpath[rpathlen + plen] = '\0';
+ mutex_enter(&pvp->v_lock);
+ if (pvp->v_path_stamp != pstamp) {
+ size_t vlen;
+
+ /*
+ * Since v_path_stamp changed on the parent, it is likely that
+ * v_path has been altered as well. If the length does not
+ * exactly match what was previously measured, the buffer
+ * allocation must be repeated for proper sizing.
+ */
+ if (pvp->v_path == vn_vpath_empty) {
+ /* Give up if parent lack v_path */
+ mutex_exit(&pvp->v_lock);
+ kmem_free(buf, buflen);
+ return;
+ }
+ vlen = strlen(pvp->v_path);
+ if (pvp->v_path[vlen - 1] == '/') {
+ vlen--;
+ }
+ if (vlen != baselen) {
+ goto retrybuf;
+ }
+ }
+ bcopy(pvp->v_path, buf, baselen);
+ mutex_exit(&pvp->v_lock);
+
+ buf[baselen] = '/';
+ baselen++;
+ bcopy(name, &buf[baselen], len + 1);
mutex_enter(&vp->v_lock);
- if (vp->v_path != NULL) {
+ if (vp->v_path_stamp == 0) {
+ /* never-visited vnode can inherit stamp from parent */
+ ASSERT(vp->v_path == vn_vpath_empty);
+ vp->v_path_stamp = pstamp;
+ vp->v_path = buf;
mutex_exit(&vp->v_lock);
- kmem_free(rpath, rpathalloc);
+ } else if (vp->v_path_stamp < pstamp || is_rename) {
+ /*
+ * Install the updated path and stamp, ensuring that the v_path
+ * pointer is valid at all times for dtrace.
+ */
+ oldbuf = vp->v_path;
+ vp->v_path = buf;
+ vp->v_path_stamp = gethrtime();
+ mutex_exit(&vp->v_lock);
+ kmem_free(oldbuf, strlen(oldbuf) + 1);
} else {
- vp->v_path = rpath;
+ /*
+ * If the timestamp matches or is greater, it means another
+ * thread performed the update first while locks were dropped
+ * here to make the allocation. We defer to the newer value.
+ */
mutex_exit(&vp->v_lock);
+ kmem_free(buf, buflen);
}
+ ASSERT(MUTEX_NOT_HELD(&vp->v_lock));
}
-/*
- * Sets the path to the vnode to be the given string, regardless of current
- * context. The string must be a complete path from rootdir. This is only used
- * by fsop_root() for setting the path based on the mountpoint.
- */
void
-vn_setpath_str(struct vnode *vp, const char *str, size_t len)
+vn_updatepath(vnode_t *pvp, vnode_t *vp, const char *name)
{
- char *buf = kmem_alloc(len + 1, KM_SLEEP);
+ size_t len;
- mutex_enter(&vp->v_lock);
- if (vp->v_path != NULL) {
- mutex_exit(&vp->v_lock);
- kmem_free(buf, len + 1);
+ /*
+ * If the parent is older or empty, there's nothing further to do.
+ */
+ if (pvp->v_path == vn_vpath_empty ||
+ pvp->v_path_stamp <= vp->v_path_stamp) {
+ return;
+ }
+
+ /*
+ * Given the lack of appropriate context, meaningful updates to v_path
+ * cannot be made for during lookups for the '.' or '..' entries.
+ */
+ len = strlen(name);
+ if (len == 0 || (len == 1 && name[0] == '.') ||
+ (len == 2 && name[0] == '.' && name[1] == '.')) {
return;
}
- vp->v_path = buf;
- bcopy(str, vp->v_path, len);
- vp->v_path[len] = '\0';
+ vn_setpath_common(pvp, vp, name, len, B_FALSE);
+}
- mutex_exit(&vp->v_lock);
+/*
+ * Given a starting vnode and a path, updates the path in the target vnode in
+ * a safe manner. If the vnode already has path information embedded, then the
+ * cached path is left untouched.
+ */
+/* ARGSUSED */
+void
+vn_setpath(vnode_t *rootvp, vnode_t *pvp, vnode_t *vp, const char *name,
+ size_t len)
+{
+ vn_setpath_common(pvp, vp, name, len, B_FALSE);
+}
+
+/*
+ * Sets the path to the vnode to be the given string, regardless of current
+ * context. The string must be a complete path from rootdir. This is only used
+ * by fsop_root() for setting the path based on the mountpoint.
+ */
+void
+vn_setpath_str(vnode_t *vp, const char *str, size_t len)
+{
+ vn_setpath_common(NULL, vp, str, len, B_FALSE);
}
/*
@@ -3060,17 +3207,9 @@ vn_setpath_str(struct vnode *vp, const char *str, size_t len)
* target vnode is available.
*/
void
-vn_renamepath(vnode_t *dvp, vnode_t *vp, const char *nm, size_t len)
+vn_renamepath(vnode_t *pvp, vnode_t *vp, const char *name, size_t len)
{
- char *tmp;
-
- mutex_enter(&vp->v_lock);
- tmp = vp->v_path;
- vp->v_path = NULL;
- mutex_exit(&vp->v_lock);
- vn_setpath(rootdir, dvp, vp, nm, len);
- if (tmp != NULL)
- kmem_free(tmp, strlen(tmp) + 1);
+ vn_setpath_common(pvp, vp, name, len, B_TRUE);
}
/*
@@ -3081,37 +3220,42 @@ void
vn_copypath(struct vnode *src, struct vnode *dst)
{
char *buf;
- int alloc;
+ hrtime_t stamp;
+ size_t buflen;
mutex_enter(&src->v_lock);
- if (src->v_path == NULL) {
+ if (src->v_path == vn_vpath_empty) {
mutex_exit(&src->v_lock);
return;
}
- alloc = strlen(src->v_path) + 1;
-
- /* avoid kmem_alloc() with lock held */
+ buflen = strlen(src->v_path) + 1;
mutex_exit(&src->v_lock);
- buf = kmem_alloc(alloc, KM_SLEEP);
+
+ buf = kmem_alloc(buflen, KM_SLEEP);
+
mutex_enter(&src->v_lock);
- if (src->v_path == NULL || strlen(src->v_path) + 1 != alloc) {
+ if (src->v_path == vn_vpath_empty ||
+ strlen(src->v_path) + 1 != buflen) {
mutex_exit(&src->v_lock);
- kmem_free(buf, alloc);
+ kmem_free(buf, buflen);
return;
}
- bcopy(src->v_path, buf, alloc);
+ bcopy(src->v_path, buf, buflen);
+ stamp = src->v_path_stamp;
mutex_exit(&src->v_lock);
mutex_enter(&dst->v_lock);
- if (dst->v_path != NULL) {
+ if (dst->v_path != vn_vpath_empty) {
mutex_exit(&dst->v_lock);
- kmem_free(buf, alloc);
+ kmem_free(buf, buflen);
return;
}
dst->v_path = buf;
+ dst->v_path_stamp = stamp;
mutex_exit(&dst->v_lock);
}
+
/*
* XXX Private interface for segvn routines that handle vnode
* large page segments.
@@ -3261,14 +3405,58 @@ fop_read(
cred_t *cr,
caller_context_t *ct)
{
- int err;
ssize_t resid_start = uiop->uio_resid;
+ zone_t *zonep = curzone;
+ zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
+
+ hrtime_t start = 0, lat;
+ ssize_t len;
+ int err;
+
+ if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+ vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+ start = gethrtime();
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_runq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ }
VOPXID_MAP_CR(vp, cr);
err = (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct);
- VOPSTATS_UPDATE_IO(vp, read,
- read_bytes, (resid_start - uiop->uio_resid));
+ len = resid_start - uiop->uio_resid;
+
+ VOPSTATS_UPDATE_IO(vp, read, read_bytes, len);
+
+ if (start != 0) {
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.reads++;
+ zonep->zone_vfs_rwstats.nread += len;
+ kstat_runq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ if (lat < VOP_LATENCY_100MS)
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+ }
+
return (err);
}
@@ -3280,14 +3468,63 @@ fop_write(
cred_t *cr,
caller_context_t *ct)
{
- int err;
ssize_t resid_start = uiop->uio_resid;
+ zone_t *zonep = curzone;
+ zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
+
+ hrtime_t start = 0, lat;
+ ssize_t len;
+ int err;
+
+ /*
+ * For the purposes of VFS kstat consumers, the "waitq" calculation is
+ * repurposed as the active queue for VFS write operations. There's no
+ * actual wait queue for VFS operations.
+ */
+ if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+ vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+ start = gethrtime();
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_waitq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ }
VOPXID_MAP_CR(vp, cr);
err = (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct);
- VOPSTATS_UPDATE_IO(vp, write,
- write_bytes, (resid_start - uiop->uio_resid));
+ len = resid_start - uiop->uio_resid;
+
+ VOPSTATS_UPDATE_IO(vp, write, write_bytes, len);
+
+ if (start != 0) {
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.writes++;
+ zonep->zone_vfs_rwstats.nwritten += len;
+ kstat_waitq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ if (lat < VOP_LATENCY_100MS)
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+ }
+
return (err);
}
@@ -3451,9 +3688,7 @@ fop_lookup(
}
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, lookup);
- if ((*vpp)->v_path == NULL) {
- vn_setpath(rootdir, dvp, *vpp, nm, strlen(nm));
- }
+ vn_updatepath(dvp, *vpp, nm);
}
return (ret);
@@ -3493,9 +3728,7 @@ fop_create(
(dvp, name, vap, excl, mode, vpp, cr, flags, ct, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, create);
- if ((*vpp)->v_path == NULL) {
- vn_setpath(rootdir, dvp, *vpp, name, strlen(name));
- }
+ vn_updatepath(dvp, *vpp, name);
}
return (ret);
@@ -3615,10 +3848,7 @@ fop_mkdir(
(dvp, dirname, vap, vpp, cr, ct, flags, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, mkdir);
- if ((*vpp)->v_path == NULL) {
- vn_setpath(rootdir, dvp, *vpp, dirname,
- strlen(dirname));
- }
+ vn_updatepath(dvp, *vpp, dirname);
}
return (ret);
diff --git a/usr/src/uts/common/fs/zfs/arc.c b/usr/src/uts/common/fs/zfs/arc.c
index 0dd1d480cd..4fe70819b2 100644
--- a/usr/src/uts/common/fs/zfs/arc.c
+++ b/usr/src/uts/common/fs/zfs/arc.c
@@ -254,6 +254,7 @@
#include <sys/vdev.h>
#include <sys/vdev_impl.h>
#include <sys/dsl_pool.h>
+#include <sys/zfs_zone.h>
#include <sys/multilist.h>
#ifdef _KERNEL
#include <sys/vmsystm.h>
@@ -4739,6 +4740,14 @@ top:
rzio = zio_read(pio, spa, bp, hdr->b_l1hdr.b_pdata, size,
arc_read_done, hdr, priority, zio_flags, zb);
+ /*
+ * At this point, this read I/O has already missed in the ARC
+ * and will be going through to the disk. The I/O throttle
+ * should delay this I/O if this zone is using more than its I/O
+ * priority allows.
+ */
+ zfs_zone_io_throttle(ZFS_ZONE_IOP_READ);
+
if (*arc_flags & ARC_FLAG_WAIT)
return (zio_wait(rzio));
diff --git a/usr/src/uts/common/fs/zfs/dbuf.c b/usr/src/uts/common/fs/zfs/dbuf.c
index 15fe94e901..9a0c75d5e2 100644
--- a/usr/src/uts/common/fs/zfs/dbuf.c
+++ b/usr/src/uts/common/fs/zfs/dbuf.c
@@ -946,8 +946,19 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags)
arc_space_consume(DN_MAX_BONUSLEN, ARC_SPACE_OTHER);
if (bonuslen < DN_MAX_BONUSLEN)
bzero(db->db.db_data, DN_MAX_BONUSLEN);
- if (bonuslen)
- bcopy(DN_BONUS(dn->dn_phys), db->db.db_data, bonuslen);
+
+ if (bonuslen) {
+ /*
+ * Absent byzantine on-disk corruption, we fully expect
+ * our bonuslen to be no more than DN_MAX_BONUSLEN --
+ * but we nonetheless explicitly clamp it on the bcopy()
+ * to prevent any on-disk corruption from becoming
+ * rampant in-kernel corruption.
+ */
+ bcopy(DN_BONUS(dn->dn_phys), db->db.db_data,
+ MIN(bonuslen, DN_MAX_BONUSLEN));
+ }
+
DB_DNODE_EXIT(db);
db->db_state = DB_CACHED;
mutex_exit(&db->db_mtx);
diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c
index 0fe695c417..756bb37086 100644
--- a/usr/src/uts/common/fs/zfs/dmu.c
+++ b/usr/src/uts/common/fs/zfs/dmu.c
@@ -1860,7 +1860,6 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp)
ZCHECKSUM_FLAG_DEDUP))
dedup_verify = B_TRUE;
}
-
/*
* Enable nopwrite if we have secure enough checksum
* algorithm (see comment in zio_nop_write) and
diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c
index cf603876d4..18ab28dc2a 100644
--- a/usr/src/uts/common/fs/zfs/dmu_send.c
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c
@@ -22,7 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
* Copyright 2014 HybridCluster. All rights reserved.
* Copyright 2016 RackTop Systems.
* Copyright (c) 2014 Integros [integros.com]
@@ -2505,8 +2505,12 @@ receive_read_record(struct receive_arg *ra)
{
struct drr_object *drro = &ra->rrd->header.drr_u.drr_object;
uint32_t size = P2ROUNDUP(drro->drr_bonuslen, 8);
- void *buf = kmem_zalloc(size, KM_SLEEP);
+ void *buf = NULL;
dmu_object_info_t doi;
+
+ if (size > 0)
+ buf = kmem_zalloc(size, KM_SLEEP);
+
err = receive_read_payload_and_next_header(ra, size, buf);
if (err != 0) {
kmem_free(buf, size);
diff --git a/usr/src/uts/common/fs/zfs/dmu_tx.c b/usr/src/uts/common/fs/zfs/dmu_tx.c
index 3f48e206fb..dadd0083cd 100644
--- a/usr/src/uts/common/fs/zfs/dmu_tx.c
+++ b/usr/src/uts/common/fs/zfs/dmu_tx.c
@@ -39,11 +39,11 @@
#include <sys/sa_impl.h>
#include <sys/zfs_context.h>
#include <sys/varargs.h>
+#include <sys/zfs_zone.h>
typedef void (*dmu_tx_hold_func_t)(dmu_tx_t *tx, struct dnode *dn,
uint64_t arg1, uint64_t arg2);
-
dmu_tx_t *
dmu_tx_create_dd(dsl_dir_t *dd)
{
@@ -236,6 +236,8 @@ dmu_tx_count_write(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
if (len == 0)
return;
+ zfs_zone_io_throttle(ZFS_ZONE_IOP_LOGICAL_WRITE);
+
min_bs = SPA_MINBLOCKSHIFT;
max_bs = highbit64(txh->txh_tx->tx_objset->os_recordsize) - 1;
min_ibs = DN_MIN_INDBLKSHIFT;
diff --git a/usr/src/uts/common/fs/zfs/dsl_dir.c b/usr/src/uts/common/fs/zfs/dsl_dir.c
index 164410d03d..27ef2838c9 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dir.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dir.c
@@ -42,6 +42,7 @@
#include <sys/zio.h>
#include <sys/arc.h>
#include <sys/sunddi.h>
+#include <sys/zfs_zone.h>
#include <sys/zfeature.h>
#include <sys/policy.h>
#include <sys/zfs_znode.h>
@@ -1265,7 +1266,7 @@ dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize, uint64_t asize,
* locks are held.
*/
txg_delay(dd->dd_pool, tx->tx_txg,
- MSEC2NSEC(10), MSEC2NSEC(10));
+ zfs_zone_txg_delay(), MSEC2NSEC(10));
err = SET_ERROR(ERESTART);
}
}
diff --git a/usr/src/uts/common/fs/zfs/dsl_pool.c b/usr/src/uts/common/fs/zfs/dsl_pool.c
index 35d7d5b421..0cbcb03319 100644
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c
@@ -43,6 +43,7 @@
#include <sys/zfs_znode.h>
#include <sys/spa_impl.h>
#include <sys/dsl_deadlist.h>
+#include <sys/zfs_zone.h>
#include <sys/bptree.h>
#include <sys/zfeature.h>
#include <sys/zil_impl.h>
diff --git a/usr/src/uts/common/fs/zfs/metaslab.c b/usr/src/uts/common/fs/zfs/metaslab.c
index e7624f040e..9efcc29646 100644
--- a/usr/src/uts/common/fs/zfs/metaslab.c
+++ b/usr/src/uts/common/fs/zfs/metaslab.c
@@ -55,6 +55,11 @@ uint64_t metaslab_gang_bang = SPA_MAXBLOCKSIZE + 1; /* force gang blocks */
int zfs_condense_pct = 200;
/*
+ * Never condense any space map. This is for debugging/recovery only.
+ */
+int zfs_condense_never = 0;
+
+/*
* Condensing a metaslab is not guaranteed to actually reduce the amount of
* space used on disk. In particular, a space map uses data in increments of
* MAX(1 << ashift, space_map_blksize), so a metaslab might use the
@@ -1763,6 +1768,9 @@ metaslab_should_condense(metaslab_t *msp)
ASSERT(MUTEX_HELD(&msp->ms_lock));
ASSERT(msp->ms_loaded);
+ if (zfs_condense_never != 0)
+ return (B_FALSE);
+
/*
* Use the ms_size_tree range tree, which is ordered by size, to
* obtain the largest segment in the free tree. We always condense
diff --git a/usr/src/uts/common/fs/zfs/sa.c b/usr/src/uts/common/fs/zfs/sa.c
index 7ddf806ec5..3168b47304 100644
--- a/usr/src/uts/common/fs/zfs/sa.c
+++ b/usr/src/uts/common/fs/zfs/sa.c
@@ -24,6 +24,7 @@
* Portions Copyright 2011 iXsystems, Inc
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -407,15 +408,18 @@ sa_add_layout_entry(objset_t *os, sa_attr_type_t *attrs, int attr_count,
{
sa_os_t *sa = os->os_sa;
sa_lot_t *tb, *findtb;
- int i;
+ int i, size;
avl_index_t loc;
ASSERT(MUTEX_HELD(&sa->sa_lock));
tb = kmem_zalloc(sizeof (sa_lot_t), KM_SLEEP);
tb->lot_attr_count = attr_count;
- tb->lot_attrs = kmem_alloc(sizeof (sa_attr_type_t) * attr_count,
- KM_SLEEP);
- bcopy(attrs, tb->lot_attrs, sizeof (sa_attr_type_t) * attr_count);
+
+ if ((size = sizeof (sa_attr_type_t) * attr_count) != 0) {
+ tb->lot_attrs = kmem_alloc(size, KM_SLEEP);
+ bcopy(attrs, tb->lot_attrs, size);
+ }
+
tb->lot_num = lot_num;
tb->lot_hash = hash;
tb->lot_instance = 0;
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 ff14aa80c8..8df5b3b785 100644
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
@@ -119,6 +119,7 @@ struct vdev_queue {
avl_tree_t vq_read_offset_tree;
avl_tree_t vq_write_offset_tree;
uint64_t vq_last_offset;
+ zoneid_t vq_last_zone_id;
hrtime_t vq_io_complete_ts; /* time last i/o completed */
kmutex_t vq_lock;
};
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_zone.h b/usr/src/uts/common/fs/zfs/sys/zfs_zone.h
new file mode 100644
index 0000000000..f1431b3f55
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_zone.h
@@ -0,0 +1,63 @@
+/*
+ * 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 2015, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FS_ZFS_ZONE_H
+#define _SYS_FS_ZFS_ZONE_H
+
+#ifdef _KERNEL
+#include <sys/isa_defs.h>
+#include <sys/types32.h>
+#include <sys/vdev_impl.h>
+#include <sys/zio.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ZFS_ZONE_IOP_READ = 0,
+ ZFS_ZONE_IOP_WRITE,
+ ZFS_ZONE_IOP_LOGICAL_WRITE,
+} zfs_zone_iop_type_t;
+
+extern void zfs_zone_io_throttle(zfs_zone_iop_type_t);
+
+extern void zfs_zone_zio_init(zio_t *);
+extern void zfs_zone_zio_start(zio_t *);
+extern void zfs_zone_zio_done(zio_t *);
+extern void zfs_zone_zio_dequeue(zio_t *);
+extern void zfs_zone_zio_enqueue(zio_t *);
+extern void zfs_zone_report_txg_sync(void *);
+extern hrtime_t zfs_zone_txg_delay();
+#ifdef _KERNEL
+extern zio_t *zfs_zone_schedule(vdev_queue_t *, zio_priority_t, avl_index_t,
+ avl_tree_t *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_ZFS_ZONE_H */
diff --git a/usr/src/uts/common/fs/zfs/sys/zio.h b/usr/src/uts/common/fs/zfs/sys/zio.h
index 88159a5408..873d38ec7f 100644
--- a/usr/src/uts/common/fs/zfs/sys/zio.h
+++ b/usr/src/uts/common/fs/zfs/sys/zio.h
@@ -433,6 +433,7 @@ struct zio {
hrtime_t io_timestamp;
hrtime_t io_queued_timestamp;
hrtime_t io_target_timestamp;
+ hrtime_t io_dispatched; /* time I/O was dispatched to disk */
avl_node_t io_queue_node;
avl_node_t io_offset_node;
avl_node_t io_alloc_node;
@@ -463,6 +464,7 @@ struct zio {
zio_cksum_report_t *io_cksum_report;
uint64_t io_ena;
+ zoneid_t io_zoneid; /* zone which originated this I/O */
/* Taskq dispatching state */
taskq_ent_t io_tqent;
};
diff --git a/usr/src/uts/common/fs/zfs/txg.c b/usr/src/uts/common/fs/zfs/txg.c
index 191259e75b..915c9bb4b2 100644
--- a/usr/src/uts/common/fs/zfs/txg.c
+++ b/usr/src/uts/common/fs/zfs/txg.c
@@ -31,6 +31,7 @@
#include <sys/dsl_pool.h>
#include <sys/dsl_scan.h>
#include <sys/callb.h>
+#include <sys/zfs_zone.h>
/*
* ZFS Transaction Groups
@@ -506,6 +507,8 @@ txg_sync_thread(dsl_pool_t *dp)
txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
mutex_exit(&tx->tx_sync_lock);
+ zfs_zone_report_txg_sync(dp);
+
start = ddi_get_lbolt();
spa_sync(spa, txg);
delta = ddi_get_lbolt() - start;
diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c
index 60075c4c15..f9191dd0f9 100644
--- a/usr/src/uts/common/fs/zfs/vdev_disk.c
+++ b/usr/src/uts/common/fs/zfs/vdev_disk.c
@@ -26,6 +26,7 @@
*/
#include <sys/zfs_context.h>
+#include <sys/zfs_zone.h>
#include <sys/spa_impl.h>
#include <sys/refcount.h>
#include <sys/vdev_disk.h>
@@ -44,6 +45,11 @@ extern ldi_ident_t zfs_li;
static void vdev_disk_close(vdev_t *);
+typedef struct vdev_disk_buf {
+ buf_t vdb_buf;
+ zio_t *vdb_io;
+} vdev_disk_buf_t;
+
typedef struct vdev_disk_ldi_cb {
list_node_t lcb_next;
ldi_callback_id_t lcb_id;
@@ -127,6 +133,8 @@ vdev_disk_off_finalize(ldi_handle_t lh, ldi_ev_cookie_t ecookie,
int ldi_result, void *arg, void *ev_data)
{
vdev_t *vd = (vdev_t *)arg;
+ vdev_disk_t *dvd = vd->vdev_tsd;
+ vdev_disk_ldi_cb_t *lcb;
/*
* Ignore events other than offline.
@@ -555,6 +563,7 @@ static void
vdev_disk_close(vdev_t *vd)
{
vdev_disk_t *dvd = vd->vdev_tsd;
+ vdev_disk_ldi_cb_t *lcb;
if (vd->vdev_reopening || dvd == NULL)
return;
@@ -784,6 +793,8 @@ vdev_disk_io_start(zio_t *zio)
bp->b_bufsize = zio->io_size;
bp->b_iodone = (int (*)())vdev_disk_io_intr;
+ zfs_zone_zio_start(zio);
+
/* ldi_strategy() will return non-zero only on programming errors */
VERIFY(ldi_strategy(dvd->vd_lh, bp) == 0);
}
@@ -793,6 +804,8 @@ vdev_disk_io_done(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
+ zfs_zone_zio_done(zio);
+
/*
* If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if
* the device has been removed. If this is the case, then we trigger an
diff --git a/usr/src/uts/common/fs/zfs/vdev_queue.c b/usr/src/uts/common/fs/zfs/vdev_queue.c
index ac586c879f..5211996f6a 100644
--- a/usr/src/uts/common/fs/zfs/vdev_queue.c
+++ b/usr/src/uts/common/fs/zfs/vdev_queue.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*/
/*
@@ -34,6 +35,7 @@
#include <sys/zio.h>
#include <sys/avl.h>
#include <sys/dsl_pool.h>
+#include <sys/zfs_zone.h>
#include <sys/metaslab_impl.h>
/*
@@ -143,7 +145,7 @@ uint32_t zfs_vdev_sync_write_min_active = 10;
uint32_t zfs_vdev_sync_write_max_active = 10;
uint32_t zfs_vdev_async_read_min_active = 1;
uint32_t zfs_vdev_async_read_max_active = 3;
-uint32_t zfs_vdev_async_write_min_active = 1;
+uint32_t zfs_vdev_async_write_min_active = 3;
uint32_t zfs_vdev_async_write_max_active = 10;
uint32_t zfs_vdev_scrub_min_active = 1;
uint32_t zfs_vdev_scrub_max_active = 2;
@@ -256,6 +258,8 @@ vdev_queue_init(vdev_t *vd)
vdev_queue_offset_compare, sizeof (zio_t),
offsetof(struct zio, io_offset_node));
+ vq->vq_last_zone_id = 0;
+
for (zio_priority_t p = 0; p < ZIO_PRIORITY_NUM_QUEUEABLE; p++) {
int (*compfn) (const void *, const void *);
@@ -294,6 +298,7 @@ vdev_queue_io_add(vdev_queue_t *vq, zio_t *zio)
spa_t *spa = zio->io_spa;
ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE);
+ zfs_zone_zio_enqueue(zio);
avl_add(vdev_queue_class_tree(vq, zio->io_priority), zio);
avl_add(vdev_queue_type_tree(vq, zio->io_type), zio);
@@ -310,6 +315,7 @@ vdev_queue_io_remove(vdev_queue_t *vq, zio_t *zio)
spa_t *spa = zio->io_spa;
ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE);
+ zfs_zone_zio_dequeue(zio);
avl_remove(vdev_queue_class_tree(vq, zio->io_priority), zio);
avl_remove(vdev_queue_type_tree(vq, zio->io_type), zio);
@@ -670,7 +676,11 @@ again:
search.io_timestamp = 0;
search.io_offset = vq->vq_last_offset + 1;
VERIFY3P(avl_find(tree, &search, &idx), ==, NULL);
+#ifdef _KERNEL
+ zio = zfs_zone_schedule(vq, p, idx, tree);
+#else
zio = avl_nearest(tree, idx, AVL_AFTER);
+#endif
if (zio == NULL)
zio = avl_first(tree);
ASSERT3U(zio->io_priority, ==, p);
diff --git a/usr/src/uts/common/fs/zfs/zfs_dir.c b/usr/src/uts/common/fs/zfs/zfs_dir.c
index 2b18ecb01c..132e84b111 100644
--- a/usr/src/uts/common/fs/zfs/zfs_dir.c
+++ b/usr/src/uts/common/fs/zfs/zfs_dir.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
* Copyright (c) 2013, 2015 by Delphix. All rights reserved.
*/
@@ -853,9 +854,9 @@ zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag,
if (zp->z_links <= zp_is_dir) {
zfs_panic_recover("zfs: link count on %s is %u, "
"should be at least %u",
- zp->z_vnode->v_path ? zp->z_vnode->v_path :
- "<unknown>", (int)zp->z_links,
- zp_is_dir + 1);
+ zp->z_vnode->v_path != vn_vpath_empty ?
+ zp->z_vnode->v_path : "<unknown>",
+ (int)zp->z_links, zp_is_dir + 1);
zp->z_links = zp_is_dir + 1;
}
if (--zp->z_links == zp_is_dir) {
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index 48be8dceb9..1948aa443b 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -624,9 +624,10 @@ zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
* Check permissions for special properties.
*/
switch (prop) {
+ case ZFS_PROP_DEDUP:
case ZFS_PROP_ZONED:
/*
- * Disallow setting of 'zoned' from within a local zone.
+ * Disallow setting these properties from within a local zone.
*/
if (!INGLOBALZONE(curproc))
return (SET_ERROR(EPERM));
@@ -956,6 +957,9 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
int error;
+ if (secpolicy_fs_import(cr) != 0)
+ return (set_errno(EPERM));
+
if ((error = zfs_secpolicy_write_perms(zc->zc_name,
ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
return (error);
@@ -2046,7 +2050,8 @@ zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
}
static int
-zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
+zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os,
+ boolean_t cachedpropsonly)
{
int error = 0;
nvlist_t *nv;
@@ -2064,7 +2069,8 @@ zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
* XXX reading with out owning
*/
if (!zc->zc_objset_stats.dds_inconsistent &&
- dmu_objset_type(os) == DMU_OST_ZVOL) {
+ dmu_objset_type(os) == DMU_OST_ZVOL &&
+ !cachedpropsonly) {
error = zvol_get_stats(os, nv);
if (error == EIO)
return (error);
@@ -2091,11 +2097,24 @@ static int
zfs_ioc_objset_stats(zfs_cmd_t *zc)
{
objset_t *os;
+ nvlist_t *nvl = NULL;
+ boolean_t cachedpropsonly = B_FALSE;
int error;
+ if (zc->zc_nvlist_src != NULL &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &nvl) != 0))
+ return (error);
+
+ if (nvl != NULL) {
+ (void) nvlist_lookup_boolean_value(nvl, "cachedpropsonly",
+ &cachedpropsonly);
+ nvlist_free(nvl);
+ }
+
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error == 0) {
- error = zfs_ioc_objset_stats_impl(zc, os);
+ error = zfs_ioc_objset_stats_impl(zc, os, cachedpropsonly);
dmu_objset_rele(os, FTAG);
}
@@ -2290,8 +2309,21 @@ static int
zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
{
objset_t *os;
+ nvlist_t *nvl = NULL;
+ boolean_t cachedpropsonly = B_FALSE;
int error;
+ if (zc->zc_nvlist_src != NULL &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &nvl) != 0))
+ return (error);
+
+ if (nvl != NULL) {
+ (void) nvlist_lookup_boolean_value(nvl, "cachedpropsonly",
+ &cachedpropsonly);
+ nvlist_free(nvl);
+ }
+
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error != 0) {
return (error == ENOENT ? ESRCH : error);
@@ -2321,8 +2353,10 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
objset_t *ossnap;
error = dmu_objset_from_ds(ds, &ossnap);
- if (error == 0)
- error = zfs_ioc_objset_stats_impl(zc, ossnap);
+ if (error == 0) {
+ error = zfs_ioc_objset_stats_impl(zc,
+ ossnap, cachedpropsonly);
+ }
dsl_dataset_rele(ds, FTAG);
}
} else if (error == ENOENT) {
@@ -3032,6 +3066,7 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
uint64_t sense = ZFS_PROP_UNDEFINED;
uint64_t norm = ZFS_PROP_UNDEFINED;
uint64_t u8 = ZFS_PROP_UNDEFINED;
+ int error;
ASSERT(zplprops != NULL);
@@ -3075,8 +3110,9 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
- if (norm == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
+ if (norm == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
@@ -3085,13 +3121,15 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
*/
if (norm)
u8 = 1;
- if (u8 == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
+ if (u8 == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
- if (sense == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
+ if (sense == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_CASE, &sense)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index 71df239ac9..1b5e4e55d7 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -1903,6 +1904,17 @@ zfs_umount(vfs_t *vfsp, int fflag, cred_t *cr)
if (zfsvfs->z_ctldir != NULL)
zfsctl_destroy(zfsvfs);
+ /*
+ * If we're doing a forced unmount on a dataset which still has
+ * references and is in a zone, then we need to cleanup the zone
+ * reference at this point or else the zone will never be able to
+ * shutdown.
+ */
+ if ((fflag & MS_FORCE) && vfsp->vfs_count > 1 && vfsp->vfs_zone) {
+ zone_rele_ref(&vfsp->vfs_implp->vi_zone_ref, ZONE_REF_VFS);
+ vfsp->vfs_zone = NULL;
+ }
+
return (0);
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c
index 9cba49b402..829d57b760 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c
@@ -23,7 +23,7 @@
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
/* Portions Copyright 2007 Jeremy Teo */
@@ -664,6 +664,7 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
rl_t *rl;
int max_blksz = zfsvfs->z_max_blksz;
int error = 0;
+ int prev_error;
arc_buf_t *abuf;
iovec_t *aiov = NULL;
xuio_t *xuio = NULL;
@@ -685,6 +686,17 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
+ /*
+ * Pre-fault the pages to ensure slow (eg NFS) pages
+ * don't hold up txg.
+ * Skip this if uio contains loaned arc_buf.
+ */
+ if ((uio->uio_extflg == UIO_XUIO) &&
+ (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY))
+ xuio = (xuio_t *)uio;
+ else
+ uio_prefaultpages(n, uio);
+
ZFS_ENTER(zfsvfs);
ZFS_VERIFY_ZP(zp);
@@ -737,17 +749,6 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
}
/*
- * Pre-fault the pages to ensure slow (eg NFS) pages
- * don't hold up txg.
- * Skip this if uio contains loaned arc_buf.
- */
- if ((uio->uio_extflg == UIO_XUIO) &&
- (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY))
- xuio = (xuio_t *)uio;
- else
- uio_prefaultpages(MIN(n, max_blksz), uio);
-
- /*
* If in append mode, set the io offset pointer to eof.
*/
if (ioflag & FAPPEND) {
@@ -968,7 +969,6 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
while ((end_size = zp->z_size) < uio->uio_loffset) {
(void) atomic_cas_64(&zp->z_size, end_size,
uio->uio_loffset);
- ASSERT(error == 0);
}
/*
* If we are replaying and eof is non zero then force
@@ -978,18 +978,20 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
if (zfsvfs->z_replay && zfsvfs->z_replay_eof != 0)
zp->z_size = zfsvfs->z_replay_eof;
+ /*
+ * Keep track of a possible pre-existing error from a partial
+ * write via dmu_write_uio_dbuf above.
+ */
+ prev_error = error;
error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag);
dmu_tx_commit(tx);
- if (error != 0)
+ if (prev_error != 0 || error != 0)
break;
ASSERT(tx_bytes == nbytes);
n -= nbytes;
-
- if (!xuio && n > 0)
- uio_prefaultpages(MIN(n, max_blksz), uio);
}
zfs_range_unlock(rl);
@@ -2832,8 +2834,11 @@ top:
return (err);
}
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(ZTOV(zp), ct);
+ } else {
+ vnevent_resize(ZTOV(zp), ct);
+ }
}
if (mask & (AT_ATIME|AT_MTIME) ||
@@ -3761,9 +3766,7 @@ top:
if (error == 0) {
vnevent_rename_src(ZTOV(szp), sdvp, snm, ct);
- /* notify the target dir if it is not the same as source dir */
- if (tdvp != sdvp)
- vnevent_rename_dest_dir(tdvp, ct);
+ vnevent_rename_dest_dir(tdvp, ZTOV(szp), tnm, ct);
}
out:
if (zl != NULL)
@@ -4255,6 +4258,8 @@ zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
&zp->z_pflags, 8);
zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime,
B_TRUE);
+ err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
+
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off, len, 0);
}
dmu_tx_commit(tx);
@@ -4790,10 +4795,6 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
ASSERT3U(VTOZ(vp)->z_mapcnt, >=, pages);
atomic_add_64(&VTOZ(vp)->z_mapcnt, -pages);
- if ((flags & MAP_SHARED) && (prot & PROT_WRITE) &&
- vn_has_cached_data(vp))
- (void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr, ct);
-
return (0);
}
@@ -4859,8 +4860,13 @@ zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag,
error = zfs_freesp(zp, off, len, flag, TRUE);
- if (error == 0 && off == 0 && len == 0)
- vnevent_truncate(ZTOV(zp), ct);
+ if (error == 0 && len == 0) {
+ if (off == 0) {
+ vnevent_truncate(ZTOV(zp), ct);
+ } else {
+ vnevent_resize(ZTOV(zp), ct);
+ }
+ }
ZFS_EXIT(zfsvfs);
return (error);
diff --git a/usr/src/uts/common/fs/zfs/zfs_zone.c b/usr/src/uts/common/fs/zfs/zfs_zone.c
new file mode 100644
index 0000000000..4861c64f8e
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/zfs_zone.c
@@ -0,0 +1,1336 @@
+/*
+ * 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 2015, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * The ZFS/Zone I/O throttle and scheduler attempts to ensure fair access to
+ * ZFS I/O resources for each zone.
+ *
+ * I/O contention can be major pain point on a multi-tenant system. A single
+ * zone can issue a stream of I/O operations, usually synchronous writes, which
+ * disrupt I/O performance for all other zones. This problem is further
+ * exacerbated by ZFS, which buffers all asynchronous writes in a single TXG,
+ * a set of blocks which are atomically synced to disk. The process of
+ * syncing a TXG can occupy all of a device's I/O bandwidth, thereby starving
+ * out any pending read operations.
+ *
+ * There are two facets to this capability; the throttle and the scheduler.
+ *
+ * Throttle
+ *
+ * The requirements on the throttle are:
+ *
+ * 1) Ensure consistent and predictable I/O latency across all zones.
+ * 2) Sequential and random workloads have very different characteristics,
+ * so it is a non-starter to track IOPS or throughput.
+ * 3) A zone should be able to use the full disk bandwidth if no other zone
+ * is actively using the disk.
+ *
+ * The throttle has two components: one to track and account for each zone's
+ * I/O requests, and another to throttle each zone's operations when it
+ * exceeds its fair share of disk I/O. When the throttle detects that a zone is
+ * consuming more than is appropriate, each read or write system call is
+ * delayed by up to 100 microseconds, which we've found is sufficient to allow
+ * other zones to interleave I/O requests during those delays.
+ *
+ * Note: The throttle will delay each logical I/O (as opposed to the physical
+ * I/O which will likely be issued asynchronously), so it may be easier to
+ * think of the I/O throttle delaying each read/write syscall instead of the
+ * actual I/O operation. For each zone, the throttle tracks an ongoing average
+ * of read and write operations performed to determine the overall I/O
+ * utilization for each zone.
+ *
+ * The throttle calculates a I/O utilization metric for each zone using the
+ * following formula:
+ *
+ * (# of read syscalls) x (Average read latency) +
+ * (# of write syscalls) x (Average write latency)
+ *
+ * Once each zone has its utilization metric, the I/O throttle will compare I/O
+ * utilization across all zones, and if a zone has a higher-than-average I/O
+ * utilization, system calls from that zone are throttled. That is, if one
+ * zone has a much higher utilization, that zone's delay is increased by 5
+ * microseconds, up to a maximum of 100 microseconds. Conversely, if a zone is
+ * already throttled and has a lower utilization than average, its delay will
+ * be lowered by 5 microseconds.
+ *
+ * The throttle calculation is driven by IO activity, but since IO does not
+ * happen at fixed intervals, timestamps are used to track when the last update
+ * was made and to drive recalculation.
+ *
+ * The throttle recalculates each zone's I/O usage and throttle delay (if any)
+ * on the zfs_zone_adjust_time interval. Overall I/O latency is maintained as
+ * a decayed average which is updated on the zfs_zone_sys_avg_cycle interval.
+ *
+ * Scheduler
+ *
+ * The I/O scheduler manages the vdev queues – the queues of pending I/Os to
+ * issue to the disks. It only makes scheduling decisions for the two
+ * synchronous I/O queues (read & write).
+ *
+ * The scheduler maintains how many I/Os in the queue are from each zone, and
+ * if one zone has a disproportionately large number of I/Os in the queue, the
+ * scheduler will allow certain I/Os from the underutilized zones to be "bumped"
+ * and pulled from the middle of the queue. This bump allows zones with a small
+ * number of I/Os (so small they may not even be taken into account by the
+ * throttle) to complete quickly instead of waiting behind dozens of I/Os from
+ * other zones.
+ */
+
+#include <sys/spa.h>
+#include <sys/vdev_impl.h>
+#include <sys/zfs_zone.h>
+
+#ifndef _KERNEL
+
+/*
+ * Stubs for when compiling for user-land.
+ */
+
+void
+zfs_zone_io_throttle(zfs_zone_iop_type_t type)
+{
+}
+
+void
+zfs_zone_zio_init(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_start(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_done(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_dequeue(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_enqueue(zio_t *zp)
+{
+}
+
+/*ARGSUSED*/
+void
+zfs_zone_report_txg_sync(void *dp)
+{
+}
+
+hrtime_t
+zfs_zone_txg_delay()
+{
+ return (MSEC2NSEC(10));
+}
+
+#else
+
+/*
+ * The real code.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/atomic.h>
+#include <sys/zio.h>
+#include <sys/zone.h>
+#include <sys/avl.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+
+/*
+ * The zone throttle delays read and write operations from certain zones based
+ * on each zone's IO utilitzation. Once a cycle (defined by zfs_zone_cycle_time
+ * below), the delays for each zone are recalculated based on the utilization
+ * over the previous window.
+ */
+boolean_t zfs_zone_delay_enable = B_TRUE; /* enable IO throttle */
+uint16_t zfs_zone_delay_step = 5; /* usec amnt to change delay */
+uint16_t zfs_zone_delay_ceiling = 100; /* usec delay max */
+
+boolean_t zfs_zone_priority_enable = B_TRUE; /* enable IO priority */
+
+/*
+ * For certain workloads, one zone may be issuing primarily sequential I/O and
+ * another primarily random I/O. The sequential I/O will complete much more
+ * quickly than the random I/O, driving the average system latency for those
+ * operations way down. As a result, the random I/O may be throttled back, even
+ * though the sequential I/O should be throttled to allow the random I/O more
+ * access to the disk.
+ *
+ * This tunable limits the discrepancy between the read and write system
+ * latency. If one becomes excessively high, this tunable prevents the I/O
+ * throttler from exacerbating the imbalance.
+ */
+uint_t zfs_zone_rw_lat_limit = 10;
+
+/*
+ * The I/O throttle will only start delaying zones when it detects disk
+ * utilization has reached a certain level. This tunable controls the
+ * threshold at which the throttle will start delaying zones. When the number
+ * of vdevs is small, the calculation should correspond closely with the %b
+ * column from iostat -- but as the number of vdevs becomes large, it will
+ * correlate less and less to any single device (therefore making it a poor
+ * approximation for the actual I/O utilization on such systems). We
+ * therefore use our derived utilization conservatively: we know that low
+ * derived utilization does indeed correlate to low I/O use -- but that a high
+ * rate of derived utilization does not necesarily alone denote saturation;
+ * where we see a high rate of utilization, we also look for laggard I/Os to
+ * attempt to detect saturation.
+ */
+uint_t zfs_zone_util_threshold = 80;
+uint_t zfs_zone_underutil_threshold = 60;
+
+/*
+ * There are three important tunables here: zfs_zone_laggard_threshold denotes
+ * the threshold at which an I/O is considered to be of notably high latency;
+ * zfs_zone_laggard_recent denotes the number of microseconds before the
+ * current time after which the last laggard is considered to be sufficiently
+ * recent to merit increasing the throttle; zfs_zone_laggard_ancient denotes
+ * the microseconds before the current time before which the last laggard is
+ * considered to be sufficiently old to merit decreasing the throttle. The
+ * most important tunable of these three is the zfs_zone_laggard_threshold: in
+ * modeling data from a large public cloud, this tunable was found to have a
+ * much greater effect on the throttle than the two time-based thresholds.
+ * This must be set high enough to not result in spurious throttling, but not
+ * so high as to allow pathological I/O to persist in the system.
+ */
+uint_t zfs_zone_laggard_threshold = 50000; /* 50 ms */
+uint_t zfs_zone_laggard_recent = 1000000; /* 1000 ms */
+uint_t zfs_zone_laggard_ancient = 5000000; /* 5000 ms */
+
+/*
+ * Throughout this subsystem, our timestamps are in microseconds. Our system
+ * average cycle is one second or 1 million microseconds. Our zone counter
+ * update cycle is two seconds or 2 million microseconds. We use a longer
+ * duration for that cycle because some ops can see a little over two seconds of
+ * latency when they are being starved by another zone.
+ */
+uint_t zfs_zone_sys_avg_cycle = 1000000; /* 1 s */
+uint_t zfs_zone_cycle_time = 2000000; /* 2 s */
+
+/*
+ * How often the I/O throttle will reevaluate each zone's utilization, in
+ * microseconds. Default is 1/4 sec.
+ */
+uint_t zfs_zone_adjust_time = 250000; /* 250 ms */
+
+typedef struct {
+ hrtime_t cycle_start;
+ int cycle_cnt;
+ hrtime_t cycle_lat;
+ hrtime_t sys_avg_lat;
+} sys_lat_cycle_t;
+
+typedef struct {
+ hrtime_t zi_now;
+ uint_t zi_avgrlat;
+ uint_t zi_avgwlat;
+ uint64_t zi_totpri;
+ uint64_t zi_totutil;
+ int zi_active;
+ uint_t zi_diskutil;
+ boolean_t zi_underutil;
+ boolean_t zi_overutil;
+} zoneio_stats_t;
+
+static sys_lat_cycle_t rd_lat;
+static sys_lat_cycle_t wr_lat;
+
+/*
+ * Some basic disk stats to determine disk utilization. The utilization info
+ * for all disks on the system is aggregated into these values.
+ *
+ * Overall disk utilization for the current cycle is calculated as:
+ *
+ * ((zfs_disk_rtime - zfs_disk_last_rtime) * 100)
+ * ----------------------------------------------
+ * ((now - zfs_zone_last_checked) * 1000);
+ */
+kmutex_t zfs_disk_lock; /* protects the following: */
+uint_t zfs_disk_rcnt; /* Number of outstanding IOs */
+hrtime_t zfs_disk_rtime = 0; /* cummulative sum of time performing IO */
+hrtime_t zfs_disk_rlastupdate = 0; /* time last IO dispatched */
+
+hrtime_t zfs_disk_last_rtime = 0; /* prev. cycle's zfs_disk_rtime val */
+/* time that we last updated per-zone throttle info */
+hrtime_t zfs_zone_last_checked = 0;
+hrtime_t zfs_disk_last_laggard = 0;
+
+/*
+ * Data used to keep track of how often txg sync is running.
+ */
+extern int zfs_txg_timeout;
+static uint_t txg_last_check;
+static uint_t txg_cnt;
+static uint_t txg_sync_rate;
+
+boolean_t zfs_zone_schedule_enable = B_TRUE; /* enable IO sched. */
+/*
+ * Threshold for when zio scheduling should kick in.
+ *
+ * This threshold is based on the zfs_vdev_sync_read_max_active value for the
+ * number of I/Os that can be pending on a device. If there are more than the
+ * max_active ops already queued up, beyond those already issued to the vdev,
+ * then use zone-based scheduling to get the next synchronous zio.
+ */
+uint32_t zfs_zone_schedule_thresh = 10;
+
+/*
+ * On each pass of the scheduler we increment the zone's weight (up to this
+ * maximum). The weight is used by the scheduler to prevent starvation so
+ * that zones which haven't been able to do any IO over many iterations
+ * will max out thier weight to this value.
+ */
+#define SCHED_WEIGHT_MAX 20
+
+/*
+ * Tunables for delay throttling when TXG sync is occurring.
+ *
+ * If the zone is performing a write and we're doing above normal TXG syncing,
+ * then throttle for longer than normal. The zone's wait time is multiplied
+ * by the scale (zfs_zone_txg_throttle_scale).
+ */
+int zfs_zone_txg_throttle_scale = 2;
+hrtime_t zfs_zone_txg_delay_nsec = MSEC2NSEC(20);
+
+typedef struct {
+ int zq_qdepth;
+ zio_priority_t zq_queue;
+ int zq_priority;
+ int zq_wt;
+ zoneid_t zq_zoneid;
+} zone_q_bump_t;
+
+/*
+ * This uses gethrtime() but returns a value in usecs.
+ */
+#define GET_USEC_TIME (gethrtime() / 1000)
+#define NANO_TO_MICRO(x) (x / (NANOSEC / MICROSEC))
+
+/*
+ * Keep track of the zone's ZFS IOPs.
+ *
+ * See the comment on the zfs_zone_io_throttle function for which/how IOPs are
+ * accounted for.
+ *
+ * If the number of ops is >1 then we can just use that value. However,
+ * if the number of ops is <2 then we might have a zone which is trying to do
+ * IO but is not able to get any ops through the system. We don't want to lose
+ * track of this zone so we factor in its decayed count into the current count.
+ *
+ * Each cycle (zfs_zone_sys_avg_cycle) we want to update the decayed count.
+ * However, since this calculation is driven by IO activity and since IO does
+ * not happen at fixed intervals, we use a timestamp to see when the last update
+ * was made. If it was more than one cycle ago, then we need to decay the
+ * historical count by the proper number of additional cycles in which no IO was
+ * performed.
+ *
+ * Return a time delta indicating how far into the current cycle we are or 0
+ * if the last IO was more than a cycle ago.
+ */
+static hrtime_t
+compute_historical_zone_cnt(hrtime_t unow, sys_zio_cntr_t *cp)
+{
+ hrtime_t delta;
+ int gen_cnt;
+
+ /*
+ * Check if its time to recompute a new zone count.
+ * If we're still collecting data for the current cycle, return false.
+ */
+ delta = unow - cp->cycle_start;
+ if (delta < zfs_zone_cycle_time)
+ return (delta);
+
+ /* A previous cycle is past, compute the new zone count. */
+
+ /*
+ * Figure out how many generations we have to decay the historical
+ * count, since multiple cycles may have elapsed since our last IO.
+ * We depend on int rounding here.
+ */
+ gen_cnt = (int)(delta / zfs_zone_cycle_time);
+
+ /* If more than 5 cycles since last the IO, reset count. */
+ if (gen_cnt > 5) {
+ cp->zone_avg_cnt = 0;
+ } else {
+ /* Update the count. */
+ int i;
+
+ /*
+ * If the zone did more than 1 IO, just use its current count
+ * as the historical value, otherwise decay the historical
+ * count and factor that into the new historical count. We
+ * pick a threshold > 1 so that we don't lose track of IO due
+ * to int rounding.
+ */
+ if (cp->cycle_cnt > 1)
+ cp->zone_avg_cnt = cp->cycle_cnt;
+ else
+ cp->zone_avg_cnt = cp->cycle_cnt +
+ (cp->zone_avg_cnt / 2);
+
+ /*
+ * If more than one generation has elapsed since the last
+ * update, decay the values further.
+ */
+ for (i = 1; i < gen_cnt; i++)
+ cp->zone_avg_cnt = cp->zone_avg_cnt / 2;
+ }
+
+ /* A new cycle begins. */
+ cp->cycle_start = unow;
+ cp->cycle_cnt = 0;
+
+ return (0);
+}
+
+/*
+ * Add IO op data to the zone.
+ */
+static void
+add_zone_iop(zone_t *zonep, hrtime_t unow, zfs_zone_iop_type_t op)
+{
+ switch (op) {
+ case ZFS_ZONE_IOP_READ:
+ (void) compute_historical_zone_cnt(unow, &zonep->zone_rd_ops);
+ zonep->zone_rd_ops.cycle_cnt++;
+ break;
+ case ZFS_ZONE_IOP_WRITE:
+ (void) compute_historical_zone_cnt(unow, &zonep->zone_wr_ops);
+ zonep->zone_wr_ops.cycle_cnt++;
+ break;
+ case ZFS_ZONE_IOP_LOGICAL_WRITE:
+ (void) compute_historical_zone_cnt(unow, &zonep->zone_lwr_ops);
+ zonep->zone_lwr_ops.cycle_cnt++;
+ break;
+ }
+}
+
+/*
+ * Use a decaying average to keep track of the overall system latency.
+ *
+ * We want to have the recent activity heavily weighted, but if the
+ * activity decreases or stops, then the average should quickly decay
+ * down to the new value.
+ *
+ * Each cycle (zfs_zone_sys_avg_cycle) we want to update the decayed average.
+ * However, since this calculation is driven by IO activity and since IO does
+ * not happen at fixed intervals, we use a timestamp to see when the last
+ * update was made. If it was more than one cycle ago, then we need to decay
+ * the average by the proper number of additional cycles in which no IO was
+ * performed.
+ *
+ * Return true if we actually computed a new system average.
+ * If we're still within an active cycle there is nothing to do, return false.
+ */
+static boolean_t
+compute_new_sys_avg(hrtime_t unow, sys_lat_cycle_t *cp)
+{
+ hrtime_t delta;
+ int gen_cnt;
+
+ /*
+ * Check if its time to recompute a new average.
+ * If we're still collecting data for the current cycle, return false.
+ */
+ delta = unow - cp->cycle_start;
+ if (delta < zfs_zone_sys_avg_cycle)
+ return (B_FALSE);
+
+ /* A previous cycle is past, compute a new system average. */
+
+ /*
+ * Figure out how many generations we have to decay, since multiple
+ * cycles may have elapsed since our last IO.
+ * We count on int rounding here.
+ */
+ gen_cnt = (int)(delta / zfs_zone_sys_avg_cycle);
+
+ /* If more than 5 cycles since last the IO, reset average. */
+ if (gen_cnt > 5) {
+ cp->sys_avg_lat = 0;
+ } else {
+ /* Update the average. */
+ int i;
+
+ cp->sys_avg_lat =
+ (cp->sys_avg_lat + cp->cycle_lat) / (1 + cp->cycle_cnt);
+
+ /*
+ * If more than one generation has elapsed since the last
+ * update, decay the values further.
+ */
+ for (i = 1; i < gen_cnt; i++)
+ cp->sys_avg_lat = cp->sys_avg_lat / 2;
+ }
+
+ /* A new cycle begins. */
+ cp->cycle_start = unow;
+ cp->cycle_cnt = 0;
+ cp->cycle_lat = 0;
+
+ return (B_TRUE);
+}
+
+static void
+add_sys_iop(hrtime_t unow, int op, int lat)
+{
+ switch (op) {
+ case ZFS_ZONE_IOP_READ:
+ (void) compute_new_sys_avg(unow, &rd_lat);
+ rd_lat.cycle_cnt++;
+ rd_lat.cycle_lat += lat;
+ break;
+ case ZFS_ZONE_IOP_WRITE:
+ (void) compute_new_sys_avg(unow, &wr_lat);
+ wr_lat.cycle_cnt++;
+ wr_lat.cycle_lat += lat;
+ break;
+ }
+}
+
+/*
+ * Get the zone IO counts.
+ */
+static uint_t
+calc_zone_cnt(hrtime_t unow, sys_zio_cntr_t *cp)
+{
+ hrtime_t delta;
+ uint_t cnt;
+
+ if ((delta = compute_historical_zone_cnt(unow, cp)) == 0) {
+ /*
+ * No activity in the current cycle, we already have the
+ * historical data so we'll use that.
+ */
+ cnt = cp->zone_avg_cnt;
+ } else {
+ /*
+ * If we're less than half way through the cycle then use
+ * the current count plus half the historical count, otherwise
+ * just use the current count.
+ */
+ if (delta < (zfs_zone_cycle_time / 2))
+ cnt = cp->cycle_cnt + (cp->zone_avg_cnt / 2);
+ else
+ cnt = cp->cycle_cnt;
+ }
+
+ return (cnt);
+}
+
+/*
+ * Get the average read/write latency in usecs for the system.
+ */
+static uint_t
+calc_avg_lat(hrtime_t unow, sys_lat_cycle_t *cp)
+{
+ if (compute_new_sys_avg(unow, cp)) {
+ /*
+ * No activity in the current cycle, we already have the
+ * historical data so we'll use that.
+ */
+ return (cp->sys_avg_lat);
+ } else {
+ /*
+ * We're within a cycle; weight the current activity higher
+ * compared to the historical data and use that.
+ */
+ DTRACE_PROBE3(zfs__zone__calc__wt__avg,
+ uintptr_t, cp->sys_avg_lat,
+ uintptr_t, cp->cycle_lat,
+ uintptr_t, cp->cycle_cnt);
+
+ return ((cp->sys_avg_lat + (cp->cycle_lat * 8)) /
+ (1 + (cp->cycle_cnt * 8)));
+ }
+}
+
+/*
+ * Account for the current IOP on the zone and for the system as a whole.
+ * The latency parameter is in usecs.
+ */
+static void
+add_iop(zone_t *zonep, hrtime_t unow, zfs_zone_iop_type_t op, hrtime_t lat)
+{
+ /* Add op to zone */
+ add_zone_iop(zonep, unow, op);
+
+ /* Track system latency */
+ if (op != ZFS_ZONE_IOP_LOGICAL_WRITE)
+ add_sys_iop(unow, op, lat);
+}
+
+/*
+ * Calculate and return the total number of read ops, write ops and logical
+ * write ops for the given zone. If the zone has issued operations of any type
+ * return a non-zero value, otherwise return 0.
+ */
+static int
+get_zone_io_cnt(hrtime_t unow, zone_t *zonep, uint_t *rops, uint_t *wops,
+ uint_t *lwops)
+{
+ *rops = calc_zone_cnt(unow, &zonep->zone_rd_ops);
+ *wops = calc_zone_cnt(unow, &zonep->zone_wr_ops);
+ *lwops = calc_zone_cnt(unow, &zonep->zone_lwr_ops);
+
+ DTRACE_PROBE4(zfs__zone__io__cnt, uintptr_t, zonep->zone_id,
+ uintptr_t, *rops, uintptr_t, *wops, uintptr_t, *lwops);
+
+ return (*rops | *wops | *lwops);
+}
+
+/*
+ * Get the average read/write latency in usecs for the system.
+ */
+static void
+get_sys_avg_lat(hrtime_t unow, uint_t *rlat, uint_t *wlat)
+{
+ *rlat = calc_avg_lat(unow, &rd_lat);
+ *wlat = calc_avg_lat(unow, &wr_lat);
+
+ /*
+ * In an attempt to improve the accuracy of the throttling algorithm,
+ * assume that IO operations can't have zero latency. Instead, assume
+ * a reasonable lower bound for each operation type. If the actual
+ * observed latencies are non-zero, use those latency values instead.
+ */
+ if (*rlat == 0)
+ *rlat = 1000;
+ if (*wlat == 0)
+ *wlat = 1000;
+
+ DTRACE_PROBE2(zfs__zone__sys__avg__lat, uintptr_t, *rlat,
+ uintptr_t, *wlat);
+}
+
+/*
+ * Find disk utilization for each zone and average utilization for all active
+ * zones.
+ */
+static int
+zfs_zone_wait_adjust_calculate_cb(zone_t *zonep, void *arg)
+{
+ zoneio_stats_t *sp = arg;
+ uint_t rops, wops, lwops;
+
+ if (zonep->zone_id == GLOBAL_ZONEID ||
+ get_zone_io_cnt(sp->zi_now, zonep, &rops, &wops, &lwops) == 0) {
+ zonep->zone_io_util = 0;
+ return (0);
+ }
+
+ zonep->zone_io_util = (rops * sp->zi_avgrlat) +
+ (wops * sp->zi_avgwlat) + (lwops * sp->zi_avgwlat);
+ sp->zi_totutil += zonep->zone_io_util;
+
+ if (zonep->zone_io_util > 0) {
+ sp->zi_active++;
+ sp->zi_totpri += zonep->zone_zfs_io_pri;
+ }
+
+ /*
+ * sdt:::zfs-zone-utilization
+ *
+ * arg0: zone ID
+ * arg1: read operations observed during time window
+ * arg2: physical write operations observed during time window
+ * arg3: logical write ops observed during time window
+ * arg4: calculated utilization given read and write ops
+ * arg5: I/O priority assigned to this zone
+ */
+ DTRACE_PROBE6(zfs__zone__utilization, uint_t, zonep->zone_id,
+ uint_t, rops, uint_t, wops, uint_t, lwops,
+ uint_t, zonep->zone_io_util, uint_t, zonep->zone_zfs_io_pri);
+
+ return (0);
+}
+
+static void
+zfs_zone_delay_inc(zone_t *zonep)
+{
+ if (zonep->zone_io_delay < zfs_zone_delay_ceiling)
+ zonep->zone_io_delay += zfs_zone_delay_step;
+}
+
+static void
+zfs_zone_delay_dec(zone_t *zonep)
+{
+ if (zonep->zone_io_delay > 0)
+ zonep->zone_io_delay -= zfs_zone_delay_step;
+}
+
+/*
+ * For all zones "far enough" away from the average utilization, increase that
+ * zones delay. Otherwise, reduce its delay.
+ */
+static int
+zfs_zone_wait_adjust_delay_cb(zone_t *zonep, void *arg)
+{
+ zoneio_stats_t *sp = arg;
+ uint16_t delay = zonep->zone_io_delay;
+ uint_t fairutil = 0;
+
+ zonep->zone_io_util_above_avg = B_FALSE;
+
+ /*
+ * Given the calculated total utilitzation for all zones, calculate the
+ * fair share of I/O for this zone.
+ */
+ if (zfs_zone_priority_enable && sp->zi_totpri > 0) {
+ fairutil = (sp->zi_totutil * zonep->zone_zfs_io_pri) /
+ sp->zi_totpri;
+ } else if (sp->zi_active > 0) {
+ fairutil = sp->zi_totutil / sp->zi_active;
+ }
+
+ /*
+ * Adjust each IO's delay. If the overall delay becomes too high, avoid
+ * increasing beyond the ceiling value.
+ */
+ if (zonep->zone_io_util > fairutil && sp->zi_overutil) {
+ zonep->zone_io_util_above_avg = B_TRUE;
+
+ if (sp->zi_active > 1)
+ zfs_zone_delay_inc(zonep);
+ } else if (zonep->zone_io_util < fairutil || sp->zi_underutil ||
+ sp->zi_active <= 1) {
+ zfs_zone_delay_dec(zonep);
+ }
+
+ /*
+ * sdt:::zfs-zone-throttle
+ *
+ * arg0: zone ID
+ * arg1: old delay for this zone
+ * arg2: new delay for this zone
+ * arg3: calculated fair I/O utilization
+ * arg4: actual I/O utilization
+ */
+ DTRACE_PROBE5(zfs__zone__throttle, uintptr_t, zonep->zone_id,
+ uintptr_t, delay, uintptr_t, zonep->zone_io_delay,
+ uintptr_t, fairutil, uintptr_t, zonep->zone_io_util);
+
+ return (0);
+}
+
+/*
+ * Examine the utilization between different zones, and adjust the delay for
+ * each zone appropriately.
+ */
+static void
+zfs_zone_wait_adjust(hrtime_t unow, hrtime_t last_checked)
+{
+ zoneio_stats_t stats;
+ hrtime_t laggard_udelta = 0;
+
+ (void) bzero(&stats, sizeof (stats));
+
+ stats.zi_now = unow;
+ get_sys_avg_lat(unow, &stats.zi_avgrlat, &stats.zi_avgwlat);
+
+ if (stats.zi_avgrlat > stats.zi_avgwlat * zfs_zone_rw_lat_limit)
+ stats.zi_avgrlat = stats.zi_avgwlat * zfs_zone_rw_lat_limit;
+ else if (stats.zi_avgrlat * zfs_zone_rw_lat_limit < stats.zi_avgwlat)
+ stats.zi_avgwlat = stats.zi_avgrlat * zfs_zone_rw_lat_limit;
+
+ if (zone_walk(zfs_zone_wait_adjust_calculate_cb, &stats) != 0)
+ return;
+
+ /*
+ * Calculate disk utilization for the most recent period.
+ */
+ if (zfs_disk_last_rtime == 0 || unow - last_checked <= 0) {
+ stats.zi_diskutil = 0;
+ } else {
+ stats.zi_diskutil =
+ ((zfs_disk_rtime - zfs_disk_last_rtime) * 100) /
+ ((unow - last_checked) * 1000);
+ }
+ zfs_disk_last_rtime = zfs_disk_rtime;
+
+ if (unow > zfs_disk_last_laggard)
+ laggard_udelta = unow - zfs_disk_last_laggard;
+
+ /*
+ * To minimize porpoising, we have three separate states for our
+ * assessment of I/O performance: overutilized, underutilized, and
+ * neither overutilized nor underutilized. We will increment the
+ * throttle if a zone is using more than its fair share _and_ I/O
+ * is overutilized; we will decrement the throttle if a zone is using
+ * less than its fair share _or_ I/O is underutilized.
+ */
+ stats.zi_underutil = stats.zi_diskutil < zfs_zone_underutil_threshold ||
+ laggard_udelta > zfs_zone_laggard_ancient;
+
+ stats.zi_overutil = stats.zi_diskutil > zfs_zone_util_threshold &&
+ laggard_udelta < zfs_zone_laggard_recent;
+
+ /*
+ * sdt:::zfs-zone-stats
+ *
+ * Statistics observed over the last period:
+ *
+ * arg0: average system read latency
+ * arg1: average system write latency
+ * arg2: number of active zones
+ * arg3: total I/O 'utilization' for all zones
+ * arg4: total I/O priority of all active zones
+ * arg5: calculated disk utilization
+ */
+ DTRACE_PROBE6(zfs__zone__stats, uintptr_t, stats.zi_avgrlat,
+ uintptr_t, stats.zi_avgwlat, uintptr_t, stats.zi_active,
+ uintptr_t, stats.zi_totutil, uintptr_t, stats.zi_totpri,
+ uintptr_t, stats.zi_diskutil);
+
+ (void) zone_walk(zfs_zone_wait_adjust_delay_cb, &stats);
+}
+
+/*
+ * Callback used to calculate a zone's IO schedule priority.
+ *
+ * We scan the zones looking for ones with ops in the queue. Out of those,
+ * we pick the one that calculates to the highest schedule priority.
+ */
+static int
+get_sched_pri_cb(zone_t *zonep, void *arg)
+{
+ int pri;
+ uint_t cnt;
+ zone_q_bump_t *qbp = arg;
+ zio_priority_t p = qbp->zq_queue;
+
+ cnt = zonep->zone_zfs_queued[p];
+ if (cnt == 0) {
+ zonep->zone_zfs_weight = 0;
+ return (0);
+ }
+
+ /*
+ * On each pass, increment the zone's weight. We use this as input
+ * to the calculation to prevent starvation. The value is reset
+ * each time we issue an IO for this zone so zones which haven't
+ * done any IO over several iterations will see their weight max
+ * out.
+ */
+ if (zonep->zone_zfs_weight < SCHED_WEIGHT_MAX)
+ zonep->zone_zfs_weight++;
+
+ /*
+ * This zone's IO priority is the inverse of the number of IOs
+ * the zone has enqueued * zone's configured priority * weight.
+ * The queue depth has already been scaled by 10 to avoid problems
+ * with int rounding.
+ *
+ * This means that zones with fewer IOs in the queue will get
+ * preference unless other zone's assigned priority pulls them
+ * ahead. The weight is factored in to help ensure that zones
+ * which haven't done IO in a while aren't getting starved.
+ */
+ pri = (qbp->zq_qdepth / cnt) *
+ zonep->zone_zfs_io_pri * zonep->zone_zfs_weight;
+
+ /*
+ * If this zone has a higher priority than what we found so far,
+ * it becomes the new leading contender.
+ */
+ if (pri > qbp->zq_priority) {
+ qbp->zq_zoneid = zonep->zone_id;
+ qbp->zq_priority = pri;
+ qbp->zq_wt = zonep->zone_zfs_weight;
+ }
+ return (0);
+}
+
+/*
+ * See if we need to bump a zone's zio to the head of the queue. This is only
+ * done on the two synchronous I/O queues (see the block comment on the
+ * zfs_zone_schedule function). We get the correct vdev_queue_class_t and
+ * queue depth from our caller.
+ *
+ * For single-threaded synchronous processes a zone cannot get more than
+ * 1 op into the queue at a time unless the zone is running multiple processes
+ * in parallel. This can cause an imbalance in performance if there are zones
+ * with many parallel processes (and ops in the queue) vs. other zones which
+ * are doing simple single-threaded processes, such as interactive tasks in the
+ * shell. These zones can get backed up behind a deep queue and their IO
+ * performance will appear to be very poor as a result. This can make the
+ * zone work badly for interactive behavior.
+ *
+ * The scheduling algorithm kicks in once we start to get a deeper queue.
+ * Once that occurs, we look at all of the zones to see which one calculates
+ * to the highest priority. We bump that zone's first zio to the head of the
+ * queue.
+ *
+ * We use a counter on the zone so that we can quickly find how many ops each
+ * zone has in the queue without having to search the entire queue itself.
+ * This scales better since the number of zones is expected to be on the
+ * order of 10-100 whereas the queue depth can be in the range of 50-2000.
+ * In addition, since the zio's in the queue only have the zoneid, we would
+ * have to look up the zone for each zio enqueued and that means the overhead
+ * for scanning the queue each time would be much higher.
+ *
+ * In all cases, we fall back to simply pulling the next op off the queue
+ * if something should go wrong.
+ */
+static zio_t *
+get_next_zio(vdev_queue_class_t *vqc, int qdepth, zio_priority_t p,
+ avl_tree_t *tree)
+{
+ zone_q_bump_t qbump;
+ zio_t *zp = NULL, *zphead;
+ int cnt = 0;
+
+ /* To avoid problems with int rounding, scale the queue depth by 10 */
+ qbump.zq_qdepth = qdepth * 10;
+ qbump.zq_priority = 0;
+ qbump.zq_zoneid = 0;
+ qbump.zq_queue = p;
+ (void) zone_walk(get_sched_pri_cb, &qbump);
+
+ zphead = avl_first(tree);
+
+ /* Check if the scheduler didn't pick a zone for some reason!? */
+ if (qbump.zq_zoneid != 0) {
+ for (zp = avl_first(tree); zp != NULL;
+ zp = avl_walk(tree, zp, AVL_AFTER)) {
+ if (zp->io_zoneid == qbump.zq_zoneid)
+ break;
+ cnt++;
+ }
+ }
+
+ if (zp == NULL) {
+ zp = zphead;
+ } else if (zp != zphead) {
+ /*
+ * Only fire the probe if we actually picked a different zio
+ * than the one already at the head of the queue.
+ */
+ DTRACE_PROBE4(zfs__zone__sched__bump, uint_t, zp->io_zoneid,
+ uint_t, cnt, int, qbump.zq_priority, int, qbump.zq_wt);
+ }
+
+ return (zp);
+}
+
+/*
+ * Add our zone ID to the zio so we can keep track of which zones are doing
+ * what, even when the current thread processing the zio is not associated
+ * with the zone (e.g. the kernel taskq which pushes out TX groups).
+ */
+void
+zfs_zone_zio_init(zio_t *zp)
+{
+ zone_t *zonep = curzone;
+
+ zp->io_zoneid = zonep->zone_id;
+}
+
+/*
+ * Track and throttle IO operations per zone. Called from:
+ * - dmu_tx_count_write for (logical) write ops (both dataset and zvol writes
+ * go through this path)
+ * - arc_read for read ops that miss the ARC (both dataset and zvol)
+ * For each operation, increment that zone's counter based on the type of
+ * operation, then delay the operation, if necessary.
+ *
+ * There are three basic ways that we can see write ops:
+ * 1) An application does write syscalls. Those ops go into a TXG which
+ * we'll count here. Sometime later a kernel taskq thread (we'll see the
+ * vdev IO as zone 0) will perform some number of physical writes to commit
+ * the TXG to disk. Those writes are not associated with the zone which
+ * made the write syscalls and the number of operations is not correlated
+ * between the taskq and the zone. We only see logical writes in this
+ * function, we see the physcial writes in the zfs_zone_zio_start and
+ * zfs_zone_zio_done functions.
+ * 2) An application opens a file with O_SYNC. Each write will result in
+ * an operation which we'll see here plus a low-level vdev write from
+ * that zone.
+ * 3) An application does write syscalls followed by an fsync(). We'll
+ * count the writes going into a TXG here. We'll also see some number
+ * (usually much smaller, maybe only 1) of low-level vdev writes from this
+ * zone when the fsync is performed, plus some other low-level vdev writes
+ * from the taskq in zone 0 (are these metadata writes?).
+ *
+ * 4) In addition to the above, there are misc. system-level writes, such as
+ * writing out dirty pages to swap, or sync(2) calls, which will be handled
+ * by the global zone and which we count but don't generally worry about.
+ *
+ * Because of the above, we can see writes twice; first because this function
+ * is always called by a zone thread for logical writes, but then we also will
+ * count the physical writes that are performed at a low level via
+ * zfs_zone_zio_start. Without this, it can look like a non-global zone never
+ * writes (case 1). Depending on when the TXG is synced, the counts may be in
+ * the same sample bucket or in a different one.
+ *
+ * Tracking read operations is simpler due to their synchronous semantics. The
+ * zfs_read function -- called as a result of a read(2) syscall -- will always
+ * retrieve the data to be read through arc_read and we only come into this
+ * function when we have an arc miss.
+ */
+void
+zfs_zone_io_throttle(zfs_zone_iop_type_t type)
+{
+ zone_t *zonep = curzone;
+ hrtime_t unow, last_checked;
+ uint16_t wait;
+
+ unow = GET_USEC_TIME;
+
+ /*
+ * Only bump the counter for logical writes here. The counters for
+ * tracking physical IO operations are handled in zfs_zone_zio_done.
+ */
+ if (type == ZFS_ZONE_IOP_LOGICAL_WRITE) {
+ mutex_enter(&zonep->zone_stg_io_lock);
+ add_iop(zonep, unow, type, 0);
+ mutex_exit(&zonep->zone_stg_io_lock);
+ }
+
+ if (!zfs_zone_delay_enable)
+ return;
+
+ /*
+ * If the zone's I/O priority is set to zero, don't throttle that zone's
+ * operations at all.
+ */
+ if (zonep->zone_zfs_io_pri == 0)
+ return;
+
+ /*
+ * XXX There's a potential race here in that more than one thread may
+ * update the zone delays concurrently. The worst outcome is corruption
+ * of our data to track each zone's IO, so the algorithm may make
+ * incorrect throttling decisions until the data is refreshed.
+ */
+ last_checked = zfs_zone_last_checked;
+ if ((unow - last_checked) > zfs_zone_adjust_time) {
+ zfs_zone_last_checked = unow;
+ zfs_zone_wait_adjust(unow, last_checked);
+ }
+
+ if ((wait = zonep->zone_io_delay) > 0) {
+ /*
+ * If this is a write and we're doing above normal TXG
+ * syncing, then throttle for longer than normal.
+ */
+ if (type == ZFS_ZONE_IOP_LOGICAL_WRITE &&
+ (txg_cnt > 1 || txg_sync_rate > 1))
+ wait *= zfs_zone_txg_throttle_scale;
+
+ /*
+ * sdt:::zfs-zone-wait
+ *
+ * arg0: zone ID
+ * arg1: type of IO operation
+ * arg2: time to delay (in us)
+ */
+ DTRACE_PROBE3(zfs__zone__wait, uintptr_t, zonep->zone_id,
+ uintptr_t, type, uintptr_t, wait);
+
+ drv_usecwait(wait);
+
+ if (zonep->zone_vfs_stats != NULL) {
+ atomic_inc_64(&zonep->zone_vfs_stats->
+ zv_delay_cnt.value.ui64);
+ atomic_add_64(&zonep->zone_vfs_stats->
+ zv_delay_time.value.ui64, wait);
+ }
+ }
+}
+
+/*
+ * XXX Ignore the pool pointer parameter for now.
+ *
+ * Keep track to see if the TXG sync rate is running above the expected rate.
+ * If so, this implies that we are filling TXG's at a high rate due to a heavy
+ * write workload. We use this as input into the zone throttle.
+ *
+ * This function is called every 5 seconds (zfs_txg_timeout) under a normal
+ * write load. In this case, the sync rate is going to be 1. When there
+ * is a heavy write load, TXG's fill up fast and the sync thread will write
+ * the TXG more frequently (perhaps once a second). In this case the rate
+ * will be > 1. The sync rate is a lagging indicator since it can be up
+ * to 5 seconds old. We use the txg_cnt to keep track of the rate in the
+ * current 5 second interval and txg_sync_rate to keep track of the previous
+ * 5 second interval. In that way we don't have a period (1 or more seconds)
+ * where the txg_cnt == 0 and we cut back on throttling even though the rate
+ * is still high.
+ */
+/*ARGSUSED*/
+void
+zfs_zone_report_txg_sync(void *dp)
+{
+ uint_t now;
+
+ txg_cnt++;
+ now = (uint_t)(gethrtime() / NANOSEC);
+ if ((now - txg_last_check) >= zfs_txg_timeout) {
+ txg_sync_rate = txg_cnt / 2;
+ txg_cnt = 0;
+ txg_last_check = now;
+ }
+}
+
+hrtime_t
+zfs_zone_txg_delay()
+{
+ if (curzone->zone_io_util_above_avg)
+ return (zfs_zone_txg_delay_nsec);
+
+ return (MSEC2NSEC(10));
+}
+
+/*
+ * Called from vdev_disk_io_start when an IO hits the end of the zio pipeline
+ * and is issued.
+ * Keep track of start time for latency calculation in zfs_zone_zio_done.
+ */
+void
+zfs_zone_zio_start(zio_t *zp)
+{
+ zone_t *zonep;
+
+ /*
+ * I/Os of type ZIO_TYPE_IOCTL are used to flush the disk cache, not for
+ * an actual I/O operation. Ignore those operations as they relate to
+ * throttling and scheduling.
+ */
+ if (zp->io_type == ZIO_TYPE_IOCTL)
+ return;
+
+ if ((zonep = zone_find_by_id(zp->io_zoneid)) == NULL)
+ return;
+
+ mutex_enter(&zonep->zone_zfs_lock);
+ if (zp->io_type == ZIO_TYPE_READ)
+ kstat_runq_enter(&zonep->zone_zfs_rwstats);
+ zonep->zone_zfs_weight = 0;
+ mutex_exit(&zonep->zone_zfs_lock);
+
+ mutex_enter(&zfs_disk_lock);
+ zp->io_dispatched = gethrtime();
+
+ if (zfs_disk_rcnt++ != 0)
+ zfs_disk_rtime += (zp->io_dispatched - zfs_disk_rlastupdate);
+ zfs_disk_rlastupdate = zp->io_dispatched;
+ mutex_exit(&zfs_disk_lock);
+
+ zone_rele(zonep);
+}
+
+/*
+ * Called from vdev_disk_io_done when an IO completes.
+ * Increment our counter for zone ops.
+ * Calculate the IO latency avg. for this zone.
+ */
+void
+zfs_zone_zio_done(zio_t *zp)
+{
+ zone_t *zonep;
+ hrtime_t now, unow, udelta;
+
+ if (zp->io_type == ZIO_TYPE_IOCTL)
+ return;
+
+ if (zp->io_dispatched == 0)
+ return;
+
+ if ((zonep = zone_find_by_id(zp->io_zoneid)) == NULL)
+ return;
+
+ now = gethrtime();
+ unow = NANO_TO_MICRO(now);
+ udelta = unow - NANO_TO_MICRO(zp->io_dispatched);
+
+ mutex_enter(&zonep->zone_zfs_lock);
+
+ /*
+ * To calculate the wsvc_t average, keep a cumulative sum of all the
+ * wait time before each I/O was dispatched. Since most writes are
+ * asynchronous, only track the wait time for read I/Os.
+ */
+ if (zp->io_type == ZIO_TYPE_READ) {
+ zonep->zone_zfs_rwstats.reads++;
+ zonep->zone_zfs_rwstats.nread += zp->io_size;
+
+ zonep->zone_zfs_stats->zz_waittime.value.ui64 +=
+ zp->io_dispatched - zp->io_timestamp;
+
+ kstat_runq_exit(&zonep->zone_zfs_rwstats);
+ } else {
+ zonep->zone_zfs_rwstats.writes++;
+ zonep->zone_zfs_rwstats.nwritten += zp->io_size;
+ }
+
+ mutex_exit(&zonep->zone_zfs_lock);
+
+ mutex_enter(&zfs_disk_lock);
+ zfs_disk_rcnt--;
+ zfs_disk_rtime += (now - zfs_disk_rlastupdate);
+ zfs_disk_rlastupdate = now;
+
+ if (udelta > zfs_zone_laggard_threshold)
+ zfs_disk_last_laggard = unow;
+
+ mutex_exit(&zfs_disk_lock);
+
+ if (zfs_zone_delay_enable) {
+ mutex_enter(&zonep->zone_stg_io_lock);
+ add_iop(zonep, unow, zp->io_type == ZIO_TYPE_READ ?
+ ZFS_ZONE_IOP_READ : ZFS_ZONE_IOP_WRITE, udelta);
+ mutex_exit(&zonep->zone_stg_io_lock);
+ }
+
+ zone_rele(zonep);
+
+ /*
+ * sdt:::zfs-zone-latency
+ *
+ * arg0: zone ID
+ * arg1: type of I/O operation
+ * arg2: I/O latency (in us)
+ */
+ DTRACE_PROBE3(zfs__zone__latency, uintptr_t, zp->io_zoneid,
+ uintptr_t, zp->io_type, uintptr_t, udelta);
+}
+
+void
+zfs_zone_zio_dequeue(zio_t *zp)
+{
+ zio_priority_t p;
+ zone_t *zonep;
+
+ p = zp->io_priority;
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return;
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ if ((zonep = zone_find_by_id(zp->io_zoneid)) == NULL)
+ return;
+
+ mutex_enter(&zonep->zone_stg_io_lock);
+ ASSERT(zonep->zone_zfs_queued[p] > 0);
+ if (zonep->zone_zfs_queued[p] == 0)
+ cmn_err(CE_WARN, "zfs_zone_zio_dequeue: count==0");
+ else
+ zonep->zone_zfs_queued[p]--;
+ mutex_exit(&zonep->zone_stg_io_lock);
+ zone_rele(zonep);
+}
+
+void
+zfs_zone_zio_enqueue(zio_t *zp)
+{
+ zio_priority_t p;
+ zone_t *zonep;
+
+ p = zp->io_priority;
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return;
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ if ((zonep = zone_find_by_id(zp->io_zoneid)) == NULL)
+ return;
+
+ mutex_enter(&zonep->zone_stg_io_lock);
+ zonep->zone_zfs_queued[p]++;
+ mutex_exit(&zonep->zone_stg_io_lock);
+ zone_rele(zonep);
+}
+
+/*
+ * Called from vdev_queue_io_to_issue. That function is where zio's are listed
+ * in FIFO order on one of the sync queues, then pulled off (by
+ * vdev_queue_io_remove) and issued. We potentially do zone-based scheduling
+ * here to find a zone's zio deeper in the sync queue and issue that instead
+ * of simply doing FIFO.
+ *
+ * We only do zone-based zio scheduling for the two synchronous I/O queues
+ * (read & write). These queues are normally serviced in FIFO order but we
+ * may decide to move a zone's zio to the head of the line. A typical I/O
+ * load will be mostly synchronous reads and some asynchronous writes (which
+ * are scheduled differently due to transaction groups). There will also be
+ * some synchronous writes for those apps which want to ensure their data is on
+ * disk. We want to make sure that a zone with a single-threaded app (e.g. the
+ * shell) that is doing synchronous I/O (typically reads) isn't penalized by
+ * other zones which are doing lots of synchronous I/O because they have many
+ * running threads.
+ *
+ * The vq->vq_lock mutex is held when we're executing this function so we
+ * can safely access the "last zone" variable on the queue.
+ */
+zio_t *
+zfs_zone_schedule(vdev_queue_t *vq, zio_priority_t p, avl_index_t idx,
+ avl_tree_t *tree)
+{
+ vdev_queue_class_t *vqc = &vq->vq_class[p];
+ uint_t cnt;
+ zoneid_t last_zone;
+ zio_t *zio;
+
+ ASSERT(MUTEX_HELD(&vq->vq_lock));
+
+ /* Don't change the order on the LBA ordered queues. */
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return (avl_nearest(tree, idx, AVL_AFTER));
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ cnt = avl_numnodes(tree);
+ last_zone = vq->vq_last_zone_id;
+
+ /*
+ * If there are only a few zios in the queue then just issue the head.
+ * If there are more than a few zios already queued up, then use
+ * scheduling to get the next zio.
+ */
+ if (!zfs_zone_schedule_enable || cnt < zfs_zone_schedule_thresh)
+ zio = avl_nearest(tree, idx, AVL_AFTER);
+ else
+ zio = get_next_zio(vqc, cnt, p, tree);
+
+ vq->vq_last_zone_id = zio->io_zoneid;
+
+ /*
+ * Probe with 4 args; the number of IOs in the queue, the zone that
+ * was last scheduled off this queue, the zone that was associated
+ * with the next IO that is scheduled, and which queue (priority).
+ */
+ DTRACE_PROBE4(zfs__zone__sched, uint_t, cnt, uint_t, last_zone,
+ uint_t, zio->io_zoneid, uint_t, p);
+
+ return (zio);
+}
+
+#endif
diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c
index 72a24580ff..96bd320404 100644
--- a/usr/src/uts/common/fs/zfs/zil.c
+++ b/usr/src/uts/common/fs/zfs/zil.c
@@ -22,6 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -1803,9 +1804,18 @@ zil_close(zilog_t *zilog)
if (lwb != NULL)
txg = lwb->lwb_max_txg;
mutex_exit(&zilog->zl_lock);
- if (txg)
+
+ if (zilog_is_dirty(zilog)) {
+ /*
+ * If we're dirty, always wait for the current transaction --
+ * our lwb_max_txg may be in the past.
+ */
+ txg_wait_synced(zilog->zl_dmu_pool, 0);
+ } else if (txg) {
txg_wait_synced(zilog->zl_dmu_pool, txg);
- ASSERT(!zilog_is_dirty(zilog));
+ }
+
+ VERIFY(!zilog_is_dirty(zilog));
taskq_destroy(zilog->zl_clean_taskq);
zilog->zl_clean_taskq = NULL;
diff --git a/usr/src/uts/common/fs/zfs/zio.c b/usr/src/uts/common/fs/zfs/zio.c
index be7087d4db..74d00c5b3d 100644
--- a/usr/src/uts/common/fs/zfs/zio.c
+++ b/usr/src/uts/common/fs/zfs/zio.c
@@ -22,6 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -40,6 +41,7 @@
#include <sys/ddt.h>
#include <sys/blkptr.h>
#include <sys/zfeature.h>
+#include <sys/zfs_zone.h>
#include <sys/metaslab_impl.h>
/*
@@ -588,11 +590,14 @@ zio_create(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp,
zio->io_bookmark = *zb;
if (pio != NULL) {
+ zio->io_zoneid = pio->io_zoneid;
if (zio->io_logical == NULL)
zio->io_logical = pio->io_logical;
if (zio->io_child_type == ZIO_CHILD_GANG)
zio->io_gang_leader = pio->io_gang_leader;
zio_add_child(pio, zio);
+ } else {
+ zfs_zone_zio_init(zio);
}
return (zio);
diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c
index 95bb26c211..535bc057b9 100644
--- a/usr/src/uts/common/fs/zfs/zvol.c
+++ b/usr/src/uts/common/fs/zfs/zvol.c
@@ -25,7 +25,7 @@
*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -84,6 +84,7 @@
#include <sys/zvol.h>
#include <sys/dumphdr.h>
#include <sys/zil_impl.h>
+#include <sys/sdt.h>
#include <sys/dbuf.h>
#include <sys/dmu_tx.h>
#include <sys/zfeature.h>
@@ -138,6 +139,11 @@ typedef struct zvol_state {
#define ZVOL_EXCL 0x4
#define ZVOL_WCE 0x8
+#define VOP_LATENCY_10MS 10000000
+#define VOP_LATENCY_100MS 100000000
+#define VOP_LATENCY_1S 1000000000
+#define VOP_LATENCY_10S 10000000000
+
/*
* zvol maximum transfer in one DMU tx.
*/
@@ -1379,6 +1385,9 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
uint64_t volsize;
rl_t *rl;
int error = 0;
+ zone_t *zonep = curzone;
+ uint64_t tot_bytes;
+ hrtime_t start, lat;
zv = zfsdev_get_soft_state(minor, ZSST_ZVOL);
if (zv == NULL)
@@ -1395,6 +1404,14 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
return (error);
}
+ DTRACE_PROBE3(zvol__uio__start, dev_t, dev, uio_t *, uio, int, 0);
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_runq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ start = gethrtime();
+ tot_bytes = 0;
+
rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid,
RL_READER);
while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
@@ -1404,6 +1421,7 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
if (bytes > volsize - uio->uio_loffset)
bytes = volsize - uio->uio_loffset;
+ tot_bytes += bytes;
error = dmu_read_uio(zv->zv_objset, ZVOL_OBJ, uio, bytes);
if (error) {
/* convert checksum errors into IO errors */
@@ -1413,6 +1431,39 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
}
}
zfs_range_unlock(rl);
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.reads++;
+ zonep->zone_vfs_rwstats.nread += tot_bytes;
+ kstat_runq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ zone_vfs_kstat_t *zvp;
+
+ zvp = zonep->zone_vfs_stats;
+ if (lat < VOP_LATENCY_100MS) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+
+ DTRACE_PROBE4(zvol__uio__done, dev_t, dev, uio_t *, uio, int, 0, int,
+ error);
+
return (error);
}
@@ -1426,6 +1477,9 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
rl_t *rl;
int error = 0;
boolean_t sync;
+ zone_t *zonep = curzone;
+ uint64_t tot_bytes;
+ hrtime_t start, lat;
zv = zfsdev_get_soft_state(minor, ZSST_ZVOL);
if (zv == NULL)
@@ -1442,6 +1496,19 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
return (error);
}
+ DTRACE_PROBE3(zvol__uio__start, dev_t, dev, uio_t *, uio, int, 1);
+
+ /*
+ * For the purposes of VFS kstat consumers, the "waitq" calculation is
+ * repurposed as the active queue for zvol write operations. There's no
+ * actual wait queue for zvol operations.
+ */
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_waitq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ start = gethrtime();
+ tot_bytes = 0;
+
sync = !(zv->zv_flags & ZVOL_WCE) ||
(zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS);
@@ -1455,6 +1522,7 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
if (bytes > volsize - off) /* don't write past the end */
bytes = volsize - off;
+ tot_bytes += bytes;
dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes);
error = dmu_tx_assign(tx, TXG_WAIT);
if (error) {
@@ -1472,6 +1540,39 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
zfs_range_unlock(rl);
if (sync)
zil_commit(zv->zv_zilog, ZVOL_OBJ);
+
+ DTRACE_PROBE4(zvol__uio__done, dev_t, dev, uio_t *, uio, int, 1, int,
+ error);
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.writes++;
+ zonep->zone_vfs_rwstats.nwritten += tot_bytes;
+ kstat_waitq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ zone_vfs_kstat_t *zvp;
+
+ zvp = zonep->zone_vfs_stats;
+ if (lat < VOP_LATENCY_100MS) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+
return (error);
}
diff --git a/usr/src/uts/common/inet/bpf.h b/usr/src/uts/common/inet/bpf.h
new file mode 100644
index 0000000000..e3eac799e5
--- /dev/null
+++ b/usr/src/uts/common/inet/bpf.h
@@ -0,0 +1,49 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _INET_BPF_H
+#define _INET_BPF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef _KERNEL
+
+#include <sys/types.h>
+
+/*
+ * Clone bpf_insn definition so that consumers don't need net/bpf.h to reason
+ * about struct sizing.
+ */
+typedef struct ip_bpf_insn {
+ uint16_t code;
+ uint8_t jt;
+ uint8_t jf;
+ uint32_t k;
+} ip_bpf_insn_t;
+
+extern uint32_t ip_bpf_filter(ip_bpf_insn_t *, uchar_t *, uint_t, uint_t);
+extern boolean_t ip_bpf_validate(ip_bpf_insn_t *, uint_t);
+
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INET_BPF_H */
diff --git a/usr/src/uts/common/io/bpf/bpf_filter.c b/usr/src/uts/common/inet/bpf_filter.c
index db5b224a5e..5a9ba38da6 100644
--- a/usr/src/uts/common/io/bpf/bpf_filter.c
+++ b/usr/src/uts/common/inet/bpf_filter.c
@@ -38,6 +38,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/param.h>
@@ -45,11 +46,12 @@
#include <sys/stream.h>
#include <sys/byteorder.h>
#include <sys/sdt.h>
+#include <inet/bpf.h>
+#include <net/bpf.h>
#define EXTRACT_SHORT(p) BE_IN16(p)
#define EXTRACT_LONG(p) BE_IN32(p)
-#ifdef _KERNEL
#define M_LEN(_m) ((_m)->b_wptr - (_m)->b_rptr)
#define mtod(_a, _t) ((_t)((_a)->b_rptr))
#define MINDEX(len, m, k) \
@@ -123,11 +125,7 @@ m_xhalf(mblk_t *m, uint32_t k, int *err)
*err = 0;
return ((cp[0] << 8) | mtod(m0, uchar_t *)[0]);
}
-#else /* _KERNEL */
-#include <stdlib.h>
-#endif /* !_KERNEL */
-#include <net/bpf.h>
/*
* Execute the filter program starting at pc on the packet p
@@ -137,8 +135,8 @@ m_xhalf(mblk_t *m, uint32_t k, int *err)
* packet is only in one mblk_t.
* When buflen is 0, p is an mblk_t pointer.
*/
-uint_t
-bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
+uint32_t
+ip_bpf_filter(ip_bpf_insn_t *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
{
uint32_t A, X, k;
uint32_t mem[BPF_MEMWORDS];
@@ -147,7 +145,7 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
/*
* No filter means accept all.
*/
- return ((uint_t)-1);
+ return ((uint32_t)-1);
A = 0;
X = 0;
--pc;
@@ -165,10 +163,10 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
abort();
#endif
case BPF_RET|BPF_K:
- return ((uint_t)pc->k);
+ return (pc->k);
case BPF_RET|BPF_A:
- return ((uint_t)A);
+ return (A);
case BPF_LD|BPF_W|BPF_ABS:
k = pc->k;
@@ -456,7 +454,6 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
/* NOTREACHED */
}
-#ifdef _KERNEL
/*
* Return true if the 'fcode' is a valid filter program.
* The constraints are that each jump be forward and to a valid
@@ -468,14 +465,14 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
* The kernel needs to be able to verify an application's filter code.
* Otherwise, a bogus program could easily crash the system.
*/
-int
-bpf_validate(struct bpf_insn *f, int len)
+boolean_t
+ip_bpf_validate(ip_bpf_insn_t *f, uint_t len)
{
uint_t i, from;
- struct bpf_insn *p;
+ ip_bpf_insn_t *p;
if (len < 1 || len > BPF_MAXINSNS)
- return (0);
+ return (B_FALSE);
for (i = 0; i < len; ++i) {
p = &f[i];
@@ -489,7 +486,7 @@ bpf_validate(struct bpf_insn *f, int len)
switch (BPF_MODE(p->code)) {
case BPF_MEM:
if (p->k >= BPF_MEMWORDS)
- return (0);
+ return (B_FALSE);
break;
case BPF_ABS:
case BPF_IND:
@@ -498,13 +495,13 @@ bpf_validate(struct bpf_insn *f, int len)
case BPF_LEN:
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_ST:
case BPF_STX:
if (p->k >= BPF_MEMWORDS)
- return (0);
+ return (B_FALSE);
break;
case BPF_ALU:
switch (BPF_OP(p->code)) {
@@ -522,10 +519,10 @@ bpf_validate(struct bpf_insn *f, int len)
* Check for constant division by 0.
*/
if (BPF_RVAL(p->code) == BPF_K && p->k == 0)
- return (0);
+ return (B_FALSE);
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_JMP:
@@ -549,17 +546,17 @@ bpf_validate(struct bpf_insn *f, int len)
switch (BPF_OP(p->code)) {
case BPF_JA:
if (from + p->k < from || from + p->k >= len)
- return (0);
+ return (B_FALSE);
break;
case BPF_JEQ:
case BPF_JGT:
case BPF_JGE:
case BPF_JSET:
if (from + p->jt >= len || from + p->jf >= len)
- return (0);
+ return (B_FALSE);
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_RET:
@@ -567,10 +564,9 @@ bpf_validate(struct bpf_insn *f, int len)
case BPF_MISC:
break;
default:
- return (0);
+ return (B_FALSE);
}
}
return (BPF_CLASS(f[len - 1].code) == BPF_RET);
}
-#endif
diff --git a/usr/src/uts/common/inet/inet_hash.h b/usr/src/uts/common/inet/inet_hash.h
new file mode 100644
index 0000000000..a790a797d1
--- /dev/null
+++ b/usr/src/uts/common/inet/inet_hash.h
@@ -0,0 +1,37 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _INET_INET_HASH_H
+#define _INET_INET_HASH_H
+
+/*
+ * Common packet hashing routines shared across MAC, UDP, and others.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define INET_PKT_HASH_L2 0x01
+#define INET_PKT_HASH_L3 0x02
+#define INET_PKT_HASH_L4 0x04
+
+extern uint64_t inet_pkt_hash(uint_t, mblk_t *, uint8_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INET_INET_HASH_H */
diff --git a/usr/src/uts/common/inet/ip/conn_opt.c b/usr/src/uts/common/inet/ip/conn_opt.c
index bcbc1c4949..b4bff4d7b4 100644
--- a/usr/src/uts/common/inet/ip/conn_opt.c
+++ b/usr/src/uts/common/inet/ip/conn_opt.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -619,6 +620,9 @@ conn_opt_get(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
case SO_REUSEADDR:
*i1 = connp->conn_reuseaddr ? SO_REUSEADDR : 0;
break; /* goto sizeof (int) option return */
+ case SO_REUSEPORT:
+ *i1 = connp->conn_reuseport;
+ break; /* goto sizeof (int) option return */
case SO_TYPE:
*i1 = connp->conn_so_type;
break; /* goto sizeof (int) option return */
@@ -1186,8 +1190,24 @@ conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
int error;
- if (connp->conn_family != AF_INET)
+ if (connp->conn_family == AF_INET6 &&
+ connp->conn_ipversion == IPV4_VERSION) {
+ /*
+ * Allow certain IPv4 options to be set on an AF_INET6 socket
+ * if the connection is still IPv4.
+ */
+ switch (name) {
+ case IP_TOS:
+ case T_IP_TOS:
+ case IP_TTL:
+ case IP_DONTFRAG:
+ break;
+ default:
+ return (EINVAL);
+ }
+ } else if (connp->conn_family != AF_INET) {
return (EINVAL);
+ }
switch (name) {
case IP_TTL:
diff --git a/usr/src/uts/common/inet/ip/icmp.c b/usr/src/uts/common/inet/ip/icmp.c
index a4abdbd130..01119bf65b 100644
--- a/usr/src/uts/common/inet/ip/icmp.c
+++ b/usr/src/uts/common/inet/ip/icmp.c
@@ -22,6 +22,7 @@
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -80,6 +81,7 @@
#include <sys/tsol/tnet.h>
#include <inet/rawip_impl.h>
+#include <net/bpf.h>
#include <sys/disp.h>
@@ -1011,6 +1013,12 @@ icmp_close_free(conn_t *connp)
icmp->icmp_filter = NULL;
}
+ if (icmp->icmp_bpf_len != 0) {
+ kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+ icmp->icmp_bpf_len = 0;
+ icmp->icmp_bpf_prog = NULL;
+ }
+
/*
* Clear any fields which the kmem_cache constructor clears.
* Only icmp_connp needs to be preserved.
@@ -1964,6 +1972,104 @@ icmp_tpi_opt_get(queue_t *q, int level, int name, uchar_t *ptr)
return (err);
}
+static int
+icmp_attach_filter(icmp_t *icmp, uint_t inlen, const uchar_t *invalp)
+{
+ struct bpf_program prog;
+ ip_bpf_insn_t *insns = NULL;
+ unsigned int size;
+
+#ifdef _LP64
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ struct bpf_program32 *prog32;
+
+ if (inlen != sizeof (struct bpf_program32)) {
+ return (EINVAL);
+ }
+ prog32 = (struct bpf_program32 *)invalp;
+ prog.bf_len = prog32->bf_len;
+ prog.bf_insns = (void *)(uint64_t)prog32->bf_insns;
+ } else
+#endif
+ if (inlen == sizeof (struct bpf_program)) {
+ bcopy(invalp, &prog, sizeof (prog));
+ } else {
+ return (EINVAL);
+ }
+
+ if (prog.bf_len > BPF_MAXINSNS || prog.bf_len == 0) {
+ return (EINVAL);
+ }
+ size = prog.bf_len * sizeof (struct bpf_insn);
+ insns = kmem_alloc(size, KM_SLEEP);
+ if (copyin(prog.bf_insns, insns, size) != 0) {
+ kmem_free(insns, size);
+ return (EFAULT);
+ }
+ if (!ip_bpf_validate(insns, prog.bf_len)) {
+ kmem_free(insns, size);
+ return (EINVAL);
+ }
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+ if (icmp->icmp_bpf_len != 0) {
+ ASSERT(icmp->icmp_bpf_prog != NULL);
+
+ kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+ }
+ icmp->icmp_bpf_len = size;
+ icmp->icmp_bpf_prog = insns;
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (0);
+}
+
+static int
+icmp_detach_filter(icmp_t *icmp)
+{
+ int error;
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+ if (icmp->icmp_bpf_len == 0) {
+ ASSERT(icmp->icmp_bpf_prog == NULL);
+ error = ENOENT;
+ } else {
+ kmem_free(icmp->icmp_bpf_prog,
+ icmp->icmp_bpf_len);
+ icmp->icmp_bpf_len = 0;
+ icmp->icmp_bpf_prog = NULL;
+ error = 0;
+ }
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (error);
+}
+
+static boolean_t
+icmp_eval_filter(icmp_t *icmp, mblk_t *mp, ip_recv_attr_t *ira)
+{
+ boolean_t res;
+ uchar_t *buf = mp->b_rptr;
+ uint_t wirelen, len = MBLKL(mp);
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_READER);
+ if (icmp->icmp_bpf_len == 0) {
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (B_FALSE);
+ }
+ if (ira->ira_flags & IRAF_IS_IPV4) {
+ ipha_t *ipha = (ipha_t *)buf;
+
+ wirelen = ntohs(ipha->ipha_length);
+ } else {
+ ip6_t *ip6h = (ip6_t *)buf;
+
+ wirelen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
+ }
+ res = !ip_bpf_filter(icmp->icmp_bpf_prog, buf, wirelen, len);
+ rw_exit(&icmp->icmp_bpf_lock);
+
+ return (res);
+}
+
/*
* This routine sets socket options.
*/
@@ -2053,6 +2159,10 @@ icmp_do_opt_set(conn_opt_arg_t *coa, int level, int name,
return (ENOBUFS);
}
break;
+ case SO_ATTACH_FILTER:
+ return (icmp_attach_filter(icmp, inlen, invalp));
+ case SO_DETACH_FILTER:
+ return (icmp_detach_filter(icmp));
}
break;
@@ -2598,6 +2708,14 @@ icmp_input(void *arg1, mblk_t *mp, void *arg2, ip_recv_attr_t *ira)
/* Initialize regardless of IP version */
ipps.ipp_fields = 0;
+ /* Apply socket filter, if needed */
+ if (icmp->icmp_bpf_len != 0) {
+ if (icmp_eval_filter(icmp, mp, ira)) {
+ freemsg(mp);
+ return;
+ }
+ }
+
if (ira->ira_flags & IRAF_IS_IPV4) {
ASSERT(IPH_HDR_VERSION(rptr) == IPV4_VERSION);
ASSERT(MBLKL(mp) >= sizeof (ipha_t));
@@ -5027,7 +5145,8 @@ rawip_stack_fini(netstackid_t stackid, void *arg)
}
static void *
-rawip_kstat_init(netstackid_t stackid) {
+rawip_kstat_init(netstackid_t stackid)
+{
kstat_t *ksp;
rawip_named_kstat_t template = {
@@ -5039,9 +5158,7 @@ rawip_kstat_init(netstackid_t stackid) {
};
ksp = kstat_create_netstack("icmp", 0, "rawip", "mib2",
- KSTAT_TYPE_NAMED,
- NUM_OF_FIELDS(rawip_named_kstat_t),
- 0, stackid);
+ KSTAT_TYPE_NAMED, NUM_OF_FIELDS(rawip_named_kstat_t), 0, stackid);
if (ksp == NULL || ksp->ks_data == NULL)
return (NULL);
diff --git a/usr/src/uts/common/inet/ip/icmp_opt_data.c b/usr/src/uts/common/inet/ip/icmp_opt_data.c
index ff0310de0c..d65d3164d3 100644
--- a/usr/src/uts/common/inet/ip/icmp_opt_data.c
+++ b/usr/src/uts/common/inet/ip/icmp_opt_data.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -41,6 +42,7 @@
#include <netinet/ip_mroute.h>
#include <inet/optcom.h>
#include <inet/rawip_impl.h>
+#include <net/bpf.h>
/*
* Table of all known options handled on a ICMP protocol stack.
@@ -86,6 +88,10 @@ opdes_t icmp_opt_arr[] = {
0 },
{ SO_DOMAIN, SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
+{ SO_ATTACH_FILTER, SOL_SOCKET, OA_W, OA_W, OP_NP, 0,
+ sizeof (struct bpf_program), 0 },
+{ SO_DETACH_FILTER, SOL_SOCKET, OA_W, OA_W, OP_NP, 0, 0, 0 },
+
{ IP_OPTIONS, IPPROTO_IP, OA_RW, OA_RW, OP_NP,
(OP_VARLEN|OP_NODEFAULT),
IP_MAX_OPT_LENGTH + IP_ADDR_LEN, -1 /* not initialized */ },
diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c
index f006e83a1f..73081b9c1c 100644
--- a/usr/src/uts/common/inet/ip/ip.c
+++ b/usr/src/uts/common/inet/ip/ip.c
@@ -12577,6 +12577,7 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
ip_ioctl_cmd_t *ipip = arg;
ip_extract_func_t *extract_funcp;
+ ill_t *ill;
cmd_info_t ci;
int err;
boolean_t entered_ipsq = B_FALSE;
@@ -12697,6 +12698,13 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
ipsq_current_start(ipsq, ci.ci_ipif, ipip->ipi_cmd);
/*
+ * We need to cache the ill_t that we're going to use as the argument
+ * to the ipif-ioctl DTrace probe (below) because the ci_ipif can be
+ * blown away by calling ipi_func.
+ */
+ ill = ci.ci_ipif == NULL ? NULL : ci.ci_ipif->ipif_ill;
+
+ /*
* A return value of EINPROGRESS means the ioctl is
* either queued and waiting for some reason or has
* already completed.
@@ -12704,9 +12712,7 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
err = (*ipip->ipi_func)(ci.ci_ipif, ci.ci_sin, q, mp, ipip, ci.ci_lifr);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_process_ioctl finish WR",
- int, ipip->ipi_cmd,
- ill_t *, ci.ci_ipif == NULL ? NULL : ci.ci_ipif->ipif_ill,
- ipif_t *, ci.ci_ipif);
+ int, ipip->ipi_cmd, ill_t *, ill, ipif_t *, ci.ci_ipif);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), ipsq);
if (entered_ipsq)
diff --git a/usr/src/uts/common/inet/ip/ip_attr.c b/usr/src/uts/common/inet/ip/ip_attr.c
index 85ee142dfc..c350d67c2d 100644
--- a/usr/src/uts/common/inet/ip/ip_attr.c
+++ b/usr/src/uts/common/inet/ip/ip_attr.c
@@ -909,6 +909,11 @@ ixa_safe_copy(ip_xmit_attr_t *src, ip_xmit_attr_t *ixa)
*/
if (ixa->ixa_free_flags & IXA_FREE_CRED)
crhold(ixa->ixa_cred);
+
+ /*
+ * There is no cleanup in progress on this new copy.
+ */
+ ixa->ixa_tcpcleanup = IXATC_IDLE;
}
/*
diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c
index 62d85b202b..b88dcae2d1 100644
--- a/usr/src/uts/common/inet/ip/ip_if.c
+++ b/usr/src/uts/common/inet/ip/ip_if.c
@@ -22,7 +22,7 @@
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1990 Mentat Inc.
* Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright 2013 Joyent, Inc.
+ * Copyright (c) 2016, Joyent, Inc. All rights reserved.
* Copyright (c) 2014, OmniTI Computer Consulting, Inc. All rights reserved.
*/
@@ -718,7 +718,7 @@ ill_dlur_copy_address(uchar_t *phys_src, uint_t phys_length,
*/
mblk_t *
ill_dlur_gen(uchar_t *addr, uint_t addr_length, t_uscalar_t sap,
- t_scalar_t sap_length)
+ t_scalar_t sap_length)
{
dl_unitdata_req_t *dlur;
mblk_t *mp;
@@ -3855,15 +3855,18 @@ ill_lookup_on_ifindex_global_instance(uint_t index, boolean_t isv6)
{
ip_stack_t *ipst;
ill_t *ill;
+ netstack_t *ns;
- ipst = netstack_find_by_stackid(GLOBAL_NETSTACKID)->netstack_ip;
- if (ipst == NULL) {
+ ns = netstack_find_by_stackid(GLOBAL_NETSTACKID);
+
+ if ((ipst = ns->netstack_ip) == NULL) {
cmn_err(CE_WARN, "No ip_stack_t for zoneid zero!\n");
+ netstack_rele(ns);
return (NULL);
}
ill = ill_lookup_on_ifindex(index, isv6, ipst);
- netstack_rele(ipst->ips_netstack);
+ netstack_rele(ns);
return (ill);
}
@@ -10828,7 +10831,7 @@ ip_sioctl_mtu(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
/* ARGSUSED */
int
ip_sioctl_get_mtu(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
- ip_ioctl_cmd_t *ipip, void *if_req)
+ ip_ioctl_cmd_t *ipip, void *if_req)
{
struct ifreq *ifr;
struct lifreq *lifr;
@@ -10854,7 +10857,7 @@ ip_sioctl_get_mtu(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
/* ARGSUSED2 */
int
ip_sioctl_brdaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
- ip_ioctl_cmd_t *ipip, void *if_req)
+ ip_ioctl_cmd_t *ipip, void *if_req)
{
ipaddr_t addr;
ire_t *ire;
@@ -15590,7 +15593,7 @@ ip_select_source_v4(ill_t *ill, ipaddr_t setsrc, ipaddr_t dst,
/* ARGSUSED */
int
if_unitsel_restart(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
- ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
+ ip_ioctl_cmd_t *ipip, void *dummy_ifreq)
{
/*
* ill_phyint_reinit merged the v4 and v6 into a single
@@ -16247,7 +16250,7 @@ ill_ptpaddr_cnt(const ill_t *ill)
/* ARGSUSED */
int
ip_sioctl_get_lifusesrc(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
- ip_ioctl_cmd_t *ipip, void *ifreq)
+ ip_ioctl_cmd_t *ipip, void *ifreq)
{
struct lifreq *lifr = ifreq;
diff --git a/usr/src/uts/common/inet/ip/ip_squeue.c b/usr/src/uts/common/inet/ip/ip_squeue.c
index 33a2fa5935..dedb4dadcc 100644
--- a/usr/src/uts/common/inet/ip/ip_squeue.c
+++ b/usr/src/uts/common/inet/ip/ip_squeue.c
@@ -163,7 +163,7 @@ ip_squeue_create(pri_t pri)
{
squeue_t *sqp;
- sqp = squeue_create(ip_squeue_worker_wait, pri);
+ sqp = squeue_create(ip_squeue_worker_wait, pri, B_TRUE);
ASSERT(sqp != NULL);
if (ip_squeue_create_callback != NULL)
ip_squeue_create_callback(sqp);
diff --git a/usr/src/uts/common/inet/ip/ipclassifier.c b/usr/src/uts/common/inet/ip/ipclassifier.c
index bc2173ff24..a59027801f 100644
--- a/usr/src/uts/common/inet/ip/ipclassifier.c
+++ b/usr/src/uts/common/inet/ip/ipclassifier.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -868,67 +869,91 @@ ipcl_hash_remove_locked(conn_t *connp, connf_t *connfp)
mutex_exit(&(connfp)->connf_lock); \
}
-#define IPCL_HASH_INSERT_BOUND(connfp, connp) { \
- conn_t *pconnp = NULL, *nconnp; \
- IPCL_HASH_REMOVE((connp)); \
- mutex_enter(&(connfp)->connf_lock); \
- nconnp = (connfp)->connf_head; \
- while (nconnp != NULL && \
- !_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6)) { \
- pconnp = nconnp; \
- nconnp = nconnp->conn_next; \
- } \
- if (pconnp != NULL) { \
- pconnp->conn_next = (connp); \
- (connp)->conn_prev = pconnp; \
- } else { \
- (connfp)->connf_head = (connp); \
- } \
- if (nconnp != NULL) { \
- (connp)->conn_next = nconnp; \
- nconnp->conn_prev = (connp); \
- } \
- (connp)->conn_fanout = (connfp); \
- (connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) | \
- IPCL_BOUND; \
- CONN_INC_REF(connp); \
- mutex_exit(&(connfp)->connf_lock); \
-}
+/*
+ * When inserting bound or wildcard entries into the hash, ordering rules are
+ * used to facilitate timely and correct lookups. The order is as follows:
+ * 1. Entries bound to a specific address
+ * 2. Entries bound to INADDR_ANY
+ * 3. Entries bound to ADDR_UNSPECIFIED
+ * Entries in a category which share conn_lport (such as those using
+ * SO_REUSEPORT) will be ordered such that the newest inserted is first.
+ */
-#define IPCL_HASH_INSERT_WILDCARD(connfp, connp) { \
- conn_t **list, *prev, *next; \
- boolean_t isv4mapped = \
- IN6_IS_ADDR_V4MAPPED(&(connp)->conn_laddr_v6); \
- IPCL_HASH_REMOVE((connp)); \
- mutex_enter(&(connfp)->connf_lock); \
- list = &(connfp)->connf_head; \
- prev = NULL; \
- while ((next = *list) != NULL) { \
- if (isv4mapped && \
- IN6_IS_ADDR_UNSPECIFIED(&next->conn_laddr_v6) && \
- connp->conn_zoneid == next->conn_zoneid) { \
- (connp)->conn_next = next; \
- if (prev != NULL) \
- prev = next->conn_prev; \
- next->conn_prev = (connp); \
- break; \
- } \
- list = &next->conn_next; \
- prev = next; \
- } \
- (connp)->conn_prev = prev; \
- *list = (connp); \
- (connp)->conn_fanout = (connfp); \
- (connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) | \
- IPCL_BOUND; \
- CONN_INC_REF((connp)); \
- mutex_exit(&(connfp)->connf_lock); \
+void
+ipcl_hash_insert_bound(connf_t *connfp, conn_t *connp)
+{
+ conn_t *pconnp, *nconnp;
+
+ IPCL_HASH_REMOVE(connp);
+ mutex_enter(&connfp->connf_lock);
+ nconnp = connfp->connf_head;
+ pconnp = NULL;
+ while (nconnp != NULL) {
+ /*
+ * Walk though entries associated with the fanout until one is
+ * found which fulfills any of these conditions:
+ * 1. Listen address of ADDR_ANY/ADDR_UNSPECIFIED
+ * 2. Listen port the same as connp
+ */
+ if (_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6) ||
+ connp->conn_lport == nconnp->conn_lport)
+ break;
+ pconnp = nconnp;
+ nconnp = nconnp->conn_next;
+ }
+ if (pconnp != NULL) {
+ pconnp->conn_next = connp;
+ connp->conn_prev = pconnp;
+ } else {
+ connfp->connf_head = connp;
+ }
+ if (nconnp != NULL) {
+ connp->conn_next = nconnp;
+ nconnp->conn_prev = connp;
+ }
+ connp->conn_fanout = connfp;
+ connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+ CONN_INC_REF(connp);
+ mutex_exit(&connfp->connf_lock);
}
void
ipcl_hash_insert_wildcard(connf_t *connfp, conn_t *connp)
{
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ conn_t **list, *prev, *next;
+ conn_t *pconnp = NULL, *nconnp;
+ boolean_t isv4mapped = IN6_IS_ADDR_V4MAPPED(&connp->conn_laddr_v6);
+
+ IPCL_HASH_REMOVE(connp);
+ mutex_enter(&connfp->connf_lock);
+ nconnp = connfp->connf_head;
+ pconnp = NULL;
+ while (nconnp != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED_ANY(&nconnp->conn_laddr_v6) &&
+ isv4mapped && connp->conn_lport == nconnp->conn_lport)
+ break;
+ if (IN6_IS_ADDR_UNSPECIFIED(&nconnp->conn_laddr_v6) &&
+ (isv4mapped ||
+ connp->conn_lport == nconnp->conn_lport))
+ break;
+
+ pconnp = nconnp;
+ nconnp = nconnp->conn_next;
+ }
+ if (pconnp != NULL) {
+ pconnp->conn_next = connp;
+ connp->conn_prev = pconnp;
+ } else {
+ connfp->connf_head = connp;
+ }
+ if (nconnp != NULL) {
+ connp->conn_next = nconnp;
+ nconnp->conn_prev = connp;
+ }
+ connp->conn_fanout = connfp;
+ connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+ CONN_INC_REF(connp);
+ mutex_exit(&connfp->connf_lock);
}
/*
@@ -1034,9 +1059,9 @@ ipcl_sctp_hash_insert(conn_t *connp, in_port_t lport)
IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
if (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6) ||
IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
} else {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
}
} else {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
@@ -1205,9 +1230,9 @@ ipcl_bind_insert_v4(conn_t *connp)
if (connp->conn_faddr_v4 != INADDR_ANY) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (protocol == IPPROTO_RSVP)
ill_set_inputfn_all(ipst);
@@ -1219,9 +1244,9 @@ ipcl_bind_insert_v4(conn_t *connp)
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(lport, ipst)];
if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (cl_inet_listen != NULL) {
ASSERT(connp->conn_ipversion == IPV4_VERSION);
@@ -1271,9 +1296,9 @@ ipcl_bind_insert_v6(conn_t *connp)
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
@@ -1283,9 +1308,9 @@ ipcl_bind_insert_v6(conn_t *connp)
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(lport, ipst)];
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (cl_inet_listen != NULL) {
sa_family_t addr_family;
@@ -1416,9 +1441,9 @@ ipcl_conn_insert_v4(conn_t *connp)
if (connp->conn_faddr_v4 != INADDR_ANY) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
}
@@ -1504,9 +1529,9 @@ ipcl_conn_insert_v6(conn_t *connp)
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
}
@@ -2092,6 +2117,7 @@ rawip_conn_constructor(void *buf, void *cdrarg, int kmflags)
connp->conn_flags = IPCL_RAWIPCONN;
connp->conn_proto = IPPROTO_ICMP;
icmp->icmp_connp = connp;
+ rw_init(&icmp->icmp_bpf_lock, NULL, RW_DEFAULT, NULL);
rw_init(&connp->conn_ilg_lock, NULL, RW_DEFAULT, NULL);
connp->conn_ixa = kmem_zalloc(sizeof (ip_xmit_attr_t), kmflags);
if (connp->conn_ixa == NULL)
@@ -2116,6 +2142,7 @@ rawip_conn_destructor(void *buf, void *cdrarg)
mutex_destroy(&connp->conn_lock);
cv_destroy(&connp->conn_cv);
rw_destroy(&connp->conn_ilg_lock);
+ rw_destroy(&icmp->icmp_bpf_lock);
/* Can be NULL if constructor failed */
if (connp->conn_ixa != NULL) {
diff --git a/usr/src/uts/common/inet/ip/ipsecesp.c b/usr/src/uts/common/inet/ip/ipsecesp.c
index c325e8dc26..2ca770ebe9 100644
--- a/usr/src/uts/common/inet/ip/ipsecesp.c
+++ b/usr/src/uts/common/inet/ip/ipsecesp.c
@@ -234,8 +234,7 @@ esp_kstat_init(ipsecesp_stack_t *espstack, netstackid_t stackid)
{
espstack->esp_ksp = kstat_create_netstack("ipsecesp", 0, "esp_stat",
"net", KSTAT_TYPE_NAMED,
- sizeof (esp_kstats_t) / sizeof (kstat_named_t),
- KSTAT_FLAG_PERSISTENT, stackid);
+ sizeof (esp_kstats_t) / sizeof (kstat_named_t), 0, stackid);
if (espstack->esp_ksp == NULL || espstack->esp_ksp->ks_data == NULL)
return (B_FALSE);
diff --git a/usr/src/uts/common/inet/ipclassifier.h b/usr/src/uts/common/inet/ipclassifier.h
index f6466434f6..c3139d9288 100644
--- a/usr/src/uts/common/inet/ipclassifier.h
+++ b/usr/src/uts/common/inet/ipclassifier.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _INET_IPCLASSIFIER_H
@@ -293,7 +294,8 @@ struct conn_s {
conn_ipv6_recvpathmtu : 1, /* IPV6_RECVPATHMTU */
conn_mcbc_bind : 1, /* Bound to multi/broadcast */
- conn_pad_to_bit_31 : 12;
+ conn_reuseport : 1, /* SO_REUSEPORT state */
+ conn_pad_to_bit_31 : 11;
boolean_t conn_blocked; /* conn is flow-controlled */
diff --git a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
index f958ca2261..2a32ccc940 100644
--- a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
+++ b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#if !defined(lint)
@@ -83,6 +83,14 @@ static int ipf_hook6_loop_out __P((hook_event_token_t, hook_data_t,
static int ipf_hook6_loop_in __P((hook_event_token_t, hook_data_t,
void *));
static int ipf_hook6 __P((hook_data_t, int, int, void *));
+static int ipf_hookvndl3v4_in __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v6_in __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v4_out __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v6_out __P((hook_event_token_t, hook_data_t,
+ void *));
extern int ipf_geniter __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *));
extern int ipf_frruleiter __P((void *, int, void *, ipf_stack_t *));
@@ -152,6 +160,16 @@ char *hook6_loop_in_gz = "ipfilter_hook6_loop_in_gz";
char *hook6_loop_out = "ipfilter_hook6_loop_out";
char *hook6_loop_out_gz = "ipfilter_hook6_loop_out_gz";
+/* vnd IPv4/v6 hook names */
+char *hook4_vnd_in = "ipfilter_hookvndl3v4_in";
+char *hook4_vnd_in_gz = "ipfilter_hookvndl3v4_in_gz";
+char *hook6_vnd_in = "ipfilter_hookvndl3v6_in";
+char *hook6_vnd_in_gz = "ipfilter_hookvndl3v6_in_gz";
+char *hook4_vnd_out = "ipfilter_hookvndl3v4_out";
+char *hook4_vnd_out_gz = "ipfilter_hookvndl3v4_out_gz";
+char *hook6_vnd_out = "ipfilter_hookvndl3v6_out";
+char *hook6_vnd_out_gz = "ipfilter_hookvndl3v6_out_gz";
+
/* ------------------------------------------------------------------------ */
/* Function: ipldetach */
/* Returns: int - 0 == success, else error. */
@@ -248,6 +266,31 @@ ipf_stack_t *ifs;
ifs->ifs_ipf_ipv4 = NULL;
}
+ /*
+ * Remove VND hooks
+ */
+ if (ifs->ifs_ipf_vndl3v4 != NULL) {
+ UNDO_HOOK(ifs_ipf_vndl3v4, ifs_hookvndl3v4_physical_in,
+ NH_PHYSICAL_IN, ifs_ipfhookvndl3v4_in);
+ UNDO_HOOK(ifs_ipf_vndl3v4, ifs_hookvndl3v4_physical_out,
+ NH_PHYSICAL_OUT, ifs_ipfhookvndl3v4_out);
+
+ if (net_protocol_release(ifs->ifs_ipf_vndl3v4) != 0)
+ goto detach_failed;
+ ifs->ifs_ipf_vndl3v4 = NULL;
+ }
+
+ if (ifs->ifs_ipf_vndl3v6 != NULL) {
+ UNDO_HOOK(ifs_ipf_vndl3v6, ifs_hookvndl3v6_physical_in,
+ NH_PHYSICAL_IN, ifs_ipfhookvndl3v6_in);
+ UNDO_HOOK(ifs_ipf_vndl3v6, ifs_hookvndl3v6_physical_out,
+ NH_PHYSICAL_OUT, ifs_ipfhookvndl3v6_out);
+
+ if (net_protocol_release(ifs->ifs_ipf_vndl3v6) != 0)
+ goto detach_failed;
+ ifs->ifs_ipf_vndl3v6 = NULL;
+ }
+
#undef UNDO_HOOK
#ifdef IPFDEBUG
@@ -445,6 +488,48 @@ ipf_stack_t *ifs;
}
/*
+ * Add VND INET hooks
+ */
+ ifs->ifs_ipf_vndl3v4 = net_protocol_lookup(id, NHF_VND_INET);
+ if (ifs->ifs_ipf_vndl3v4 == NULL)
+ goto hookup_failed;
+
+ HOOK_INIT_GZ_BEFORE(ifs->ifs_ipfhookvndl3v4_in, ipf_hookvndl3v4_in,
+ hook4_vnd_in, hook4_vnd_in_gz, ifs);
+ HOOK_INIT_GZ_AFTER(ifs->ifs_ipfhookvndl3v4_out, ipf_hookvndl3v4_out,
+ hook4_vnd_out, hook4_vnd_out_gz, ifs);
+ ifs->ifs_hookvndl3v4_physical_in = (net_hook_register(ifs->ifs_ipf_vndl3v4,
+ NH_PHYSICAL_IN, ifs->ifs_ipfhookvndl3v4_in) == 0);
+ if (!ifs->ifs_hookvndl3v4_physical_in)
+ goto hookup_failed;
+
+ ifs->ifs_hookvndl3v4_physical_out = (net_hook_register(ifs->ifs_ipf_vndl3v4,
+ NH_PHYSICAL_OUT, ifs->ifs_ipfhookvndl3v4_out) == 0);
+ if (!ifs->ifs_hookvndl3v4_physical_out)
+ goto hookup_failed;
+
+
+ /*
+ * VND INET6 hooks
+ */
+ ifs->ifs_ipf_vndl3v6 = net_protocol_lookup(id, NHF_VND_INET6);
+ if (ifs->ifs_ipf_vndl3v6 == NULL)
+ goto hookup_failed;
+
+ HOOK_INIT_GZ_BEFORE(ifs->ifs_ipfhookvndl3v6_in, ipf_hookvndl3v6_in,
+ hook6_vnd_in, hook6_vnd_in_gz, ifs);
+ HOOK_INIT_GZ_AFTER(ifs->ifs_ipfhookvndl3v6_out, ipf_hookvndl3v6_out,
+ hook6_vnd_out, hook6_vnd_out_gz, ifs);
+ ifs->ifs_hookvndl3v6_physical_in = (net_hook_register(ifs->ifs_ipf_vndl3v6,
+ NH_PHYSICAL_IN, ifs->ifs_ipfhookvndl3v6_in) == 0);
+ if (!ifs->ifs_hookvndl3v6_physical_in)
+ goto hookup_failed;
+
+ ifs->ifs_hookvndl3v6_physical_out = (net_hook_register(ifs->ifs_ipf_vndl3v6,
+ NH_PHYSICAL_OUT, ifs->ifs_ipfhookvndl3v6_out) == 0);
+ if (!ifs->ifs_hookvndl3v6_physical_out)
+ goto hookup_failed;
+ /*
* Reacquire ipf_global, now it is safe.
*/
WRITE_ENTER(&ifs->ifs_ipf_global);
@@ -1011,7 +1096,6 @@ cred_t *cp;
return ENXIO;
unit = isp->ipfs_minor;
-
/*
* ipf_find_stack returns with a read lock on ifs_ipf_global
*/
@@ -1856,8 +1940,12 @@ frdest_t *fdp;
return (-1);
}
- /* Check the src here, fin_ifp is the src interface. */
- if (!fr_forwarding_enabled((phy_if_t)fin->fin_ifp, net_data_p))
+ /*
+ * If we're forwarding (vs. injecting), check the src here, fin_ifp is
+ * the src interface.
+ */
+ if (fdp != NULL &&
+ !fr_forwarding_enabled((phy_if_t)fin->fin_ifp, net_data_p))
return (-1);
inj = net_inject_alloc(NETINFO_VERSION);
@@ -1924,8 +2012,8 @@ frdest_t *fdp;
inj->ni_physical = net_routeto(net_data_p, sinp, NULL);
}
- /* we're checking the destinatation here */
- if (!fr_forwarding_enabled(inj->ni_physical, net_data_p))
+ /* If we're forwarding (vs. injecting), check the destinatation here. */
+ if (fdp != NULL && !fr_forwarding_enabled(inj->ni_physical, net_data_p))
goto bad_fastroute;
/*
@@ -2045,6 +2133,42 @@ int ipf_hook6_loop_out(hook_event_token_t token, hook_data_t info, void *arg)
}
/* ------------------------------------------------------------------------ */
+/* Function: ipf_hookvndl3_in */
+/* Returns: int - 0 == packet ok, else problem, free packet if not done */
+/* Parameters: event(I) - pointer to event */
+/* info(I) - pointer to hook information for firewalling */
+/* */
+/* The vnd hooks are private hooks to ON. They represents a layer 2 */
+/* datapath generally used to implement virtual machines. The driver sends */
+/* along L3 packets of either type IP or IPv6. The ethertype to distinguish */
+/* them is in the upper 16 bits while the remaining bits are the */
+/* traditional packet hook flags. */
+/* */
+/* They end up calling the appropriate traditional ip hooks. */
+/* ------------------------------------------------------------------------ */
+/*ARGSUSED*/
+int ipf_hookvndl3v4_in(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook4_in(token, info, arg);
+}
+
+int ipf_hookvndl3v6_in(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook6_in(token, info, arg);
+}
+
+/*ARGSUSED*/
+int ipf_hookvndl3v4_out(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook4_out(token, info, arg);
+}
+
+int ipf_hookvndl3v6_out(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook6_out(token, info, arg);
+}
+
+/* ------------------------------------------------------------------------ */
/* Function: ipf_hook4_loop_in */
/* Returns: int - 0 == packet ok, else problem, free packet if not done */
/* Parameters: event(I) - pointer to event */
diff --git a/usr/src/uts/common/inet/ipf/ipf.conf b/usr/src/uts/common/inet/ipf/ipf.conf
index 6b36f9fdbf..f49e024a72 100644
--- a/usr/src/uts/common/inet/ipf/ipf.conf
+++ b/usr/src/uts/common/inet/ipf/ipf.conf
@@ -1,3 +1,8 @@
#
#
name="ipf" parent="pseudo" instance=0;
+
+# Increase the state table limits. fr_statemax should be ~70% of fr_statesize,
+# and both should be prime numbers
+fr_statesize=151007;
+fr_statemax=113279;
diff --git a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
index a239f1c1ca..9aa2478c6a 100644
--- a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
+++ b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
@@ -125,6 +125,10 @@ struct ipf_stack {
hook_t *ifs_ipfhook6_loop_in;
hook_t *ifs_ipfhook6_loop_out;
hook_t *ifs_ipfhook6_nicevents;
+ hook_t *ifs_ipfhookvndl3v4_in;
+ hook_t *ifs_ipfhookvndl3v6_in;
+ hook_t *ifs_ipfhookvndl3v4_out;
+ hook_t *ifs_ipfhookvndl3v6_out;
/* flags to indicate whether hooks are registered. */
boolean_t ifs_hook4_physical_in;
@@ -137,10 +141,16 @@ struct ipf_stack {
boolean_t ifs_hook6_nic_events;
boolean_t ifs_hook6_loopback_in;
boolean_t ifs_hook6_loopback_out;
+ boolean_t ifs_hookvndl3v4_physical_in;
+ boolean_t ifs_hookvndl3v6_physical_in;
+ boolean_t ifs_hookvndl3v4_physical_out;
+ boolean_t ifs_hookvndl3v6_physical_out;
int ifs_ipf_loopback;
net_handle_t ifs_ipf_ipv4;
net_handle_t ifs_ipf_ipv6;
+ net_handle_t ifs_ipf_vndl3v4;
+ net_handle_t ifs_ipf_vndl3v6;
/* ip_auth.c */
int ifs_fr_authsize;
diff --git a/usr/src/uts/common/inet/ipf/solaris.c b/usr/src/uts/common/inet/ipf/solaris.c
index c541f4dddc..5d56debc31 100644
--- a/usr/src/uts/common/inet/ipf/solaris.c
+++ b/usr/src/uts/common/inet/ipf/solaris.c
@@ -625,7 +625,6 @@ ipf_stack_shutdown(const netid_t id, void *arg)
/*
* Destroy things for ipf for one stack.
*/
-/* ARGSUSED */
static void
ipf_stack_destroy_one(const netid_t id, ipf_stack_t *ifs)
{
diff --git a/usr/src/uts/common/inet/ipnet/ipnet.c b/usr/src/uts/common/inet/ipnet/ipnet.c
index 75c30120f6..2f53a48d80 100644
--- a/usr/src/uts/common/inet/ipnet/ipnet.c
+++ b/usr/src/uts/common/inet/ipnet/ipnet.c
@@ -25,6 +25,10 @@
*/
/*
+ * Copyright (c) 2016, Joyent, Inc. All rights reserved.
+ */
+
+/*
* The ipnet device defined here provides access to packets at the IP layer. To
* provide access to packets at this layer it registers a callback function in
* the ip module and when there are open instances of the device ip will pass
@@ -2181,14 +2185,15 @@ ipnet_promisc_add(void *handle, uint_t how, void *data, uintptr_t *mhandle,
int error;
ifp = (ipnetif_t *)handle;
- ns = netstack_find_by_zoneid(ifp->if_zoneid);
- if ((how == DL_PROMISC_PHYS) || (how == DL_PROMISC_MULTI)) {
- error = ipnet_join_allmulti(ifp, ns->netstack_ipnet);
- if (error != 0)
- return (error);
- } else {
+ if (how != DL_PROMISC_PHYS && how != DL_PROMISC_MULTI)
return (EINVAL);
+
+ ns = netstack_find_by_zoneid(ifp->if_zoneid);
+
+ if ((error = ipnet_join_allmulti(ifp, ns->netstack_ipnet)) != 0) {
+ netstack_rele(ns);
+ return (error);
}
ipnet = kmem_zalloc(sizeof (*ipnet), KM_SLEEP);
diff --git a/usr/src/uts/common/inet/iptun/iptun.c b/usr/src/uts/common/inet/iptun/iptun.c
index c933efb470..fb4402dc17 100644
--- a/usr/src/uts/common/inet/iptun/iptun.c
+++ b/usr/src/uts/common/inet/iptun/iptun.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Joyent, Inc. All rights reserved.
*/
/*
@@ -1355,6 +1356,7 @@ iptun_free(iptun_t *iptun)
iptun->iptun_connp = NULL;
}
+ netstack_rele(iptun->iptun_ns);
kmem_cache_free(iptun_cache, iptun);
atomic_dec_32(&iptun_tunnelcount);
}
diff --git a/usr/src/uts/common/inet/rawip_impl.h b/usr/src/uts/common/inet/rawip_impl.h
index 6fb72d1d08..ddb482db78 100644
--- a/usr/src/uts/common/inet/rawip_impl.h
+++ b/usr/src/uts/common/inet/rawip_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -43,6 +44,7 @@ extern "C" {
#include <inet/ip.h>
#include <inet/optcom.h>
#include <inet/tunables.h>
+#include <inet/bpf.h>
/*
* ICMP stack instances
@@ -84,6 +86,10 @@ typedef struct icmp_s {
mblk_t *icmp_fallback_queue_head;
mblk_t *icmp_fallback_queue_tail;
struct sockaddr_storage icmp_delayed_addr;
+
+ krwlock_t icmp_bpf_lock; /* protects icmp_bpf */
+ ip_bpf_insn_t *icmp_bpf_prog; /* SO_ATTACH_FILTER bpf */
+ uint_t icmp_bpf_len;
} icmp_t;
/*
diff --git a/usr/src/uts/common/inet/sockmods/datafilt.c b/usr/src/uts/common/inet/sockmods/datafilt.c
new file mode 100644
index 0000000000..6e1171de46
--- /dev/null
+++ b/usr/src/uts/common/inet/sockmods/datafilt.c
@@ -0,0 +1,116 @@
+/*
+ * 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) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
+ */
+
+/*
+ * This file implements a socketfilter used to deter TCP connections.
+ * To defer a connection means to delay the return of accept(3SOCKET)
+ * until at least one byte is ready to be read(2). This filter may be
+ * applied automatically or programmatically through the use of
+ * soconfig(1M) and setsockopt(3SOCKET).
+ */
+
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/socketvar.h>
+#include <sys/sockfilter.h>
+#include <sys/note.h>
+#include <sys/taskq.h>
+
+#define DATAFILT_MODULE "datafilt"
+
+static struct modlmisc dataf_modlmisc = {
+ &mod_miscops,
+ "Kernel data-ready socket filter"
+};
+
+static struct modlinkage dataf_modlinkage = {
+ MODREV_1,
+ &dataf_modlmisc,
+ NULL
+};
+
+static sof_rval_t
+dataf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
+ void *parg, struct sockaddr *laddr, socklen_t laddrlen,
+ struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
+{
+ _NOTE(ARGUNUSED(handle, ph, parg, laddr, laddrlen, faddr, faddrlen,
+ cookiep));
+ return (SOF_RVAL_DEFER);
+}
+
+static void
+dataf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
+{
+ _NOTE(ARGUNUSED(handle, cookie, cr));
+}
+
+static mblk_t *
+dataf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
+ size_t *lenp)
+{
+ _NOTE(ARGUNUSED(cookie, flags, lenp));
+
+ if (mp != NULL && MBLKL(mp) > 0) {
+ sof_newconn_ready(handle);
+ sof_bypass(handle);
+ }
+
+ return (mp);
+}
+
+static sof_ops_t dataf_ops = {
+ .sofop_attach_passive = dataf_attach_passive_cb,
+ .sofop_detach = dataf_detach_cb,
+ .sofop_data_in = dataf_data_in_cb
+};
+
+int
+_init(void)
+{
+ int err;
+
+ /*
+ * This module is safe to attach even after some preliminary socket
+ * setup calls have taken place. See the comment for SOF_ATT_SAFE.
+ */
+ err = sof_register(SOF_VERSION, DATAFILT_MODULE, &dataf_ops,
+ SOF_ATT_SAFE);
+ if (err != 0)
+ return (err);
+ if ((err = mod_install(&dataf_modlinkage)) != 0)
+ (void) sof_unregister(DATAFILT_MODULE);
+
+ return (err);
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = sof_unregister(DATAFILT_MODULE)) != 0)
+ return (err);
+
+ return (mod_remove(&dataf_modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&dataf_modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/inet/sockmods/sockmod_pfp.c b/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
index 586d7f06f8..76191e93b8 100644
--- a/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
+++ b/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -51,6 +51,7 @@
#include <sys/mac_client.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
+#include <inet/bpf.h>
#include <netpacket/packet.h>
@@ -448,7 +449,7 @@ pfp_packet(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t flag)
buffer = (uchar_t *)mp;
}
rw_enter(&ps->ps_bpflock, RW_READER);
- if (bpf_filter(ps->ps_bpf.bf_insns, buffer,
+ if (ip_bpf_filter((ip_bpf_insn_t *)ps->ps_bpf.bf_insns, buffer,
hdr.mhi_pktsize, buflen) == 0) {
rw_exit(&ps->ps_bpflock);
ps->ps_stats.tp_drops++;
@@ -1336,7 +1337,7 @@ pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name,
const void *optval, socklen_t optlen)
{
struct bpf_program prog;
- struct bpf_insn *fcode;
+ ip_bpf_insn_t *fcode;
struct pfpsock *ps;
struct sock_proto_props sopp;
int error = 0;
@@ -1370,10 +1371,10 @@ pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name,
return (EFAULT);
}
- if (bpf_validate(fcode, (int)prog.bf_len)) {
+ if (ip_bpf_validate(fcode, prog.bf_len)) {
rw_enter(&ps->ps_bpflock, RW_WRITER);
pfp_release_bpf(ps);
- ps->ps_bpf.bf_insns = fcode;
+ ps->ps_bpf.bf_insns = (struct bpf_insn *)fcode;
ps->ps_bpf.bf_len = size;
rw_exit(&ps->ps_bpflock);
diff --git a/usr/src/uts/common/inet/squeue.c b/usr/src/uts/common/inet/squeue.c
index 2e08dc359b..1009f0700f 100644
--- a/usr/src/uts/common/inet/squeue.c
+++ b/usr/src/uts/common/inet/squeue.c
@@ -23,7 +23,7 @@
*/
/*
- * Copyright 2012 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014 Joyent, Inc. All rights reserved.
*/
/*
@@ -61,6 +61,10 @@
* connection are processed on that squeue. The connection ("conn") to
* squeue mapping is stored in "conn_t" member "conn_sqp".
*
+ * If the squeue is not related to TCP/IP, then the value of sqp->sq_isip is
+ * false and it will not have an associated conn_t, which means many aspects of
+ * the system, such as polling and swtiching squeues will not be used.
+ *
* Since the processing of the connection cuts across multiple layers
* but still allows packets for different connnection to be processed on
* other CPU/squeues, squeues are also termed as "Vertical Perimeter" or
@@ -244,7 +248,7 @@ squeue_init(void)
/* ARGSUSED */
squeue_t *
-squeue_create(clock_t wait, pri_t pri)
+squeue_create(clock_t wait, pri_t pri, boolean_t isip)
{
squeue_t *sqp = kmem_cache_alloc(squeue_cache, KM_SLEEP);
@@ -260,11 +264,36 @@ squeue_create(clock_t wait, pri_t pri)
sqp->sq_enter = squeue_enter;
sqp->sq_drain = squeue_drain;
+ sqp->sq_isip = isip;
return (sqp);
}
/*
+ * We need to kill the threads and then clean up. We should VERIFY that
+ * polling is disabled so we don't have to worry about disassociating from
+ * MAC/IP/etc.
+ */
+void
+squeue_destroy(squeue_t *sqp)
+{
+ kt_did_t worker, poll;
+ mutex_enter(&sqp->sq_lock);
+ VERIFY(!(sqp->sq_state & (SQS_POLL_THR_QUIESCED |
+ SQS_POLL_QUIESCE_DONE | SQS_PAUSE | SQS_EXIT)));
+ worker = sqp->sq_worker->t_did;
+ poll = sqp->sq_poll_thr->t_did;
+ sqp->sq_state |= SQS_EXIT;
+ cv_signal(&sqp->sq_poll_cv);
+ cv_signal(&sqp->sq_worker_cv);
+ mutex_exit(&sqp->sq_lock);
+
+ thread_join(poll);
+ thread_join(worker);
+ kmem_cache_free(squeue_cache, sqp);
+}
+
+/*
* Bind squeue worker thread to the specified CPU, given by CPU id.
* If the CPU id value is -1, bind the worker thread to the value
* specified in sq_bind field. If a thread is already bound to a
@@ -475,18 +504,21 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
* Handle squeue switching. More details in the
* block comment at the top of the file
*/
- if (connp->conn_sqp == sqp) {
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
SQUEUE_DBG_SET(sqp, mp, proc, connp,
tag);
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
SQUEUE_DBG_CLEAR(sqp);
- CONN_DEC_REF(connp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc,
connp, ira, SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -513,7 +545,7 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
return;
}
} else {
- if (ira != NULL) {
+ if (sqp->sq_isip == B_TRUE && ira != NULL) {
mblk_t *attrmp;
ASSERT(cnt == 1);
@@ -587,7 +619,8 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
if (!(sqp->sq_state & SQS_REENTER) &&
(process_flag != SQ_FILL) && (sqp->sq_first == NULL) &&
(sqp->sq_run == curthread) && (cnt == 1) &&
- (connp->conn_on_sqp == B_FALSE)) {
+ (sqp->sq_isip == B_FALSE ||
+ connp->conn_on_sqp == B_FALSE)) {
sqp->sq_state |= SQS_REENTER;
mutex_exit(&sqp->sq_lock);
@@ -602,15 +635,21 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
* Handle squeue switching. More details in the
* block comment at the top of the file
*/
- if (connp->conn_sqp == sqp) {
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
+ SQUEUE_DBG_SET(sqp, mp, proc, connp,
+ tag);
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
- CONN_DEC_REF(connp);
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
+ SQUEUE_DBG_CLEAR(sqp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc,
connp, ira, SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -631,7 +670,7 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
#ifdef DEBUG
mp->b_tag = tag;
#endif
- if (ira != NULL) {
+ if (sqp->sq_isip && ira != NULL) {
mblk_t *attrmp;
ASSERT(cnt == 1);
@@ -779,7 +818,7 @@ again:
mp->b_prev = NULL;
/* Is there an ip_recv_attr_t to handle? */
- if (ip_recv_attr_is_mblk(mp)) {
+ if (sqp->sq_isip == B_TRUE && ip_recv_attr_is_mblk(mp)) {
mblk_t *attrmp = mp;
ASSERT(attrmp->b_cont != NULL);
@@ -804,20 +843,25 @@ again:
/*
- * Handle squeue switching. More details in the
- * block comment at the top of the file
+ * Handle squeue switching. More details in the block comment at
+ * the top of the file. non-IP squeues cannot switch, as there
+ * is no conn_t.
*/
- if (connp->conn_sqp == sqp) {
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
SQUEUE_DBG_SET(sqp, mp, proc, connp,
mp->b_tag);
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
- CONN_DEC_REF(connp);
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
+ SQUEUE_DBG_CLEAR(sqp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc, connp, ira,
SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -1051,6 +1095,11 @@ squeue_polling_thread(squeue_t *sqp)
cv_wait(async, lock);
CALLB_CPR_SAFE_END(&cprinfo, lock);
+ if (sqp->sq_state & SQS_EXIT) {
+ mutex_exit(lock);
+ thread_exit();
+ }
+
ctl_state = sqp->sq_state & (SQS_POLL_THR_CONTROL |
SQS_POLL_THR_QUIESCED);
if (ctl_state != 0) {
@@ -1076,6 +1125,9 @@ squeue_polling_thread(squeue_t *sqp)
(SQS_PROC|SQS_POLLING|SQS_GET_PKTS)) ==
(SQS_PROC|SQS_POLLING|SQS_GET_PKTS));
+ /* Only IP related squeues should reach this point */
+ VERIFY(sqp->sq_isip == B_TRUE);
+
poll_again:
sq_rx_ring = sqp->sq_rx_ring;
sq_get_pkts = sq_rx_ring->rr_rx;
@@ -1205,6 +1257,7 @@ squeue_worker_thr_control(squeue_t *sqp)
ill_rx_ring_t *rx_ring;
ASSERT(MUTEX_HELD(&sqp->sq_lock));
+ VERIFY(sqp->sq_isip == B_TRUE);
if (sqp->sq_state & SQS_POLL_RESTART) {
/* Restart implies a previous quiesce. */
@@ -1316,6 +1369,11 @@ squeue_worker(squeue_t *sqp)
for (;;) {
for (;;) {
+ if (sqp->sq_state & SQS_EXIT) {
+ mutex_exit(lock);
+ thread_exit();
+ }
+
/*
* If the poll thread has handed control to us
* we need to break out of the wait.
@@ -1412,6 +1470,7 @@ squeue_synch_enter(conn_t *connp, mblk_t *use_mp)
again:
sqp = connp->conn_sqp;
+ VERIFY(sqp->sq_isip == B_TRUE);
mutex_enter(&sqp->sq_lock);
if (sqp->sq_first == NULL && !(sqp->sq_state & SQS_PROC)) {
@@ -1487,6 +1546,7 @@ void
squeue_synch_exit(conn_t *connp)
{
squeue_t *sqp = connp->conn_sqp;
+ VERIFY(sqp->sq_isip == B_TRUE);
mutex_enter(&sqp->sq_lock);
if (sqp->sq_run == curthread) {
diff --git a/usr/src/uts/common/inet/tcp.h b/usr/src/uts/common/inet/tcp.h
index b2b9973291..6ec2e6b2d7 100644
--- a/usr/src/uts/common/inet/tcp.h
+++ b/usr/src/uts/common/inet/tcp.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 by Delphix. All rights reserved.
*/
@@ -134,6 +134,7 @@ typedef struct tcphdra_s {
struct conn_s;
struct tcp_listen_cnt_s;
+struct tcp_rg_s;
/*
* Control structure for each open TCP stream,
@@ -404,6 +405,13 @@ typedef struct tcp_s {
struct tcp_s *tcp_bind_hash_port; /* tcp_t's bound to the same lport */
struct tcp_s **tcp_ptpbhn;
+ /*
+ * Group of tcp_t entries bound to the same adress and port via
+ * SO_REUSEPORT. The pointer itself is protected by tf_lock in the
+ * containing tcps_bind_fanout slot.
+ */
+ struct tcp_rg_s *tcp_rg_bind;
+
uint_t tcp_maxpsz_multiplier;
uint32_t tcp_lso_max; /* maximum LSO payload */
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index fba7125690..cf046c968e 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013,2014 by Delphix. All rights reserved.
* Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
@@ -1423,6 +1423,21 @@ tcp_free(tcp_t *tcp)
tcp_close_mpp(&tcp->tcp_conn.tcp_eager_conn_ind);
/*
+ * Destroy any association with SO_REUSEPORT group.
+ */
+ if (tcp->tcp_rg_bind != NULL) {
+ /*
+ * This is only necessary for connections which enabled
+ * SO_REUSEPORT but were never bound. Such connections should
+ * be the one and only member of the tcp_rg_tp to which they
+ * have been associated.
+ */
+ VERIFY(tcp_rg_remove(tcp->tcp_rg_bind, tcp));
+ tcp_rg_destroy(tcp->tcp_rg_bind);
+ tcp->tcp_rg_bind = NULL;
+ }
+
+ /*
* If this is a non-STREAM socket still holding on to an upper
* handle, release it. As a result of fallback we might also see
* STREAMS based conns with upper handles, in which case there is
@@ -2054,8 +2069,7 @@ tcp_reinit(tcp_t *tcp)
* structure!
*/
static void
-tcp_reinit_values(tcp)
- tcp_t *tcp;
+tcp_reinit_values(tcp_t *tcp)
{
tcp_stack_t *tcps = tcp->tcp_tcps;
conn_t *connp = tcp->tcp_connp;
diff --git a/usr/src/uts/common/inet/tcp/tcp_bind.c b/usr/src/uts/common/inet/tcp/tcp_bind.c
index c6df39b91e..7ea9dc3413 100644
--- a/usr/src/uts/common/inet/tcp/tcp_bind.c
+++ b/usr/src/uts/common/inet/tcp/tcp_bind.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -55,6 +56,7 @@ static uint32_t tcp_random_anon_port = 1;
static int tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
cred_t *cr);
static in_port_t tcp_get_next_priv_port(const tcp_t *);
+static int tcp_rg_insert(tcp_rg_t *, struct tcp_s *);
/*
* Hash list insertion routine for tcp_t structures. Each hash bucket
@@ -172,6 +174,16 @@ tcp_bind_hash_remove(tcp_t *tcp)
ASSERT(lockp != NULL);
mutex_enter(lockp);
+
+ /* destroy any association with SO_REUSEPORT group */
+ if (tcp->tcp_rg_bind != NULL) {
+ if (tcp_rg_remove(tcp->tcp_rg_bind, tcp)) {
+ /* Last one out turns off the lights */
+ tcp_rg_destroy(tcp->tcp_rg_bind);
+ }
+ tcp->tcp_rg_bind = NULL;
+ }
+
if (tcp->tcp_ptpbhn) {
tcpnext = tcp->tcp_bind_hash_port;
if (tcpnext != NULL) {
@@ -636,13 +648,12 @@ tcp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
}
/*
- * If the "bind_to_req_port_only" parameter is set, if the requested port
- * number is available, return it, If not return 0
+ * If the "bind_to_req_port_only" parameter is set and the requested port
+ * number is available, return it (else return 0).
*
- * If "bind_to_req_port_only" parameter is not set and
- * If the requested port number is available, return it. If not, return
- * the first anonymous port we happen across. If no anonymous ports are
- * available, return 0. addr is the requested local address, if any.
+ * If "bind_to_req_port_only" parameter is not set and the requested port
+ * number is available, return it. If not, return the first anonymous port we
+ * happen across. If no anonymous ports are available, return 0.
*
* In either case, when succeeding update the tcp_t to record the port number
* and insert it in the bind hash table.
@@ -662,6 +673,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
int loopmax;
conn_t *connp = tcp->tcp_connp;
tcp_stack_t *tcps = tcp->tcp_tcps;
+ boolean_t reuseport = connp->conn_reuseport;
/*
* Lookup for free addresses is done in a loop and "loopmax"
@@ -698,6 +710,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
tf_t *tbf;
tcp_t *ltcp;
conn_t *lconnp;
+ boolean_t attempt_reuse = B_FALSE;
lport = htons(port);
@@ -724,6 +737,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
for (; ltcp != NULL; ltcp = ltcp->tcp_bind_hash_port) {
boolean_t not_socket;
boolean_t exclbind;
+ boolean_t addrmatch;
lconnp = ltcp->tcp_connp;
@@ -829,22 +843,35 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
&lconnp->conn_faddr_v6)))
continue;
+ addrmatch = IN6_ARE_ADDR_EQUAL(laddr,
+ &lconnp->conn_bound_addr_v6);
+
+ if (addrmatch && reuseport && bind_to_req_port_only &&
+ (ltcp->tcp_state == TCPS_BOUND ||
+ ltcp->tcp_state == TCPS_LISTEN)) {
+ /*
+ * This entry is bound to the exact same
+ * address and port. If SO_REUSEPORT is set on
+ * the calling socket, attempt to reuse this
+ * binding if it too had SO_REUSEPORT enabled
+ * when it was bound.
+ */
+ attempt_reuse = (ltcp->tcp_rg_bind != NULL);
+ break;
+ }
+
if (!reuseaddr) {
/*
- * No socket option SO_REUSEADDR.
- * If existing port is bound to
- * a non-wildcard IP address
- * and the requesting stream is
- * bound to a distinct
- * different IP addresses
- * (non-wildcard, also), keep
- * going.
+ * No socket option SO_REUSEADDR. If an
+ * existing port is bound to a non-wildcard IP
+ * address and the requesting stream is bound
+ * to a distinct different IP address
+ * (non-wildcard, also), keep going.
*/
if (!V6_OR_V4_INADDR_ANY(*laddr) &&
!V6_OR_V4_INADDR_ANY(
lconnp->conn_bound_addr_v6) &&
- !IN6_ARE_ADDR_EQUAL(laddr,
- &lconnp->conn_bound_addr_v6))
+ !addrmatch)
continue;
if (ltcp->tcp_state >= TCPS_BOUND) {
/*
@@ -859,27 +886,49 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
* socket option SO_REUSEADDR is set on the
* binding tcp_t.
*
- * If two streams are bound to
- * same IP address or both addr
- * and bound source are wildcards
- * (INADDR_ANY), we want to stop
- * searching.
- * We have found a match of IP source
- * address and source port, which is
- * refused regardless of the
- * SO_REUSEADDR setting, so we break.
+ * If two streams are bound to the same IP
+ * address or both addr and bound source are
+ * wildcards (INADDR_ANY), we want to stop
+ * searching. We have found a match of IP
+ * source address and source port, which is
+ * refused regardless of the SO_REUSEADDR
+ * setting, so we break.
*/
- if (IN6_ARE_ADDR_EQUAL(laddr,
- &lconnp->conn_bound_addr_v6) &&
+ if (addrmatch &&
(ltcp->tcp_state == TCPS_LISTEN ||
ltcp->tcp_state == TCPS_BOUND))
break;
}
}
- if (ltcp != NULL) {
+ if (ltcp != NULL && !attempt_reuse) {
/* The port number is busy */
mutex_exit(&tbf->tf_lock);
} else {
+ if (attempt_reuse) {
+ int err;
+ struct tcp_rg_s *rg;
+
+ ASSERT(ltcp != NULL);
+ ASSERT(ltcp->tcp_rg_bind != NULL);
+ ASSERT(tcp->tcp_rg_bind != NULL);
+ ASSERT(ltcp->tcp_rg_bind != tcp->tcp_rg_bind);
+
+ err = tcp_rg_insert(ltcp->tcp_rg_bind, tcp);
+ if (err != 0) {
+ mutex_exit(&tbf->tf_lock);
+ return (0);
+ }
+ /*
+ * Now that the newly-binding socket has joined
+ * the existing reuseport group on ltcp, it
+ * should clean up its own (empty) group.
+ */
+ rg = tcp->tcp_rg_bind;
+ tcp->tcp_rg_bind = ltcp->tcp_rg_bind;
+ VERIFY(tcp_rg_remove(rg, tcp));
+ tcp_rg_destroy(rg);
+ }
+
/*
* This port is ours. Insert in fanout and mark as
* bound to prevent others from getting the port
@@ -944,3 +993,125 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
} while (++count < loopmax);
return (0);
}
+
+/* Max number of members in TCP SO_REUSEPORT group */
+#define TCP_RG_SIZE_MAX 64
+/* Step size when expanding members array */
+#define TCP_RG_SIZE_STEP 2
+
+
+tcp_rg_t *
+tcp_rg_init(tcp_t *tcp)
+{
+ tcp_rg_t *rg;
+ rg = kmem_alloc(sizeof (tcp_rg_t), KM_NOSLEEP|KM_NORMALPRI);
+ if (rg == NULL)
+ return (NULL);
+ rg->tcprg_members = kmem_zalloc(2 * sizeof (tcp_t *),
+ KM_NOSLEEP|KM_NORMALPRI);
+ if (rg->tcprg_members == NULL) {
+ kmem_free(rg, sizeof (tcp_rg_t));
+ return (NULL);
+ }
+
+ mutex_init(&rg->tcprg_lock, NULL, MUTEX_DEFAULT, NULL);
+ rg->tcprg_size = 2;
+ rg->tcprg_count = 1;
+ rg->tcprg_active = 1;
+ rg->tcprg_members[0] = tcp;
+ return (rg);
+}
+
+void
+tcp_rg_destroy(tcp_rg_t *rg)
+{
+ mutex_enter(&rg->tcprg_lock);
+ ASSERT(rg->tcprg_count == 0);
+ ASSERT(rg->tcprg_active == 0);
+ kmem_free(rg->tcprg_members, rg->tcprg_size * sizeof (tcp_t *));
+ mutex_destroy(&rg->tcprg_lock);
+ kmem_free(rg, sizeof (struct tcp_rg_s));
+}
+
+static int
+tcp_rg_insert(tcp_rg_t *rg, tcp_t *tcp)
+{
+ mutex_enter(&rg->tcprg_lock);
+
+ VERIFY(rg->tcprg_size > 0);
+ VERIFY(rg->tcprg_count <= rg->tcprg_size);
+ if (rg->tcprg_count != 0) {
+ cred_t *oldcred = rg->tcprg_members[0]->tcp_connp->conn_cred;
+ cred_t *newcred = tcp->tcp_connp->conn_cred;
+
+ if (crgetuid(oldcred) != crgetuid(newcred) ||
+ crgetzoneid(oldcred) != crgetzoneid(newcred)) {
+ mutex_exit(&rg->tcprg_lock);
+ return (EPERM);
+ }
+ }
+
+ if (rg->tcprg_count == rg->tcprg_size) {
+ unsigned int oldalloc = rg->tcprg_size * sizeof (tcp_t *);
+ unsigned int newsize = rg->tcprg_size + TCP_RG_SIZE_STEP;
+ tcp_t **newmembers;
+
+ if (newsize > TCP_RG_SIZE_MAX) {
+ mutex_exit(&rg->tcprg_lock);
+ return (EINVAL);
+ }
+ newmembers = kmem_zalloc(newsize * sizeof (tcp_t *),
+ KM_NOSLEEP|KM_NORMALPRI);
+ if (newmembers == NULL) {
+ mutex_exit(&rg->tcprg_lock);
+ return (ENOMEM);
+ }
+ bcopy(rg->tcprg_members, newmembers, oldalloc);
+ kmem_free(rg->tcprg_members, oldalloc);
+ rg->tcprg_members = newmembers;
+ rg->tcprg_size = newsize;
+ }
+
+ rg->tcprg_members[rg->tcprg_count] = tcp;
+ rg->tcprg_count++;
+ rg->tcprg_active++;
+
+ mutex_exit(&rg->tcprg_lock);
+ return (0);
+}
+
+boolean_t
+tcp_rg_remove(tcp_rg_t *rg, tcp_t *tcp)
+{
+ int i;
+ boolean_t is_empty;
+
+ mutex_enter(&rg->tcprg_lock);
+ for (i = 0; i < rg->tcprg_count; i++) {
+ if (rg->tcprg_members[i] == tcp)
+ break;
+ }
+ /* The item should be present */
+ ASSERT(i < rg->tcprg_count);
+ /* Move the last member into this position */
+ rg->tcprg_count--;
+ rg->tcprg_members[i] = rg->tcprg_members[rg->tcprg_count];
+ rg->tcprg_members[rg->tcprg_count] = NULL;
+ if (tcp->tcp_connp->conn_reuseport != 0)
+ rg->tcprg_active--;
+ is_empty = (rg->tcprg_count == 0);
+ mutex_exit(&rg->tcprg_lock);
+ return (is_empty);
+}
+
+void
+tcp_rg_setactive(tcp_rg_t *rg, boolean_t is_active)
+{
+ mutex_enter(&rg->tcprg_lock);
+ if (is_active) {
+ rg->tcprg_active++;
+ } else {
+ rg->tcprg_active--;
+ }
+ mutex_exit(&rg->tcprg_lock);
+}
diff --git a/usr/src/uts/common/inet/tcp/tcp_opt_data.c b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
index 1a5363bedc..50d97b6ea2 100644
--- a/usr/src/uts/common/inet/tcp/tcp_opt_data.c
+++ b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -62,7 +63,8 @@ opdes_t tcp_opt_arr[] = {
{ SO_USELOOPBACK, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0
},
{ SO_BROADCAST, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
-{ SO_REUSEADDR, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEADDR, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEPORT, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
{ SO_OOBINLINE, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
{ SO_TYPE, SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
{ SO_SNDBUF, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
@@ -484,6 +486,104 @@ tcp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr)
}
/*
+ * Set a TCP connection's participation in SO_REUSEPORT. This operation is
+ * performed under the protection of the squeue via tcp_setsockopt.
+ * The manipulation of tcp_rg_bind, as part of this operation, is subject to
+ * these constraints:
+ * 1. Prior to bind(), tcp_rg_bind can be set/cleared in tcp_set_reuseport
+ * under the protection of the squeue.
+ * 2. Once the connection has been bound, the tcp_rg_bind pointer must not be
+ * altered until such time as tcp_free() cleans up the connection.
+ * 3. A connection undergoing bind, which matches to a connection participating
+ * in port-reuse, will switch its tcp_rg_bind pointer when it joins the
+ * group of an existing connection in tcp_bindi().
+ */
+static int
+tcp_set_reuseport(conn_t *connp, boolean_t do_enable)
+{
+ tcp_t *tcp = connp->conn_tcp;
+ struct tcp_rg_s *rg;
+
+ if (!IPCL_IS_NONSTR(connp)) {
+ if (do_enable) {
+ /*
+ * SO_REUSEPORT cannot be enabled on sockets which have
+ * fallen back to the STREAMS API.
+ */
+ return (EINVAL);
+ } else {
+ /*
+ * A connection with SO_REUSEPORT enabled should be
+ * prevented from falling back to STREAMS mode via
+ * logic in tcp_fallback. It is legal, however, for
+ * fallen-back connections to affirm the disabled state
+ * of SO_REUSEPORT.
+ */
+ ASSERT(connp->conn_reuseport == 0);
+ return (0);
+ }
+ }
+ if (tcp->tcp_state <= TCPS_CLOSED) {
+ return (EINVAL);
+ }
+ if (connp->conn_reuseport == 0 && do_enable) {
+ /* disabled -> enabled */
+ if (tcp->tcp_rg_bind != NULL) {
+ tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+ } else {
+ /*
+ * Connection state is not a concern when initially
+ * populating tcp_rg_bind. Setting it to non-NULL on a
+ * bound or listening connection would only mean that
+ * new reused-port binds become a possibility.
+ */
+ if ((rg = tcp_rg_init(tcp)) == NULL) {
+ return (ENOMEM);
+ }
+ tcp->tcp_rg_bind = rg;
+ }
+ connp->conn_reuseport = 1;
+ } else if (connp->conn_reuseport != 0 && !do_enable) {
+ /* enabled -> disabled */
+ ASSERT(tcp->tcp_rg_bind != NULL);
+ if (tcp->tcp_state == TCPS_IDLE) {
+ /*
+ * If the connection has not been bound yet, discard
+ * the reuse group state. Since disabling SO_REUSEPORT
+ * on a bound socket will _not_ prevent others from
+ * reusing the port, the presence of tcp_rg_bind is
+ * used to determine reuse availability, not
+ * conn_reuseport.
+ *
+ * This allows proper behavior for examples such as:
+ *
+ * setsockopt(fd1, ... SO_REUSEPORT, &on_val...);
+ * bind(fd1, &myaddr, ...);
+ * setsockopt(fd1, ... SO_REUSEPORT, &off_val...);
+ *
+ * setsockopt(fd2, ... SO_REUSEPORT, &on_val...);
+ * bind(fd2, &myaddr, ...); // <- SHOULD SUCCEED
+ *
+ */
+ rg = tcp->tcp_rg_bind;
+ tcp->tcp_rg_bind = NULL;
+ VERIFY(tcp_rg_remove(rg, tcp));
+ tcp_rg_destroy(rg);
+ } else {
+ /*
+ * If a connection has been bound, it's no longer safe
+ * to manipulate tcp_rg_bind until connection clean-up
+ * during tcp_free. Just mark the member status of the
+ * connection as inactive.
+ */
+ tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+ }
+ connp->conn_reuseport = 0;
+ }
+ return (0);
+}
+
+/*
* We declare as 'int' rather than 'void' to satisfy pfi_t arg requirements.
* Parameters are assumed to be verified by the caller.
*/
@@ -653,6 +753,11 @@ tcp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
}
*outlenp = inlen;
return (0);
+ case SO_REUSEPORT:
+ if (!checkonly) {
+ return (tcp_set_reuseport(connp, *i1 != 0));
+ }
+ return (0);
}
break;
case IPPROTO_TCP:
@@ -769,14 +874,37 @@ tcp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
if (*i1 == 0) {
return (EINVAL);
} else if (tcp->tcp_ka_rinterval == 0) {
- if ((tcp->tcp_ka_abort_thres / *i1) <
- tcp->tcp_rto_min ||
- (tcp->tcp_ka_abort_thres / *i1) >
- tcp->tcp_rto_max)
- return (EINVAL);
+ /*
+ * When TCP_KEEPCNT is specified without first
+ * specifying a TCP_KEEPINTVL, we infer an
+ * interval based on a tunable specific to our
+ * stack: the tcp_keepalive_abort_interval.
+ * (Or the TCP_KEEPALIVE_ABORT_THRESHOLD, in
+ * the unlikely event that that has been set.)
+ * Given the abort interval's default value of
+ * 480 seconds, low TCP_KEEPCNT values can
+ * result in intervals that exceed the default
+ * maximum RTO of 60 seconds. Rather than
+ * fail in these cases, we (implicitly) clamp
+ * the interval at the maximum RTO; if the
+ * TCP_KEEPCNT is shortly followed by a
+ * TCP_KEEPINTVL (as we expect), the abort
+ * threshold will be recalculated correctly --
+ * and if a TCP_KEEPINTVL is not forthcoming,
+ * keep-alive will at least operate reasonably
+ * given the underconfigured state.
+ */
+ uint32_t interval;
+
+ interval = tcp->tcp_ka_abort_thres / *i1;
- tcp->tcp_ka_rinterval =
- tcp->tcp_ka_abort_thres / *i1;
+ if (interval < tcp->tcp_rto_min)
+ interval = tcp->tcp_rto_min;
+
+ if (interval > tcp->tcp_rto_max)
+ interval = tcp->tcp_rto_max;
+
+ tcp->tcp_ka_rinterval = interval;
} else {
if ((*i1 * tcp->tcp_ka_rinterval) <
tcps->tcps_keepalive_abort_interval_low ||
@@ -953,10 +1081,6 @@ tcp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
}
break;
case IPPROTO_IP:
- if (connp->conn_family != AF_INET) {
- *outlenp = 0;
- return (EINVAL);
- }
switch (name) {
case IP_SEC_OPT:
/*
diff --git a/usr/src/uts/common/inet/tcp/tcp_socket.c b/usr/src/uts/common/inet/tcp/tcp_socket.c
index a431bf63d1..8f535a5dd1 100644
--- a/usr/src/uts/common/inet/tcp/tcp_socket.c
+++ b/usr/src/uts/common/inet/tcp/tcp_socket.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/* This file contains all TCP kernel socket related functions. */
@@ -1022,6 +1023,16 @@ tcp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
}
/*
+ * Do not allow fallback on connections making use of SO_REUSEPORT.
+ */
+ if (tcp->tcp_rg_bind != NULL) {
+ freeb(stropt_mp);
+ freeb(ordrel_mp);
+ squeue_synch_exit(connp);
+ return (EINVAL);
+ }
+
+ /*
* Both endpoints must be of the same type (either STREAMS or
* non-STREAMS) for fusion to be enabled. So if we are fused,
* we have to unfuse.
diff --git a/usr/src/uts/common/inet/tcp_impl.h b/usr/src/uts/common/inet/tcp_impl.h
index ab7ffa4594..cb83b91fad 100644
--- a/usr/src/uts/common/inet/tcp_impl.h
+++ b/usr/src/uts/common/inet/tcp_impl.h
@@ -61,9 +61,9 @@ extern sock_downcalls_t sock_tcp_downcalls;
* by setting it to 0.
*/
#define TCP_XMIT_LOWATER 4096
-#define TCP_XMIT_HIWATER 49152
+#define TCP_XMIT_HIWATER 128000
#define TCP_RECV_LOWATER 2048
-#define TCP_RECV_HIWATER 128000
+#define TCP_RECV_HIWATER 1048576
/*
* Bind hash list size and has function. It has to be a power of 2 for
@@ -406,6 +406,22 @@ typedef struct tcp_listen_cnt_s {
uint32_t tlc_drop;
} tcp_listen_cnt_t;
+/*
+ * Track tcp_t entities bound to the same port/address tuple via SO_REUSEPORT.
+ * - tcprg_lock: Protects the other fields
+ * - tcprg_size: Allocated size (in entries) of tcprg_members array
+ * - tcprg_count: Count of occupied tcprg_members slots
+ * - tcprg_active: Count of members which still have SO_REUSEPORT set
+ * - tcprg_members: Connections associated with address/port group
+ */
+typedef struct tcp_rg_s {
+ kmutex_t tcprg_lock;
+ unsigned int tcprg_size;
+ unsigned int tcprg_count;
+ unsigned int tcprg_active;
+ tcp_t **tcprg_members;
+} tcp_rg_t;
+
#define TCP_TLC_REPORT_INTERVAL (30 * MINUTES)
#define TCP_DECR_LISTEN_CNT(tcp) \
@@ -649,6 +665,10 @@ extern in_port_t tcp_bindi(tcp_t *, in_port_t, const in6_addr_t *,
int, boolean_t, boolean_t, boolean_t);
extern in_port_t tcp_update_next_port(in_port_t, const tcp_t *,
boolean_t);
+extern tcp_rg_t *tcp_rg_init(tcp_t *);
+extern boolean_t tcp_rg_remove(tcp_rg_t *, tcp_t *);
+extern void tcp_rg_destroy(tcp_rg_t *);
+extern void tcp_rg_setactive(tcp_rg_t *, boolean_t);
/*
* Fusion related functions in tcp_fusion.c.
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index 5a15aea4de..a88bac932c 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -22,6 +22,7 @@
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -76,7 +77,8 @@
#include <inet/ipclassifier.h>
#include <sys/squeue_impl.h>
#include <inet/ipnet.h>
-#include <sys/ethernet.h>
+#include <sys/vxlan.h>
+#include <inet/inet_hash.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tnet.h>
@@ -346,6 +348,89 @@ void (*cl_inet_unbind)(netstackid_t stack_id, uint8_t protocol,
typedef union T_primitives *t_primp_t;
/*
+ * Various protocols that encapsulate UDP have no real use for the source port.
+ * Instead, they want to vary the source port to provide better equal-cost
+ * multipathing and other systems that use fanout. Consider something like
+ * VXLAN. If you're actually sending multiple different streams to a single
+ * host, if you don't vary the source port, then the tuple of ( SRC IP, DST IP,
+ * SRC Port, DST Port) will always be the same.
+ *
+ * Here, we return a port to hash this to, if we know how to hash it. If for
+ * some reason we can't perform an L4 hash, then we just return the default
+ * value, usually the default port. After we determine the hash we transform it
+ * so that it's in the range of [ min, max ].
+ *
+ * We'd like to avoid a pull up for the sake of performing the hash. If the
+ * first mblk_t doesn't have the full protocol header, then we just send it to
+ * the default. If for some reason we have an encapsulated packet that has its
+ * protocol header in different parts of an mblk_t, then we'll go with the
+ * default port. This means that that if a driver isn't consistent about how it
+ * generates the frames for a given flow, it will not always be consistently
+ * hashed. That should be an uncommon event.
+ */
+uint16_t
+udp_srcport_hash(mblk_t *mp, int type, uint16_t min, uint16_t max,
+ uint16_t def)
+{
+ size_t szused = 0;
+ struct ether_header *ether;
+ struct ether_vlan_header *vether;
+ ip6_t *ip6h;
+ ipha_t *ipha;
+ uint16_t sap;
+ uint64_t hash;
+ uint32_t mod;
+
+ ASSERT(min <= max);
+
+ if (type != UDP_HASH_VXLAN)
+ return (def);
+
+ if (!IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t)))
+ return (def);
+
+ /*
+ * The following logic is VXLAN specific to get at the header, if we
+ * have formats, eg. GENEVE, then we should ignore this.
+ *
+ * The kernel overlay device often puts a first mblk_t for the data
+ * which is just the encap. If so, then we're going to use that and try
+ * to avoid a pull up.
+ */
+ if (MBLKL(mp) == VXLAN_HDR_LEN) {
+ if (mp->b_cont == NULL)
+ return (def);
+ mp = mp->b_cont;
+ ether = (struct ether_header *)mp->b_rptr;
+ } else if (MBLKL(mp) < VXLAN_HDR_LEN) {
+ return (def);
+ } else {
+ szused = VXLAN_HDR_LEN;
+ ether = (struct ether_header *)((uintptr_t)mp->b_rptr + szused);
+ }
+
+ /* Can we hold a MAC header? */
+ if (MBLKL(mp) + szused < sizeof (struct ether_header))
+ return (def);
+
+ /*
+ * We need to lie about the starting offset into the message block for
+ * convenience. Undo it at the end. We know that inet_pkt_hash() won't
+ * modify the mblk_t.
+ */
+ mp->b_rptr += szused;
+ hash = inet_pkt_hash(DL_ETHER, mp, INET_PKT_HASH_L2 |
+ INET_PKT_HASH_L3 | INET_PKT_HASH_L4);
+ mp->b_rptr -= szused;
+
+ if (hash == 0)
+ return (def);
+
+ mod = max - min + 1;
+ return ((hash % mod) + min);
+}
+
+/*
* Return the next anonymous port in the privileged port range for
* bind checking.
*
@@ -1583,6 +1668,16 @@ udp_opt_get(conn_t *connp, t_scalar_t level, t_scalar_t name,
*i1 = udp->udp_rcvhdr ? 1 : 0;
mutex_exit(&connp->conn_lock);
return (sizeof (int));
+ case UDP_SRCPORT_HASH:
+ mutex_enter(&connp->conn_lock);
+ *i1 = udp->udp_vxlanhash;
+ mutex_exit(&connp->conn_lock);
+ return (sizeof (int));
+ case UDP_SND_TO_CONNECTED:
+ mutex_enter(&connp->conn_lock);
+ *i1 = udp->udp_snd_to_conn ? 1 : 0;
+ mutex_exit(&connp->conn_lock);
+ return (sizeof (int));
}
}
mutex_enter(&connp->conn_lock);
@@ -1718,6 +1813,31 @@ udp_do_opt_set(conn_opt_arg_t *coa, int level, int name,
udp->udp_rcvhdr = onoff;
mutex_exit(&connp->conn_lock);
return (0);
+ case UDP_SRCPORT_HASH:
+ /*
+ * This should have already been verified, but double
+ * check.
+ */
+ if ((error = secpolicy_ip_config(cr, B_FALSE)) != 0) {
+ return (error);
+ }
+
+ /* First see if the val is something we understand */
+ if (*i1 != UDP_HASH_DISABLE && *i1 != UDP_HASH_VXLAN)
+ return (EINVAL);
+
+ if (!checkonly) {
+ mutex_enter(&connp->conn_lock);
+ udp->udp_vxlanhash = *i1;
+ mutex_exit(&connp->conn_lock);
+ }
+ /* Fully handled this option. */
+ return (0);
+ case UDP_SND_TO_CONNECTED:
+ mutex_enter(&connp->conn_lock);
+ udp->udp_snd_to_conn = onoff;
+ mutex_exit(&connp->conn_lock);
+ return (0);
}
break;
}
@@ -2001,13 +2121,25 @@ udp_prepend_hdr(conn_t *connp, ip_xmit_attr_t *ixa, const ip_pkt_t *ipp,
uint32_t cksum;
udp_t *udp = connp->conn_udp;
boolean_t insert_spi = udp->udp_nat_t_endpoint;
+ boolean_t hash_srcport = udp->udp_vxlanhash;
uint_t ulp_hdr_len;
+ uint16_t srcport;
data_len = msgdsize(data_mp);
ulp_hdr_len = UDPH_SIZE;
if (insert_spi)
ulp_hdr_len += sizeof (uint32_t);
+ /*
+ * If we have source port hashing going on, determine the hash before
+ * we modify the mblk_t.
+ */
+ if (hash_srcport == B_TRUE) {
+ srcport = udp_srcport_hash(mp, UDP_HASH_VXLAN,
+ IPPORT_DYNAMIC_MIN, IPPORT_DYNAMIC_MAX,
+ ntohs(connp->conn_lport));
+ }
+
mp = conn_prepend_hdr(ixa, ipp, v6src, v6dst, IPPROTO_UDP, flowinfo,
ulp_hdr_len, data_mp, data_len, us->us_wroff_extra, &cksum, errorp);
if (mp == NULL) {
@@ -2019,7 +2151,11 @@ udp_prepend_hdr(conn_t *connp, ip_xmit_attr_t *ixa, const ip_pkt_t *ipp,
ixa->ixa_pktlen = data_len + ixa->ixa_ip_hdr_length;
udpha = (udpha_t *)(mp->b_rptr + ixa->ixa_ip_hdr_length);
- udpha->uha_src_port = connp->conn_lport;
+ if (hash_srcport == B_TRUE) {
+ udpha->uha_src_port = htons(srcport);
+ } else {
+ udpha->uha_src_port = connp->conn_lport;
+ }
udpha->uha_dst_port = dstport;
udpha->uha_checksum = 0;
udpha->uha_length = htons(data_len);
@@ -3194,6 +3330,7 @@ udp_prepend_header_template(conn_t *connp, ip_xmit_attr_t *ixa, mblk_t *mp,
udp_t *udp = connp->conn_udp;
udp_stack_t *us = udp->udp_us;
boolean_t insert_spi = udp->udp_nat_t_endpoint;
+ boolean_t hash_srcport = udp->udp_vxlanhash;
uint_t pktlen;
uint_t alloclen;
uint_t copylen;
@@ -3202,10 +3339,21 @@ udp_prepend_header_template(conn_t *connp, ip_xmit_attr_t *ixa, mblk_t *mp,
udpha_t *udpha;
uint32_t cksum;
ip_pkt_t *ipp;
+ uint16_t srcport;
ASSERT(MUTEX_HELD(&connp->conn_lock));
/*
+ * If we have source port hashing going on, determine the hash before
+ * we modify the mblk_t.
+ */
+ if (hash_srcport == B_TRUE) {
+ srcport = udp_srcport_hash(mp, UDP_HASH_VXLAN,
+ IPPORT_DYNAMIC_MIN, IPPORT_DYNAMIC_MAX,
+ ntohs(connp->conn_lport));
+ }
+
+ /*
* Copy the header template and leave space for an SPI
*/
copylen = connp->conn_ht_iphc_len;
@@ -3303,6 +3451,9 @@ udp_prepend_header_template(conn_t *connp, ip_xmit_attr_t *ixa, mblk_t *mp,
*((uint32_t *)(udpha + 1)) = 0;
udpha->uha_dst_port = dstport;
+ if (hash_srcport == B_TRUE)
+ udpha->uha_src_port = htons(srcport);
+
return (mp);
}
@@ -5947,10 +6098,18 @@ udp_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
else
return (error);
}
- if (udp->udp_state == TS_DATA_XFER) {
+
+ /*
+ * Check if we're allowed to send to a connection on which we've
+ * already called 'connect'. The posix spec. allows both behaviors but
+ * historically we've returned an error if already connected. The
+ * client can allow this via a sockopt.
+ */
+ if (udp->udp_state == TS_DATA_XFER && !udp->udp_snd_to_conn) {
UDPS_BUMP_MIB(us, udpOutErrors);
return (EISCONN);
}
+
error = proto_verify_ip_addr(connp->conn_family,
(struct sockaddr *)msg->msg_name, msg->msg_namelen);
if (error != 0) {
diff --git a/usr/src/uts/common/inet/udp/udp_opt_data.c b/usr/src/uts/common/inet/udp/udp_opt_data.c
index c279bb4a21..847e2cdde6 100644
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -292,6 +293,9 @@ opdes_t udp_opt_arr[] = {
},
{ UDP_NAT_T_ENDPOINT, IPPROTO_UDP, OA_RW, OA_RW, OP_PRIVPORT, 0, sizeof (int),
0 },
+{ UDP_SRCPORT_HASH, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int), 0 },
+{ UDP_SND_TO_CONNECTED, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int),
+ 0 }
};
/*
diff --git a/usr/src/uts/common/inet/udp_impl.h b/usr/src/uts/common/inet/udp_impl.h
index 6a31ce5c22..ebba10c0f7 100644
--- a/usr/src/uts/common/inet/udp_impl.h
+++ b/usr/src/uts/common/inet/udp_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _UDP_IMPL_H
@@ -178,8 +179,12 @@ typedef struct udp_s {
udp_issocket : 1, /* socket mode; sockfs is on top */
udp_nat_t_endpoint : 1, /* UDP_NAT_T_ENDPOINT option */
udp_rcvhdr : 1, /* UDP_RCVHDR option */
+ udp_vxlanhash: 1, /* UDP_SRCPORT_HASH option */
+ /* Because there's only VXLAN, cheat */
+ /* and only use a single bit */
+ udp_snd_to_conn: 1, /* UDP_SND_TO_CONNECTED option */
- udp_pad_to_bit_31 : 29;
+ udp_pad_to_bit_31 : 27;
/* Following 2 fields protected by the uf_lock */
struct udp_s *udp_bind_hash; /* Bind hash chain */
diff --git a/usr/src/uts/common/io/aggr/aggr_port.c b/usr/src/uts/common/io/aggr/aggr_port.c
index 00545d2c03..a39110255a 100644
--- a/usr/src/uts/common/io/aggr/aggr_port.c
+++ b/usr/src/uts/common/io/aggr/aggr_port.c
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 OmniTI Computer Consulting, Inc All rights reserved.
*/
/*
@@ -528,8 +529,13 @@ aggr_port_promisc(aggr_port_t *port, boolean_t on)
if (on) {
mac_rx_clear(port->lp_mch);
+ /* We use the promisc callback because without hardware
+ * rings, we deliver through flows that will cause duplicate
+ * delivery of packets when we've flipped into this mode
+ * to compensate for the lack of hardware MAC matching
+ */
rc = mac_promisc_add(port->lp_mch, MAC_CLIENT_PROMISC_ALL,
- aggr_recv_cb, port, &port->lp_mphp,
+ aggr_recv_promisc_cb, port, &port->lp_mphp,
MAC_PROMISC_FLAGS_NO_TX_LOOP);
if (rc != 0) {
mac_rx_set(port->lp_mch, aggr_recv_cb, port);
diff --git a/usr/src/uts/common/io/aggr/aggr_recv.c b/usr/src/uts/common/io/aggr/aggr_recv.c
index 2bdb7872e3..0dfe234b70 100644
--- a/usr/src/uts/common/io/aggr/aggr_recv.c
+++ b/usr/src/uts/common/io/aggr/aggr_recv.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 OmniTI Computer Consulting, Inc All rights reserved.
*/
/*
@@ -68,16 +69,27 @@ aggr_recv_lacp(aggr_port_t *port, mac_resource_handle_t mrh, mblk_t *mp)
/*
* Callback function invoked by MAC service module when packets are
- * made available by a MAC port.
+ * made available by a MAC port, both in promisc_on mode and not.
*/
/* ARGSUSED */
-void
-aggr_recv_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
- boolean_t loopback)
+static void
+aggr_recv_path_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
+ boolean_t loopback, boolean_t promisc_path)
{
aggr_port_t *port = (aggr_port_t *)arg;
aggr_grp_t *grp = port->lp_grp;
+ /* In the case where lp_promisc_on has been turned on to
+ * compensate for insufficient hardware MAC matching and
+ * hardware rings are not in use we will fall back to
+ * using flows for delivery which can result in duplicates
+ * pushed up the stack. Only respect the chosen path.
+ */
+ if (port->lp_promisc_on != promisc_path) {
+ freemsgchain(mp);
+ return;
+ }
+
if (grp->lg_lacp_mode == AGGR_LACP_OFF) {
aggr_mac_rx(grp->lg_mh, mrh, mp);
} else {
@@ -161,3 +173,19 @@ aggr_recv_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
}
}
}
+
+/* ARGSUSED */
+void
+aggr_recv_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
+ boolean_t loopback)
+{
+ aggr_recv_path_cb(arg, mrh, mp, loopback, B_FALSE);
+}
+
+/* ARGSUSED */
+void
+aggr_recv_promisc_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
+ boolean_t loopback)
+{
+ aggr_recv_path_cb(arg, mrh, mp, loopback, B_TRUE);
+}
diff --git a/usr/src/uts/common/io/axf/ax88172reg.h b/usr/src/uts/common/io/axf/ax88172reg.h
new file mode 100644
index 0000000000..8ca6ebc187
--- /dev/null
+++ b/usr/src/uts/common/io/axf/ax88172reg.h
@@ -0,0 +1,163 @@
+/*
+ * @(#)ax88172reg.h 1.1 09/06/15
+ * Macro definitions for ASIX AX88172 USB to fast ethernet controler
+ * based on ASIX AX88172/88772 data sheet
+ * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com)
+ */
+
+#ifndef __AX88172_H__
+#define __AX88172_H__
+
+/*
+ * Vendor command definitions
+ */
+#define VCMD_READ_SRAM 0x02
+#define VCMD_WRITE_RXSRAM 0x03
+#define VCMD_WRITE_TXSRAM 0x04
+#define VCMD_SOFTWARE_MII_OP 0x06
+#define VCMD_READ_MII_REG 0x07
+#define VCMD_WRITE_MII_REG 0x08
+#define VCMD_READ_MII_OPMODE 0x09
+#define VCMD_HARDWARE_MII_OP 0x0a
+#define VCMD_READ_SROM 0x0b
+#define VCMD_WRITE_SROM 0x0c
+#define VCMD_WRITE_SROM_ENABLE 0x0d
+#define VCMD_WRITE_SROM_DISABLE 0x0e
+#define VCMD_READ_RXCTRL 0x0f
+#define VCMD_WRITE_RXCTRL 0x10
+#define VCMD_READ_IPGS 0x11
+#define VCMD_WRITE_IPG 0x12
+#define VCMD_WRITE_IPG1 0x13
+#define VCMD_WRITE_IPG2 0x14
+#define VCMD_READ_MCAST_FILTER 0x15
+#define VCMD_WRITE_MCAST_FILTER 0x16
+#define VCMD_READ_NODE_ID 0x17
+#define VCMD_READ_PHY_IDS 0x19
+#define VCMD_READ_MEDIUM_STATUS 0x1a
+#define VCMD_WRITE_MEDIUM_STATUS 0x1b
+#define VCMD_SET_MONITOR_MODE 0x1c
+#define VCMD_GET_MONITOR_MODE 0x1d
+#define VCMD_READ_GPIO 0x1e
+#define VCMD_WRITE_GPIO 0x1f
+
+/* ax88772 only, currently not supported */
+#define VCMD_WRITE_IPGS_88772 0x12
+#define VCMD_READ_NODE_ID_88772 0x13
+#define VCMD_WRITE_NODE_ID_88772 0x14
+#define VCMD_WRITE_TEST_REG_88772 0x17
+#define VCMD_SOFTWARE_RESET_88772 0x20
+#define VCMD_READ_PHY_SELECT_88772 0x21
+#define VCMD_WRITE_PHY_SELECT_88772 0x22
+
+
+/*
+ * Register definitions
+ */
+
+/* Rx control register */
+#define RCR_SO 0x80 /* Start Operation */
+#define RCR_AP_88772 0x20 /* accept physical address from mcast filter */
+#define RCR_AM 0x10 /* accept multicast address */
+#define RCR_AB 0x08 /* accept broadcast address */
+#define RCR_SEP 0x04 /* save error packet */
+#define RCR_AMALL 0x02 /* accept all multicast address */
+#define RCR_PRO 0x01 /* promiscious, all frames received */
+
+#define RCR_MFB 0x0300
+#define RCR_MFB_SHIFT 8
+#define RCR_MFB_2K (0U << RCR_MFB_SHIFT)
+#define RCR_MFB_4K (1U << RCR_MFB_SHIFT)
+#define RCR_MFB_8K (2U << RCR_MFB_SHIFT)
+#define RCR_MFB_16K (3U << RCR_MFB_SHIFT)
+
+#define RCR_BITS \
+ "\020" \
+ "\010SO" \
+ "\006AP" \
+ "\005AM" \
+ "\004AB" \
+ "\003SEP" \
+ "\002AMALL" \
+ "\001PRO"
+
+/* Medium status register */
+#define MSR_SM 0x1000 /* super mac support */
+#define MSR_SBP 0x0800 /* stop backpressure */
+#define MSR_PS 0x0200 /* port speed in mii mode */
+#define MSR_RE 0x0100 /* rx enable */
+#define MSR_PF 0x0080 /* check only length/type for pause frame */
+#define MSR_JFE 0x0040 /* jumbo frame enable */
+#define MSR_TFC 0x0020 /* tx flow control enable */
+#define MSR_RFC 0x0010 /* rx flow control enable (178) */
+#define MSR_FCEN 0x0010 /* flow control enable (172/772) */
+#define MSR_ENCK 0x0008 /* Enable GTX_CLK and TXC clock output (178) */
+#define MSR_TXABT 0x0004 /* Tx abort allow, always set */
+#define MSR_FDPX 0x0002 /* full duplex */
+#define MSR_GM 0x0001 /* Gigabit mode (178) */
+
+#define MSR_BITS \
+ "\020" \
+ "\015SM" \
+ "\014SBP" \
+ "\012PS" \
+ "\011RE" \
+ "\005FCEN" \
+ "\004ENCK" \
+ "\003TXABT" \
+ "\002FDPX" \
+ "\001GM"
+
+/* monitor mode register */
+#define MMR_RWMP 0x04 /* remote wakeup by magic pkt */
+#define MMR_RWLU 0x02 /* remote wakeup by linkup */
+#define MMR_MOM 0x01 /* monitor mode 1:en, 0:dis */
+
+#define MMR_BITS \
+ "\020" \
+ "\003RWMP" \
+ "\002RWLU" \
+ "\001MOM"
+
+/* GPIO register */
+#define GPIO_RSE 0x80 /* reload serial eeprom (88772)*/
+#define GPIO_DATA2 0x20
+#define GPIO_EN2 0x10
+#define GPIO_DATA1 0x08
+#define GPIO_EN1 0x04
+#define GPIO_DATA0 0x02
+#define GPIO_EN0 0x01
+
+#define GPIO_BITS \
+ "\020" \
+ "\010RSE" \
+ "\006DATA2" \
+ "\005EN2" \
+ "\004DATA1" \
+ "\003EN1" \
+ "\002DATA0" \
+ "\001EN0"
+
+/* Software reset register */
+#define SWRST_IPPD 0x40 /* internal phy power down control */
+#define SWRST_IPRL 0x20 /* internal phy reset control */
+#define SWRST_BZ 0x10 /* force Bulk In to return zero-length pkt */
+#define SWRST_PRL 0x08 /* external phy reset pin level */
+#define SWRST_PRTE 0x04 /* external phy tri-state enable */
+#define SWRST_RT 0x02 /* clear frame length error for Bulk-Out */
+#define SWRST_RR 0x01 /* clear frame length error for Bulk-In */
+
+#define SWRST_BITS \
+ "\020" \
+ "\007IPPD" \
+ "\006IPRL" \
+ "\005BZ" \
+ "\004PRL" \
+ "\003PRTE" \
+ "\002RT" \
+ "\001RR"
+
+/* Software PHY Select Status register */
+#define SPSS_ASEL 0x02 /* 1:auto select 0:manual select */
+#define SPSS_PSEL 0x01 /* 1:intenal phy, 0:external (when ASEL=0) */
+
+#endif /* __AX88172_H__ */
diff --git a/usr/src/uts/common/io/axf/axf_usbgem.c b/usr/src/uts/common/io/axf/axf_usbgem.c
new file mode 100644
index 0000000000..28963f6849
--- /dev/null
+++ b/usr/src/uts/common/io/axf/axf_usbgem.c
@@ -0,0 +1,1539 @@
+/*
+ * axf_usbgem.c : ASIX AX88172/772 USB to Fast Ethernet Driver for Solaris
+ *
+ * Copyright (c) 2004-2012 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#pragma ident "@(#)axf_usbgem.c 1.3 12/02/09"
+
+/*
+ * Changelog:
+ */
+
+/*
+ * TODO
+ * handle RXMODE_ENABLE in set_rx_filter()
+ */
+/* ======================================================= */
+
+/*
+ * Solaris system header files and macros
+ */
+
+/* minimum kernel headers for drivers */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+
+/* ethernet stuff */
+#include <sys/ethernet.h>
+
+/* interface card depend stuff */
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/usb/usba.h>
+#include "usbgem.h"
+
+/* hardware stuff */
+#include "usbgem_mii.h"
+#include "ax88172reg.h"
+
+char ident[] = "ax88x72 usbnic driver v" VERSION;
+
+/*
+ * Useful macros
+ */
+#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label
+#define LE16P(p) ((((uint8_t *)(p))[1] << 8) | ((uint8_t *)(p))[0])
+
+#define AX88172(dp) \
+ (((struct axf_dev *)(dp)->private)->chip->type == CHIP_TYPE_AX88172)
+
+#define AX88772(dp) \
+ (((struct axf_dev *)(dp)->private)->chip->type == CHIP_TYPE_AX88772)
+
+/*
+ * Debugging
+ */
+#ifdef DEBUG_LEVEL
+static int axf_debug = DEBUG_LEVEL;
+#define DPRINTF(n, args) if (axf_debug > (n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+/*
+ * Our configration for ax88172
+ */
+/* timeouts */
+#define ONESEC (drv_usectohz(1*1000000))
+
+/*
+ * RX/TX buffer size
+ */
+
+/*
+ * Local device definitions
+ */
+struct chip_info {
+ uint16_t vid; /* usb vendor id */
+ uint16_t pid; /* usb product id */
+ int type;
+ uint8_t gpio_reset[2];
+ uint8_t gpio_speed[2];
+ uint8_t gpio_duplex[2];
+ char *name;
+#define CHIP_TYPE_AX88172 0
+#define CHIP_TYPE_AX88772 1
+#define CHIP_TYPE_AX88178 2
+};
+
+#define GPIO_DEFAULT {0x00, 0x15}, {0, 0}, {0, 0}
+struct chip_info chiptbl_88x7x[] = {
+/* AX88172 */
+{
+ /* Planex UE2-100TX, Hawking UF200, TrendNet TU2-ET100 */
+ 0x07b8, 0x420a, CHIP_TYPE_AX88172,
+
+ /*
+ * the default setting covers below:
+ * gpio bit2 has to be 0 and gpio bit0 has to be 1
+ */
+ {0, 0},
+ {GPIO_EN1, GPIO_DATA1 | GPIO_EN1},
+ {0, 0},
+ "Planex UE2-100TX", /* tested */
+},
+{
+ 0x2001, 0x1a00, CHIP_TYPE_AX88172,
+ {0x9f, 0x9e}, {0, 0}, {0, 0},
+ "D-Link dube100", /* XXX */
+},
+{
+ 0x077b, 0x2226, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Linksys USB200M",
+},
+{
+ 0x0846, 0x1040, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Netgear FA120",
+},
+{
+ 0x0b95, 0x1720, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Intellinet, ST Lab USB Ethernet",
+},
+{
+ 0x08dd, 0x90ff, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Billionton Systems, USB2AR",
+},
+{
+ 0x0557, 0x2009, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "ATEN UC210T",
+},
+{
+ 0x0411, 0x003d, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Buffalo LUA-U2-KTX",
+},
+{
+ 0x6189, 0x182d, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Sitecom LN-029 USB 2.0 10/100 Ethernet adapter",
+},
+{
+ 0x07aa, 0x0017, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "corega FEther USB2-TX",
+},
+{
+ 0x1189, 0x0893, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "Surecom EP-1427X-2",
+},
+{
+ 0x1631, 0x6200, CHIP_TYPE_AX88172,
+ GPIO_DEFAULT,
+ "goodway corp usb gwusb2e",
+},
+/* AX88772 and AX88178 */
+{
+ 0x13b1, 0x0018, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "Linksys USB200M rev.2",
+},
+{
+ 0x1557, 0x7720, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "0Q0 cable ethernet",
+},
+{
+ 0x07d1, 0x3c05, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "DLink DUB E100 ver B1",
+},
+{
+ 0x2001, 0x3c05, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "DLink DUB E100 ver B1(2)",
+},
+{
+ 0x05ac, 0x1402, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "Apple Ethernet USB Adapter",
+},
+{
+ 0x1737, 0x0039, CHIP_TYPE_AX88178,
+ {0, 0}, {0, 0}, {0, 0},
+ "Linksys USB1000",
+},
+{
+ 0x0411, 0x006e, CHIP_TYPE_AX88178,
+ {0, 0}, {0, 0}, {0, 0},
+ "Buffalo LUA-U2-KGT/LUA-U2-GT",
+},
+{
+ 0x04bb, 0x0930, CHIP_TYPE_AX88178,
+ {0, 0}, {0, 0}, {0, 0},
+ "I/O DATA ETG-US2",
+},
+{
+ 0x050d, 0x5055, CHIP_TYPE_AX88178,
+ {0, 0}, {0, 0}, {0, 0},
+ "Belkin F5D5055",
+},
+{
+ /* generic ax88772 must be the last entry */
+ /* planex UE-200TX-G */
+ 0x0b95, 0x7720, CHIP_TYPE_AX88772,
+ {0, 0}, {0, 0}, {0, 0},
+ "ASIX AX88772/AX88178", /* tested */
+},
+};
+
+#define CHIPTABLESIZE (sizeof (chiptbl_88x7x) / sizeof (struct chip_info))
+
+struct axf_dev {
+ /*
+ * Misc HW information
+ */
+ struct chip_info *chip;
+ uint8_t ipg[3];
+ uint8_t gpio;
+ uint16_t rcr;
+ uint16_t msr;
+ uint8_t last_link_state;
+ boolean_t phy_has_reset;
+};
+
+/*
+ * private functions
+ */
+
+/* mii operations */
+static uint16_t axf_mii_read(struct usbgem_dev *, uint_t, int *errp);
+static void axf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp);
+
+/* nic operations */
+static int axf_reset_chip(struct usbgem_dev *);
+static int axf_init_chip(struct usbgem_dev *);
+static int axf_start_chip(struct usbgem_dev *);
+static int axf_stop_chip(struct usbgem_dev *);
+static int axf_set_media(struct usbgem_dev *);
+static int axf_set_rx_filter(struct usbgem_dev *);
+static int axf_get_stats(struct usbgem_dev *);
+static void axf_interrupt(struct usbgem_dev *, mblk_t *);
+
+/* packet operations */
+static mblk_t *axf_tx_make_packet(struct usbgem_dev *, mblk_t *);
+static mblk_t *axf_rx_make_packet(struct usbgem_dev *, mblk_t *);
+
+/* =============================================================== */
+/*
+ * I/O functions
+ */
+/* =============================================================== */
+#define OUT(dp, req, val, ix, len, buf, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ (req), \
+ /* wValue */ (val), \
+ /* wIndex */ (ix), \
+ /* wLength */ (len), \
+ /* value */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+#define IN(dp, req, val, ix, len, buf, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ (req), \
+ /* wValue */ (val), \
+ /* wIndex */ (ix), \
+ /* wLength */ (len), \
+ /* valuep */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+/* =============================================================== */
+/*
+ * Hardware manupilation
+ */
+/* =============================================================== */
+static int
+axf_reset_phy(struct usbgem_dev *dp)
+{
+ uint8_t phys[2];
+ uint8_t val8;
+ int err;
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ if (AX88172(dp)) {
+ delay(drv_usectohz(5000));
+ IN(dp, VCMD_READ_GPIO, 0, 0, 1, &val8, &err, usberr);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: gpio 0x%b",
+ dp->name, __func__, val8, GPIO_BITS));
+
+ /* reset MII PHY */
+ val8 = lp->chip->gpio_reset[1]
+ | lp->chip->gpio_speed[dp->speed]
+ | lp->chip->gpio_duplex[dp->full_duplex];
+
+ OUT(dp, VCMD_WRITE_GPIO,
+ val8, 0, 0, NULL, &err, usberr);
+ delay(drv_usectohz(5000));
+
+ val8 = lp->chip->gpio_reset[0]
+ | lp->chip->gpio_speed[dp->speed]
+ | lp->chip->gpio_duplex[dp->full_duplex];
+
+ OUT(dp, VCMD_WRITE_GPIO,
+ val8, 0, 0, NULL, &err, usberr);
+ delay(drv_usectohz(5000));
+ } else {
+ lp->gpio = GPIO_RSE | GPIO_DATA2 | GPIO_EN2;
+ OUT(dp, VCMD_WRITE_GPIO, lp->gpio, 0,
+ 0, NULL, &err, usberr);
+ drv_usecwait(1000);
+
+ OUT(dp, VCMD_WRITE_PHY_SELECT_88772,
+ dp->mii_phy_addr == 16 ? 1 : 0, 0, 0, NULL, &err, usberr);
+
+ OUT(dp, VCMD_SOFTWARE_RESET_88772,
+ SWRST_IPPD | SWRST_PRL, 0, 0, NULL, &err, usberr);
+ delay(drv_usectohz(150*1000));
+ OUT(dp, VCMD_SOFTWARE_RESET_88772,
+ 0, 0, 0, NULL, &err, usberr);
+
+ OUT(dp, VCMD_SOFTWARE_RESET_88772,
+ dp->mii_phy_addr == 16 ? SWRST_IPRL : SWRST_PRTE,
+ 0, 0, NULL, &err, usberr);
+ delay(drv_usectohz(150*1000));
+ }
+
+
+ return (USB_SUCCESS);
+
+usberr:
+ return (USB_FAILURE);
+}
+
+static int
+axf_reset_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+
+ if (AX88172(dp)) {
+ /* there are no ways to reset nic */
+ return (USB_SUCCESS);
+ }
+#ifdef NEVER
+ OUT(dp, VCMD_SOFTWARE_RESET_88772,
+ SWRST_RR | SWRST_RT, 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_SOFTWARE_RESET_88772,
+ 0, 0, 0, NULL, &err, usberr);
+usberr:
+#endif
+ return (err);
+}
+
+/*
+ * Setup ax88172
+ */
+static int
+axf_init_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint32_t val;
+ int err = USB_SUCCESS;
+ uint16_t reg;
+ uint8_t buf[2];
+ uint16_t tmp16;
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* rx conrol register: read default value */
+ if (!AX88172(dp)) {
+ /* clear rx control */
+ OUT(dp, VCMD_WRITE_RXCTRL, 0, 0, 0, NULL, &err, usberr);
+ }
+
+ IN(dp, VCMD_READ_RXCTRL, 0, 0, 2, buf, &err, usberr);
+ lp->rcr = LE16P(buf);
+ DPRINTF(0, (CE_CONT, "!%s: %s: rcr(default):%b",
+ dp->name, __func__, lp->rcr, RCR_BITS));
+
+ lp->rcr &= ~RCR_SO;
+
+ /* Media status register */
+ if (AX88172(dp)) {
+#ifdef notdef
+ lp->msr = MSR_TXABT;
+#else
+ lp->msr = 0;
+#endif
+ } else {
+ lp->msr = MSR_RE | MSR_TXABT;
+ }
+ DPRINTF(0, (CE_CONT, "!%s: %s: msr:%b",
+ dp->name, __func__, lp->msr, MSR_BITS));
+ err = axf_set_media(dp);
+ CHECK_AND_JUMP(err, usberr);
+
+ /* write IPG0-2 registers */
+ if (AX88172(dp)) {
+ OUT(dp, VCMD_WRITE_IPG, lp->ipg[0], 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_IPG1, lp->ipg[1], 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_IPG2, lp->ipg[2], 0, 0, NULL, &err, usberr);
+ } else {
+ /* EMPTY */
+ }
+#ifdef ENABLE_RX_IN_INIT_CHIP
+ /* enable Rx */
+ lp->rcr |= RCR_SO;
+ OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr);
+#endif
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+axf_start_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ struct axf_dev *lp = dp->private;
+#ifndef ENABLE_RX_IN_INIT_CHIP
+ /* enable Rx */
+ lp->rcr |= RCR_SO;
+ OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+#endif
+ return (err);
+}
+
+static int
+axf_stop_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ struct axf_dev *lp = dp->private;
+
+ /* Disable Rx */
+ lp->rcr &= ~RCR_SO;
+ OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr);
+
+ /*
+ * Restore factory mac address
+ * if we have changed current mac address
+ */
+ if (!AX88172(dp) &&
+ bcmp(dp->dev_addr.ether_addr_octet,
+ dp->cur_addr.ether_addr_octet,
+ ETHERADDRL) != 0) {
+ OUT(dp, VCMD_WRITE_NODE_ID_88772, 0, 0,
+ ETHERADDRL, dp->cur_addr.ether_addr_octet, &err, usberr);
+ }
+usberr:
+ return (axf_reset_chip(dp));
+}
+
+static int
+axf_get_stats(struct usbgem_dev *dp)
+{
+ /* EMPTY */
+ return (USB_SUCCESS);
+}
+
+static uint_t
+axf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr)
+{
+ return (usbgem_ether_crc_be(addr) >> (32 - 6));
+}
+
+static int
+axf_set_rx_filter(struct usbgem_dev *dp)
+{
+ int i;
+ uint8_t mode;
+ uint8_t mhash[8];
+ uint8_t buf[2];
+ uint_t h;
+ int err = USB_SUCCESS;
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x",
+ dp->name, __func__, dp->rxmode));
+
+ if (lp->rcr & RCR_SO) {
+ /* set promiscuous mode before changing it. */
+ OUT(dp, VCMD_WRITE_RXCTRL,
+ lp->rcr | RCR_PRO, 0, 0, NULL, &err, usberr);
+ }
+
+ lp->rcr &= ~(RCR_AP_88772 | RCR_AM | RCR_SEP | RCR_AMALL | RCR_PRO);
+ mode = RCR_AB; /* accept broadcast packets */
+
+ bzero(mhash, sizeof (mhash));
+
+ if (dp->rxmode & RXMODE_PROMISC) {
+ /* promiscious mode implies all multicast and all physical */
+ mode |= RCR_PRO;
+ } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 32) {
+ /* accept all multicast packets */
+ mode |= RCR_AMALL;
+ } else if (dp->mc_count > 0) {
+ /*
+ * make hash table to select interresting
+ * multicast address only.
+ */
+ mode |= RCR_AM;
+ for (i = 0; i < dp->mc_count; i++) {
+ h = dp->mc_list[i].hash;
+ mhash[h / 8] |= 1 << (h % 8);
+ }
+ }
+ if (AX88172(dp)) {
+ if (bcmp(dp->dev_addr.ether_addr_octet,
+ dp->cur_addr.ether_addr_octet, ETHERADDRL) != 0) {
+ /*
+ * we use promiscious mode instead of changing the
+ * mac address in ax88172
+ */
+ mode |= RCR_PRO;
+ }
+ } else {
+ OUT(dp, VCMD_WRITE_NODE_ID_88772, 0, 0,
+ ETHERADDRL, dp->cur_addr.ether_addr_octet, &err, usberr);
+ }
+ lp->rcr |= mode;
+
+ /* set multicast hash table */
+ if (mode & RCR_AM) {
+ /* need to set up multicast hash table */
+ OUT(dp, VCMD_WRITE_MCAST_FILTER, 0, 0,
+ sizeof (mhash), mhash, &err, usberr);
+ }
+
+ /* update rcr */
+ OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0,
+ 0, NULL, &err, usberr);
+
+#if DEBUG_LEVEL > 1
+ /* verify rxctrl reg */
+ IN(dp, VCMD_READ_RXCTRL, 0, 0, 2, buf, &err, usberr);
+ cmn_err(CE_CONT, "!%s: %s: rcr:%b returned",
+ dp->name, __func__, LE16P(buf), RCR_BITS);
+#endif
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+axf_set_media(struct usbgem_dev *dp)
+{
+ uint8_t val8;
+ uint8_t gpio;
+ uint8_t gpio_old;
+ int err = USB_SUCCESS;
+ uint16_t msr;
+ struct axf_dev *lp = dp->private;
+
+ IN(dp, VCMD_READ_GPIO, 0, 0, 1, &gpio, &err, usberr);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called, gpio:%b",
+ dp->name, __func__, gpio, GPIO_BITS));
+
+ msr = lp->msr;
+ gpio_old = gpio;
+ gpio = lp->chip->gpio_reset[0];
+
+ /* setup speed */
+ if (AX88172(dp)) {
+ /* EMPTY */
+ } else {
+ msr &= ~(MSR_PS | MSR_GM | MSR_ENCK);
+
+ switch (dp->speed) {
+ case USBGEM_SPD_1000:
+ msr |= MSR_GM | MSR_ENCK;
+ break;
+
+ case USBGEM_SPD_100:
+ msr |= MSR_PS;
+ break;
+
+ case USBGEM_SPD_10:
+ break;
+ }
+ }
+ gpio |= lp->chip->gpio_speed[dp->speed == USBGEM_SPD_100 ? 1 : 0];
+
+ /* select duplex */
+ msr &= ~MSR_FDPX;
+ if (dp->full_duplex) {
+ msr |= MSR_FDPX;
+
+ /* select flow control */
+ if (AX88172(dp)) {
+ msr &= ~MSR_FCEN;
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_TX_PAUSE:
+ case FLOW_CONTROL_SYMMETRIC:
+ case FLOW_CONTROL_RX_PAUSE:
+ msr |= MSR_FCEN;
+ break;
+ }
+ } else {
+ msr &= ~(MSR_RFC | MSR_TFC);
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_TX_PAUSE:
+ msr |= MSR_TFC;
+ break;
+
+ case FLOW_CONTROL_SYMMETRIC:
+ msr |= MSR_TFC | MSR_RFC;
+ break;
+
+ case FLOW_CONTROL_RX_PAUSE:
+ msr |= MSR_RFC;
+ break;
+ }
+ }
+ }
+ gpio |= lp->chip->gpio_duplex[dp->full_duplex ? 1 : 0];
+
+ /* update medium status register */
+ lp->msr = msr;
+ OUT(dp, VCMD_WRITE_MEDIUM_STATUS, lp->msr, 0,
+ 0, NULL, &err, usberr);
+
+ if (gpio != gpio_old) {
+ /* LED control required for some products */
+ OUT(dp, VCMD_WRITE_GPIO,
+ gpio, 0, 0, NULL, &err, usberr);
+ }
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+#define FILL_PKT_HEADER(bp, len) { \
+ (bp)[0] = (uint8_t)(len); \
+ (bp)[1] = (uint8_t)((len) >> 8); \
+ (bp)[2] = (uint8_t)(~(len)); \
+ (bp)[3] = (uint8_t)((~(len)) >> 8); \
+}
+
+#define PKT_HEADER_SIZE 4
+
+/*
+ * send/receive packet check
+ */
+static mblk_t *
+axf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ int n;
+ size_t len;
+ size_t pkt_size;
+ mblk_t *new;
+ mblk_t *tp;
+ uint8_t *bp;
+ uint8_t *last_pos;
+ uint_t align_mask;
+ size_t header_size;
+ int pad_size;
+
+ len = msgdsize(mp);
+
+ if (AX88172(dp)) {
+#ifdef notdef
+ align_mask = 63;
+#else
+ align_mask = 511;
+#endif
+ header_size = 0;
+
+ if (len >= ETHERMIN && mp->b_cont == NULL &&
+ (len & align_mask) != 0) {
+ /* use the mp "as is" */
+ return (mp);
+ }
+ } else {
+ align_mask = 511;
+ header_size = PKT_HEADER_SIZE;
+ }
+
+ /*
+ * re-allocate the mp
+ */
+ /* minimum ethernet packet size of ETHERMIN */
+ pkt_size = max(len, ETHERMIN);
+
+ if (((pkt_size + header_size) & align_mask) == 0) {
+ /* padding is required in usb communication */
+ pad_size = PKT_HEADER_SIZE;
+ } else {
+ pad_size = 0;
+ }
+
+ if ((new = allocb(header_size + pkt_size + pad_size, 0)) == NULL) {
+ return (NULL);
+ }
+
+ bp = new->b_rptr;
+ if (header_size) {
+ uint16_t tmp;
+
+ /* add a header */
+ tmp = (uint16_t)pkt_size;
+ FILL_PKT_HEADER(bp, tmp);
+ bp += header_size;
+ }
+
+ /* copy contents of the buffer */
+ for (tp = mp; tp; tp = tp->b_cont) {
+ n = tp->b_wptr - tp->b_rptr;
+ bcopy(tp->b_rptr, bp, n);
+ bp += n;
+ }
+
+ /* add pads for ethernet packets */
+ last_pos = new->b_rptr + header_size + pkt_size;
+ while (bp < last_pos) {
+ *bp++ = 0;
+ }
+
+ /* add a zero-length pad segment for usb communications */
+ if (pad_size) {
+ /* add a dummy header for zero-length packet */
+ FILL_PKT_HEADER(bp, 0);
+ bp += pad_size;
+ }
+
+ /* close the payload of the packet */
+ new->b_wptr = bp;
+
+ return (new);
+}
+
+static void
+axf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i += 8, bp += 8) {
+ cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]);
+ }
+}
+
+static mblk_t *
+axf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ mblk_t *tp;
+ int rest;
+
+ if (AX88172(dp)) {
+ return (mp);
+ }
+
+ tp = mp;
+ rest = tp->b_wptr - tp->b_rptr;
+
+ if (rest <= PKT_HEADER_SIZE) {
+ /*
+ * the usb bulk-in frame doesn't include any valid
+ * ethernet packets.
+ */
+ return (NULL);
+ }
+
+ for (; ; ) {
+ uint16_t len;
+ uint16_t cksum;
+
+ /* analyse the header of the received usb frame */
+ len = LE16P(tp->b_rptr + 0);
+ cksum = LE16P(tp->b_rptr + 2);
+
+ /* test if the header is valid */
+ if (len + cksum != 0xffff) {
+ /* discard whole the packet */
+ cmn_err(CE_WARN,
+ "!%s: %s: corrupted header:%04x %04x",
+ dp->name, __func__, len, cksum);
+ return (NULL);
+ }
+#if DEBUG_LEVEL > 0
+ if (len < ETHERMIN || len > ETHERMAX) {
+ cmn_err(CE_NOTE,
+ "!%s: %s: incorrect pktsize:%d",
+ dp->name, __func__, len);
+ }
+#endif
+ /* extract a ethernet packet from the bulk-in frame */
+ tp->b_rptr += PKT_HEADER_SIZE;
+ tp->b_wptr = tp->b_rptr + len;
+
+ if (len & 1) {
+ /*
+ * skip a tailing pad byte if the packet
+ * length is odd
+ */
+ len++;
+ }
+ rest -= len + PKT_HEADER_SIZE;
+
+ if (rest <= PKT_HEADER_SIZE) {
+ /* no more vaild ethernet packets */
+ break;
+ }
+
+#if DEBUG_LEVEL > 10
+ axf_dump_packet(dp, tp->b_wptr, 18);
+#endif
+ /* allocate a mblk_t header for the next ethernet packet */
+ tp->b_next = dupb(mp);
+ tp->b_next->b_rptr = tp->b_rptr + len;
+ tp = tp->b_next;
+ }
+
+ return (mp);
+}
+
+/*
+ * MII Interfaces
+ */
+static uint16_t
+axf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp)
+{
+ uint8_t buf[2];
+ uint16_t val;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d",
+ dp->name, __func__, index));
+
+ /* switch to software MII operation mode */
+ OUT(dp, VCMD_SOFTWARE_MII_OP, 0, 0, 0, NULL, errp, usberr);
+
+ /* Read MII register */
+ IN(dp, VCMD_READ_MII_REG, dp->mii_phy_addr, index,
+ 2, buf, errp, usberr);
+
+ /* switch to hardware MII operation mode */
+ OUT(dp, VCMD_HARDWARE_MII_OP, 0, 0, 0, NULL, errp, usberr);
+
+ return (LE16P(buf));
+
+usberr:
+ cmn_err(CE_CONT,
+ "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp);
+ return (0);
+}
+
+static void
+axf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp)
+{
+ uint8_t buf[2];
+
+ DPRINTF(4, (CE_CONT, "!%s: %s called, reg:%x val:%x",
+ dp->name, __func__, index, val));
+
+ /* switch software MII operation mode */
+ OUT(dp, VCMD_SOFTWARE_MII_OP, 0, 0, 0, NULL, errp, usberr);
+
+ /* Write to the specified MII register */
+ buf[0] = (uint8_t)val;
+ buf[1] = (uint8_t)(val >> 8);
+ OUT(dp, VCMD_WRITE_MII_REG, dp->mii_phy_addr, index,
+ 2, buf, errp, usberr);
+
+ /* switch to hardware MII operation mode */
+ OUT(dp, VCMD_HARDWARE_MII_OP, 0, 0, 0, NULL, errp, usberr);
+
+usberr:
+ ;
+}
+
+static void
+axf_interrupt(struct usbgem_dev *dp, mblk_t *mp)
+{
+ uint8_t *bp;
+ struct axf_dev *lp = dp->private;
+
+ bp = mp->b_rptr;
+
+ DPRINTF(2, (CE_CONT,
+ "!%s: %s: size:%d, %02x %02x %02x %02x %02x %02x %02x %02x",
+ dp->name, __func__, mp->b_wptr - mp->b_rptr,
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]));
+
+ if (lp->last_link_state ^ bp[2]) {
+ usbgem_mii_update_link(dp);
+ }
+
+ lp->last_link_state = bp[2];
+}
+
+/* ======================================================== */
+/*
+ * OS depend (device driver DKI) routine
+ */
+/* ======================================================== */
+#ifdef DEBUG_LEVEL
+static void
+axf_eeprom_dump(struct usbgem_dev *dp, int size)
+{
+ int i;
+ int err;
+ uint8_t w0[2], w1[2], w2[2], w3[2];
+
+ cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name);
+
+ err = USB_SUCCESS;
+
+ for (i = 0; i < size; i += 4) {
+ IN(dp, VCMD_READ_SROM, i + 0, 0, 2, w0, &err, usberr);
+ IN(dp, VCMD_READ_SROM, i + 1, 0, 2, w1, &err, usberr);
+ IN(dp, VCMD_READ_SROM, i + 2, 0, 2, w2, &err, usberr);
+ IN(dp, VCMD_READ_SROM, i + 3, 0, 2, w3, &err, usberr);
+ cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x",
+ i,
+ (w0[1] << 8) | w0[0],
+ (w1[1] << 8) | w1[0],
+ (w2[1] << 8) | w2[0],
+ (w3[1] << 8) | w3[0]);
+ }
+usberr:
+ ;
+}
+#endif
+
+static int
+axf_attach_chip(struct usbgem_dev *dp)
+{
+ uint8_t phys[2];
+ int err;
+ uint_t vcmd;
+ int ret;
+#ifdef CONFIG_FULLSIZE_VLAN
+ uint8_t maxpktsize[2];
+ uint16_t vlan_pktsize;
+#endif
+#ifdef DEBUG_LEVEL
+ uint8_t val8;
+#endif
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s enter", dp->name, __func__));
+
+ ret = USB_SUCCESS;
+ /*
+ * mac address in EEPROM has loaded to ID registers.
+ */
+ vcmd = AX88172(dp) ? VCMD_READ_NODE_ID : VCMD_READ_NODE_ID_88772;
+ IN(dp, vcmd, 0, 0,
+ ETHERADDRL, dp->dev_addr.ether_addr_octet, &err, usberr);
+
+ /*
+ * setup IPG values
+ */
+ lp->ipg[0] = 0x15;
+ lp->ipg[1] = 0x0c;
+ lp->ipg[2] = 0x12;
+
+ /*
+ * We cannot scan phy because the nic returns undefined
+ * value, i.e. remained garbage, when MII phy is not at the
+ * specified index.
+ */
+#ifdef DEBUG_LEVELx
+ if (lp->chip->vid == 0x07b8 && lp->chip->pid == 0x420a) {
+ /*
+ * restore the original phy address of brain
+ * damaged Planex UE2-100TX
+ */
+ OUT(dp, VCMD_WRITE_SROM_ENABLE, 0, 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM, 0x11, 0xe004, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM_DISABLE, 0, 0, 0, NULL, &err, usberr);
+ }
+#endif
+ if (AX88172(dp)) {
+ IN(dp, VCMD_READ_PHY_IDS, 0, 0, 2, &phys, &err, usberr);
+ dp->mii_phy_addr = phys[1];
+ DPRINTF(0, (CE_CONT, "!%s: %s: phys_addr:%d %d",
+ dp->name, __func__, phys[0], phys[1]));
+ } else {
+ /* use built-in phy */
+ dp->mii_phy_addr = 0x10;
+ }
+
+ dp->misc_flag |= USBGEM_VLAN;
+#ifdef CONFIG_FULLSIZE_VLAN
+ if (AX88172(dp) || AX88772(dp)) {
+ /* check max packet size in srom */
+ IN(dp, VCMD_READ_SROM, 0x10, 0, 2, maxpktsize, &err, usberr);
+ vlan_pktsize = ETHERMAX + ETHERFCSL + 4 /* VTAG_SIZE */;
+
+ if (LE16P(maxpktsize) < vlan_pktsize) {
+ cmn_err(CE_CONT,
+ "!%s: %s: max packet size in srom is too small, "
+ "changing %d -> %d, do power cycle for the device",
+ dp->name, __func__,
+ LE16P(maxpktsize), vlan_pktsize);
+ OUT(dp, VCMD_WRITE_SROM_ENABLE,
+ 0, 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM, 0x10,
+ vlan_pktsize, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM_DISABLE,
+ 0, 0, 0, NULL, &err, usberr);
+
+ /* need to power off the device */
+ ret = USB_FAILURE;
+ }
+ }
+#endif
+#ifdef DEBUG_LEVEL
+ IN(dp, VCMD_READ_GPIO, 0, 0, 1, &val8, &err, usberr);
+ cmn_err(CE_CONT,
+ "!%s: %s: ipg 0x%02x 0x%02x 0x%02x, gpio 0x%b",
+ dp->name, __func__, lp->ipg[0], lp->ipg[1], lp->ipg[2],
+ val8, GPIO_BITS);
+#endif
+ /* fix rx buffer size */
+ if (!AX88172(dp)) {
+ dp->rx_buf_len = 2048;
+ }
+
+#if DEBUG_LEVEL > 0
+ axf_eeprom_dump(dp, 0x20);
+#endif
+ return (ret);
+
+usberr:
+ cmn_err(CE_WARN, "%s: %s: usb error detected (%d)",
+ dp->name, __func__, err);
+ return (USB_FAILURE);
+}
+
+static boolean_t
+axf_scan_phy(struct usbgem_dev *dp)
+{
+ int i;
+ int err;
+ uint16_t val;
+ int phy_addr_saved;
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ phy_addr_saved = dp->mii_phy_addr;
+
+ /* special probe routine for unreliable MII addr */
+#define PROBE_PAT \
+ (MII_ABILITY_100BASE_TX_FD | \
+ MII_ABILITY_100BASE_TX | \
+ MII_ABILITY_10BASE_T_FD | \
+ MII_ABILITY_10BASE_T)
+
+ for (i = 0; i < 32; i++) {
+ dp->mii_phy_addr = i;
+ axf_mii_write(dp, MII_AN_ADVERT, 0, &err);
+ if (err != USBGEM_SUCCESS) {
+ break;
+ }
+ val = axf_mii_read(dp, MII_AN_ADVERT, &err);
+ if (err != USBGEM_SUCCESS) {
+ break;
+ }
+ if (val != 0) {
+ DPRINTF(0, (CE_CONT, "!%s: %s: index:%d, val %b != 0",
+ dp->name, __func__, i, val, MII_ABILITY_BITS));
+ continue;
+ }
+
+ axf_mii_write(dp, MII_AN_ADVERT, PROBE_PAT, &err);
+ if (err != USBGEM_SUCCESS) {
+ break;
+ }
+ val = axf_mii_read(dp, MII_AN_ADVERT, &err);
+ if (err != USBGEM_SUCCESS) {
+ break;
+ }
+ if ((val & MII_ABILITY_TECH) != PROBE_PAT) {
+ DPRINTF(0, (CE_CONT, "!%s: %s: "
+ "index:%d, pat:%x != val:%b",
+ dp->name, __func__, i,
+ PROBE_PAT, val, MII_ABILITY_BITS));
+ continue;
+ }
+
+ /* found */
+ dp->mii_phy_addr = phy_addr_saved;
+ return (i);
+ }
+#undef PROBE_PAT
+ if (i == 32) {
+ cmn_err(CE_CONT, "!%s: %s: no mii phy found",
+ dp->name, __func__);
+ } else {
+ cmn_err(CE_CONT, "!%s: %s: i/o error while scanning phy",
+ dp->name, __func__);
+ }
+ dp->mii_phy_addr = phy_addr_saved;
+ return (-1);
+}
+
+static int
+axf_mii_probe(struct usbgem_dev *dp)
+{
+ int my_guess;
+ int err;
+ uint8_t old_11th[2];
+ uint8_t new_11th[2];
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ (void) axf_reset_phy(dp);
+ lp->phy_has_reset = B_TRUE;
+
+ if (AX88172(dp)) {
+ my_guess = axf_scan_phy(dp);
+ if (my_guess >= 0 && my_guess < 32 &&
+ my_guess != dp->mii_phy_addr) {
+ /*
+ * phy addr in srom is wrong, need to fix it
+ */
+ IN(dp, VCMD_READ_SROM,
+ 0x11, 0, 2, old_11th, &err, usberr);
+
+ new_11th[0] = my_guess;
+ new_11th[1] = old_11th[1];
+
+ OUT(dp, VCMD_WRITE_SROM_ENABLE,
+ 0, 0, 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM,
+ 0x11, LE16P(new_11th), 0, NULL, &err, usberr);
+ OUT(dp, VCMD_WRITE_SROM_DISABLE,
+ 0, 0, 0, NULL, &err, usberr);
+#if 1
+ /* XXX - read back, but it doesn't work, why? */
+ delay(drv_usectohz(1000*1000));
+ IN(dp, VCMD_READ_SROM,
+ 0x11, 0, 2, new_11th, &err, usberr);
+#endif
+ cmn_err(CE_NOTE, "!%s: %s: phy addr in srom fixed: "
+ "%04x -> %04x",
+ dp->name, __func__,
+ LE16P(old_11th), LE16P(new_11th));
+ return (USBGEM_FAILURE);
+usberr:
+ cmn_err(CE_NOTE,
+ "!%s: %s: failed to patch phy addr, "
+ "current: %04x",
+ dp->name, __func__, LE16P(old_11th));
+ return (USBGEM_FAILURE);
+ }
+ }
+ return (usbgem_mii_probe_default(dp));
+}
+
+static int
+axf_mii_init(struct usbgem_dev *dp)
+{
+ struct axf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ if (!lp->phy_has_reset) {
+ (void) axf_reset_phy(dp);
+ }
+
+ /* prepare to reset phy on the next reconnect or resume */
+ lp->phy_has_reset = B_FALSE;
+
+ return (USB_SUCCESS);
+}
+
+static int
+axfattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i;
+ ddi_iblock_cookie_t c;
+ int ret;
+ int revid;
+ int unit;
+ int vid;
+ int pid;
+ struct chip_info *p;
+ int len;
+ const char *drv_name;
+ struct usbgem_dev *dp;
+ void *base;
+ struct usbgem_conf *ugcp;
+ struct axf_dev *lp;
+
+ unit = ddi_get_instance(dip);
+ drv_name = ddi_driver_name(dip);
+
+ DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d",
+ drv_name, unit, __func__, cmd));
+
+ if (cmd == DDI_ATTACH) {
+ /*
+ * Check if the chip is supported.
+ */
+ vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "usb-vendor-id", -1);
+ pid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "usb-product-id", -1);
+ revid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "usb-revision-id", -1);
+
+ for (i = 0, p = chiptbl_88x7x; i < CHIPTABLESIZE; i++, p++) {
+ if (p->vid == vid && p->pid == pid) {
+ /* found */
+ cmn_err(CE_CONT, "!%s%d: %s "
+ "(vid: 0x%04x, did: 0x%04x, revid: 0x%02x)",
+ drv_name, unit, p->name, vid, pid, revid);
+ goto chip_found;
+ }
+ }
+
+ /* Not found */
+ cmn_err(CE_WARN, "!%s: %s: wrong usb venid/prodid (0x%x, 0x%x)",
+ drv_name, __func__, vid, pid);
+
+ /* assume 88772 */
+ p = &chiptbl_88x7x[CHIPTABLESIZE - 1];
+chip_found:
+ /*
+ * construct usbgem configration
+ */
+ ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP);
+
+ /* name */
+ /*
+ * softmac requires that ppa is the instance number
+ * of the device, otherwise it hangs in seaching the device.
+ */
+ sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit);
+ ugcp->usbgc_ppa = unit;
+
+ ugcp->usbgc_ifnum = 0;
+ ugcp->usbgc_alt = 0;
+
+ ugcp->usbgc_tx_list_max = 64;
+
+ ugcp->usbgc_rx_header_len = 0;
+ ugcp->usbgc_rx_list_max = 64;
+
+ /* time out parameters */
+ ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT;
+ ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL;
+
+ /* flow control */
+ /*
+ * XXX - flow control caused link down frequently under
+ * heavy traffic
+ */
+ ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE;
+
+ /* MII timeout parameters */
+ ugcp->usbgc_mii_link_watch_interval = ONESEC;
+ ugcp->usbgc_mii_an_watch_interval = ONESEC/5;
+ ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */
+ ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */
+ ugcp->usbgc_mii_an_wait = 0;
+ ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT;
+
+ ugcp->usbgc_mii_an_delay = ONESEC/10;
+ ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA;
+ ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET;
+ ugcp->usbgc_mii_dont_reset = B_FALSE;
+ ugcp->usbgc_mii_hw_link_detection = B_TRUE;
+ ugcp->usbgc_mii_stop_mac_on_linkdown = B_FALSE;
+
+ /* I/O methods */
+
+ /* mac operation */
+ ugcp->usbgc_attach_chip = &axf_attach_chip;
+ ugcp->usbgc_reset_chip = &axf_reset_chip;
+ ugcp->usbgc_init_chip = &axf_init_chip;
+ ugcp->usbgc_start_chip = &axf_start_chip;
+ ugcp->usbgc_stop_chip = &axf_stop_chip;
+ ugcp->usbgc_multicast_hash = &axf_mcast_hash;
+
+ ugcp->usbgc_set_rx_filter = &axf_set_rx_filter;
+ ugcp->usbgc_set_media = &axf_set_media;
+ ugcp->usbgc_get_stats = &axf_get_stats;
+ ugcp->usbgc_interrupt = &axf_interrupt;
+
+ /* packet operation */
+ ugcp->usbgc_tx_make_packet = &axf_tx_make_packet;
+ ugcp->usbgc_rx_make_packet = &axf_rx_make_packet;
+
+ /* mii operations */
+ ugcp->usbgc_mii_probe = &axf_mii_probe;
+ ugcp->usbgc_mii_init = &axf_mii_init;
+ ugcp->usbgc_mii_config = &usbgem_mii_config_default;
+ ugcp->usbgc_mii_read = &axf_mii_read;
+ ugcp->usbgc_mii_write = &axf_mii_write;
+
+ /* mtu */
+ ugcp->usbgc_min_mtu = ETHERMTU;
+ ugcp->usbgc_max_mtu = ETHERMTU;
+ ugcp->usbgc_default_mtu = ETHERMTU;
+
+ lp = kmem_zalloc(sizeof (struct axf_dev), KM_SLEEP);
+ lp->chip = p;
+ lp->last_link_state = 0;
+ lp->phy_has_reset = B_FALSE;
+
+ dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct axf_dev));
+
+ kmem_free(ugcp, sizeof (*ugcp));
+
+ if (dp != NULL) {
+ return (DDI_SUCCESS);
+ }
+
+err_free_mem:
+ kmem_free(lp, sizeof (struct axf_dev));
+err_close_pipe:
+err:
+ return (DDI_FAILURE);
+ }
+
+ if (cmd == DDI_RESUME) {
+ return (usbgem_resume(dip));
+ }
+
+ return (DDI_FAILURE);
+}
+
+static int
+axfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int ret;
+
+ if (cmd == DDI_DETACH) {
+ ret = usbgem_do_detach(dip);
+ if (ret != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+ }
+ if (cmd == DDI_SUSPEND) {
+ return (usbgem_suspend(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+/* ======================================================== */
+/*
+ * OS depend (loadable streams driver) routine
+ */
+/* ======================================================== */
+#ifdef USBGEM_CONFIG_GLDv3
+USBGEM_STREAM_OPS(axf_ops, axfattach, axfdetach);
+#else
+static struct module_info axfminfo = {
+ 0, /* mi_idnum */
+ "axf", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ ETHERMTU*128, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit axfrinit = {
+ (int (*)()) NULL, /* qi_putp */
+ usbgem_rsrv, /* qi_srvp */
+ usbgem_open, /* qi_qopen */
+ usbgem_close, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &axfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit axfwinit = {
+ usbgem_wput, /* qi_putp */
+ usbgem_wsrv, /* qi_srvp */
+ (int (*)()) NULL, /* qi_qopen */
+ (int (*)()) NULL, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &axfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab axf_info = {
+ &axfrinit, /* st_rdinit */
+ &axfwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_axf_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &axf_info, /* cb_stream */
+ D_NEW|D_MP /* cb_flag */
+};
+
+static struct dev_ops axf_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ usbgem_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ axfattach, /* devo_attach */
+ axfdetach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_axf_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ usbgem_power, /* devo_power */
+#if DEVO_REV >= 4
+ usbgem_quiesce, /* devo_quiesce */
+#endif
+};
+#endif
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ ident,
+ &axf_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+/* ======================================================== */
+/*
+ * _init : done
+ */
+/* ======================================================== */
+int
+_init(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!axf: _init: called"));
+
+ status = usbgem_mod_init(&axf_ops, "axf");
+ if (status != DDI_SUCCESS) {
+ return (status);
+ }
+ status = mod_install(&modlinkage);
+ if (status != DDI_SUCCESS) {
+ usbgem_mod_fini(&axf_ops);
+ }
+ return (status);
+}
+
+/*
+ * _fini : done
+ */
+int
+_fini(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!axf: _fini: called"));
+ status = mod_remove(&modlinkage);
+ if (status == DDI_SUCCESS) {
+ usbgem_mod_fini(&axf_ops);
+ }
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/io/bpf/bpf_wrap.c b/usr/src/uts/common/io/bpf/bpf_wrap.c
new file mode 100644
index 0000000000..6cbde58a20
--- /dev/null
+++ b/usr/src/uts/common/io/bpf/bpf_wrap.c
@@ -0,0 +1,35 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <net/bpf.h>
+#include <inet/bpf.h>
+
+/*
+ * With BPF filter validation and evaluation moved into the 'ip' module, these
+ * wrapper functions are provided to expose the original interface.
+ */
+
+uint_t
+bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
+{
+ return ((uint_t)ip_bpf_filter((ip_bpf_insn_t *)pc, p, wirelen, buflen));
+}
+
+int
+bpf_validate(struct bpf_insn *f, int len)
+{
+ return ((int)ip_bpf_validate((ip_bpf_insn_t *)f, (uint_t)len));
+}
diff --git a/usr/src/uts/common/io/cons.c b/usr/src/uts/common/io/cons.c
index 6ef1b0b9f7..495ae93cf9 100644
--- a/usr/src/uts/common/io/cons.c
+++ b/usr/src/uts/common/io/cons.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
/*
@@ -53,6 +54,7 @@
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/stat.h>
+#include <sys/limits.h>
#include <sys/console.h>
#include <sys/consdev.h>
@@ -414,14 +416,24 @@ cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
*/
if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
struiod_t uiod;
+ struct iovec buf[IOV_MAX_STACK];
+ int iovlen = 0;
+
+ if (uio->uio_iovcnt > IOV_MAX_STACK) {
+ iovlen = uio->uio_iovcnt * sizeof (iovec_t);
+ uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
+ } else {
+ uiod.d_iov = buf;
+ }
/*
* strwrite modifies uio so need to make copy.
*/
- (void) uiodup(uio, &uiod.d_uio, uiod.d_iov,
- sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+ (void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt);
(void) strwrite(vsconsvp, &uiod.d_uio, cred);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
}
if (rconsvp->v_stream != NULL)
diff --git a/usr/src/uts/common/io/cpqary3/cpqary3.c b/usr/src/uts/common/io/cpqary3/cpqary3.c
index 622f0dcf68..f67d77b3d2 100644
--- a/usr/src/uts/common/io/cpqary3/cpqary3.c
+++ b/usr/src/uts/common/io/cpqary3/cpqary3.c
@@ -41,7 +41,7 @@ extern cpqary3_driver_info_t gdriver_info;
* Global Variables Definitions
*/
-static char cpqary3_brief[] = "HP Smart Array Driver";
+static char cpqary3_brief[] = "HP Smart Array (Legacy)";
void *cpqary3_state;
/* HPQaculi Changes */
diff --git a/usr/src/uts/common/io/devpoll.c b/usr/src/uts/common/io/devpoll.c
index 8c63043d00..4fce431e00 100644
--- a/usr/src/uts/common/io/devpoll.c
+++ b/usr/src/uts/common/io/devpoll.c
@@ -353,8 +353,9 @@ repoll:
pdp->pd_fp = NULL;
pdp->pd_events = 0;
- if (php != NULL) {
- pollhead_delete(php, pdp);
+ if (pdp->pd_php != NULL) {
+ pollhead_delete(pdp->pd_php,
+ pdp);
pdp->pd_php = NULL;
}
@@ -503,8 +504,9 @@ repoll:
pdp->pd_fp = NULL;
pdp->pd_events = 0;
- if (php != NULL) {
- pollhead_delete(php, pdp);
+ if (pdp->pd_php != NULL) {
+ pollhead_delete(pdp->pd_php,
+ pdp);
pdp->pd_php = NULL;
}
@@ -639,6 +641,7 @@ dpwrite(dev_t dev, struct uio *uiop, cred_t *credp)
uintptr_t limit;
int error, size;
ssize_t uiosize;
+ size_t copysize;
nfds_t pollfdnum;
struct pollhead *php = NULL;
polldat_t *pdp;
@@ -704,11 +707,19 @@ dpwrite(dev_t dev, struct uio *uiop, cred_t *credp)
* here for every call.
*/
uiop->uio_loffset = 0;
- if ((error = uiomove((caddr_t)pollfdp, uiosize, UIO_WRITE, uiop))
- != 0) {
+
+ /*
+ * Use uiocopy instead of uiomove when populating pollfdp, keeping
+ * uio_resid untouched for now. Write syscalls will translate EINTR
+ * into a success if they detect "successfully transfered" data via an
+ * updated uio_resid. Falsely suppressing such errors is disastrous.
+ */
+ if ((error = uiocopy((caddr_t)pollfdp, uiosize, UIO_WRITE, uiop,
+ &copysize)) != 0) {
kmem_free(pollfdp, uiosize);
return (error);
}
+
/*
* We are about to enter the core portion of dpwrite(). Make sure this
* write has exclusive access in this portion of the code, i.e., no
@@ -981,6 +992,13 @@ bypass:
cv_broadcast(&dpep->dpe_cv);
mutex_exit(&dpep->dpe_lock);
kmem_free(pollfdp, uiosize);
+ if (error == 0) {
+ /*
+ * The state of uio_resid is updated only after the pollcache
+ * is successfully modified.
+ */
+ uioskip(uiop, copysize);
+ }
return (error);
}
@@ -1123,14 +1141,18 @@ dpioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
void *setp = STRUCT_FGETP(dvpoll, dp_setp);
if (setp != NULL) {
- if (copyin(setp, &set, sizeof (set))) {
- DP_REFRELE(dpep);
- return (EFAULT);
+ if ((mode & FKIOCTL) != 0) {
+ /* Use the signal set directly */
+ ksetp = (k_sigset_t *)setp;
+ } else {
+ if (copyin(setp, &set, sizeof (set))) {
+ DP_REFRELE(dpep);
+ return (EFAULT);
+ }
+ sigutok(&set, &kset);
+ ksetp = &kset;
}
- sigutok(&set, &kset);
- ksetp = &kset;
-
mutex_enter(&p->p_lock);
schedctl_finish_sigblock(t);
lwp->lwp_sigoldmask = t->t_hold;
@@ -1279,6 +1301,10 @@ dpioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
DP_SIGMASK_RESTORE(ksetp);
if (error == 0 && fdcnt > 0) {
+ /*
+ * It should be noted that FKIOCTL does not influence
+ * the copyout (vs bcopy) of dp_fds at this time.
+ */
if (copyout(ps->ps_dpbuf,
STRUCT_FGETP(dvpoll, dp_fds), fdcnt * fdsize)) {
DP_REFRELE(dpep);
diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c
index 40cbe86170..62bc4a8ecf 100644
--- a/usr/src/uts/common/io/dld/dld_drv.c
+++ b/usr/src/uts/common/io/dld/dld_drv.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent Inc.
*/
/*
@@ -701,7 +702,8 @@ drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
err = EACCES;
goto done;
}
- err = dls_devnet_setzid(dlh, dzp->diz_zid);
+ err = dls_devnet_setzid(dlh, dzp->diz_zid,
+ dzp->diz_transient);
} else {
kprop->pr_perm_flags = MAC_PROP_PERM_RW;
(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
@@ -865,7 +867,7 @@ drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
return (err);
if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
- dir->dir_link)) != 0)
+ dir->dir_link, dir->dir_zoneinit)) != 0)
return (err);
if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
@@ -1376,7 +1378,8 @@ static dld_ioc_modentry_t dld_ioc_modtable[] = {
{SIMNET_IOC, "simnet", 0, NULL, 0},
{BRIDGE_IOC, "bridge", 0, NULL, 0},
{IPTUN_IOC, "iptun", 0, NULL, 0},
- {IBPART_IOC, "ibp", -1, NULL, 0}
+ {IBPART_IOC, "ibp", -1, NULL, 0},
+ {OVERLAY_IOC, "overlay", 0, NULL, 0}
};
#define DLDIOC_CNT \
(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
diff --git a/usr/src/uts/common/io/dld/dld_proto.c b/usr/src/uts/common/io/dld/dld_proto.c
index a438e43d91..661d8b2f4f 100644
--- a/usr/src/uts/common/io/dld/dld_proto.c
+++ b/usr/src/uts/common/io/dld/dld_proto.c
@@ -41,7 +41,7 @@ static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
- proto_notify_req, proto_passive_req;
+ proto_notify_req, proto_passive_req, proto_exclusive_req;
static void proto_capability_advertise(dld_str_t *, mblk_t *);
static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
@@ -121,6 +121,9 @@ dld_proto(dld_str_t *dsp, mblk_t *mp)
case DL_PASSIVE_REQ:
proto_passive_req(dsp, mp);
break;
+ case DL_EXCLUSIVE_REQ:
+ proto_exclusive_req(dsp, mp);
+ break;
default:
proto_req(dsp, mp);
break;
@@ -605,6 +608,14 @@ proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
new_flags |= DLS_PROMISC_PHYS;
break;
+ case DL_PROMISC_RX_ONLY:
+ new_flags |= DLS_PROMISC_RX_ONLY;
+ break;
+
+ case DL_PROMISC_FIXUPS:
+ new_flags |= DLS_PROMISC_FIXUPS;
+ break;
+
default:
dl_err = DL_NOTSUPPORTED;
goto failed2;
@@ -692,6 +703,22 @@ proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
new_flags &= ~DLS_PROMISC_PHYS;
break;
+ case DL_PROMISC_RX_ONLY:
+ if (!(dsp->ds_promisc & DLS_PROMISC_RX_ONLY)) {
+ dl_err = DL_NOTENAB;
+ goto failed;
+ }
+ new_flags &= ~DLS_PROMISC_RX_ONLY;
+ break;
+
+ case DL_PROMISC_FIXUPS:
+ if (!(dsp->ds_promisc & DLS_PROMISC_FIXUPS)) {
+ dl_err = DL_NOTENAB;
+ goto failed;
+ }
+ new_flags &= ~DLS_PROMISC_FIXUPS;
+ break;
+
default:
dl_err = DL_NOTSUPPORTED;
mac_perim_exit(mph);
@@ -1295,7 +1322,8 @@ proto_passive_req(dld_str_t *dsp, mblk_t *mp)
* If we've already become active by issuing an active primitive,
* then it's too late to try to become passive.
*/
- if (dsp->ds_passivestate == DLD_ACTIVE) {
+ if (dsp->ds_passivestate == DLD_ACTIVE ||
+ dsp->ds_passivestate == DLD_EXCLUSIVE) {
dl_err = DL_OUTSTATE;
goto failed;
}
@@ -1354,7 +1382,12 @@ dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
direct->di_rx_ch);
- direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
+ if (direct->di_flags & DI_DIRECT_RAW) {
+ direct->di_tx_df =
+ (uintptr_t)str_mdata_raw_fastpath_put;
+ } else {
+ direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
+ }
direct->di_tx_dh = dsp;
direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
direct->di_tx_cb_dh = dsp->ds_mch;
@@ -1516,8 +1549,9 @@ dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
* completes. So we limit the check to DLD_ENABLE case.
*/
if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
- (dsp->ds_sap != ETHERTYPE_IP ||
- !check_mod_above(dsp->ds_rq, "ip"))) {
+ ((dsp->ds_sap != ETHERTYPE_IP ||
+ !check_mod_above(dsp->ds_rq, "ip")) &&
+ !check_mod_above(dsp->ds_rq, "vnd"))) {
return (ENOTSUP);
}
@@ -1599,9 +1633,15 @@ proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
}
/*
- * Direct capability negotiation interface between IP and DLD
+ * Direct capability negotiation interface between IP/VND and DLD. Note
+ * that for vnd we only allow the case where the media type is the
+ * native media type so we know that there are no transformations that
+ * would have to happen to the mac header that it receives.
*/
- if (dsp->ds_sap == ETHERTYPE_IP && check_mod_above(dsp->ds_rq, "ip")) {
+ if ((dsp->ds_sap == ETHERTYPE_IP &&
+ check_mod_above(dsp->ds_rq, "ip")) ||
+ (check_mod_above(dsp->ds_rq, "vnd") &&
+ dsp->ds_mip->mi_media == dsp->ds_mip->mi_nativemedia)) {
dld_capable = B_TRUE;
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_dld_t);
@@ -1720,3 +1760,36 @@ dld_capabilities_disable(dld_str_t *dsp)
if (dsp->ds_polling)
(void) dld_capab_poll_disable(dsp, NULL);
}
+
+static void
+proto_exclusive_req(dld_str_t *dsp, mblk_t *mp)
+{
+ int ret = 0;
+ t_uscalar_t dl_err;
+ mac_perim_handle_t mph;
+
+ if (dsp->ds_passivestate != DLD_UNINITIALIZED) {
+ dl_err = DL_OUTSTATE;
+ goto failed;
+ }
+
+ if (MBLKL(mp) < DL_EXCLUSIVE_REQ_SIZE) {
+ dl_err = DL_BADPRIM;
+ goto failed;
+ }
+
+ mac_perim_enter_by_mh(dsp->ds_mh, &mph);
+ ret = dls_exclusive_set(dsp, B_TRUE);
+ mac_perim_exit(mph);
+
+ if (ret != 0) {
+ dl_err = DL_SYSERR;
+ goto failed;
+ }
+
+ dsp->ds_passivestate = DLD_EXCLUSIVE;
+ dlokack(dsp->ds_wq, mp, DL_EXCLUSIVE_REQ);
+ return;
+failed:
+ dlerrorack(dsp->ds_wq, mp, DL_EXCLUSIVE_REQ, dl_err, (t_uscalar_t)ret);
+}
diff --git a/usr/src/uts/common/io/dld/dld_str.c b/usr/src/uts/common/io/dld/dld_str.c
index 6f0d0b9a6c..f5308e70ff 100644
--- a/usr/src/uts/common/io/dld/dld_str.c
+++ b/usr/src/uts/common/io/dld/dld_str.c
@@ -854,6 +854,77 @@ i_dld_ether_header_update_tag(mblk_t *mp, uint_t pri, uint16_t vid,
return (mp);
}
+static boolean_t
+i_dld_raw_ether_check(dld_str_t *dsp, mac_header_info_t *mhip, mblk_t **mpp)
+{
+ mblk_t *mp = *mpp;
+ mblk_t *newmp;
+ uint_t pri, vid, dvid;
+
+ dvid = mac_client_vid(dsp->ds_mch);
+
+ /*
+ * Discard the packet if this is a VLAN stream but the VID in
+ * the packet is not correct.
+ */
+ vid = VLAN_ID(mhip->mhi_tci);
+ if ((dvid != VLAN_ID_NONE) && (vid != VLAN_ID_NONE))
+ return (B_FALSE);
+
+ /*
+ * Discard the packet if this packet is a tagged packet
+ * but both pri and VID are 0.
+ */
+ pri = VLAN_PRI(mhip->mhi_tci);
+ if (mhip->mhi_istagged && !mhip->mhi_ispvid && pri == 0 &&
+ vid == VLAN_ID_NONE)
+ return (B_FALSE);
+
+ /*
+ * Update the priority bits to the per-stream priority if
+ * priority is not set in the packet. Update the VID for
+ * packets on a VLAN stream.
+ */
+ pri = (pri == 0) ? dsp->ds_pri : 0;
+ if ((pri != 0) || (dvid != VLAN_ID_NONE)) {
+ if ((newmp = i_dld_ether_header_update_tag(mp, pri,
+ dvid, dsp->ds_dlp->dl_tagmode)) == NULL) {
+ return (B_FALSE);
+ }
+ *mpp = newmp;
+ }
+
+ return (B_TRUE);
+}
+
+mac_tx_cookie_t
+str_mdata_raw_fastpath_put(dld_str_t *dsp, mblk_t *mp, uintptr_t f_hint,
+ uint16_t flag)
+{
+ boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
+ mac_header_info_t mhi;
+ mac_tx_cookie_t cookie;
+
+ if (mac_vlan_header_info(dsp->ds_mh, mp, &mhi) != 0)
+ goto discard;
+
+ if (is_ethernet) {
+ if (i_dld_raw_ether_check(dsp, &mhi, &mp) == B_FALSE)
+ goto discard;
+ }
+
+ if ((cookie = DLD_TX(dsp, mp, f_hint, flag)) != NULL) {
+ DLD_SETQFULL(dsp);
+ }
+ return (cookie);
+discard:
+ /* TODO: bump kstat? */
+ freemsg(mp);
+ return (NULL);
+}
+
+
+
/*
* M_DATA put (IP fast-path mode)
*/
@@ -902,7 +973,6 @@ str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
mblk_t *bp, *newmp;
size_t size;
mac_header_info_t mhi;
- uint_t pri, vid, dvid;
uint_t max_sdu;
/*
@@ -948,38 +1018,8 @@ str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
goto discard;
if (is_ethernet) {
- dvid = mac_client_vid(dsp->ds_mch);
-
- /*
- * Discard the packet if this is a VLAN stream but the VID in
- * the packet is not correct.
- */
- vid = VLAN_ID(mhi.mhi_tci);
- if ((dvid != VLAN_ID_NONE) && (vid != VLAN_ID_NONE))
- goto discard;
-
- /*
- * Discard the packet if this packet is a tagged packet
- * but both pri and VID are 0.
- */
- pri = VLAN_PRI(mhi.mhi_tci);
- if (mhi.mhi_istagged && !mhi.mhi_ispvid && pri == 0 &&
- vid == VLAN_ID_NONE)
+ if (i_dld_raw_ether_check(dsp, &mhi, &mp) == B_FALSE)
goto discard;
-
- /*
- * Update the priority bits to the per-stream priority if
- * priority is not set in the packet. Update the VID for
- * packets on a VLAN stream.
- */
- pri = (pri == 0) ? dsp->ds_pri : 0;
- if ((pri != 0) || (dvid != VLAN_ID_NONE)) {
- if ((newmp = i_dld_ether_header_update_tag(mp, pri,
- dvid, dsp->ds_dlp->dl_tagmode)) == NULL) {
- goto discard;
- }
- mp = newmp;
- }
}
if (DLD_TX(dsp, mp, 0, 0) != NULL) {
diff --git a/usr/src/uts/common/io/dls/dls.c b/usr/src/uts/common/io/dls/dls.c
index 92993ada58..0f8dbcb57a 100644
--- a/usr/src/uts/common/io/dls/dls.c
+++ b/usr/src/uts/common/io/dls/dls.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -248,19 +248,69 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
{
int err = 0;
uint32_t old_flags = dsp->ds_promisc;
+ uint32_t new_type = new_flags &
+ ~(DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS);
mac_client_promisc_type_t mptype = MAC_CLIENT_PROMISC_ALL;
+ uint16_t mac_flags = 0;
+ boolean_t doremove = B_FALSE;
ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
ASSERT(!(new_flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
- DLS_PROMISC_PHYS)));
+ DLS_PROMISC_PHYS | DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS)));
+
+ /*
+ * If we only have the non-data receive flags set or are only changing
+ * them, then there's nothing to do other than update the flags here.
+ * Basically when we only have something in the set of
+ * DLS_PROMISC_RX_ONLY and DLS_PROMISC_FIXUPS around, then there's
+ * nothing else for us to do other than toggle it, as there's no need to
+ * talk to MAC and we don't have to do anything else.
+ */
+ if ((old_flags & ~(DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS)) == 0 &&
+ (new_flags & ~(DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS)) == 0) {
+ dsp->ds_promisc = new_flags;
+ return (0);
+ }
/*
* If the user has only requested DLS_PROMISC_MULTI then we need to make
* sure that they don't see all packets.
*/
- if (new_flags == DLS_PROMISC_MULTI)
+ if (new_type == DLS_PROMISC_MULTI)
mptype = MAC_CLIENT_PROMISC_MULTI;
+ /*
+ * Look at new flags and figure out the correct mac promisc flags.
+ * If we've only requested DLS_PROMISC_SAP and not _MULTI or _PHYS,
+ * don't turn on physical promisc mode.
+ */
+ if (new_flags & DLS_PROMISC_RX_ONLY)
+ mac_flags |= MAC_PROMISC_FLAGS_NO_TX_LOOP;
+ if (new_flags & DLS_PROMISC_FIXUPS)
+ mac_flags |= MAC_PROMISC_FLAGS_DO_FIXUPS;
+ if (new_type == DLS_PROMISC_SAP)
+ mac_flags |= MAC_PROMISC_FLAGS_NO_PHYS;
+
+ /*
+ * If we're coming in and we're being asked to transition to a state
+ * where the only DLS flags would be enabled are flags that change what
+ * we do with promiscuous packets (DLS_PROMISC_RX_ONLY and
+ * DLS_PROMISC_FIXUPS) and not which packets we should receive, then we
+ * need to remove the MAC layer promiscuous handler.
+ */
+ if ((new_flags & ~(DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS)) == 0 &&
+ (old_flags & ~(DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS)) != 0 &&
+ new_flags != 0) {
+ doremove = B_TRUE;
+ }
+
+ /*
+ * There are three cases we care about here with respect to MAC. Going
+ * from nothing to something, something to nothing, something to
+ * something where we need to change how we're getting stuff from mac.
+ * In the last case, as long as they're not equal, we need to assume
+ * something has changed and do something about it.
+ */
if (dsp->ds_promisc == 0 && new_flags != 0) {
/*
* If only DLS_PROMISC_SAP, we don't turn on the
@@ -268,9 +318,7 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
*/
dsp->ds_promisc = new_flags;
err = mac_promisc_add(dsp->ds_mch, mptype,
- dls_rx_promisc, dsp, &dsp->ds_mph,
- (new_flags != DLS_PROMISC_SAP) ? 0 :
- MAC_PROMISC_FLAGS_NO_PHYS);
+ dls_rx_promisc, dsp, &dsp->ds_mph, mac_flags);
if (err != 0) {
dsp->ds_promisc = old_flags;
return (err);
@@ -281,7 +329,8 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
mac_promisc_remove(dsp->ds_vlan_mph);
dsp->ds_vlan_mph = NULL;
}
- } else if (dsp->ds_promisc != 0 && new_flags == 0) {
+ } else if (dsp->ds_promisc != 0 &&
+ (new_flags == 0 || doremove == B_TRUE)) {
ASSERT(dsp->ds_mph != NULL);
mac_promisc_remove(dsp->ds_mph);
@@ -296,19 +345,13 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
&dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
}
- } else if (dsp->ds_promisc == DLS_PROMISC_SAP && new_flags != 0 &&
- new_flags != dsp->ds_promisc) {
- /*
- * If the old flag is PROMISC_SAP, but the current flag has
- * changed to some new non-zero value, we need to turn the
- * physical promiscuous mode.
- */
+ } else if (new_flags != 0 && new_flags != old_flags) {
ASSERT(dsp->ds_mph != NULL);
mac_promisc_remove(dsp->ds_mph);
/* Honors both after-remove and before-add semantics! */
dsp->ds_promisc = new_flags;
err = mac_promisc_add(dsp->ds_mch, mptype,
- dls_rx_promisc, dsp, &dsp->ds_mph, 0);
+ dls_rx_promisc, dsp, &dsp->ds_mph, mac_flags);
if (err != 0)
dsp->ds_promisc = old_flags;
} else {
@@ -629,6 +672,22 @@ boolean_t
dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
void **ds_rx_arg, boolean_t loopback)
{
+ if (dsp->ds_promisc == 0) {
+ /*
+ * If there are active walkers of the mi_promisc_list when
+ * promiscuousness is disabled, ds_promisc will be cleared,
+ * but the DLS will remain on the mi_promisc_list until the
+ * walk is completed. If we do not recognize this case here,
+ * we won't properly execute the ds_promisc case in the common
+ * accept routine -- and we will potentially accept a packet
+ * that has originated with this DLS (which in turn can
+ * induce recursion and death by stack overflow). If
+ * ds_promisc is zero, we know that we are in this window --
+ * and we refuse to accept the packet.
+ */
+ return (B_FALSE);
+ }
+
return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
loopback));
}
@@ -659,7 +718,10 @@ dls_mac_active_set(dls_link_t *dlp)
* Set the function to start receiving packets.
*/
mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
+ } else if (dlp->dl_exclusive == B_TRUE) {
+ return (EBUSY);
}
+
dlp->dl_nactive++;
return (0);
}
@@ -685,7 +747,11 @@ dls_active_set(dld_str_t *dsp)
if (dsp->ds_passivestate == DLD_PASSIVE)
return (0);
- /* If we're already active, then there's nothing more to do. */
+ if (dsp->ds_dlp->dl_exclusive == B_TRUE &&
+ dsp->ds_passivestate != DLD_EXCLUSIVE)
+ return (EBUSY);
+
+ /* If we're already active, we need to check the link's exclusivity */
if ((dsp->ds_nactive == 0) &&
((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
/* except for ENXIO all other errors are mapped to EBUSY */
@@ -694,7 +760,8 @@ dls_active_set(dld_str_t *dsp)
return (err);
}
- dsp->ds_passivestate = DLD_ACTIVE;
+ dsp->ds_passivestate = dsp->ds_dlp->dl_exclusive == B_TRUE ?
+ DLD_EXCLUSIVE : DLD_ACTIVE;
dsp->ds_nactive++;
return (0);
}
@@ -725,7 +792,32 @@ dls_active_clear(dld_str_t *dsp, boolean_t all)
if (dsp->ds_nactive != 0)
return;
- ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
+ ASSERT(dsp->ds_passivestate == DLD_ACTIVE ||
+ dsp->ds_passivestate == DLD_EXCLUSIVE);
dls_mac_active_clear(dsp->ds_dlp);
+ /*
+ * We verify below to ensure that no other part of DLS has mucked with
+ * our exclusive state.
+ */
+ if (dsp->ds_passivestate == DLD_EXCLUSIVE)
+ VERIFY(dls_exclusive_set(dsp, B_FALSE) == 0);
dsp->ds_passivestate = DLD_UNINITIALIZED;
}
+
+int
+dls_exclusive_set(dld_str_t *dsp, boolean_t enable)
+{
+ ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+
+ if (enable == B_FALSE) {
+ dsp->ds_dlp->dl_exclusive = B_FALSE;
+ return (0);
+ }
+
+ if (dsp->ds_dlp->dl_nactive != 0)
+ return (EBUSY);
+
+ dsp->ds_dlp->dl_exclusive = B_TRUE;
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/dls/dls_link.c b/usr/src/uts/common/io/dls/dls_link.c
index 6b92a81e77..4a735d870e 100644
--- a/usr/src/uts/common/io/dls/dls_link.c
+++ b/usr/src/uts/common/io/dls/dls_link.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -34,6 +35,9 @@
#include <sys/dld_impl.h>
#include <sys/sdt.h>
#include <sys/atomic.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/datalink.h>
static kmem_cache_t *i_dls_link_cachep;
mod_hash_t *i_dls_link_hash;
@@ -579,6 +583,67 @@ drop:
freemsg(mp);
}
+/*
+ * We'd like to notify via sysevents that a link state change has occurred.
+ * There are a couple of challenges associated with this. The first is that if
+ * the link is flapping a lot, we may not see an accurate state when we launch
+ * the notification, we're told it changed, not what it changed to.
+ *
+ * The next problem is that all of the information that a user has associated
+ * with this device is the exact opposite of what we have on the dls_link_t. We
+ * have the name of the mac device, which has no bearing on what users see.
+ * Likewise, we don't have the datalink id either. So we're going to have to get
+ * this from dls.
+ *
+ * This is all further complicated by the fact that this could be going on in
+ * another thread at the same time as someone is tearing down the dls_link_t
+ * that we're associated with. We need to be careful not to grab the mac
+ * perimeter, otherwise we stand a good chance of deadlock.
+ */
+static void
+dls_link_notify(void *arg, mac_notify_type_t type)
+{
+ dls_link_t *dlp = arg;
+ dls_dl_handle_t dhp;
+ nvlist_t *nvp;
+ sysevent_t *event;
+ sysevent_id_t eid;
+
+ if (type != MAC_NOTE_LINK && type != MAC_NOTE_LOWLINK)
+ return;
+
+ /*
+ * If we can't find a devnet handle for this link, then there is no user
+ * knowable device for this at the moment and there's nothing we can
+ * really share with them that will make sense.
+ */
+ if (dls_devnet_hold_tmp_by_link(dlp, &dhp) != 0)
+ return;
+
+ /*
+ * Because we're attaching this nvlist_t to the sysevent, it'll get
+ * cleaned up when we call sysevent_free.
+ */
+ VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_int32(nvp, DATALINK_EV_LINK_ID,
+ dls_devnet_linkid(dhp)) == 0);
+ VERIFY(nvlist_add_string(nvp, DATALINK_EV_LINK_NAME,
+ dls_devnet_link(dhp)) == 0);
+ VERIFY(nvlist_add_int32(nvp, DATALINK_EV_ZONE_ID,
+ dls_devnet_getzid(dhp)) == 0);
+
+ dls_devnet_rele_tmp(dhp);
+
+ event = sysevent_alloc(EC_DATALINK, ESC_DATALINK_LINK_STATE,
+ ILLUMOS_KERN_PUB"dls", SE_SLEEP);
+ VERIFY(event != NULL);
+ (void) sysevent_attach_attributes(event, (sysevent_attr_list_t *)nvp);
+
+ (void) log_sysevent(event, SE_SLEEP, &eid);
+ sysevent_free(event);
+
+}
+
static void
i_dls_link_destroy(dls_link_t *dlp)
{
@@ -589,6 +654,9 @@ i_dls_link_destroy(dls_link_t *dlp)
/*
* Free the structure back to the cache.
*/
+ if (dlp->dl_mnh != NULL)
+ mac_notify_remove(dlp->dl_mnh, B_TRUE);
+
if (dlp->dl_mch != NULL)
mac_client_close(dlp->dl_mch, 0);
@@ -600,8 +668,10 @@ i_dls_link_destroy(dls_link_t *dlp)
dlp->dl_mh = NULL;
dlp->dl_mch = NULL;
dlp->dl_mip = NULL;
+ dlp->dl_mnh = NULL;
dlp->dl_unknowns = 0;
dlp->dl_nonip_cnt = 0;
+ dlp->dl_exclusive = B_FALSE;
kmem_cache_free(i_dls_link_cachep, dlp);
}
@@ -640,6 +710,8 @@ i_dls_link_create(const char *name, dls_link_t **dlpp)
if (err != 0)
goto bail;
+ dlp->dl_mnh = mac_notify_add(dlp->dl_mh, dls_link_notify, dlp);
+
DTRACE_PROBE2(dls__primary__client, char *, dlp->dl_name, void *,
dlp->dl_mch);
diff --git a/usr/src/uts/common/io/dls/dls_mgmt.c b/usr/src/uts/common/io/dls/dls_mgmt.c
index 049c4bd757..105c55c7ce 100644
--- a/usr/src/uts/common/io/dls/dls_mgmt.c
+++ b/usr/src/uts/common/io/dls/dls_mgmt.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -105,12 +106,13 @@ typedef struct dls_devnet_s {
zoneid_t dd_zid; /* current zone */
boolean_t dd_prop_loaded;
taskqid_t dd_prop_taskid;
+ boolean_t dd_transient; /* link goes away when zone does */
} dls_devnet_t;
static int i_dls_devnet_create_iptun(const char *, const char *,
datalink_id_t *);
static int i_dls_devnet_destroy_iptun(datalink_id_t);
-static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t);
+static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t, boolean_t);
static int dls_devnet_unset(const char *, datalink_id_t *, boolean_t);
/*ARGSUSED*/
@@ -145,7 +147,12 @@ dls_zone_remove(datalink_id_t linkid, void *arg)
dls_devnet_t *ddp;
if (dls_devnet_hold_tmp(linkid, &ddp) == 0) {
- (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID);
+ /*
+ * Don't bother moving transient links back to the global zone
+ * since we will simply delete them in dls_devnet_unset.
+ */
+ if (!ddp->dd_transient)
+ (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE);
dls_devnet_rele_tmp(ddp);
}
return (0);
@@ -526,6 +533,7 @@ dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
+ getlinkid.ld_zoneid = getzoneid();
if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
sizeof (retval))) == 0) {
@@ -534,6 +542,27 @@ dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
return (err);
}
+int
+dls_mgmt_get_linkid_in_zone(const char *link, datalink_id_t *linkid,
+ zoneid_t zid)
+{
+ dlmgmt_door_getlinkid_t getlinkid;
+ dlmgmt_getlinkid_retval_t retval;
+ int err;
+
+ ASSERT(getzoneid() == GLOBAL_ZONEID || zid == getzoneid());
+ getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
+ (void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
+ getlinkid.ld_zoneid = zid;
+
+ if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
+ sizeof (retval))) == 0) {
+ *linkid = retval.lr_linkid;
+ }
+ return (err);
+}
+
+
datalink_id_t
dls_mgmt_get_next(datalink_id_t linkid, datalink_class_t class,
datalink_media_t dmedia, uint32_t flags)
@@ -740,12 +769,23 @@ dls_devnet_stat_update(kstat_t *ksp, int rw)
* Create the "link" kstats.
*/
static void
-dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid)
+dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid, zoneid_t newzoneid)
{
kstat_t *ksp;
+ char *nm;
+ char kname[MAXLINKNAMELEN];
+
+ if (zoneid != newzoneid) {
+ ASSERT(zoneid == GLOBAL_ZONEID);
+ (void) snprintf(kname, sizeof (kname), "z%d_%s", newzoneid,
+ ddp->dd_linkname);
+ nm = kname;
+ } else {
+ nm = ddp->dd_linkname;
+ }
- if (dls_stat_create("link", 0, ddp->dd_linkname, zoneid,
- dls_devnet_stat_update, ddp, &ksp) == 0) {
+ if (dls_stat_create("link", 0, nm, zoneid,
+ dls_devnet_stat_update, ddp, &ksp, newzoneid) == 0) {
ASSERT(ksp != NULL);
if (zoneid == ddp->dd_owner_zid) {
ASSERT(ddp->dd_ksp == NULL);
@@ -765,12 +805,12 @@ dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
{
if (zoneid == ddp->dd_owner_zid) {
if (ddp->dd_ksp != NULL) {
- kstat_delete(ddp->dd_ksp);
+ dls_stat_delete(ddp->dd_ksp);
ddp->dd_ksp = NULL;
}
} else {
if (ddp->dd_zone_ksp != NULL) {
- kstat_delete(ddp->dd_zone_ksp);
+ dls_stat_delete(ddp->dd_zone_ksp);
ddp->dd_zone_ksp = NULL;
}
}
@@ -781,15 +821,25 @@ dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
* and create the new set using the new name.
*/
static void
-dls_devnet_stat_rename(dls_devnet_t *ddp)
+dls_devnet_stat_rename(dls_devnet_t *ddp, boolean_t zoneinit)
{
if (ddp->dd_ksp != NULL) {
- kstat_delete(ddp->dd_ksp);
+ dls_stat_delete(ddp->dd_ksp);
ddp->dd_ksp = NULL;
}
- /* We can't rename a link while it's assigned to a non-global zone. */
+ if (zoneinit && ddp->dd_zone_ksp != NULL) {
+ dls_stat_delete(ddp->dd_zone_ksp);
+ ddp->dd_zone_ksp = NULL;
+ }
+ /*
+ * We can't rename a link while it's assigned to a non-global zone
+ * unless we're first initializing the zone while readying it.
+ */
ASSERT(ddp->dd_zone_ksp == NULL);
- dls_devnet_stat_create(ddp, ddp->dd_owner_zid);
+ dls_devnet_stat_create(ddp, ddp->dd_owner_zid,
+ (zoneinit ? ddp->dd_zid : ddp->dd_owner_zid));
+ if (zoneinit)
+ dls_devnet_stat_create(ddp, ddp->dd_zid, ddp->dd_zid);
}
/*
@@ -878,7 +928,8 @@ done:
rw_exit(&i_dls_devnet_lock);
if (err == 0) {
if (zoneid != GLOBAL_ZONEID &&
- (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE)) != 0)
+ (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE,
+ B_FALSE)) != 0)
(void) dls_devnet_unset(macname, &linkid, B_TRUE);
/*
* The kstat subsystem holds its own locks (rather perimeter)
@@ -887,7 +938,7 @@ done:
* lock hierarchy is kstat locks -> i_dls_devnet_lock.
*/
if (stat_create)
- dls_devnet_stat_create(ddp, zoneid);
+ dls_devnet_stat_create(ddp, zoneid, zoneid);
if (ddpp != NULL)
*ddpp = ddp;
}
@@ -924,17 +975,78 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
ASSERT(ddp->dd_ref != 0);
if ((ddp->dd_ref != 1) || (!wait &&
(ddp->dd_tref != 0 || ddp->dd_prop_taskid != NULL))) {
- mutex_exit(&ddp->dd_mutex);
- rw_exit(&i_dls_devnet_lock);
- return (EBUSY);
+ int zstatus = 0;
+
+ /*
+ * There are a couple of alternatives that might be going on
+ * here; a) the zone is shutting down and it has a transient
+ * link assigned, in which case we want to clean it up instead
+ * of moving it back to the global zone, or b) its possible
+ * that we're trying to clean up an orphaned vnic that was
+ * delegated to a zone and which wasn't cleaned up properly
+ * when the zone went away. Check for either of these cases
+ * before we simply return EBUSY.
+ *
+ * zstatus indicates which situation we are dealing with:
+ * 0 - means return EBUSY
+ * 1 - means case (a), cleanup transient link
+ * -1 - means case (b), orphained VNIC
+ */
+ if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) {
+ zone_t *zp;
+
+ if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) {
+ zstatus = -1;
+ } else {
+ if (ddp->dd_transient) {
+ zone_status_t s = zone_status_get(zp);
+
+ if (s >= ZONE_IS_SHUTTING_DOWN)
+ zstatus = 1;
+ }
+ zone_rele(zp);
+ }
+ }
+
+ if (zstatus == 0) {
+ mutex_exit(&ddp->dd_mutex);
+ rw_exit(&i_dls_devnet_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * We want to delete the link, reset ref to 1;
+ */
+ if (zstatus == -1)
+ /* Log a warning, but continue in this case */
+ cmn_err(CE_WARN, "clear orphaned datalink: %s\n",
+ ddp->dd_linkname);
+ ddp->dd_ref = 1;
}
ddp->dd_flags |= DD_CONDEMNED;
ddp->dd_ref--;
*id = ddp->dd_linkid;
- if (ddp->dd_zid != GLOBAL_ZONEID)
- (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE);
+ if (ddp->dd_zid != GLOBAL_ZONEID) {
+ /*
+ * We need to release the dd_mutex before we try and destroy the
+ * stat. When we destroy it, we'll need to grab the lock for the
+ * kstat but if there's a concurrent reader of the kstat, we'll
+ * be blocked on it. This will lead to deadlock because these
+ * kstats employ a ks_update function (dls_devnet_stat_update)
+ * which needs the dd_mutex that we currently hold.
+ *
+ * Because we've already flagged the dls_devnet_t as
+ * DD_CONDEMNED and we still have a write lock on
+ * i_dls_devnet_lock, we should be able to release the dd_mutex.
+ */
+ mutex_exit(&ddp->dd_mutex);
+ dls_devnet_stat_destroy(ddp, ddp->dd_zid);
+ mutex_enter(&ddp->dd_mutex);
+ (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE,
+ B_FALSE);
+ }
/*
* Remove this dls_devnet_t from the hash table.
@@ -960,8 +1072,15 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
ASSERT(ddp->dd_tref == 0 && ddp->dd_prop_taskid == NULL);
}
- if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
+ /*
+ * See the earlier call in this function for an explanation.
+ */
+ mutex_exit(&ddp->dd_mutex);
dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
+ mutex_enter(&ddp->dd_mutex);
+ }
+
ddp->dd_prop_loaded = B_FALSE;
ddp->dd_linkid = DATALINK_INVALID_LINKID;
@@ -972,6 +1091,39 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
return (0);
}
+/*
+ * This is a private hold routine used when we already have the dls_link_t, thus
+ * we know that it cannot go away.
+ */
+int
+dls_devnet_hold_tmp_by_link(dls_link_t *dlp, dls_dl_handle_t *ddhp)
+{
+ int err;
+ dls_devnet_t *ddp = NULL;
+
+ rw_enter(&i_dls_devnet_lock, RW_WRITER);
+ if ((err = mod_hash_find(i_dls_devnet_hash,
+ (mod_hash_key_t)dlp->dl_name, (mod_hash_val_t *)&ddp)) != 0) {
+ ASSERT(err == MH_ERR_NOTFOUND);
+ rw_exit(&i_dls_devnet_lock);
+ return (ENOENT);
+ }
+
+ mutex_enter(&ddp->dd_mutex);
+ ASSERT(ddp->dd_ref > 0);
+ if (ddp->dd_flags & DD_CONDEMNED) {
+ mutex_exit(&ddp->dd_mutex);
+ rw_exit(&i_dls_devnet_lock);
+ return (ENOENT);
+ }
+ ddp->dd_tref++;
+ mutex_exit(&ddp->dd_mutex);
+ rw_exit(&i_dls_devnet_lock);
+
+ *ddhp = ddp;
+ return (0);
+}
+
static int
dls_devnet_hold_common(datalink_id_t linkid, dls_devnet_t **ddpp,
boolean_t tmp_hold)
@@ -1111,7 +1263,7 @@ dls_devnet_rele(dls_devnet_t *ddp)
}
static int
-dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
+dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp, zoneid_t zid)
{
char drv[MAXLINKNAMELEN];
uint_t ppa;
@@ -1121,7 +1273,7 @@ dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
dls_dev_handle_t ddh;
int err;
- if ((err = dls_mgmt_get_linkid(link, &linkid)) == 0)
+ if ((err = dls_mgmt_get_linkid_in_zone(link, &linkid, zid)) == 0)
return (dls_devnet_hold(linkid, ddpp));
/*
@@ -1261,9 +1413,15 @@ dls_devnet_phydev(datalink_id_t vlanid, dev_t *devp)
*
* This case does not change the <link name, linkid> mapping, so the link's
* kstats need to be updated with using name associated the given id2.
+ *
+ * The zonename parameter is used to allow us to create a VNIC in the global
+ * zone which is assigned to a non-global zone. Since there is a race condition
+ * in the create process if two VNICs have the same name, we need to rename it
+ * after it has been assigned to the zone.
*/
int
-dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
+dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link,
+ boolean_t zoneinit)
{
dls_dev_handle_t ddh = NULL;
int err = 0;
@@ -1313,13 +1471,16 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
* is currently accessing the link kstats, or if the link is on-loan
* to a non-global zone. Then set the DD_KSTAT_CHANGING flag to
* prevent any access to the kstats while we delete and recreate
- * kstats below.
+ * kstats below. However, we skip this check if we're renaming the
+ * vnic as part of bringing it up for a zone.
*/
mutex_enter(&ddp->dd_mutex);
- if (ddp->dd_ref > 1) {
- mutex_exit(&ddp->dd_mutex);
- err = EBUSY;
- goto done;
+ if (!zoneinit) {
+ if (ddp->dd_ref > 1) {
+ mutex_exit(&ddp->dd_mutex);
+ err = EBUSY;
+ goto done;
+ }
}
ddp->dd_flags |= DD_KSTAT_CHANGING;
@@ -1333,7 +1494,15 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
/* rename mac client name and its flow if exists */
if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
goto done;
- (void) mac_rename_primary(mh, link);
+ if (zoneinit) {
+ char tname[MAXLINKNAMELEN];
+
+ (void) snprintf(tname, sizeof (tname), "z%d_%s",
+ ddp->dd_zid, link);
+ (void) mac_rename_primary(mh, tname);
+ } else {
+ (void) mac_rename_primary(mh, link);
+ }
mac_close(mh);
goto done;
}
@@ -1406,7 +1575,7 @@ done:
*/
rw_exit(&i_dls_devnet_lock);
if (err == 0)
- dls_devnet_stat_rename(ddp);
+ dls_devnet_stat_rename(ddp, zoneinit);
if (clear_dd_flag) {
mutex_enter(&ddp->dd_mutex);
@@ -1421,7 +1590,8 @@ done:
}
static int
-i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop)
+i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop,
+ boolean_t transient)
{
int err;
mac_perim_handle_t mph;
@@ -1454,6 +1624,7 @@ i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop)
}
if ((err = dls_link_setzid(ddp->dd_mac, new_zoneid)) == 0) {
ddp->dd_zid = new_zoneid;
+ ddp->dd_transient = transient;
devnet_need_rebuild = B_TRUE;
}
@@ -1468,7 +1639,7 @@ done:
}
int
-dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
+dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid, boolean_t transient)
{
dls_devnet_t *ddp;
int err;
@@ -1490,7 +1661,7 @@ dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
refheld = B_TRUE;
}
- if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE)) != 0) {
+ if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE, transient)) != 0) {
if (refheld)
dls_devnet_rele(ddp);
return (err);
@@ -1507,7 +1678,7 @@ dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
if (old_zid != GLOBAL_ZONEID)
dls_devnet_stat_destroy(ddh, old_zid);
if (new_zid != GLOBAL_ZONEID)
- dls_devnet_stat_create(ddh, new_zid);
+ dls_devnet_stat_create(ddh, new_zid, new_zid);
return (0);
}
@@ -1545,15 +1716,19 @@ dls_devnet_islinkvisible(datalink_id_t linkid, zoneid_t zoneid)
* Access a vanity naming node.
*/
int
-dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
+dls_devnet_open_in_zone(const char *link, dls_dl_handle_t *dhp, dev_t *devp,
+ zoneid_t zid)
{
dls_devnet_t *ddp;
dls_link_t *dlp;
- zoneid_t zid = getzoneid();
+ zoneid_t czid = getzoneid();
int err;
mac_perim_handle_t mph;
- if ((err = dls_devnet_hold_by_name(link, &ddp)) != 0)
+ if (czid != GLOBAL_ZONEID && czid != zid)
+ return (ENOENT);
+
+ if ((err = dls_devnet_hold_by_name(link, &ddp, zid)) != 0)
return (err);
dls_devnet_prop_task_wait(ddp);
@@ -1586,6 +1761,12 @@ dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
return (0);
}
+int
+dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
+{
+ return (dls_devnet_open_in_zone(link, dhp, devp, getzoneid()));
+}
+
/*
* Close access to a vanity naming node.
*/
@@ -1765,6 +1946,12 @@ i_dls_devnet_destroy_iptun(datalink_id_t linkid)
}
const char *
+dls_devnet_link(dls_dl_handle_t ddh)
+{
+ return (ddh->dd_linkname);
+}
+
+const char *
dls_devnet_mac(dls_dl_handle_t ddh)
{
return (ddh->dd_mac);
diff --git a/usr/src/uts/common/io/dls/dls_stat.c b/usr/src/uts/common/io/dls/dls_stat.c
index 51e4be7260..82dceff278 100644
--- a/usr/src/uts/common/io/dls/dls_stat.c
+++ b/usr/src/uts/common/io/dls/dls_stat.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
*/
/*
@@ -30,30 +31,33 @@
#include <sys/dld_impl.h>
#include <sys/mac_ether.h>
-static mac_stat_info_t i_dls_si[] = {
- { MAC_STAT_IFSPEED, "ifspeed", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_MULTIRCV, "multircv", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_BRDCSTRCV, "brdcstrcv", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_MULTIXMT, "multixmt", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_BRDCSTXMT, "brdcstxmt", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_NORCVBUF, "norcvbuf", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_IERRORS, "ierrors", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_NOXMTBUF, "noxmtbuf", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OERRORS, "oerrors", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_COLLISIONS, "collisions", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_RBYTES, "rbytes", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_IPACKETS, "ipackets", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OBYTES, "obytes", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OPACKETS, "opackets", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_RBYTES, "rbytes64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_IPACKETS, "ipackets64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_OBYTES, "obytes64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_OPACKETS, "opackets64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_LINK_STATE, "link_state", KSTAT_DATA_UINT32,
- (uint64_t)LINK_STATE_UNKNOWN}
-};
-
-#define STAT_INFO_COUNT (sizeof (i_dls_si) / sizeof (i_dls_si[0]))
+/*
+ * structure for link kstats
+ */
+typedef struct {
+ kstat_named_t dk_ifspeed;
+ kstat_named_t dk_multircv;
+ kstat_named_t dk_brdcstrcv;
+ kstat_named_t dk_multixmt;
+ kstat_named_t dk_brdcstxmt;
+ kstat_named_t dk_norcvbuf;
+ kstat_named_t dk_ierrors;
+ kstat_named_t dk_noxmtbuf;
+ kstat_named_t dk_oerrors;
+ kstat_named_t dk_collisions;
+ kstat_named_t dk_rbytes;
+ kstat_named_t dk_ipackets;
+ kstat_named_t dk_obytes;
+ kstat_named_t dk_opackets;
+ kstat_named_t dk_rbytes64;
+ kstat_named_t dk_ipackets64;
+ kstat_named_t dk_obytes64;
+ kstat_named_t dk_opackets64;
+ kstat_named_t dk_link_state;
+ kstat_named_t dk_link_duplex;
+ kstat_named_t dk_unknowns;
+ kstat_named_t dk_zonename;
+} dls_kstat_t;
/*
* Exported functions.
@@ -61,42 +65,54 @@ static mac_stat_info_t i_dls_si[] = {
int
dls_stat_update(kstat_t *ksp, dls_link_t *dlp, int rw)
{
- kstat_named_t *knp;
- uint_t i;
- uint64_t val;
+ dls_kstat_t *dkp = ksp->ks_data;
if (rw != KSTAT_READ)
return (EACCES);
- knp = (kstat_named_t *)ksp->ks_data;
- for (i = 0; i < STAT_INFO_COUNT; i++) {
- val = mac_stat_get(dlp->dl_mh, i_dls_si[i].msi_stat);
-
- switch (i_dls_si[i].msi_type) {
- case KSTAT_DATA_UINT64:
- knp->value.ui64 = val;
- break;
- case KSTAT_DATA_UINT32:
- knp->value.ui32 = (uint32_t)val;
- break;
- default:
- ASSERT(B_FALSE);
- }
-
- knp++;
- }
+ dkp->dk_ifspeed.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_IFSPEED);
+ dkp->dk_multircv.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_MULTIRCV);
+ dkp->dk_brdcstrcv.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_BRDCSTRCV);
+ dkp->dk_multixmt.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_MULTIXMT);
+ dkp->dk_brdcstxmt.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_BRDCSTXMT);
+ dkp->dk_norcvbuf.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_NORCVBUF);
+ dkp->dk_ierrors.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_IERRORS);
+ dkp->dk_noxmtbuf.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_NOXMTBUF);
+ dkp->dk_oerrors.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_OERRORS);
+ dkp->dk_collisions.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_COLLISIONS);
+ dkp->dk_rbytes.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_RBYTES);
+ dkp->dk_ipackets.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_IPACKETS);
+ dkp->dk_obytes.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_OBYTES);
+ dkp->dk_opackets.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_OPACKETS);
+ dkp->dk_rbytes64.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_RBYTES);
+ dkp->dk_ipackets64.value.ui64 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_IPACKETS);
+ dkp->dk_obytes64.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_OBYTES);
+ dkp->dk_opackets64.value.ui64 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_OPACKETS);
+ dkp->dk_link_state.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_LINK_STATE);
/*
* Ethernet specific kstat "link_duplex"
*/
if (dlp->dl_mip->mi_nativemedia != DL_ETHER) {
- knp->value.ui32 = LINK_DUPLEX_UNKNOWN;
+ dkp->dk_link_duplex.value.ui32 = LINK_DUPLEX_UNKNOWN;
} else {
- val = mac_stat_get(dlp->dl_mh, ETHER_STAT_LINK_DUPLEX);
- knp->value.ui32 = (uint32_t)val;
+ dkp->dk_link_duplex.value.ui32 =
+ (uint32_t)mac_stat_get(dlp->dl_mh, ETHER_STAT_LINK_DUPLEX);
}
- knp++;
- knp->value.ui32 = dlp->dl_unknowns;
+
+ dkp->dk_unknowns.value.ui32 = dlp->dl_unknowns;
return (0);
}
@@ -104,30 +120,66 @@ dls_stat_update(kstat_t *ksp, dls_link_t *dlp, int rw)
int
dls_stat_create(const char *module, int instance, const char *name,
zoneid_t zoneid, int (*update)(struct kstat *, int), void *private,
- kstat_t **kspp)
+ kstat_t **kspp, zoneid_t newzoneid)
{
kstat_t *ksp;
- kstat_named_t *knp;
- uint_t i;
+ zone_t *zone;
+ dls_kstat_t *dkp;
if ((ksp = kstat_create_zone(module, instance, name, "net",
- KSTAT_TYPE_NAMED, STAT_INFO_COUNT + 2, 0, zoneid)) == NULL) {
+ KSTAT_TYPE_NAMED, sizeof (dls_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zoneid)) == NULL) {
return (EINVAL);
}
ksp->ks_update = update;
ksp->ks_private = private;
+ dkp = ksp->ks_data = kmem_zalloc(sizeof (dls_kstat_t), KM_SLEEP);
+ if ((zone = zone_find_by_id(newzoneid)) != NULL) {
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ }
- knp = (kstat_named_t *)ksp->ks_data;
- for (i = 0; i < STAT_INFO_COUNT; i++) {
- kstat_named_init(knp, i_dls_si[i].msi_name,
- i_dls_si[i].msi_type);
- knp++;
+ kstat_named_init(&dkp->dk_ifspeed, "ifspeed", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_multircv, "multircv", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_brdcstrcv, "brdcstrcv", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_multixmt, "multixmt", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_brdcstxmt, "brdcstxmt", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_norcvbuf, "norcvbuf", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_ierrors, "ierrors", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_noxmtbuf, "noxmtbuf", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_oerrors, "oerrors", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_collisions, "collisions", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_rbytes, "rbytes", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_ipackets, "ipackets", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_obytes, "obytes", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_opackets, "opackets", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_rbytes64, "rbytes64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_ipackets64, "ipackets64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_obytes64, "obytes64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_opackets64, "opackets64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_link_state, "link_state", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_link_duplex, "link_duplex",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_unknowns, "unknowns", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_zonename, "zonename", KSTAT_DATA_STRING);
+
+ if (zone != NULL) {
+ kstat_named_setstr(&dkp->dk_zonename, zone->zone_name);
+ zone_rele(zone);
}
- kstat_named_init(knp++, "link_duplex", KSTAT_DATA_UINT32);
- kstat_named_init(knp, "unknowns", KSTAT_DATA_UINT32);
kstat_install(ksp);
*kspp = ksp;
return (0);
}
+
+void
+dls_stat_delete(kstat_t *ksp)
+{
+ void *data;
+ if (ksp != NULL) {
+ data = ksp->ks_data;
+ kstat_delete(ksp);
+ kmem_free(data, sizeof (dls_kstat_t));
+ }
+}
diff --git a/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE b/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..00aefb6f51
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE
@@ -0,0 +1,32 @@
+/*
+ * MegaRAID device driver for SAS2.0 controllers
+ * Copyright (c) 2009, LSI Logic Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
diff --git a/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..ac6d2d1b15
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+DR_SAS DRIVER
diff --git a/usr/src/uts/common/io/dr_sas/dr_sas.c b/usr/src/uts/common/io/dr_sas/dr_sas.c
new file mode 100644
index 0000000000..5b1dc82938
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/dr_sas.c
@@ -0,0 +1,5506 @@
+/*
+ * dr_sas.c: source for dr_sas driver
+ *
+ * MegaRAID device driver for SAS2.0 controllers
+ * Copyright (c) 2008-2009, LSI Logic Corporation.
+ * All rights reserved.
+ *
+ * Version:
+ * Author:
+ * Arun Chandrashekhar
+ * Manju R
+ * Rajesh Prabhakaran
+ * Seokmann Ju
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/cmn_err.h>
+#include <sys/kmem.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <sys/pci.h>
+#include <sys/scsi/scsi.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/atomic.h>
+#include <sys/signal.h>
+#include <sys/fs/dv_node.h> /* devfs_clean */
+
+#include "dr_sas.h"
+
+/*
+ * FMA header files
+ */
+#include <sys/ddifm.h>
+#include <sys/fm/protocol.h>
+#include <sys/fm/util.h>
+#include <sys/fm/io/ddi.h>
+
+/*
+ * Local static data
+ */
+static void *drsas_state = NULL;
+static int debug_level_g = CL_NONE;
+
+#pragma weak scsi_hba_open
+#pragma weak scsi_hba_close
+#pragma weak scsi_hba_ioctl
+
+static ddi_dma_attr_t drsas_generic_dma_attr = {
+ DMA_ATTR_V0, /* dma_attr_version */
+ 0, /* low DMA address range */
+ 0xFFFFFFFFU, /* high DMA address range */
+ 0xFFFFFFFFU, /* DMA counter register */
+ 8, /* DMA address alignment */
+ 0x07, /* DMA burstsizes */
+ 1, /* min DMA size */
+ 0xFFFFFFFFU, /* max DMA size */
+ 0xFFFFFFFFU, /* segment boundary */
+ DRSAS_MAX_SGE_CNT, /* dma_attr_sglen */
+ 512, /* granularity of device */
+ 0 /* bus specific DMA flags */
+};
+
+int32_t drsas_max_cap_maxxfer = 0x1000000;
+
+/*
+ * cb_ops contains base level routines
+ */
+static struct cb_ops drsas_cb_ops = {
+ drsas_open, /* open */
+ drsas_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ drsas_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ nodev, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_HOTPLUG, /* cb_flag */
+ CB_REV, /* cb_rev */
+ nodev, /* cb_aread */
+ nodev /* cb_awrite */
+};
+
+/*
+ * dev_ops contains configuration routines
+ */
+static struct dev_ops drsas_ops = {
+ DEVO_REV, /* rev, */
+ 0, /* refcnt */
+ drsas_getinfo, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ drsas_attach, /* attach */
+ drsas_detach, /* detach */
+ drsas_reset, /* reset */
+ &drsas_cb_ops, /* char/block ops */
+ NULL, /* bus ops */
+ NULL, /* power */
+ ddi_quiesce_not_supported, /* quiesce */
+};
+
+char _depends_on[] = "misc/scsi";
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type - driver */
+ DRSAS_VERSION,
+ &drsas_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, /* ml_rev - must be MODREV_1 */
+ &modldrv, /* ml_linkage */
+ NULL /* end of driver linkage */
+};
+
+static struct ddi_device_acc_attr endian_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+
+/*
+ * ************************************************************************** *
+ * *
+ * common entry points - for loadable kernel modules *
+ * *
+ * ************************************************************************** *
+ */
+
+int
+_init(void)
+{
+ int ret;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ ret = ddi_soft_state_init(&drsas_state,
+ sizeof (struct drsas_instance), 0);
+
+ if (ret != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: could not init state"));
+ return (ret);
+ }
+
+ if ((ret = scsi_hba_init(&modlinkage)) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: could not init scsi hba"));
+ ddi_soft_state_fini(&drsas_state);
+ return (ret);
+ }
+
+ ret = mod_install(&modlinkage);
+
+ if (ret != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: mod_install failed"));
+ scsi_hba_fini(&modlinkage);
+ ddi_soft_state_fini(&drsas_state);
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ if ((ret = mod_remove(&modlinkage)) != DDI_SUCCESS)
+ return (ret);
+
+ scsi_hba_fini(&modlinkage);
+
+ ddi_soft_state_fini(&drsas_state);
+
+ return (ret);
+}
+
+
+/*
+ * ************************************************************************** *
+ * *
+ * common entry points - for autoconfiguration *
+ * *
+ * ************************************************************************** *
+ */
+
+static int
+drsas_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance_no;
+ int nregs;
+ uint8_t added_isr_f = 0;
+ uint8_t added_soft_isr_f = 0;
+ uint8_t create_devctl_node_f = 0;
+ uint8_t create_scsi_node_f = 0;
+ uint8_t create_ioc_node_f = 0;
+ uint8_t tran_alloc_f = 0;
+ uint8_t irq;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t subsysvid;
+ uint16_t subsysid;
+ uint16_t command;
+ off_t reglength = 0;
+ int intr_types = 0;
+ char *data;
+ int msi_enable = 0;
+
+ scsi_hba_tran_t *tran;
+ ddi_dma_attr_t tran_dma_attr;
+ struct drsas_instance *instance;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* CONSTCOND */
+ ASSERT(NO_COMPETING_THREADS);
+
+ instance_no = ddi_get_instance(dip);
+
+ /*
+ * check to see whether this device is in a DMA-capable slot.
+ */
+ if (ddi_slaveonly(dip) == DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas%d: Device in slave-only slot, unused",
+ instance_no));
+ return (DDI_FAILURE);
+ }
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ con_log(CL_DLEVEL1, (CE_NOTE, "dr_sas: DDI_ATTACH"));
+ /* allocate the soft state for the instance */
+ if (ddi_soft_state_zalloc(drsas_state, instance_no)
+ != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas%d: Failed to allocate soft state",
+ instance_no));
+
+ return (DDI_FAILURE);
+ }
+
+ instance = (struct drsas_instance *)ddi_get_soft_state
+ (drsas_state, instance_no);
+
+ if (instance == NULL) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas%d: Bad soft state", instance_no));
+
+ ddi_soft_state_free(drsas_state, instance_no);
+
+ return (DDI_FAILURE);
+ }
+
+ bzero((caddr_t)instance,
+ sizeof (struct drsas_instance));
+
+ instance->func_ptr = kmem_zalloc(
+ sizeof (struct drsas_func_ptr), KM_SLEEP);
+ ASSERT(instance->func_ptr);
+
+ /* Setup the PCI configuration space handles */
+ if (pci_config_setup(dip, &instance->pci_handle) !=
+ DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas%d: pci config setup failed ",
+ instance_no));
+
+ kmem_free(instance->func_ptr,
+ sizeof (struct drsas_func_ptr));
+ ddi_soft_state_free(drsas_state, instance_no);
+
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to get registers."));
+
+ pci_config_teardown(&instance->pci_handle);
+ kmem_free(instance->func_ptr,
+ sizeof (struct drsas_func_ptr));
+ ddi_soft_state_free(drsas_state, instance_no);
+
+ return (DDI_FAILURE);
+ }
+
+ vendor_id = pci_config_get16(instance->pci_handle,
+ PCI_CONF_VENID);
+ device_id = pci_config_get16(instance->pci_handle,
+ PCI_CONF_DEVID);
+
+ subsysvid = pci_config_get16(instance->pci_handle,
+ PCI_CONF_SUBVENID);
+ subsysid = pci_config_get16(instance->pci_handle,
+ PCI_CONF_SUBSYSID);
+
+ pci_config_put16(instance->pci_handle, PCI_CONF_COMM,
+ (pci_config_get16(instance->pci_handle,
+ PCI_CONF_COMM) | PCI_COMM_ME));
+ irq = pci_config_get8(instance->pci_handle,
+ PCI_CONF_ILINE);
+
+ con_log(CL_DLEVEL1, (CE_CONT, "dr_sas%d: "
+ "0x%x:0x%x 0x%x:0x%x, irq:%d drv-ver:%s",
+ instance_no, vendor_id, device_id, subsysvid,
+ subsysid, irq, DRSAS_VERSION));
+
+ /* enable bus-mastering */
+ command = pci_config_get16(instance->pci_handle,
+ PCI_CONF_COMM);
+
+ if (!(command & PCI_COMM_ME)) {
+ command |= PCI_COMM_ME;
+
+ pci_config_put16(instance->pci_handle,
+ PCI_CONF_COMM, command);
+
+ con_log(CL_ANN, (CE_CONT, "dr_sas%d: "
+ "enable bus-mastering", instance_no));
+ } else {
+ con_log(CL_DLEVEL1, (CE_CONT, "dr_sas%d: "
+ "bus-mastering already set", instance_no));
+ }
+
+ /* initialize function pointers */
+ if ((device_id == PCI_DEVICE_ID_LSI_2108VDE) ||
+ (device_id == PCI_DEVICE_ID_LSI_2108V)) {
+ con_log(CL_DLEVEL1, (CE_CONT, "dr_sas%d: "
+ "2108V/DE detected", instance_no));
+ instance->func_ptr->read_fw_status_reg =
+ read_fw_status_reg_ppc;
+ instance->func_ptr->issue_cmd = issue_cmd_ppc;
+ instance->func_ptr->issue_cmd_in_sync_mode =
+ issue_cmd_in_sync_mode_ppc;
+ instance->func_ptr->issue_cmd_in_poll_mode =
+ issue_cmd_in_poll_mode_ppc;
+ instance->func_ptr->enable_intr =
+ enable_intr_ppc;
+ instance->func_ptr->disable_intr =
+ disable_intr_ppc;
+ instance->func_ptr->intr_ack = intr_ack_ppc;
+ } else {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: Invalid device detected"));
+
+ pci_config_teardown(&instance->pci_handle);
+ kmem_free(instance->func_ptr,
+ sizeof (struct drsas_func_ptr));
+ ddi_soft_state_free(drsas_state, instance_no);
+
+ return (DDI_FAILURE);
+ }
+
+ instance->baseaddress = pci_config_get32(
+ instance->pci_handle, PCI_CONF_BASE0);
+ instance->baseaddress &= 0x0fffc;
+
+ instance->dip = dip;
+ instance->vendor_id = vendor_id;
+ instance->device_id = device_id;
+ instance->subsysvid = subsysvid;
+ instance->subsysid = subsysid;
+ instance->instance = instance_no;
+
+ /* Initialize FMA */
+ instance->fm_capabilities = ddi_prop_get_int(
+ DDI_DEV_T_ANY, instance->dip, DDI_PROP_DONTPASS,
+ "fm-capable", DDI_FM_EREPORT_CAPABLE |
+ DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE
+ | DDI_FM_ERRCB_CAPABLE);
+
+ drsas_fm_init(instance);
+
+ /* Initialize Interrupts */
+ if ((ddi_dev_regsize(instance->dip,
+ REGISTER_SET_IO_2108, &reglength) != DDI_SUCCESS) ||
+ reglength < MINIMUM_MFI_MEM_SZ) {
+ return (DDI_FAILURE);
+ }
+ if (reglength > DEFAULT_MFI_MEM_SZ) {
+ reglength = DEFAULT_MFI_MEM_SZ;
+ con_log(CL_DLEVEL1, (CE_NOTE,
+ "dr_sas: register length to map is "
+ "0x%lx bytes", reglength));
+ }
+ if (ddi_regs_map_setup(instance->dip,
+ REGISTER_SET_IO_2108, &instance->regmap, 0,
+ reglength, &endian_attr, &instance->regmap_handle)
+ != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: couldn't map control registers"));
+ goto fail_attach;
+ }
+
+ /*
+ * Disable Interrupt Now.
+ * Setup Software interrupt
+ */
+ instance->func_ptr->disable_intr(instance);
+
+ msi_enable = 0;
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
+ "drsas-enable-msi", &data) == DDI_SUCCESS) {
+ if (strncmp(data, "yes", 3) == 0) {
+ msi_enable = 1;
+ con_log(CL_ANN, (CE_WARN,
+ "msi_enable = %d ENABLED",
+ msi_enable));
+ }
+ ddi_prop_free(data);
+ }
+
+ con_log(CL_DLEVEL1, (CE_WARN, "msi_enable = %d",
+ msi_enable));
+
+ /* Check for all supported interrupt types */
+ if (ddi_intr_get_supported_types(
+ dip, &intr_types) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "ddi_intr_get_supported_types() failed"));
+ goto fail_attach;
+ }
+
+ con_log(CL_DLEVEL1, (CE_NOTE,
+ "ddi_intr_get_supported_types() ret: 0x%x",
+ intr_types));
+
+ /* Initialize and Setup Interrupt handler */
+ if (msi_enable && (intr_types & DDI_INTR_TYPE_MSIX)) {
+ if (drsas_add_intrs(instance,
+ DDI_INTR_TYPE_MSIX) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "MSIX interrupt query failed"));
+ goto fail_attach;
+ }
+ instance->intr_type = DDI_INTR_TYPE_MSIX;
+ } else if (msi_enable && (intr_types &
+ DDI_INTR_TYPE_MSI)) {
+ if (drsas_add_intrs(instance,
+ DDI_INTR_TYPE_MSI) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "MSI interrupt query failed"));
+ goto fail_attach;
+ }
+ instance->intr_type = DDI_INTR_TYPE_MSI;
+ } else if (intr_types & DDI_INTR_TYPE_FIXED) {
+ msi_enable = 0;
+ if (drsas_add_intrs(instance,
+ DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "FIXED interrupt query failed"));
+ goto fail_attach;
+ }
+ instance->intr_type = DDI_INTR_TYPE_FIXED;
+ } else {
+ con_log(CL_ANN, (CE_WARN, "Device cannot "
+ "suppport either FIXED or MSI/X "
+ "interrupts"));
+ goto fail_attach;
+ }
+
+ added_isr_f = 1;
+
+ /* setup the mfi based low level driver */
+ if (init_mfi(instance) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: "
+ "could not initialize the low level driver"));
+
+ goto fail_attach;
+ }
+
+ /* Initialize all Mutex */
+ INIT_LIST_HEAD(&instance->completed_pool_list);
+ mutex_init(&instance->completed_pool_mtx,
+ "completed_pool_mtx", MUTEX_DRIVER,
+ DDI_INTR_PRI(instance->intr_pri));
+
+ mutex_init(&instance->int_cmd_mtx, "int_cmd_mtx",
+ MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri));
+ cv_init(&instance->int_cmd_cv, NULL, CV_DRIVER, NULL);
+
+ mutex_init(&instance->cmd_pool_mtx, "cmd_pool_mtx",
+ MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri));
+
+ /* Register our soft-isr for highlevel interrupts. */
+ instance->isr_level = instance->intr_pri;
+ if (instance->isr_level == HIGH_LEVEL_INTR) {
+ if (ddi_add_softintr(dip, DDI_SOFTINT_HIGH,
+ &instance->soft_intr_id, NULL, NULL,
+ drsas_softintr, (caddr_t)instance) !=
+ DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ " Software ISR did not register"));
+
+ goto fail_attach;
+ }
+
+ added_soft_isr_f = 1;
+ }
+
+ /* Allocate a transport structure */
+ tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
+
+ if (tran == NULL) {
+ con_log(CL_ANN, (CE_WARN,
+ "scsi_hba_tran_alloc failed"));
+ goto fail_attach;
+ }
+
+ tran_alloc_f = 1;
+
+ instance->tran = tran;
+
+ tran->tran_hba_private = instance;
+ tran->tran_tgt_init = drsas_tran_tgt_init;
+ tran->tran_tgt_probe = scsi_hba_probe;
+ tran->tran_tgt_free = drsas_tran_tgt_free;
+ tran->tran_init_pkt = drsas_tran_init_pkt;
+ tran->tran_start = drsas_tran_start;
+ tran->tran_abort = drsas_tran_abort;
+ tran->tran_reset = drsas_tran_reset;
+ tran->tran_getcap = drsas_tran_getcap;
+ tran->tran_setcap = drsas_tran_setcap;
+ tran->tran_destroy_pkt = drsas_tran_destroy_pkt;
+ tran->tran_dmafree = drsas_tran_dmafree;
+ tran->tran_sync_pkt = drsas_tran_sync_pkt;
+ tran->tran_bus_config = drsas_tran_bus_config;
+
+ tran_dma_attr = drsas_generic_dma_attr;
+ tran_dma_attr.dma_attr_sgllen = instance->max_num_sge;
+
+ /* Attach this instance of the hba */
+ if (scsi_hba_attach_setup(dip, &tran_dma_attr, tran, 0)
+ != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "scsi_hba_attach failed"));
+
+ goto fail_attach;
+ }
+
+ /* create devctl node for cfgadm command */
+ if (ddi_create_minor_node(dip, "devctl",
+ S_IFCHR, INST2DEVCTL(instance_no),
+ DDI_NT_SCSI_NEXUS, 0) == DDI_FAILURE) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to create devctl node."));
+
+ goto fail_attach;
+ }
+
+ create_devctl_node_f = 1;
+
+ /* create scsi node for cfgadm command */
+ if (ddi_create_minor_node(dip, "scsi", S_IFCHR,
+ INST2SCSI(instance_no),
+ DDI_NT_SCSI_ATTACHMENT_POINT, 0) ==
+ DDI_FAILURE) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to create scsi node."));
+
+ goto fail_attach;
+ }
+
+ create_scsi_node_f = 1;
+
+ (void) sprintf(instance->iocnode, "%d:lsirdctl",
+ instance_no);
+
+ /*
+ * Create a node for applications
+ * for issuing ioctl to the driver.
+ */
+ if (ddi_create_minor_node(dip, instance->iocnode,
+ S_IFCHR, INST2LSIRDCTL(instance_no),
+ DDI_PSEUDO, 0) == DDI_FAILURE) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to create ioctl node."));
+
+ goto fail_attach;
+ }
+
+ create_ioc_node_f = 1;
+
+ /* Create a taskq to handle dr events */
+ if ((instance->taskq = ddi_taskq_create(dip,
+ "drsas_dr_taskq", 1,
+ TASKQ_DEFAULTPRI, 0)) == NULL) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to create taskq "));
+ instance->taskq = NULL;
+ goto fail_attach;
+ }
+
+ /* enable interrupt */
+ instance->func_ptr->enable_intr(instance);
+
+ /* initiate AEN */
+ if (start_mfi_aen(instance)) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: failed to initiate AEN."));
+ goto fail_initiate_aen;
+ }
+
+ con_log(CL_DLEVEL1, (CE_NOTE,
+ "AEN started for instance %d.", instance_no));
+
+ /* Finally! We are on the air. */
+ ddi_report_dev(dip);
+
+ if (drsas_check_acc_handle(instance->regmap_handle) !=
+ DDI_SUCCESS) {
+ goto fail_attach;
+ }
+ if (drsas_check_acc_handle(instance->pci_handle) !=
+ DDI_SUCCESS) {
+ goto fail_attach;
+ }
+ instance->dr_ld_list =
+ kmem_zalloc(MRDRV_MAX_LD * sizeof (struct drsas_ld),
+ KM_SLEEP);
+ break;
+ case DDI_PM_RESUME:
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: DDI_PM_RESUME"));
+ break;
+ case DDI_RESUME:
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: DDI_RESUME"));
+ break;
+ default:
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: invalid attach cmd=%x", cmd));
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+
+fail_initiate_aen:
+fail_attach:
+ if (create_devctl_node_f) {
+ ddi_remove_minor_node(dip, "devctl");
+ }
+
+ if (create_scsi_node_f) {
+ ddi_remove_minor_node(dip, "scsi");
+ }
+
+ if (create_ioc_node_f) {
+ ddi_remove_minor_node(dip, instance->iocnode);
+ }
+
+ if (tran_alloc_f) {
+ scsi_hba_tran_free(tran);
+ }
+
+
+ if (added_soft_isr_f) {
+ ddi_remove_softintr(instance->soft_intr_id);
+ }
+
+ if (added_isr_f) {
+ drsas_rem_intrs(instance);
+ }
+
+ if (instance && instance->taskq) {
+ ddi_taskq_destroy(instance->taskq);
+ }
+
+ drsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+
+ drsas_fm_fini(instance);
+
+ pci_config_teardown(&instance->pci_handle);
+
+ ddi_soft_state_free(drsas_state, instance_no);
+
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: return failure from drsas_attach"));
+
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+drsas_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+ int rval;
+ int drsas_minor = getminor((dev_t)arg);
+
+ struct drsas_instance *instance;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ instance = (struct drsas_instance *)
+ ddi_get_soft_state(drsas_state,
+ MINOR2INST(drsas_minor));
+
+ if (instance == NULL) {
+ *resultp = NULL;
+ rval = DDI_FAILURE;
+ } else {
+ *resultp = instance->dip;
+ rval = DDI_SUCCESS;
+ }
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)instance;
+ rval = DDI_SUCCESS;
+ break;
+ default:
+ *resultp = NULL;
+ rval = DDI_FAILURE;
+ }
+
+ return (rval);
+}
+
+static int
+drsas_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int instance_no;
+
+ struct drsas_instance *instance;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* CONSTCOND */
+ ASSERT(NO_COMPETING_THREADS);
+
+ instance_no = ddi_get_instance(dip);
+
+ instance = (struct drsas_instance *)ddi_get_soft_state(drsas_state,
+ instance_no);
+
+ if (!instance) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas:%d could not get instance in detach",
+ instance_no));
+
+ return (DDI_FAILURE);
+ }
+
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas%d: detaching device 0x%4x:0x%4x:0x%4x:0x%4x",
+ instance_no, instance->vendor_id, instance->device_id,
+ instance->subsysvid, instance->subsysid));
+
+ switch (cmd) {
+ case DDI_DETACH:
+ con_log(CL_ANN, (CE_NOTE,
+ "drsas_detach: DDI_DETACH"));
+
+ if (scsi_hba_detach(dip) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas:%d failed to detach",
+ instance_no));
+
+ return (DDI_FAILURE);
+ }
+
+ scsi_hba_tran_free(instance->tran);
+
+ flush_cache(instance);
+
+ if (abort_aen_cmd(instance, instance->aen_cmd)) {
+ con_log(CL_ANN, (CE_WARN, "drsas_detach: "
+ "failed to abort prevous AEN command"));
+
+ return (DDI_FAILURE);
+ }
+
+ instance->func_ptr->disable_intr(instance);
+
+ if (instance->isr_level == HIGH_LEVEL_INTR) {
+ ddi_remove_softintr(instance->soft_intr_id);
+ }
+
+ drsas_rem_intrs(instance);
+
+ if (instance->taskq) {
+ ddi_taskq_destroy(instance->taskq);
+ }
+ kmem_free(instance->dr_ld_list, MRDRV_MAX_LD
+ * sizeof (struct drsas_ld));
+ free_space_for_mfi(instance);
+
+ drsas_fm_fini(instance);
+
+ pci_config_teardown(&instance->pci_handle);
+
+ kmem_free(instance->func_ptr,
+ sizeof (struct drsas_func_ptr));
+
+ ddi_soft_state_free(drsas_state, instance_no);
+ break;
+ case DDI_PM_SUSPEND:
+ con_log(CL_ANN, (CE_NOTE,
+ "drsas_detach: DDI_PM_SUSPEND"));
+
+ break;
+ case DDI_SUSPEND:
+ con_log(CL_ANN, (CE_NOTE,
+ "drsas_detach: DDI_SUSPEND"));
+
+ break;
+ default:
+ con_log(CL_ANN, (CE_WARN,
+ "invalid detach command:0x%x", cmd));
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * ************************************************************************** *
+ * *
+ * common entry points - for character driver types *
+ * *
+ * ************************************************************************** *
+ */
+static int
+drsas_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
+{
+ int rval = 0;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* Check root permissions */
+ if (drv_priv(credp) != 0) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: Non-root ioctl access denied!"));
+ return (EPERM);
+ }
+
+ /* Verify we are being opened as a character device */
+ if (otyp != OTYP_CHR) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: ioctl node must be a char node"));
+ return (EINVAL);
+ }
+
+ if (ddi_get_soft_state(drsas_state, MINOR2INST(getminor(*dev)))
+ == NULL) {
+ return (ENXIO);
+ }
+
+ if (scsi_hba_open) {
+ rval = scsi_hba_open(dev, openflags, otyp, credp);
+ }
+
+ return (rval);
+}
+
+static int
+drsas_close(dev_t dev, int openflags, int otyp, cred_t *credp)
+{
+ int rval = 0;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* no need for locks! */
+
+ if (scsi_hba_close) {
+ rval = scsi_hba_close(dev, openflags, otyp, credp);
+ }
+
+ return (rval);
+}
+
+static int
+drsas_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ int rval = 0;
+
+ struct drsas_instance *instance;
+ struct drsas_ioctl *ioctl;
+ struct drsas_aen aen;
+ int i;
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ instance = ddi_get_soft_state(drsas_state, MINOR2INST(getminor(dev)));
+
+ if (instance == NULL) {
+ /* invalid minor number */
+ con_log(CL_ANN, (CE_WARN, "dr_sas: adapter not found."));
+ return (ENXIO);
+ }
+
+ ioctl = (struct drsas_ioctl *)kmem_zalloc(sizeof (struct drsas_ioctl),
+ KM_SLEEP);
+ ASSERT(ioctl);
+
+ switch ((uint_t)cmd) {
+ case DRSAS_IOCTL_FIRMWARE:
+ for (i = 0; i < sizeof (struct drsas_ioctl); i++) {
+ if (ddi_copyin((uint8_t *)arg+i,
+ (uint8_t *)ioctl+i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "drsas_ioctl "
+ "ERROR IOCTL copyin"));
+ kmem_free(ioctl,
+ sizeof (struct drsas_ioctl));
+ return (EFAULT);
+ }
+ }
+ if (ioctl->control_code == DRSAS_DRIVER_IOCTL_COMMON) {
+ rval = handle_drv_ioctl(instance, ioctl, mode);
+ } else {
+ rval = handle_mfi_ioctl(instance, ioctl, mode);
+ }
+ for (i = 0; i < sizeof (struct drsas_ioctl) - 1; i++) {
+ if (ddi_copyout((uint8_t *)ioctl+i,
+ (uint8_t *)arg+i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "drsas_ioctl: ddi_copyout "
+ "failed"));
+ rval = 1;
+ break;
+ }
+ }
+
+ break;
+ case DRSAS_IOCTL_AEN:
+ for (i = 0; i < sizeof (struct drsas_aen); i++) {
+ if (ddi_copyin((uint8_t *)arg+i,
+ (uint8_t *)&aen+i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "drsas_ioctl: "
+ "ERROR AEN copyin"));
+ kmem_free(ioctl,
+ sizeof (struct drsas_ioctl));
+ return (EFAULT);
+ }
+ }
+
+ rval = handle_mfi_aen(instance, &aen);
+ for (i = 0; i < sizeof (struct drsas_aen); i++) {
+ if (ddi_copyout((uint8_t *)&aen + i,
+ (uint8_t *)arg + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "drsas_ioctl: "
+ "ddi_copyout failed"));
+ rval = 1;
+ break;
+ }
+ }
+
+ break;
+ default:
+ rval = scsi_hba_ioctl(dev, cmd, arg,
+ mode, credp, rvalp);
+
+ con_log(CL_DLEVEL1, (CE_NOTE, "drsas_ioctl: "
+ "scsi_hba_ioctl called, ret = %x.", rval));
+ }
+
+ kmem_free(ioctl, sizeof (struct drsas_ioctl));
+ return (rval);
+}
+
+/*
+ * ************************************************************************** *
+ * *
+ * common entry points - for block driver types *
+ * *
+ * ************************************************************************** *
+ */
+/*ARGSUSED*/
+static int
+drsas_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
+{
+ int instance_no;
+
+ struct drsas_instance *instance;
+
+ instance_no = ddi_get_instance(dip);
+ instance = (struct drsas_instance *)ddi_get_soft_state
+ (drsas_state, instance_no);
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ if (!instance) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas:%d could not get adapter "
+ "in reset", instance_no));
+ return (DDI_FAILURE);
+ }
+
+ instance->func_ptr->disable_intr(instance);
+
+ con_log(CL_ANN1, (CE_NOTE, "flushing cache for instance %d",
+ instance_no));
+
+ flush_cache(instance);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * ************************************************************************** *
+ * *
+ * entry points (SCSI HBA) *
+ * *
+ * ************************************************************************** *
+ */
+/*ARGSUSED*/
+static int
+drsas_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+ scsi_hba_tran_t *tran, struct scsi_device *sd)
+{
+ struct drsas_instance *instance;
+ uint16_t tgt = sd->sd_address.a_target;
+ uint8_t lun = sd->sd_address.a_lun;
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_tgt_init target %d lun %d",
+ tgt, lun));
+
+ instance = ADDR2MR(&sd->sd_address);
+
+ if (ndi_dev_is_persistent_node(tgt_dip) == 0) {
+ (void) ndi_merge_node(tgt_dip, drsas_name_node);
+ ddi_set_name_addr(tgt_dip, NULL);
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_tgt_init in "
+ "ndi_dev_is_persistent_node DDI_FAILURE t = %d l = %d",
+ tgt, lun));
+ return (DDI_FAILURE);
+ }
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_tgt_init dev_dip %p tgt_dip %p",
+ (void *)instance->dr_ld_list[tgt].dip, (void *)tgt_dip));
+
+ if (tgt < MRDRV_MAX_LD && lun == 0) {
+ if (instance->dr_ld_list[tgt].dip == NULL &&
+ strcmp(ddi_driver_name(sd->sd_dev), "sd") == 0) {
+ instance->dr_ld_list[tgt].dip = tgt_dip;
+ instance->dr_ld_list[tgt].lun_type = DRSAS_LD_LUN;
+ }
+ }
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static void
+drsas_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+ scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+ struct drsas_instance *instance;
+ int tgt = sd->sd_address.a_target;
+ int lun = sd->sd_address.a_lun;
+
+ instance = ADDR2MR(&sd->sd_address);
+
+ con_log(CL_ANN1, (CE_NOTE, "tgt_free t = %d l = %d", tgt, lun));
+
+ if (tgt < MRDRV_MAX_LD && lun == 0) {
+ if (instance->dr_ld_list[tgt].dip == tgt_dip) {
+ instance->dr_ld_list[tgt].dip = NULL;
+ }
+ }
+}
+
+static dev_info_t *
+drsas_find_child(struct drsas_instance *instance, uint16_t tgt, uint8_t lun)
+{
+ dev_info_t *child = NULL;
+ char addr[SCSI_MAXNAMELEN];
+ char tmp[MAXNAMELEN];
+
+ (void) sprintf(addr, "%x,%x", tgt, lun);
+ for (child = ddi_get_child(instance->dip); child;
+ child = ddi_get_next_sibling(child)) {
+
+ if (drsas_name_node(child, tmp, MAXNAMELEN) !=
+ DDI_SUCCESS) {
+ continue;
+ }
+
+ if (strcmp(addr, tmp) == 0) {
+ break;
+ }
+ }
+ con_log(CL_ANN1, (CE_NOTE, "drsas_find_child: return child = %p",
+ (void *)child));
+ return (child);
+}
+
+static int
+drsas_name_node(dev_info_t *dip, char *name, int len)
+{
+ int tgt, lun;
+
+ tgt = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "target", -1);
+ con_log(CL_ANN1, (CE_NOTE,
+ "drsas_name_node: dip %p tgt %d", (void *)dip, tgt));
+ if (tgt == -1) {
+ return (DDI_FAILURE);
+ }
+ lun = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "lun", -1);
+ con_log(CL_ANN1,
+ (CE_NOTE, "drsas_name_node: tgt %d lun %d", tgt, lun));
+ if (lun == -1) {
+ return (DDI_FAILURE);
+ }
+ (void) snprintf(name, len, "%x,%x", tgt, lun);
+ return (DDI_SUCCESS);
+}
+
+static struct scsi_pkt *
+drsas_tran_init_pkt(struct scsi_address *ap, register struct scsi_pkt *pkt,
+ struct buf *bp, int cmdlen, int statuslen, int tgtlen,
+ int flags, int (*callback)(), caddr_t arg)
+{
+ struct scsa_cmd *acmd;
+ struct drsas_instance *instance;
+ struct scsi_pkt *new_pkt;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ instance = ADDR2MR(ap);
+
+ /* step #1 : pkt allocation */
+ if (pkt == NULL) {
+ pkt = scsi_hba_pkt_alloc(instance->dip, ap, cmdlen, statuslen,
+ tgtlen, sizeof (struct scsa_cmd), callback, arg);
+ if (pkt == NULL) {
+ return (NULL);
+ }
+
+ acmd = PKT2CMD(pkt);
+
+ /*
+ * Initialize the new pkt - we redundantly initialize
+ * all the fields for illustrative purposes.
+ */
+ acmd->cmd_pkt = pkt;
+ acmd->cmd_flags = 0;
+ acmd->cmd_scblen = statuslen;
+ acmd->cmd_cdblen = cmdlen;
+ acmd->cmd_dmahandle = NULL;
+ acmd->cmd_ncookies = 0;
+ acmd->cmd_cookie = 0;
+ acmd->cmd_cookiecnt = 0;
+ acmd->cmd_nwin = 0;
+
+ pkt->pkt_address = *ap;
+ pkt->pkt_comp = (void (*)())NULL;
+ pkt->pkt_flags = 0;
+ pkt->pkt_time = 0;
+ pkt->pkt_resid = 0;
+ pkt->pkt_state = 0;
+ pkt->pkt_statistics = 0;
+ pkt->pkt_reason = 0;
+ new_pkt = pkt;
+ } else {
+ acmd = PKT2CMD(pkt);
+ new_pkt = NULL;
+ }
+
+ /* step #2 : dma allocation/move */
+ if (bp && bp->b_bcount != 0) {
+ if (acmd->cmd_dmahandle == NULL) {
+ if (drsas_dma_alloc(instance, pkt, bp, flags,
+ callback) == DDI_FAILURE) {
+ if (new_pkt) {
+ scsi_hba_pkt_free(ap, new_pkt);
+ }
+ return ((struct scsi_pkt *)NULL);
+ }
+ } else {
+ if (drsas_dma_move(instance, pkt, bp) == DDI_FAILURE) {
+ return ((struct scsi_pkt *)NULL);
+ }
+ }
+ }
+
+ return (pkt);
+}
+
+static int
+drsas_tran_start(struct scsi_address *ap, register struct scsi_pkt *pkt)
+{
+ uchar_t cmd_done = 0;
+
+ struct drsas_instance *instance = ADDR2MR(ap);
+ struct drsas_cmd *cmd;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d:SCSI CDB[0]=0x%x",
+ __func__, __LINE__, pkt->pkt_cdbp[0]));
+
+ pkt->pkt_reason = CMD_CMPLT;
+ *pkt->pkt_scbp = STATUS_GOOD; /* clear arq scsi_status */
+
+ cmd = build_cmd(instance, ap, pkt, &cmd_done);
+
+ /*
+ * Check if the command is already completed by the drsas_build_cmd()
+ * routine. In which case the busy_flag would be clear and scb will be
+ * NULL and appropriate reason provided in pkt_reason field
+ */
+ if (cmd_done) {
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_scbp[0] = STATUS_GOOD;
+ pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET
+ | STATE_SENT_CMD;
+ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) {
+ (*pkt->pkt_comp)(pkt);
+ }
+
+ return (TRAN_ACCEPT);
+ }
+
+ if (cmd == NULL) {
+ return (TRAN_BUSY);
+ }
+
+ if ((pkt->pkt_flags & FLAG_NOINTR) == 0) {
+ if (instance->fw_outstanding > instance->max_fw_cmds) {
+ con_log(CL_ANN, (CE_CONT, "dr_sas:Firmware busy"));
+ return_mfi_pkt(instance, cmd);
+ return (TRAN_BUSY);
+ }
+
+ /* Synchronize the Cmd frame for the controller */
+ (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle, 0, 0,
+ DDI_DMA_SYNC_FORDEV);
+
+ instance->func_ptr->issue_cmd(cmd, instance);
+
+ } else {
+ struct drsas_header *hdr = &cmd->frame->hdr;
+
+ cmd->sync_cmd = DRSAS_TRUE;
+
+ instance->func_ptr-> issue_cmd_in_poll_mode(instance, cmd);
+
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_statistics = 0;
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+
+ switch (ddi_get8(cmd->frame_dma_obj.acc_handle,
+ &hdr->cmd_status)) {
+ case MFI_STAT_OK:
+ pkt->pkt_scbp[0] = STATUS_GOOD;
+ break;
+
+ case MFI_STAT_SCSI_DONE_WITH_ERROR:
+
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_statistics = 0;
+
+ ((struct scsi_status *)pkt->pkt_scbp)->sts_chk = 1;
+ break;
+
+ case MFI_STAT_DEVICE_NOT_FOUND:
+ pkt->pkt_reason = CMD_DEV_GONE;
+ pkt->pkt_statistics = STAT_DISCON;
+ break;
+
+ default:
+ ((struct scsi_status *)pkt->pkt_scbp)->sts_busy = 1;
+ }
+
+ return_mfi_pkt(instance, cmd);
+ (void) drsas_common_check(instance, cmd);
+
+ if (pkt->pkt_comp) {
+ (*pkt->pkt_comp)(pkt);
+ }
+
+ }
+
+ return (TRAN_ACCEPT);
+}
+
+/*ARGSUSED*/
+static int
+drsas_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* abort command not supported by H/W */
+
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+drsas_tran_reset(struct scsi_address *ap, int level)
+{
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* reset command not supported by H/W */
+
+ return (DDI_FAILURE);
+
+}
+
+/*ARGSUSED*/
+static int
+drsas_tran_getcap(struct scsi_address *ap, char *cap, int whom)
+{
+ int rval = 0;
+
+ struct drsas_instance *instance = ADDR2MR(ap);
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* we do allow inquiring about capabilities for other targets */
+ if (cap == NULL) {
+ return (-1);
+ }
+
+ switch (scsi_hba_lookup_capstr(cap)) {
+ case SCSI_CAP_DMA_MAX:
+ /* Limit to 16MB max transfer */
+ rval = drsas_max_cap_maxxfer;
+ break;
+ case SCSI_CAP_MSG_OUT:
+ rval = 1;
+ break;
+ case SCSI_CAP_DISCONNECT:
+ rval = 0;
+ break;
+ case SCSI_CAP_SYNCHRONOUS:
+ rval = 0;
+ break;
+ case SCSI_CAP_WIDE_XFER:
+ rval = 1;
+ break;
+ case SCSI_CAP_TAGGED_QING:
+ rval = 1;
+ break;
+ case SCSI_CAP_UNTAGGED_QING:
+ rval = 1;
+ break;
+ case SCSI_CAP_PARITY:
+ rval = 1;
+ break;
+ case SCSI_CAP_INITIATOR_ID:
+ rval = instance->init_id;
+ break;
+ case SCSI_CAP_ARQ:
+ rval = 1;
+ break;
+ case SCSI_CAP_LINKED_CMDS:
+ rval = 0;
+ break;
+ case SCSI_CAP_RESET_NOTIFICATION:
+ rval = 1;
+ break;
+ case SCSI_CAP_GEOMETRY:
+ rval = -1;
+
+ break;
+ default:
+ con_log(CL_DLEVEL2, (CE_NOTE, "Default cap coming 0x%x",
+ scsi_hba_lookup_capstr(cap)));
+ rval = -1;
+ break;
+ }
+
+ return (rval);
+}
+
+/*ARGSUSED*/
+static int
+drsas_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom)
+{
+ int rval = 1;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ /* We don't allow setting capabilities for other targets */
+ if (cap == NULL || whom == 0) {
+ return (-1);
+ }
+
+ switch (scsi_hba_lookup_capstr(cap)) {
+ case SCSI_CAP_DMA_MAX:
+ case SCSI_CAP_MSG_OUT:
+ case SCSI_CAP_PARITY:
+ case SCSI_CAP_LINKED_CMDS:
+ case SCSI_CAP_RESET_NOTIFICATION:
+ case SCSI_CAP_DISCONNECT:
+ case SCSI_CAP_SYNCHRONOUS:
+ case SCSI_CAP_UNTAGGED_QING:
+ case SCSI_CAP_WIDE_XFER:
+ case SCSI_CAP_INITIATOR_ID:
+ case SCSI_CAP_ARQ:
+ /*
+ * None of these are settable via
+ * the capability interface.
+ */
+ break;
+ case SCSI_CAP_TAGGED_QING:
+ rval = 1;
+ break;
+ case SCSI_CAP_SECTOR_SIZE:
+ rval = 1;
+ break;
+
+ case SCSI_CAP_TOTAL_SECTORS:
+ rval = 1;
+ break;
+ default:
+ rval = -1;
+ break;
+ }
+
+ return (rval);
+}
+
+static void
+drsas_tran_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+ struct scsa_cmd *acmd = PKT2CMD(pkt);
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ if (acmd->cmd_flags & CFLAG_DMAVALID) {
+ acmd->cmd_flags &= ~CFLAG_DMAVALID;
+
+ (void) ddi_dma_unbind_handle(acmd->cmd_dmahandle);
+
+ ddi_dma_free_handle(&acmd->cmd_dmahandle);
+
+ acmd->cmd_dmahandle = NULL;
+ }
+
+ /* free the pkt */
+ scsi_hba_pkt_free(ap, pkt);
+}
+
+/*ARGSUSED*/
+static void
+drsas_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+ register struct scsa_cmd *acmd = PKT2CMD(pkt);
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ if (acmd->cmd_flags & CFLAG_DMAVALID) {
+ acmd->cmd_flags &= ~CFLAG_DMAVALID;
+
+ (void) ddi_dma_unbind_handle(acmd->cmd_dmahandle);
+
+ ddi_dma_free_handle(&acmd->cmd_dmahandle);
+
+ acmd->cmd_dmahandle = NULL;
+ }
+}
+
+/*ARGSUSED*/
+static void
+drsas_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+ register struct scsa_cmd *acmd = PKT2CMD(pkt);
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ if (acmd->cmd_flags & CFLAG_DMAVALID) {
+ (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset,
+ acmd->cmd_dma_len, (acmd->cmd_flags & CFLAG_DMASEND) ?
+ DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU);
+ }
+}
+
+/*
+ * drsas_isr(caddr_t)
+ *
+ * The Interrupt Service Routine
+ *
+ * Collect status for all completed commands and do callback
+ *
+ */
+static uint_t
+drsas_isr(struct drsas_instance *instance)
+{
+ int need_softintr;
+ uint32_t producer;
+ uint32_t consumer;
+ uint32_t context;
+
+ struct drsas_cmd *cmd;
+
+ con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
+
+ ASSERT(instance);
+ if ((instance->intr_type == DDI_INTR_TYPE_FIXED) &&
+ !instance->func_ptr->intr_ack(instance)) {
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ (void) ddi_dma_sync(instance->mfi_internal_dma_obj.dma_handle,
+ 0, 0, DDI_DMA_SYNC_FORCPU);
+
+ if (drsas_check_dma_handle(instance->mfi_internal_dma_obj.dma_handle)
+ != DDI_SUCCESS) {
+ drsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ producer = ddi_get32(instance->mfi_internal_dma_obj.acc_handle,
+ instance->producer);
+ consumer = ddi_get32(instance->mfi_internal_dma_obj.acc_handle,
+ instance->consumer);
+
+ con_log(CL_ANN1, (CE_CONT, " producer %x consumer %x ",
+ producer, consumer));
+ if (producer == consumer) {
+ con_log(CL_ANN1, (CE_WARN, "producer = consumer case"));
+ return (DDI_INTR_UNCLAIMED);
+ }
+ mutex_enter(&instance->completed_pool_mtx);
+
+ while (consumer != producer) {
+ context = ddi_get32(instance->mfi_internal_dma_obj.acc_handle,
+ &instance->reply_queue[consumer]);
+ cmd = instance->cmd_list[context];
+ mlist_add_tail(&cmd->list, &instance->completed_pool_list);
+
+ consumer++;
+ if (consumer == (instance->max_fw_cmds + 1)) {
+ consumer = 0;
+ }
+ }
+
+ mutex_exit(&instance->completed_pool_mtx);
+
+ ddi_put32(instance->mfi_internal_dma_obj.acc_handle,
+ instance->consumer, consumer);
+ (void) ddi_dma_sync(instance->mfi_internal_dma_obj.dma_handle,
+ 0, 0, DDI_DMA_SYNC_FORDEV);
+
+ if (instance->softint_running) {
+ need_softintr = 0;
+ } else {
+ need_softintr = 1;
+ }
+
+ if (instance->isr_level == HIGH_LEVEL_INTR) {
+ if (need_softintr) {
+ ddi_trigger_softintr(instance->soft_intr_id);
+ }
+ } else {
+ /*
+ * Not a high-level interrupt, therefore call the soft level
+ * interrupt explicitly
+ */
+ (void) drsas_softintr(instance);
+ }
+
+ return (DDI_INTR_CLAIMED);
+}
+
+
+/*
+ * ************************************************************************** *
+ * *
+ * libraries *
+ * *
+ * ************************************************************************** *
+ */
+/*
+ * get_mfi_pkt : Get a command from the free pool
+ * After successful allocation, the caller of this routine
+ * must clear the frame buffer (memset to zero) before
+ * using the packet further.
+ *
+ * ***** Note *****
+ * After clearing the frame buffer the context id of the
+ * frame buffer SHOULD be restored back.
+ */
+static struct drsas_cmd *
+get_mfi_pkt(struct drsas_instance *instance)
+{
+ mlist_t *head = &instance->cmd_pool_list;
+ struct drsas_cmd *cmd = NULL;
+
+ mutex_enter(&instance->cmd_pool_mtx);
+ ASSERT(mutex_owned(&instance->cmd_pool_mtx));
+
+ if (!mlist_empty(head)) {
+ cmd = mlist_entry(head->next, struct drsas_cmd, list);
+ mlist_del_init(head->next);
+ }
+ if (cmd != NULL)
+ cmd->pkt = NULL;
+ mutex_exit(&instance->cmd_pool_mtx);
+
+ return (cmd);
+}
+
+/*
+ * return_mfi_pkt : Return a cmd to free command pool
+ */
+static void
+return_mfi_pkt(struct drsas_instance *instance, struct drsas_cmd *cmd)
+{
+ mutex_enter(&instance->cmd_pool_mtx);
+ ASSERT(mutex_owned(&instance->cmd_pool_mtx));
+
+ mlist_add(&cmd->list, &instance->cmd_pool_list);
+
+ mutex_exit(&instance->cmd_pool_mtx);
+}
+
+/*
+ * destroy_mfi_frame_pool
+ */
+static void
+destroy_mfi_frame_pool(struct drsas_instance *instance)
+{
+ int i;
+ uint32_t max_cmd = instance->max_fw_cmds;
+
+ struct drsas_cmd *cmd;
+
+ /* return all frames to pool */
+ for (i = 0; i < max_cmd+1; i++) {
+
+ cmd = instance->cmd_list[i];
+
+ if (cmd->frame_dma_obj_status == DMA_OBJ_ALLOCATED)
+ (void) drsas_free_dma_obj(instance, cmd->frame_dma_obj);
+
+ cmd->frame_dma_obj_status = DMA_OBJ_FREED;
+ }
+
+}
+
+/*
+ * create_mfi_frame_pool
+ */
+static int
+create_mfi_frame_pool(struct drsas_instance *instance)
+{
+ int i = 0;
+ int cookie_cnt;
+ uint16_t max_cmd;
+ uint16_t sge_sz;
+ uint32_t sgl_sz;
+ uint32_t tot_frame_size;
+
+ struct drsas_cmd *cmd;
+
+ max_cmd = instance->max_fw_cmds;
+
+ sge_sz = sizeof (struct drsas_sge64);
+
+ /* calculated the number of 64byte frames required for SGL */
+ sgl_sz = sge_sz * instance->max_num_sge;
+ tot_frame_size = sgl_sz + MRMFI_FRAME_SIZE + SENSE_LENGTH;
+
+ con_log(CL_DLEVEL3, (CE_NOTE, "create_mfi_frame_pool: "
+ "sgl_sz %x tot_frame_size %x", sgl_sz, tot_frame_size));
+
+ while (i < max_cmd+1) {
+ cmd = instance->cmd_list[i];
+
+ cmd->frame_dma_obj.size = tot_frame_size;
+ cmd->frame_dma_obj.dma_attr = drsas_generic_dma_attr;
+ cmd->frame_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ cmd->frame_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ cmd->frame_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ cmd->frame_dma_obj.dma_attr.dma_attr_align = 64;
+
+
+ cookie_cnt = drsas_alloc_dma_obj(instance, &cmd->frame_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC);
+
+ if (cookie_cnt == -1 || cookie_cnt > 1) {
+ con_log(CL_ANN, (CE_WARN,
+ "create_mfi_frame_pool: could not alloc."));
+ return (DDI_FAILURE);
+ }
+
+ bzero(cmd->frame_dma_obj.buffer, tot_frame_size);
+
+ cmd->frame_dma_obj_status = DMA_OBJ_ALLOCATED;
+ cmd->frame = (union drsas_frame *)cmd->frame_dma_obj.buffer;
+ cmd->frame_phys_addr =
+ cmd->frame_dma_obj.dma_cookie[0].dmac_address;
+
+ cmd->sense = (uint8_t *)(((unsigned long)
+ cmd->frame_dma_obj.buffer) +
+ tot_frame_size - SENSE_LENGTH);
+ cmd->sense_phys_addr =
+ cmd->frame_dma_obj.dma_cookie[0].dmac_address +
+ tot_frame_size - SENSE_LENGTH;
+
+ if (!cmd->frame || !cmd->sense) {
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: pci_pool_alloc failed"));
+
+ return (ENOMEM);
+ }
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &cmd->frame->io.context, cmd->index);
+ i++;
+
+ con_log(CL_DLEVEL3, (CE_NOTE, "[%x]-%x",
+ cmd->index, cmd->frame_phys_addr));
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * free_additional_dma_buffer
+ */
+static void
+free_additional_dma_buffer(struct drsas_instance *instance)
+{
+ if (instance->mfi_internal_dma_obj.status == DMA_OBJ_ALLOCATED) {
+ (void) drsas_free_dma_obj(instance,
+ instance->mfi_internal_dma_obj);
+ instance->mfi_internal_dma_obj.status = DMA_OBJ_FREED;
+ }
+
+ if (instance->mfi_evt_detail_obj.status == DMA_OBJ_ALLOCATED) {
+ (void) drsas_free_dma_obj(instance,
+ instance->mfi_evt_detail_obj);
+ instance->mfi_evt_detail_obj.status = DMA_OBJ_FREED;
+ }
+}
+
+/*
+ * alloc_additional_dma_buffer
+ */
+static int
+alloc_additional_dma_buffer(struct drsas_instance *instance)
+{
+ uint32_t reply_q_sz;
+ uint32_t internal_buf_size = PAGESIZE*2;
+
+ /* max cmds plus 1 + producer & consumer */
+ reply_q_sz = sizeof (uint32_t) * (instance->max_fw_cmds + 1 + 2);
+
+ instance->mfi_internal_dma_obj.size = internal_buf_size;
+ instance->mfi_internal_dma_obj.dma_attr = drsas_generic_dma_attr;
+ instance->mfi_internal_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ instance->mfi_internal_dma_obj.dma_attr.dma_attr_count_max =
+ 0xFFFFFFFFU;
+ instance->mfi_internal_dma_obj.dma_attr.dma_attr_sgllen = 1;
+
+ if (drsas_alloc_dma_obj(instance, &instance->mfi_internal_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas: could not alloc reply queue"));
+ return (DDI_FAILURE);
+ }
+
+ bzero(instance->mfi_internal_dma_obj.buffer, internal_buf_size);
+
+ instance->mfi_internal_dma_obj.status |= DMA_OBJ_ALLOCATED;
+
+ instance->producer = (uint32_t *)((unsigned long)
+ instance->mfi_internal_dma_obj.buffer);
+ instance->consumer = (uint32_t *)((unsigned long)
+ instance->mfi_internal_dma_obj.buffer + 4);
+ instance->reply_queue = (uint32_t *)((unsigned long)
+ instance->mfi_internal_dma_obj.buffer + 8);
+ instance->internal_buf = (caddr_t)(((unsigned long)
+ instance->mfi_internal_dma_obj.buffer) + reply_q_sz + 8);
+ instance->internal_buf_dmac_add =
+ instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address +
+ (reply_q_sz + 8);
+ instance->internal_buf_size = internal_buf_size -
+ (reply_q_sz + 8);
+
+ /* allocate evt_detail */
+ instance->mfi_evt_detail_obj.size = sizeof (struct drsas_evt_detail);
+ instance->mfi_evt_detail_obj.dma_attr = drsas_generic_dma_attr;
+ instance->mfi_evt_detail_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ instance->mfi_evt_detail_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ instance->mfi_evt_detail_obj.dma_attr.dma_attr_sgllen = 1;
+ instance->mfi_evt_detail_obj.dma_attr.dma_attr_align = 1;
+
+ if (drsas_alloc_dma_obj(instance, &instance->mfi_evt_detail_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "alloc_additional_dma_buffer: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ bzero(instance->mfi_evt_detail_obj.buffer,
+ sizeof (struct drsas_evt_detail));
+
+ instance->mfi_evt_detail_obj.status |= DMA_OBJ_ALLOCATED;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * free_space_for_mfi
+ */
+static void
+free_space_for_mfi(struct drsas_instance *instance)
+{
+ int i;
+ uint32_t max_cmd = instance->max_fw_cmds;
+
+ /* already freed */
+ if (instance->cmd_list == NULL) {
+ return;
+ }
+
+ free_additional_dma_buffer(instance);
+
+ /* first free the MFI frame pool */
+ destroy_mfi_frame_pool(instance);
+
+ /* free all the commands in the cmd_list */
+ for (i = 0; i < instance->max_fw_cmds+1; i++) {
+ kmem_free(instance->cmd_list[i],
+ sizeof (struct drsas_cmd));
+
+ instance->cmd_list[i] = NULL;
+ }
+
+ /* free the cmd_list buffer itself */
+ kmem_free(instance->cmd_list,
+ sizeof (struct drsas_cmd *) * (max_cmd+1));
+
+ instance->cmd_list = NULL;
+
+ INIT_LIST_HEAD(&instance->cmd_pool_list);
+}
+
+/*
+ * alloc_space_for_mfi
+ */
+static int
+alloc_space_for_mfi(struct drsas_instance *instance)
+{
+ int i;
+ uint32_t max_cmd;
+ size_t sz;
+
+ struct drsas_cmd *cmd;
+
+ max_cmd = instance->max_fw_cmds;
+
+ /* reserve 1 more slot for flush_cache */
+ sz = sizeof (struct drsas_cmd *) * (max_cmd+1);
+
+ /*
+ * instance->cmd_list is an array of struct drsas_cmd pointers.
+ * Allocate the dynamic array first and then allocate individual
+ * commands.
+ */
+ instance->cmd_list = kmem_zalloc(sz, KM_SLEEP);
+ ASSERT(instance->cmd_list);
+
+ for (i = 0; i < max_cmd+1; i++) {
+ instance->cmd_list[i] = kmem_zalloc(sizeof (struct drsas_cmd),
+ KM_SLEEP);
+ ASSERT(instance->cmd_list[i]);
+ }
+
+ INIT_LIST_HEAD(&instance->cmd_pool_list);
+
+ /* add all the commands to command pool (instance->cmd_pool) */
+ for (i = 0; i < max_cmd; i++) {
+ cmd = instance->cmd_list[i];
+ cmd->index = i;
+
+ mlist_add_tail(&cmd->list, &instance->cmd_pool_list);
+ }
+
+ /* single slot for flush_cache won't be added in command pool */
+ cmd = instance->cmd_list[max_cmd];
+ cmd->index = i;
+
+ /* create a frame pool and assign one frame to each cmd */
+ if (create_mfi_frame_pool(instance)) {
+ con_log(CL_ANN, (CE_NOTE, "error creating frame DMA pool"));
+ return (DDI_FAILURE);
+ }
+
+ /* create a frame pool and assign one frame to each cmd */
+ if (alloc_additional_dma_buffer(instance)) {
+ con_log(CL_ANN, (CE_NOTE, "error creating frame DMA pool"));
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * get_ctrl_info
+ */
+static int
+get_ctrl_info(struct drsas_instance *instance,
+ struct drsas_ctrl_info *ctrl_info)
+{
+ int ret = 0;
+
+ struct drsas_cmd *cmd;
+ struct drsas_dcmd_frame *dcmd;
+ struct drsas_ctrl_info *ci;
+
+ cmd = get_mfi_pkt(instance);
+
+ if (!cmd) {
+ con_log(CL_ANN, (CE_WARN,
+ "Failed to get a cmd for ctrl info"));
+ return (DDI_FAILURE);
+ }
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ dcmd = &cmd->frame->dcmd;
+
+ ci = (struct drsas_ctrl_info *)instance->internal_buf;
+
+ if (!ci) {
+ con_log(CL_ANN, (CE_WARN,
+ "Failed to alloc mem for ctrl info"));
+ return_mfi_pkt(instance, cmd);
+ return (DDI_FAILURE);
+ }
+
+ (void) memset(ci, 0, sizeof (struct drsas_ctrl_info));
+
+ /* for( i = 0; i < DCMD_MBOX_SZ; i++ ) dcmd->mbox.b[i] = 0; */
+ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ);
+
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status,
+ MFI_CMD_STATUS_POLL_MODE);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags,
+ MFI_FRAME_DIR_READ);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len,
+ sizeof (struct drsas_ctrl_info));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode,
+ DR_DCMD_CTRL_GET_INFO);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr,
+ instance->internal_buf_dmac_add);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length,
+ sizeof (struct drsas_ctrl_info));
+
+ cmd->frame_count = 1;
+
+ if (!instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) {
+ ret = 0;
+ ddi_rep_get8(cmd->frame_dma_obj.acc_handle,
+ (uint8_t *)ctrl_info, (uint8_t *)ci,
+ sizeof (struct drsas_ctrl_info), DDI_DEV_AUTOINCR);
+ } else {
+ con_log(CL_ANN, (CE_WARN, "get_ctrl_info: Ctrl info failed"));
+ ret = -1;
+ }
+
+ return_mfi_pkt(instance, cmd);
+ if (drsas_common_check(instance, cmd) != DDI_SUCCESS) {
+ ret = -1;
+ }
+
+ return (ret);
+}
+
+/*
+ * abort_aen_cmd
+ */
+static int
+abort_aen_cmd(struct drsas_instance *instance,
+ struct drsas_cmd *cmd_to_abort)
+{
+ int ret = 0;
+
+ struct drsas_cmd *cmd;
+ struct drsas_abort_frame *abort_fr;
+
+ cmd = get_mfi_pkt(instance);
+
+ if (!cmd) {
+ con_log(CL_ANN, (CE_WARN,
+ "Failed to get a cmd for ctrl info"));
+ return (DDI_FAILURE);
+ }
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ abort_fr = &cmd->frame->abort;
+
+ /* prepare and issue the abort frame */
+ ddi_put8(cmd->frame_dma_obj.acc_handle,
+ &abort_fr->cmd, MFI_CMD_OP_ABORT);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &abort_fr->cmd_status,
+ MFI_CMD_STATUS_SYNC_MODE);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &abort_fr->flags, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_context,
+ cmd_to_abort->index);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &abort_fr->abort_mfi_phys_addr_lo, cmd_to_abort->frame_phys_addr);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &abort_fr->abort_mfi_phys_addr_hi, 0);
+
+ instance->aen_cmd->abort_aen = 1;
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN,
+ "abort_aen_cmd: issue_cmd_in_sync_mode failed"));
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+
+ instance->aen_cmd->abort_aen = 1;
+ instance->aen_cmd = 0;
+
+ return_mfi_pkt(instance, cmd);
+ (void) drsas_common_check(instance, cmd);
+
+ return (ret);
+}
+
+/*
+ * init_mfi
+ */
+static int
+init_mfi(struct drsas_instance *instance)
+{
+ struct drsas_cmd *cmd;
+ struct drsas_ctrl_info ctrl_info;
+ struct drsas_init_frame *init_frame;
+ struct drsas_init_queue_info *initq_info;
+
+ /* we expect the FW state to be READY */
+ if (mfi_state_transition_to_ready(instance)) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: F/W is not ready"));
+ goto fail_ready_state;
+ }
+
+ /* get various operational parameters from status register */
+ instance->max_num_sge =
+ (instance->func_ptr->read_fw_status_reg(instance) &
+ 0xFF0000) >> 0x10;
+ /*
+ * Reduce the max supported cmds by 1. This is to ensure that the
+ * reply_q_sz (1 more than the max cmd that driver may send)
+ * does not exceed max cmds that the FW can support
+ */
+ instance->max_fw_cmds =
+ instance->func_ptr->read_fw_status_reg(instance) & 0xFFFF;
+ instance->max_fw_cmds = instance->max_fw_cmds - 1;
+
+ instance->max_num_sge =
+ (instance->max_num_sge > DRSAS_MAX_SGE_CNT) ?
+ DRSAS_MAX_SGE_CNT : instance->max_num_sge;
+
+ /* create a pool of commands */
+ if (alloc_space_for_mfi(instance) != DDI_SUCCESS)
+ goto fail_alloc_fw_space;
+
+ /*
+ * Prepare a init frame. Note the init frame points to queue info
+ * structure. Each frame has SGL allocated after first 64 bytes. For
+ * this frame - since we don't need any SGL - we use SGL's space as
+ * queue info structure
+ */
+ cmd = get_mfi_pkt(instance);
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ init_frame = (struct drsas_init_frame *)cmd->frame;
+ initq_info = (struct drsas_init_queue_info *)
+ ((unsigned long)init_frame + 64);
+
+ (void) memset(init_frame, 0, MRMFI_FRAME_SIZE);
+ (void) memset(initq_info, 0, sizeof (struct drsas_init_queue_info));
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->init_flags, 0);
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->reply_queue_entries, instance->max_fw_cmds + 1);
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->producer_index_phys_addr_hi, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->producer_index_phys_addr_lo,
+ instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address);
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->consumer_index_phys_addr_hi, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->consumer_index_phys_addr_lo,
+ instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 4);
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->reply_queue_start_phys_addr_hi, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &initq_info->reply_queue_start_phys_addr_lo,
+ instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 8);
+
+ ddi_put8(cmd->frame_dma_obj.acc_handle,
+ &init_frame->cmd, MFI_CMD_OP_INIT);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &init_frame->cmd_status,
+ MFI_CMD_STATUS_POLL_MODE);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &init_frame->flags, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &init_frame->queue_info_new_phys_addr_lo,
+ cmd->frame_phys_addr + 64);
+ ddi_put32(cmd->frame_dma_obj.acc_handle,
+ &init_frame->queue_info_new_phys_addr_hi, 0);
+
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->data_xfer_len,
+ sizeof (struct drsas_init_queue_info));
+
+ cmd->frame_count = 1;
+
+ /* issue the init frame in polled mode */
+ if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN, "failed to init firmware"));
+ goto fail_fw_init;
+ }
+
+ return_mfi_pkt(instance, cmd);
+ if (drsas_common_check(instance, cmd) != DDI_SUCCESS) {
+ goto fail_fw_init;
+ }
+
+ /* gather misc FW related information */
+ if (!get_ctrl_info(instance, &ctrl_info)) {
+ instance->max_sectors_per_req = ctrl_info.max_request_size;
+ con_log(CL_ANN1, (CE_NOTE, "product name %s ld present %d",
+ ctrl_info.product_name, ctrl_info.ld_present_count));
+ } else {
+ instance->max_sectors_per_req = instance->max_num_sge *
+ PAGESIZE / 512;
+ }
+
+ if (drsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) {
+ goto fail_fw_init;
+ }
+
+ return (DDI_SUCCESS);
+
+fail_fw_init:
+fail_alloc_fw_space:
+
+ free_space_for_mfi(instance);
+
+fail_ready_state:
+ ddi_regs_map_free(&instance->regmap_handle);
+
+fail_mfi_reg_setup:
+ return (DDI_FAILURE);
+}
+
+/*
+ * mfi_state_transition_to_ready : Move the FW to READY state
+ *
+ * @reg_set : MFI register set
+ */
+static int
+mfi_state_transition_to_ready(struct drsas_instance *instance)
+{
+ int i;
+ uint8_t max_wait;
+ uint32_t fw_ctrl;
+ uint32_t fw_state;
+ uint32_t cur_state;
+
+ fw_state =
+ instance->func_ptr->read_fw_status_reg(instance) & MFI_STATE_MASK;
+ con_log(CL_ANN1, (CE_NOTE,
+ "mfi_state_transition_to_ready:FW state = 0x%x", fw_state));
+
+ while (fw_state != MFI_STATE_READY) {
+ con_log(CL_ANN, (CE_NOTE,
+ "mfi_state_transition_to_ready:FW state%x", fw_state));
+
+ switch (fw_state) {
+ case MFI_STATE_FAULT:
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: FW in FAULT state!!"));
+
+ return (ENODEV);
+ case MFI_STATE_WAIT_HANDSHAKE:
+ /* set the CLR bit in IMR0 */
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: FW waiting for HANDSHAKE"));
+ /*
+ * PCI_Hot Plug: MFI F/W requires
+ * (MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG)
+ * to be set
+ */
+ /* WR_IB_MSG_0(MFI_INIT_CLEAR_HANDSHAKE, instance); */
+ WR_IB_DOORBELL(MFI_INIT_CLEAR_HANDSHAKE |
+ MFI_INIT_HOTPLUG, instance);
+
+ max_wait = 2;
+ cur_state = MFI_STATE_WAIT_HANDSHAKE;
+ break;
+ case MFI_STATE_BOOT_MESSAGE_PENDING:
+ /* set the CLR bit in IMR0 */
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: FW state boot message pending"));
+ /*
+ * PCI_Hot Plug: MFI F/W requires
+ * (MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG)
+ * to be set
+ */
+ WR_IB_DOORBELL(MFI_INIT_HOTPLUG, instance);
+
+ max_wait = 10;
+ cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
+ break;
+ case MFI_STATE_OPERATIONAL:
+ /* bring it to READY state; assuming max wait 2 secs */
+ instance->func_ptr->disable_intr(instance);
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: FW in OPERATIONAL state"));
+ /*
+ * PCI_Hot Plug: MFI F/W requires
+ * (MFI_INIT_READY | MFI_INIT_MFIMODE | MFI_INIT_ABORT)
+ * to be set
+ */
+ /* WR_IB_DOORBELL(MFI_INIT_READY, instance); */
+ WR_IB_DOORBELL(MFI_RESET_FLAGS, instance);
+
+ max_wait = 10;
+ cur_state = MFI_STATE_OPERATIONAL;
+ break;
+ case MFI_STATE_UNDEFINED:
+ /* this state should not last for more than 2 seconds */
+ con_log(CL_ANN, (CE_NOTE, "FW state undefined"));
+
+ max_wait = 2;
+ cur_state = MFI_STATE_UNDEFINED;
+ break;
+ case MFI_STATE_BB_INIT:
+ max_wait = 2;
+ cur_state = MFI_STATE_BB_INIT;
+ break;
+ case MFI_STATE_FW_INIT:
+ max_wait = 2;
+ cur_state = MFI_STATE_FW_INIT;
+ break;
+ case MFI_STATE_DEVICE_SCAN:
+ max_wait = 10;
+ cur_state = MFI_STATE_DEVICE_SCAN;
+ break;
+ default:
+ con_log(CL_ANN, (CE_NOTE,
+ "dr_sas: Unknown state 0x%x", fw_state));
+ return (ENODEV);
+ }
+
+ /* the cur_state should not last for more than max_wait secs */
+ for (i = 0; i < (max_wait * MILLISEC); i++) {
+ /* fw_state = RD_OB_MSG_0(instance) & MFI_STATE_MASK; */
+ fw_state =
+ instance->func_ptr->read_fw_status_reg(instance) &
+ MFI_STATE_MASK;
+
+ if (fw_state == cur_state) {
+ delay(1 * drv_usectohz(MILLISEC));
+ } else {
+ break;
+ }
+ }
+
+ /* return error if fw_state hasn't changed after max_wait */
+ if (fw_state == cur_state) {
+ con_log(CL_ANN, (CE_NOTE,
+ "FW state hasn't changed in %d secs", max_wait));
+ return (ENODEV);
+ }
+ };
+
+ fw_ctrl = RD_IB_DOORBELL(instance);
+
+ con_log(CL_ANN1, (CE_NOTE,
+ "mfi_state_transition_to_ready:FW ctrl = 0x%x", fw_ctrl));
+
+ /*
+ * Write 0xF to the doorbell register to do the following.
+ * - Abort all outstanding commands (bit 0).
+ * - Transition from OPERATIONAL to READY state (bit 1).
+ * - Discard (possible) low MFA posted in 64-bit mode (bit-2).
+ * - Set to release FW to continue running (i.e. BIOS handshake
+ * (bit 3).
+ */
+ WR_IB_DOORBELL(0xF, instance);
+
+ if (drsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) {
+ return (ENODEV);
+ }
+ return (DDI_SUCCESS);
+}
+
+/*
+ * get_seq_num
+ */
+static int
+get_seq_num(struct drsas_instance *instance,
+ struct drsas_evt_log_info *eli)
+{
+ int ret = DDI_SUCCESS;
+
+ dma_obj_t dcmd_dma_obj;
+ struct drsas_cmd *cmd;
+ struct drsas_dcmd_frame *dcmd;
+
+ cmd = get_mfi_pkt(instance);
+
+ if (!cmd) {
+ cmn_err(CE_WARN, "dr_sas: failed to get a cmd");
+ return (ENOMEM);
+ }
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ dcmd = &cmd->frame->dcmd;
+
+ /* allocate the data transfer buffer */
+ dcmd_dma_obj.size = sizeof (struct drsas_evt_log_info);
+ dcmd_dma_obj.dma_attr = drsas_generic_dma_attr;
+ dcmd_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ dcmd_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ dcmd_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ dcmd_dma_obj.dma_attr.dma_attr_align = 1;
+
+ if (drsas_alloc_dma_obj(instance, &dcmd_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN,
+ "get_seq_num: could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ (void) memset(dcmd_dma_obj.buffer, 0,
+ sizeof (struct drsas_evt_log_info));
+
+ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ);
+
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags,
+ MFI_FRAME_DIR_READ);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len,
+ sizeof (struct drsas_evt_log_info));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode,
+ DR_DCMD_CTRL_EVENT_GET_INFO);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length,
+ sizeof (struct drsas_evt_log_info));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr,
+ dcmd_dma_obj.dma_cookie[0].dmac_address);
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ cmn_err(CE_WARN, "get_seq_num: "
+ "failed to issue DRSAS_DCMD_CTRL_EVENT_GET_INFO");
+ ret = DDI_FAILURE;
+ } else {
+ /* copy the data back into callers buffer */
+ ddi_rep_get8(cmd->frame_dma_obj.acc_handle, (uint8_t *)eli,
+ (uint8_t *)dcmd_dma_obj.buffer,
+ sizeof (struct drsas_evt_log_info), DDI_DEV_AUTOINCR);
+ ret = DDI_SUCCESS;
+ }
+
+ if (drsas_free_dma_obj(instance, dcmd_dma_obj) != DDI_SUCCESS)
+ ret = DDI_FAILURE;
+
+ return_mfi_pkt(instance, cmd);
+ if (drsas_common_check(instance, cmd) != DDI_SUCCESS) {
+ ret = DDI_FAILURE;
+ }
+ return (ret);
+}
+
+/*
+ * start_mfi_aen
+ */
+static int
+start_mfi_aen(struct drsas_instance *instance)
+{
+ int ret = 0;
+
+ struct drsas_evt_log_info eli;
+ union drsas_evt_class_locale class_locale;
+
+ /* get the latest sequence number from FW */
+ (void) memset(&eli, 0, sizeof (struct drsas_evt_log_info));
+
+ if (get_seq_num(instance, &eli)) {
+ cmn_err(CE_WARN, "start_mfi_aen: failed to get seq num");
+ return (-1);
+ }
+
+ /* register AEN with FW for latest sequence number plus 1 */
+ class_locale.members.reserved = 0;
+ class_locale.members.locale = DR_EVT_LOCALE_ALL;
+ class_locale.members.class = DR_EVT_CLASS_INFO;
+ ret = register_mfi_aen(instance, eli.newest_seq_num + 1,
+ class_locale.word);
+
+ if (ret) {
+ cmn_err(CE_WARN, "start_mfi_aen: aen registration failed");
+ return (-1);
+ }
+
+ return (ret);
+}
+
+/*
+ * flush_cache
+ */
+static void
+flush_cache(struct drsas_instance *instance)
+{
+ struct drsas_cmd *cmd = NULL;
+ struct drsas_dcmd_frame *dcmd;
+ uint32_t max_cmd = instance->max_fw_cmds;
+
+ cmd = instance->cmd_list[max_cmd];
+
+ if (cmd == NULL)
+ return;
+
+ dcmd = &cmd->frame->dcmd;
+
+ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ);
+
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0x0);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 0);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags,
+ MFI_FRAME_DIR_NONE);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode,
+ DR_DCMD_CTRL_CACHE_FLUSH);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.b[0],
+ DR_FLUSH_CTRL_CACHE | DR_FLUSH_DISK_CACHE);
+
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) {
+ con_log(CL_ANN1, (CE_WARN,
+ "flush_cache: failed to issue MFI_DCMD_CTRL_CACHE_FLUSH"));
+ }
+ con_log(CL_DLEVEL1, (CE_NOTE, "done"));
+}
+
+/*
+ * service_mfi_aen- Completes an AEN command
+ * @instance: Adapter soft state
+ * @cmd: Command to be completed
+ *
+ */
+static void
+service_mfi_aen(struct drsas_instance *instance, struct drsas_cmd *cmd)
+{
+ uint32_t seq_num;
+ struct drsas_evt_detail *evt_detail =
+ (struct drsas_evt_detail *)instance->mfi_evt_detail_obj.buffer;
+ int rval = 0;
+ int tgt = 0;
+ ddi_acc_handle_t acc_handle;
+
+ acc_handle = cmd->frame_dma_obj.acc_handle;
+
+ cmd->cmd_status = ddi_get8(acc_handle, &cmd->frame->io.cmd_status);
+
+ if (cmd->cmd_status == ENODATA) {
+ cmd->cmd_status = 0;
+ }
+
+ /*
+ * log the MFI AEN event to the sysevent queue so that
+ * application will get noticed
+ */
+ if (ddi_log_sysevent(instance->dip, DDI_VENDOR_LSI, "LSIMEGA", "SAS",
+ NULL, NULL, DDI_NOSLEEP) != DDI_SUCCESS) {
+ int instance_no = ddi_get_instance(instance->dip);
+ con_log(CL_ANN, (CE_WARN,
+ "dr_sas%d: Failed to log AEN event", instance_no));
+ }
+ /*
+ * Check for any ld devices that has changed state. i.e. online
+ * or offline.
+ */
+ con_log(CL_ANN1, (CE_NOTE,
+ "AEN: code = %x class = %x locale = %x args = %x",
+ ddi_get32(acc_handle, &evt_detail->code),
+ evt_detail->cl.members.class,
+ ddi_get16(acc_handle, &evt_detail->cl.members.locale),
+ ddi_get8(acc_handle, &evt_detail->arg_type)));
+
+ switch (ddi_get32(acc_handle, &evt_detail->code)) {
+ case DR_EVT_CFG_CLEARED: {
+ for (tgt = 0; tgt < MRDRV_MAX_LD; tgt++) {
+ if (instance->dr_ld_list[tgt].dip != NULL) {
+ rval = drsas_service_evt(instance, tgt, 0,
+ DRSAS_EVT_UNCONFIG_TGT, NULL);
+ con_log(CL_ANN1, (CE_WARN,
+ "dr_sas: CFG CLEARED AEN rval = %d "
+ "tgt id = %d", rval, tgt));
+ }
+ }
+ break;
+ }
+
+ case DR_EVT_LD_DELETED: {
+ rval = drsas_service_evt(instance,
+ ddi_get16(acc_handle, &evt_detail->args.ld.target_id), 0,
+ DRSAS_EVT_UNCONFIG_TGT, NULL);
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: LD DELETED AEN rval = %d "
+ "tgt id = %d index = %d", rval,
+ ddi_get16(acc_handle, &evt_detail->args.ld.target_id),
+ ddi_get8(acc_handle, &evt_detail->args.ld.ld_index)));
+ break;
+ } /* End of DR_EVT_LD_DELETED */
+
+ case DR_EVT_LD_CREATED: {
+ rval = drsas_service_evt(instance,
+ ddi_get16(acc_handle, &evt_detail->args.ld.target_id), 0,
+ DRSAS_EVT_CONFIG_TGT, NULL);
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: LD CREATED AEN rval = %d "
+ "tgt id = %d index = %d", rval,
+ ddi_get16(acc_handle, &evt_detail->args.ld.target_id),
+ ddi_get8(acc_handle, &evt_detail->args.ld.ld_index)));
+ break;
+ } /* End of DR_EVT_LD_CREATED */
+ } /* End of Main Switch */
+
+ /* get copy of seq_num and class/locale for re-registration */
+ seq_num = ddi_get32(acc_handle, &evt_detail->seq_num);
+ seq_num++;
+ (void) memset(instance->mfi_evt_detail_obj.buffer, 0,
+ sizeof (struct drsas_evt_detail));
+
+ ddi_put8(acc_handle, &cmd->frame->dcmd.cmd_status, 0x0);
+ ddi_put32(acc_handle, &cmd->frame->dcmd.mbox.w[0], seq_num);
+
+ instance->aen_seq_num = seq_num;
+
+ cmd->frame_count = 1;
+
+ /* Issue the aen registration frame */
+ instance->func_ptr->issue_cmd(cmd, instance);
+}
+
+/*
+ * complete_cmd_in_sync_mode - Completes an internal command
+ * @instance: Adapter soft state
+ * @cmd: Command to be completed
+ *
+ * The issue_cmd_in_sync_mode() function waits for a command to complete
+ * after it issues a command. This function wakes up that waiting routine by
+ * calling wake_up() on the wait queue.
+ */
+static void
+complete_cmd_in_sync_mode(struct drsas_instance *instance,
+ struct drsas_cmd *cmd)
+{
+ cmd->cmd_status = ddi_get8(cmd->frame_dma_obj.acc_handle,
+ &cmd->frame->io.cmd_status);
+
+ cmd->sync_cmd = DRSAS_FALSE;
+
+ if (cmd->cmd_status == ENODATA) {
+ cmd->cmd_status = 0;
+ }
+
+ cv_broadcast(&instance->int_cmd_cv);
+}
+
+/*
+ * drsas_softintr - The Software ISR
+ * @param arg : HBA soft state
+ *
+ * called from high-level interrupt if hi-level interrupt are not there,
+ * otherwise triggered as a soft interrupt
+ */
+static uint_t
+drsas_softintr(struct drsas_instance *instance)
+{
+ struct scsi_pkt *pkt;
+ struct scsa_cmd *acmd;
+ struct drsas_cmd *cmd;
+ struct mlist_head *pos, *next;
+ mlist_t process_list;
+ struct drsas_header *hdr;
+ struct scsi_arq_status *arqstat;
+
+ con_log(CL_ANN1, (CE_CONT, "drsas_softintr called"));
+
+ ASSERT(instance);
+ mutex_enter(&instance->completed_pool_mtx);
+
+ if (mlist_empty(&instance->completed_pool_list)) {
+ mutex_exit(&instance->completed_pool_mtx);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ instance->softint_running = 1;
+
+ INIT_LIST_HEAD(&process_list);
+ mlist_splice(&instance->completed_pool_list, &process_list);
+ INIT_LIST_HEAD(&instance->completed_pool_list);
+
+ mutex_exit(&instance->completed_pool_mtx);
+
+ /* perform all callbacks first, before releasing the SCBs */
+ mlist_for_each_safe(pos, next, &process_list) {
+ cmd = mlist_entry(pos, struct drsas_cmd, list);
+
+ /* syncronize the Cmd frame for the controller */
+ (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle,
+ 0, 0, DDI_DMA_SYNC_FORCPU);
+
+ if (drsas_check_dma_handle(cmd->frame_dma_obj.dma_handle) !=
+ DDI_SUCCESS) {
+ drsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ hdr = &cmd->frame->hdr;
+
+ /* remove the internal command from the process list */
+ mlist_del_init(&cmd->list);
+
+ switch (ddi_get8(cmd->frame_dma_obj.acc_handle, &hdr->cmd)) {
+ case MFI_CMD_OP_PD_SCSI:
+ case MFI_CMD_OP_LD_SCSI:
+ case MFI_CMD_OP_LD_READ:
+ case MFI_CMD_OP_LD_WRITE:
+ /*
+ * MFI_CMD_OP_PD_SCSI and MFI_CMD_OP_LD_SCSI
+ * could have been issued either through an
+ * IO path or an IOCTL path. If it was via IOCTL,
+ * we will send it to internal completion.
+ */
+ if (cmd->sync_cmd == DRSAS_TRUE) {
+ complete_cmd_in_sync_mode(instance, cmd);
+ break;
+ }
+
+ /* regular commands */
+ acmd = cmd->cmd;
+ pkt = CMD2PKT(acmd);
+
+ if (acmd->cmd_flags & CFLAG_DMAVALID) {
+ if (acmd->cmd_flags & CFLAG_CONSISTENT) {
+ (void) ddi_dma_sync(acmd->cmd_dmahandle,
+ acmd->cmd_dma_offset,
+ acmd->cmd_dma_len,
+ DDI_DMA_SYNC_FORCPU);
+ }
+ }
+
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_statistics = 0;
+ pkt->pkt_state = STATE_GOT_BUS
+ | STATE_GOT_TARGET | STATE_SENT_CMD
+ | STATE_XFERRED_DATA | STATE_GOT_STATUS;
+
+ con_log(CL_ANN1, (CE_CONT,
+ "CDB[0] = %x completed for %s: size %lx context %x",
+ pkt->pkt_cdbp[0], ((acmd->islogical) ? "LD" : "PD"),
+ acmd->cmd_dmacount, hdr->context));
+
+ if (pkt->pkt_cdbp[0] == SCMD_INQUIRY) {
+ struct scsi_inquiry *inq;
+
+ if (acmd->cmd_dmacount != 0) {
+ bp_mapin(acmd->cmd_buf);
+ inq = (struct scsi_inquiry *)
+ acmd->cmd_buf->b_un.b_addr;
+
+ /* don't expose physical drives to OS */
+ if (acmd->islogical &&
+ (hdr->cmd_status == MFI_STAT_OK)) {
+ display_scsi_inquiry(
+ (caddr_t)inq);
+ } else if ((hdr->cmd_status ==
+ MFI_STAT_OK) && inq->inq_dtype ==
+ DTYPE_DIRECT) {
+
+ display_scsi_inquiry(
+ (caddr_t)inq);
+
+ /* for physical disk */
+ hdr->cmd_status =
+ MFI_STAT_DEVICE_NOT_FOUND;
+ }
+ }
+ }
+
+ switch (hdr->cmd_status) {
+ case MFI_STAT_OK:
+ pkt->pkt_scbp[0] = STATUS_GOOD;
+ break;
+ case MFI_STAT_LD_CC_IN_PROGRESS:
+ case MFI_STAT_LD_RECON_IN_PROGRESS:
+ pkt->pkt_scbp[0] = STATUS_GOOD;
+ break;
+ case MFI_STAT_LD_INIT_IN_PROGRESS:
+ con_log(CL_ANN,
+ (CE_WARN, "Initialization in Progress"));
+ pkt->pkt_reason = CMD_TRAN_ERR;
+
+ break;
+ case MFI_STAT_SCSI_DONE_WITH_ERROR:
+ con_log(CL_ANN1, (CE_CONT, "scsi_done error"));
+
+ pkt->pkt_reason = CMD_CMPLT;
+ ((struct scsi_status *)
+ pkt->pkt_scbp)->sts_chk = 1;
+
+ if (pkt->pkt_cdbp[0] == SCMD_TEST_UNIT_READY) {
+
+ con_log(CL_ANN,
+ (CE_WARN, "TEST_UNIT_READY fail"));
+
+ } else {
+ pkt->pkt_state |= STATE_ARQ_DONE;
+ arqstat = (void *)(pkt->pkt_scbp);
+ arqstat->sts_rqpkt_reason = CMD_CMPLT;
+ arqstat->sts_rqpkt_resid = 0;
+ arqstat->sts_rqpkt_state |=
+ STATE_GOT_BUS | STATE_GOT_TARGET
+ | STATE_SENT_CMD
+ | STATE_XFERRED_DATA;
+ *(uint8_t *)&arqstat->sts_rqpkt_status =
+ STATUS_GOOD;
+ ddi_rep_get8(
+ cmd->frame_dma_obj.acc_handle,
+ (uint8_t *)
+ &(arqstat->sts_sensedata),
+ cmd->sense,
+ acmd->cmd_scblen -
+ offsetof(struct scsi_arq_status,
+ sts_sensedata), DDI_DEV_AUTOINCR);
+ }
+ break;
+ case MFI_STAT_LD_OFFLINE:
+ case MFI_STAT_DEVICE_NOT_FOUND:
+ con_log(CL_ANN1, (CE_CONT,
+ "device not found error"));
+ pkt->pkt_reason = CMD_DEV_GONE;
+ pkt->pkt_statistics = STAT_DISCON;
+ break;
+ case MFI_STAT_LD_LBA_OUT_OF_RANGE:
+ pkt->pkt_state |= STATE_ARQ_DONE;
+ pkt->pkt_reason = CMD_CMPLT;
+ ((struct scsi_status *)
+ pkt->pkt_scbp)->sts_chk = 1;
+
+ arqstat = (void *)(pkt->pkt_scbp);
+ arqstat->sts_rqpkt_reason = CMD_CMPLT;
+ arqstat->sts_rqpkt_resid = 0;
+ arqstat->sts_rqpkt_state |= STATE_GOT_BUS
+ | STATE_GOT_TARGET | STATE_SENT_CMD
+ | STATE_XFERRED_DATA;
+ *(uint8_t *)&arqstat->sts_rqpkt_status =
+ STATUS_GOOD;
+
+ arqstat->sts_sensedata.es_valid = 1;
+ arqstat->sts_sensedata.es_key =
+ KEY_ILLEGAL_REQUEST;
+ arqstat->sts_sensedata.es_class =
+ CLASS_EXTENDED_SENSE;
+
+ /*
+ * LOGICAL BLOCK ADDRESS OUT OF RANGE:
+ * ASC: 0x21h; ASCQ: 0x00h;
+ */
+ arqstat->sts_sensedata.es_add_code = 0x21;
+ arqstat->sts_sensedata.es_qual_code = 0x00;
+
+ break;
+
+ default:
+ con_log(CL_ANN, (CE_CONT, "Unknown status!"));
+ pkt->pkt_reason = CMD_TRAN_ERR;
+
+ break;
+ }
+
+ atomic_add_16(&instance->fw_outstanding, (-1));
+
+ return_mfi_pkt(instance, cmd);
+
+ (void) drsas_common_check(instance, cmd);
+
+ if (acmd->cmd_dmahandle) {
+ if (drsas_check_dma_handle(
+ acmd->cmd_dmahandle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip,
+ DDI_SERVICE_UNAFFECTED);
+ pkt->pkt_reason = CMD_TRAN_ERR;
+ pkt->pkt_statistics = 0;
+ }
+ }
+
+ /* Call the callback routine */
+ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) &&
+ pkt->pkt_comp) {
+ (*pkt->pkt_comp)(pkt);
+ }
+
+ break;
+ case MFI_CMD_OP_SMP:
+ case MFI_CMD_OP_STP:
+ complete_cmd_in_sync_mode(instance, cmd);
+ break;
+ case MFI_CMD_OP_DCMD:
+ /* see if got an event notification */
+ if (ddi_get32(cmd->frame_dma_obj.acc_handle,
+ &cmd->frame->dcmd.opcode) ==
+ DR_DCMD_CTRL_EVENT_WAIT) {
+ if ((instance->aen_cmd == cmd) &&
+ (instance->aen_cmd->abort_aen)) {
+ con_log(CL_ANN, (CE_WARN,
+ "drsas_softintr: "
+ "aborted_aen returned"));
+ } else {
+ atomic_add_16(&instance->fw_outstanding,
+ (-1));
+ service_mfi_aen(instance, cmd);
+ }
+ } else {
+ complete_cmd_in_sync_mode(instance, cmd);
+ }
+
+ break;
+ case MFI_CMD_OP_ABORT:
+ con_log(CL_ANN, (CE_WARN, "MFI_CMD_OP_ABORT complete"));
+ /*
+ * MFI_CMD_OP_ABORT successfully completed
+ * in the synchronous mode
+ */
+ complete_cmd_in_sync_mode(instance, cmd);
+ break;
+ default:
+ drsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+
+ if (cmd->pkt != NULL) {
+ pkt = cmd->pkt;
+ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) &&
+ pkt->pkt_comp) {
+ (*pkt->pkt_comp)(pkt);
+ }
+ }
+ con_log(CL_ANN, (CE_WARN, "Cmd type unknown !"));
+ break;
+ }
+ }
+
+ instance->softint_running = 0;
+
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * drsas_alloc_dma_obj
+ *
+ * Allocate the memory and other resources for an dma object.
+ */
+static int
+drsas_alloc_dma_obj(struct drsas_instance *instance, dma_obj_t *obj,
+ uchar_t endian_flags)
+{
+ int i;
+ size_t alen = 0;
+ uint_t cookie_cnt;
+ struct ddi_device_acc_attr tmp_endian_attr;
+
+ tmp_endian_attr = endian_attr;
+ tmp_endian_attr.devacc_attr_endian_flags = endian_flags;
+
+ i = ddi_dma_alloc_handle(instance->dip, &obj->dma_attr,
+ DDI_DMA_SLEEP, NULL, &obj->dma_handle);
+ if (i != DDI_SUCCESS) {
+
+ switch (i) {
+ case DDI_DMA_BADATTR :
+ con_log(CL_ANN, (CE_WARN,
+ "Failed ddi_dma_alloc_handle- Bad attribute"));
+ break;
+ case DDI_DMA_NORESOURCES :
+ con_log(CL_ANN, (CE_WARN,
+ "Failed ddi_dma_alloc_handle- No Resources"));
+ break;
+ default :
+ con_log(CL_ANN, (CE_WARN,
+ "Failed ddi_dma_alloc_handle: "
+ "unknown status %d", i));
+ break;
+ }
+
+ return (-1);
+ }
+
+ if ((ddi_dma_mem_alloc(obj->dma_handle, obj->size, &tmp_endian_attr,
+ DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL,
+ &obj->buffer, &alen, &obj->acc_handle) != DDI_SUCCESS) ||
+ alen < obj->size) {
+
+ ddi_dma_free_handle(&obj->dma_handle);
+
+ con_log(CL_ANN, (CE_WARN, "Failed : ddi_dma_mem_alloc"));
+
+ return (-1);
+ }
+
+ if (ddi_dma_addr_bind_handle(obj->dma_handle, NULL, obj->buffer,
+ obj->size, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
+ NULL, &obj->dma_cookie[0], &cookie_cnt) != DDI_SUCCESS) {
+
+ ddi_dma_mem_free(&obj->acc_handle);
+ ddi_dma_free_handle(&obj->dma_handle);
+
+ con_log(CL_ANN, (CE_WARN, "Failed : ddi_dma_addr_bind_handle"));
+
+ return (-1);
+ }
+
+ if (drsas_check_dma_handle(obj->dma_handle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+ return (-1);
+ }
+
+ if (drsas_check_acc_handle(obj->acc_handle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
+ return (-1);
+ }
+
+ return (cookie_cnt);
+}
+
+/*
+ * drsas_free_dma_obj(struct drsas_instance *, dma_obj_t)
+ *
+ * De-allocate the memory and other resources for an dma object, which must
+ * have been alloated by a previous call to drsas_alloc_dma_obj()
+ */
+static int
+drsas_free_dma_obj(struct drsas_instance *instance, dma_obj_t obj)
+{
+
+ if (drsas_check_dma_handle(obj.dma_handle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+ return (DDI_FAILURE);
+ }
+
+ if (drsas_check_acc_handle(obj.acc_handle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+ return (DDI_FAILURE);
+ }
+
+ (void) ddi_dma_unbind_handle(obj.dma_handle);
+ ddi_dma_mem_free(&obj.acc_handle);
+ ddi_dma_free_handle(&obj.dma_handle);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * drsas_dma_alloc(instance_t *, struct scsi_pkt *, struct buf *,
+ * int, int (*)())
+ *
+ * Allocate dma resources for a new scsi command
+ */
+static int
+drsas_dma_alloc(struct drsas_instance *instance, struct scsi_pkt *pkt,
+ struct buf *bp, int flags, int (*callback)())
+{
+ int dma_flags;
+ int (*cb)(caddr_t);
+ int i;
+
+ ddi_dma_attr_t tmp_dma_attr = drsas_generic_dma_attr;
+ struct scsa_cmd *acmd = PKT2CMD(pkt);
+
+ acmd->cmd_buf = bp;
+
+ if (bp->b_flags & B_READ) {
+ acmd->cmd_flags &= ~CFLAG_DMASEND;
+ dma_flags = DDI_DMA_READ;
+ } else {
+ acmd->cmd_flags |= CFLAG_DMASEND;
+ dma_flags = DDI_DMA_WRITE;
+ }
+
+ if (flags & PKT_CONSISTENT) {
+ acmd->cmd_flags |= CFLAG_CONSISTENT;
+ dma_flags |= DDI_DMA_CONSISTENT;
+ }
+
+ if (flags & PKT_DMA_PARTIAL) {
+ dma_flags |= DDI_DMA_PARTIAL;
+ }
+
+ dma_flags |= DDI_DMA_REDZONE;
+
+ cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;
+
+ tmp_dma_attr.dma_attr_sgllen = instance->max_num_sge;
+ tmp_dma_attr.dma_attr_addr_hi = 0xffffffffffffffffull;
+
+ if ((i = ddi_dma_alloc_handle(instance->dip, &tmp_dma_attr,
+ cb, 0, &acmd->cmd_dmahandle)) != DDI_SUCCESS) {
+ switch (i) {
+ case DDI_DMA_BADATTR:
+ bioerror(bp, EFAULT);
+ return (DDI_FAILURE);
+
+ case DDI_DMA_NORESOURCES:
+ bioerror(bp, 0);
+ return (DDI_FAILURE);
+
+ default:
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_alloc_handle: "
+ "impossible result (0x%x)", i));
+ bioerror(bp, EFAULT);
+ return (DDI_FAILURE);
+ }
+ }
+
+ i = ddi_dma_buf_bind_handle(acmd->cmd_dmahandle, bp, dma_flags,
+ cb, 0, &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies);
+
+ switch (i) {
+ case DDI_DMA_PARTIAL_MAP:
+ if ((dma_flags & DDI_DMA_PARTIAL) == 0) {
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle: "
+ "DDI_DMA_PARTIAL_MAP impossible"));
+ goto no_dma_cookies;
+ }
+
+ if (ddi_dma_numwin(acmd->cmd_dmahandle, &acmd->cmd_nwin) ==
+ DDI_FAILURE) {
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_numwin failed"));
+ goto no_dma_cookies;
+ }
+
+ if (ddi_dma_getwin(acmd->cmd_dmahandle, acmd->cmd_curwin,
+ &acmd->cmd_dma_offset, &acmd->cmd_dma_len,
+ &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies) ==
+ DDI_FAILURE) {
+
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_getwin failed"));
+ goto no_dma_cookies;
+ }
+
+ goto get_dma_cookies;
+ case DDI_DMA_MAPPED:
+ acmd->cmd_nwin = 1;
+ acmd->cmd_dma_len = 0;
+ acmd->cmd_dma_offset = 0;
+
+get_dma_cookies:
+ i = 0;
+ acmd->cmd_dmacount = 0;
+ for (;;) {
+ acmd->cmd_dmacount +=
+ acmd->cmd_dmacookies[i++].dmac_size;
+
+ if (i == instance->max_num_sge ||
+ i == acmd->cmd_ncookies)
+ break;
+
+ ddi_dma_nextcookie(acmd->cmd_dmahandle,
+ &acmd->cmd_dmacookies[i]);
+ }
+
+ acmd->cmd_cookie = i;
+ acmd->cmd_cookiecnt = i;
+
+ acmd->cmd_flags |= CFLAG_DMAVALID;
+
+ if (bp->b_bcount >= acmd->cmd_dmacount) {
+ pkt->pkt_resid = bp->b_bcount - acmd->cmd_dmacount;
+ } else {
+ pkt->pkt_resid = 0;
+ }
+
+ return (DDI_SUCCESS);
+ case DDI_DMA_NORESOURCES:
+ bioerror(bp, 0);
+ break;
+ case DDI_DMA_NOMAPPING:
+ bioerror(bp, EFAULT);
+ break;
+ case DDI_DMA_TOOBIG:
+ bioerror(bp, EINVAL);
+ break;
+ case DDI_DMA_INUSE:
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle:"
+ " DDI_DMA_INUSE impossible"));
+ break;
+ default:
+ con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle: "
+ "impossible result (0x%x)", i));
+ break;
+ }
+
+no_dma_cookies:
+ ddi_dma_free_handle(&acmd->cmd_dmahandle);
+ acmd->cmd_dmahandle = NULL;
+ acmd->cmd_flags &= ~CFLAG_DMAVALID;
+ return (DDI_FAILURE);
+}
+
+/*
+ * drsas_dma_move(struct drsas_instance *, struct scsi_pkt *, struct buf *)
+ *
+ * move dma resources to next dma window
+ *
+ */
+static int
+drsas_dma_move(struct drsas_instance *instance, struct scsi_pkt *pkt,
+ struct buf *bp)
+{
+ int i = 0;
+
+ struct scsa_cmd *acmd = PKT2CMD(pkt);
+
+ /*
+ * If there are no more cookies remaining in this window,
+ * must move to the next window first.
+ */
+ if (acmd->cmd_cookie == acmd->cmd_ncookies) {
+ if (acmd->cmd_curwin == acmd->cmd_nwin && acmd->cmd_nwin == 1) {
+ return (DDI_SUCCESS);
+ }
+
+ /* at last window, cannot move */
+ if (++acmd->cmd_curwin >= acmd->cmd_nwin) {
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_dma_getwin(acmd->cmd_dmahandle, acmd->cmd_curwin,
+ &acmd->cmd_dma_offset, &acmd->cmd_dma_len,
+ &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies) ==
+ DDI_FAILURE) {
+ return (DDI_FAILURE);
+ }
+
+ acmd->cmd_cookie = 0;
+ } else {
+ /* still more cookies in this window - get the next one */
+ ddi_dma_nextcookie(acmd->cmd_dmahandle,
+ &acmd->cmd_dmacookies[0]);
+ }
+
+ /* get remaining cookies in this window, up to our maximum */
+ for (;;) {
+ acmd->cmd_dmacount += acmd->cmd_dmacookies[i++].dmac_size;
+ acmd->cmd_cookie++;
+
+ if (i == instance->max_num_sge ||
+ acmd->cmd_cookie == acmd->cmd_ncookies) {
+ break;
+ }
+
+ ddi_dma_nextcookie(acmd->cmd_dmahandle,
+ &acmd->cmd_dmacookies[i]);
+ }
+
+ acmd->cmd_cookiecnt = i;
+
+ if (bp->b_bcount >= acmd->cmd_dmacount) {
+ pkt->pkt_resid = bp->b_bcount - acmd->cmd_dmacount;
+ } else {
+ pkt->pkt_resid = 0;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * build_cmd
+ */
+static struct drsas_cmd *
+build_cmd(struct drsas_instance *instance, struct scsi_address *ap,
+ struct scsi_pkt *pkt, uchar_t *cmd_done)
+{
+ uint16_t flags = 0;
+ uint32_t i;
+ uint32_t context;
+ uint32_t sge_bytes;
+ ddi_acc_handle_t acc_handle;
+ struct drsas_cmd *cmd;
+ struct drsas_sge64 *mfi_sgl;
+ struct scsa_cmd *acmd = PKT2CMD(pkt);
+ struct drsas_pthru_frame *pthru;
+ struct drsas_io_frame *ldio;
+
+ /* find out if this is logical or physical drive command. */
+ acmd->islogical = MRDRV_IS_LOGICAL(ap);
+ acmd->device_id = MAP_DEVICE_ID(instance, ap);
+ *cmd_done = 0;
+
+ /* get the command packet */
+ if (!(cmd = get_mfi_pkt(instance))) {
+ return (NULL);
+ }
+
+ acc_handle = cmd->frame_dma_obj.acc_handle;
+
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(acc_handle, &cmd->frame->hdr.context, cmd->index);
+
+ cmd->pkt = pkt;
+ cmd->cmd = acmd;
+
+ /* lets get the command directions */
+ if (acmd->cmd_flags & CFLAG_DMASEND) {
+ flags = MFI_FRAME_DIR_WRITE;
+
+ if (acmd->cmd_flags & CFLAG_CONSISTENT) {
+ (void) ddi_dma_sync(acmd->cmd_dmahandle,
+ acmd->cmd_dma_offset, acmd->cmd_dma_len,
+ DDI_DMA_SYNC_FORDEV);
+ }
+ } else if (acmd->cmd_flags & ~CFLAG_DMASEND) {
+ flags = MFI_FRAME_DIR_READ;
+
+ if (acmd->cmd_flags & CFLAG_CONSISTENT) {
+ (void) ddi_dma_sync(acmd->cmd_dmahandle,
+ acmd->cmd_dma_offset, acmd->cmd_dma_len,
+ DDI_DMA_SYNC_FORCPU);
+ }
+ } else {
+ flags = MFI_FRAME_DIR_NONE;
+ }
+
+ flags |= MFI_FRAME_SGL64;
+
+ switch (pkt->pkt_cdbp[0]) {
+
+ /*
+ * case SCMD_SYNCHRONIZE_CACHE:
+ * flush_cache(instance);
+ * return_mfi_pkt(instance, cmd);
+ * *cmd_done = 1;
+ *
+ * return (NULL);
+ */
+
+ case SCMD_READ:
+ case SCMD_WRITE:
+ case SCMD_READ_G1:
+ case SCMD_WRITE_G1:
+ if (acmd->islogical) {
+ ldio = (struct drsas_io_frame *)cmd->frame;
+
+ /*
+ * preare the Logical IO frame:
+ * 2nd bit is zero for all read cmds
+ */
+ ddi_put8(acc_handle, &ldio->cmd,
+ (pkt->pkt_cdbp[0] & 0x02) ? MFI_CMD_OP_LD_WRITE
+ : MFI_CMD_OP_LD_READ);
+ ddi_put8(acc_handle, &ldio->cmd_status, 0x0);
+ ddi_put8(acc_handle, &ldio->scsi_status, 0x0);
+ ddi_put8(acc_handle, &ldio->target_id, acmd->device_id);
+ ddi_put16(acc_handle, &ldio->timeout, 0);
+ ddi_put8(acc_handle, &ldio->reserved_0, 0);
+ ddi_put16(acc_handle, &ldio->pad_0, 0);
+ ddi_put16(acc_handle, &ldio->flags, flags);
+
+ /* Initialize sense Information */
+ bzero(cmd->sense, SENSE_LENGTH);
+ ddi_put8(acc_handle, &ldio->sense_len, SENSE_LENGTH);
+ ddi_put32(acc_handle, &ldio->sense_buf_phys_addr_hi, 0);
+ ddi_put32(acc_handle, &ldio->sense_buf_phys_addr_lo,
+ cmd->sense_phys_addr);
+ ddi_put32(acc_handle, &ldio->start_lba_hi, 0);
+ ddi_put8(acc_handle, &ldio->access_byte,
+ (acmd->cmd_cdblen != 6) ? pkt->pkt_cdbp[1] : 0);
+ ddi_put8(acc_handle, &ldio->sge_count,
+ acmd->cmd_cookiecnt);
+ mfi_sgl = (struct drsas_sge64 *)&ldio->sgl;
+
+ context = ddi_get32(acc_handle, &ldio->context);
+
+ if (acmd->cmd_cdblen == CDB_GROUP0) {
+ ddi_put32(acc_handle, &ldio->lba_count, (
+ (uint16_t)(pkt->pkt_cdbp[4])));
+
+ ddi_put32(acc_handle, &ldio->start_lba_lo, (
+ ((uint32_t)(pkt->pkt_cdbp[3])) |
+ ((uint32_t)(pkt->pkt_cdbp[2]) << 8) |
+ ((uint32_t)((pkt->pkt_cdbp[1]) & 0x1F)
+ << 16)));
+ } else if (acmd->cmd_cdblen == CDB_GROUP1) {
+ ddi_put32(acc_handle, &ldio->lba_count, (
+ ((uint16_t)(pkt->pkt_cdbp[8])) |
+ ((uint16_t)(pkt->pkt_cdbp[7]) << 8)));
+
+ ddi_put32(acc_handle, &ldio->start_lba_lo, (
+ ((uint32_t)(pkt->pkt_cdbp[5])) |
+ ((uint32_t)(pkt->pkt_cdbp[4]) << 8) |
+ ((uint32_t)(pkt->pkt_cdbp[3]) << 16) |
+ ((uint32_t)(pkt->pkt_cdbp[2]) << 24)));
+ } else if (acmd->cmd_cdblen == CDB_GROUP2) {
+ ddi_put32(acc_handle, &ldio->lba_count, (
+ ((uint16_t)(pkt->pkt_cdbp[9])) |
+ ((uint16_t)(pkt->pkt_cdbp[8]) << 8) |
+ ((uint16_t)(pkt->pkt_cdbp[7]) << 16) |
+ ((uint16_t)(pkt->pkt_cdbp[6]) << 24)));
+
+ ddi_put32(acc_handle, &ldio->start_lba_lo, (
+ ((uint32_t)(pkt->pkt_cdbp[5])) |
+ ((uint32_t)(pkt->pkt_cdbp[4]) << 8) |
+ ((uint32_t)(pkt->pkt_cdbp[3]) << 16) |
+ ((uint32_t)(pkt->pkt_cdbp[2]) << 24)));
+ } else if (acmd->cmd_cdblen == CDB_GROUP3) {
+ ddi_put32(acc_handle, &ldio->lba_count, (
+ ((uint16_t)(pkt->pkt_cdbp[13])) |
+ ((uint16_t)(pkt->pkt_cdbp[12]) << 8) |
+ ((uint16_t)(pkt->pkt_cdbp[11]) << 16) |
+ ((uint16_t)(pkt->pkt_cdbp[10]) << 24)));
+
+ ddi_put32(acc_handle, &ldio->start_lba_lo, (
+ ((uint32_t)(pkt->pkt_cdbp[9])) |
+ ((uint32_t)(pkt->pkt_cdbp[8]) << 8) |
+ ((uint32_t)(pkt->pkt_cdbp[7]) << 16) |
+ ((uint32_t)(pkt->pkt_cdbp[6]) << 24)));
+
+ ddi_put32(acc_handle, &ldio->start_lba_lo, (
+ ((uint32_t)(pkt->pkt_cdbp[5])) |
+ ((uint32_t)(pkt->pkt_cdbp[4]) << 8) |
+ ((uint32_t)(pkt->pkt_cdbp[3]) << 16) |
+ ((uint32_t)(pkt->pkt_cdbp[2]) << 24)));
+ }
+
+ break;
+ }
+ /* fall through For all non-rd/wr cmds */
+ default:
+
+ switch (pkt->pkt_cdbp[0]) {
+ case SCMD_MODE_SENSE:
+ case SCMD_MODE_SENSE_G1: {
+ union scsi_cdb *cdbp;
+ uint16_t page_code;
+
+ cdbp = (void *)pkt->pkt_cdbp;
+ page_code = (uint16_t)cdbp->cdb_un.sg.scsi[0];
+ switch (page_code) {
+ case 0x3:
+ case 0x4:
+ (void) drsas_mode_sense_build(pkt);
+ return_mfi_pkt(instance, cmd);
+ *cmd_done = 1;
+ return (NULL);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ pthru = (struct drsas_pthru_frame *)cmd->frame;
+
+ /* prepare the DCDB frame */
+ ddi_put8(acc_handle, &pthru->cmd, (acmd->islogical) ?
+ MFI_CMD_OP_LD_SCSI : MFI_CMD_OP_PD_SCSI);
+ ddi_put8(acc_handle, &pthru->cmd_status, 0x0);
+ ddi_put8(acc_handle, &pthru->scsi_status, 0x0);
+ ddi_put8(acc_handle, &pthru->target_id, acmd->device_id);
+ ddi_put8(acc_handle, &pthru->lun, 0);
+ ddi_put8(acc_handle, &pthru->cdb_len, acmd->cmd_cdblen);
+ ddi_put16(acc_handle, &pthru->timeout, 0);
+ ddi_put16(acc_handle, &pthru->flags, flags);
+ ddi_put32(acc_handle, &pthru->data_xfer_len,
+ acmd->cmd_dmacount);
+ ddi_put8(acc_handle, &pthru->sge_count, acmd->cmd_cookiecnt);
+ mfi_sgl = (struct drsas_sge64 *)&pthru->sgl;
+
+ bzero(cmd->sense, SENSE_LENGTH);
+ ddi_put8(acc_handle, &pthru->sense_len, SENSE_LENGTH);
+ ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_hi, 0);
+ ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_lo,
+ cmd->sense_phys_addr);
+
+ context = ddi_get32(acc_handle, &pthru->context);
+ ddi_rep_put8(acc_handle, (uint8_t *)pkt->pkt_cdbp,
+ (uint8_t *)pthru->cdb, acmd->cmd_cdblen, DDI_DEV_AUTOINCR);
+
+ break;
+ }
+#ifdef lint
+ context = context;
+#endif
+ /* prepare the scatter-gather list for the firmware */
+ for (i = 0; i < acmd->cmd_cookiecnt; i++, mfi_sgl++) {
+ ddi_put64(acc_handle, &mfi_sgl->phys_addr,
+ acmd->cmd_dmacookies[i].dmac_laddress);
+ ddi_put32(acc_handle, &mfi_sgl->length,
+ acmd->cmd_dmacookies[i].dmac_size);
+ }
+
+ sge_bytes = sizeof (struct drsas_sge64)*acmd->cmd_cookiecnt;
+
+ cmd->frame_count = (sge_bytes / MRMFI_FRAME_SIZE) +
+ ((sge_bytes % MRMFI_FRAME_SIZE) ? 1 : 0) + 1;
+
+ if (cmd->frame_count >= 8) {
+ cmd->frame_count = 8;
+ }
+
+ return (cmd);
+}
+
+/*
+ * issue_mfi_pthru
+ */
+static int
+issue_mfi_pthru(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ struct drsas_cmd *cmd, int mode)
+{
+ void *ubuf;
+ uint32_t kphys_addr = 0;
+ uint32_t xferlen = 0;
+ uint_t model;
+ ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle;
+ dma_obj_t pthru_dma_obj;
+ struct drsas_pthru_frame *kpthru;
+ struct drsas_pthru_frame *pthru;
+ int i;
+ pthru = &cmd->frame->pthru;
+ kpthru = (struct drsas_pthru_frame *)&ioctl->frame[0];
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_pthru: DDI_MODEL_LP32"));
+
+ xferlen = kpthru->sgl.sge32[0].length;
+
+ ubuf = (void *)(ulong_t)kpthru->sgl.sge32[0].phys_addr;
+ } else {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_pthru: DDI_MODEL_LP32"));
+ xferlen = kpthru->sgl.sge32[0].length;
+ ubuf = (void *)(ulong_t)kpthru->sgl.sge32[0].phys_addr;
+#else
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_pthru: DDI_MODEL_LP64"));
+ xferlen = kpthru->sgl.sge64[0].length;
+ ubuf = (void *)(ulong_t)kpthru->sgl.sge64[0].phys_addr;
+#endif
+ }
+
+ if (xferlen) {
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ pthru_dma_obj.size = xferlen;
+ pthru_dma_obj.dma_attr = drsas_generic_dma_attr;
+ pthru_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ pthru_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ pthru_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ pthru_dma_obj.dma_attr.dma_attr_align = 1;
+
+ /* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &pthru_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ if (kpthru->flags & MFI_FRAME_DIR_WRITE) {
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyin((uint8_t *)ubuf+i,
+ (uint8_t *)pthru_dma_obj.buffer+i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_pthru : "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ kphys_addr = pthru_dma_obj.dma_cookie[0].dmac_address;
+ }
+
+ ddi_put8(acc_handle, &pthru->cmd, kpthru->cmd);
+ ddi_put8(acc_handle, &pthru->sense_len, kpthru->sense_len);
+ ddi_put8(acc_handle, &pthru->cmd_status, 0);
+ ddi_put8(acc_handle, &pthru->scsi_status, 0);
+ ddi_put8(acc_handle, &pthru->target_id, kpthru->target_id);
+ ddi_put8(acc_handle, &pthru->lun, kpthru->lun);
+ ddi_put8(acc_handle, &pthru->cdb_len, kpthru->cdb_len);
+ ddi_put8(acc_handle, &pthru->sge_count, kpthru->sge_count);
+ ddi_put16(acc_handle, &pthru->timeout, kpthru->timeout);
+ ddi_put32(acc_handle, &pthru->data_xfer_len, kpthru->data_xfer_len);
+
+ ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_hi, 0);
+ /* pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr; */
+ ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_lo, 0);
+
+ ddi_rep_put8(acc_handle, (uint8_t *)kpthru->cdb, (uint8_t *)pthru->cdb,
+ pthru->cdb_len, DDI_DEV_AUTOINCR);
+
+ ddi_put16(acc_handle, &pthru->flags, kpthru->flags & ~MFI_FRAME_SGL64);
+ ddi_put32(acc_handle, &pthru->sgl.sge32[0].length, xferlen);
+ ddi_put32(acc_handle, &pthru->sgl.sge32[0].phys_addr, kphys_addr);
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_pthru: fw_ioctl failed"));
+ } else {
+ if (xferlen && kpthru->flags & MFI_FRAME_DIR_READ) {
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)pthru_dma_obj.buffer+i,
+ (uint8_t *)ubuf+i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_pthru : "
+ "copy to user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+ }
+
+ kpthru->cmd_status = ddi_get8(acc_handle, &pthru->cmd_status);
+ kpthru->scsi_status = ddi_get8(acc_handle, &pthru->scsi_status);
+
+ con_log(CL_ANN, (CE_NOTE, "issue_mfi_pthru: cmd_status %x, "
+ "scsi_status %x", kpthru->cmd_status, kpthru->scsi_status));
+
+ if (xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, pthru_dma_obj) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * issue_mfi_dcmd
+ */
+static int
+issue_mfi_dcmd(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ struct drsas_cmd *cmd, int mode)
+{
+ void *ubuf;
+ uint32_t kphys_addr = 0;
+ uint32_t xferlen = 0;
+ uint32_t model;
+ dma_obj_t dcmd_dma_obj;
+ struct drsas_dcmd_frame *kdcmd;
+ struct drsas_dcmd_frame *dcmd;
+ ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle;
+ int i;
+ dcmd = &cmd->frame->dcmd;
+ kdcmd = (struct drsas_dcmd_frame *)&ioctl->frame[0];
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_dcmd: DDI_MODEL_ILP32"));
+
+ xferlen = kdcmd->sgl.sge32[0].length;
+
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr;
+ } else {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_dcmd: DDI_MODEL_ILP32"));
+ xferlen = kdcmd->sgl.sge32[0].length;
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr;
+#else
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_dcmd: DDI_MODEL_LP64"));
+ xferlen = kdcmd->sgl.sge64[0].length;
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge64[0].phys_addr;
+#endif
+ }
+ if (xferlen) {
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ dcmd_dma_obj.size = xferlen;
+ dcmd_dma_obj.dma_attr = drsas_generic_dma_attr;
+ dcmd_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ dcmd_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ dcmd_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ dcmd_dma_obj.dma_attr.dma_attr_align = 1;
+
+ /* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &dcmd_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ if (kdcmd->flags & MFI_FRAME_DIR_WRITE) {
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyin((uint8_t *)ubuf + i,
+ (uint8_t *)dcmd_dma_obj.buffer + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_dcmd : "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ kphys_addr = dcmd_dma_obj.dma_cookie[0].dmac_address;
+ }
+
+ ddi_put8(acc_handle, &dcmd->cmd, kdcmd->cmd);
+ ddi_put8(acc_handle, &dcmd->cmd_status, 0);
+ ddi_put8(acc_handle, &dcmd->sge_count, kdcmd->sge_count);
+ ddi_put16(acc_handle, &dcmd->timeout, kdcmd->timeout);
+ ddi_put32(acc_handle, &dcmd->data_xfer_len, kdcmd->data_xfer_len);
+ ddi_put32(acc_handle, &dcmd->opcode, kdcmd->opcode);
+
+ ddi_rep_put8(acc_handle, (uint8_t *)kdcmd->mbox.b,
+ (uint8_t *)dcmd->mbox.b, DCMD_MBOX_SZ, DDI_DEV_AUTOINCR);
+
+ ddi_put16(acc_handle, &dcmd->flags, kdcmd->flags & ~MFI_FRAME_SGL64);
+ ddi_put32(acc_handle, &dcmd->sgl.sge32[0].length, xferlen);
+ ddi_put32(acc_handle, &dcmd->sgl.sge32[0].phys_addr, kphys_addr);
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd: fw_ioctl failed"));
+ } else {
+ if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_READ)) {
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)dcmd_dma_obj.buffer + i,
+ (uint8_t *)ubuf + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_dcmd : "
+ "copy to user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+ }
+
+ kdcmd->cmd_status = ddi_get8(acc_handle, &dcmd->cmd_status);
+
+ if (xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, dcmd_dma_obj) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * issue_mfi_smp
+ */
+static int
+issue_mfi_smp(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ struct drsas_cmd *cmd, int mode)
+{
+ void *request_ubuf;
+ void *response_ubuf;
+ uint32_t request_xferlen = 0;
+ uint32_t response_xferlen = 0;
+ uint_t model;
+ dma_obj_t request_dma_obj;
+ dma_obj_t response_dma_obj;
+ ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle;
+ struct drsas_smp_frame *ksmp;
+ struct drsas_smp_frame *smp;
+ struct drsas_sge32 *sge32;
+#ifndef _ILP32
+ struct drsas_sge64 *sge64;
+#endif
+ int i;
+ uint64_t tmp_sas_addr;
+
+ smp = &cmd->frame->smp;
+ ksmp = (struct drsas_smp_frame *)&ioctl->frame[0];
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: DDI_MODEL_ILP32"));
+
+ sge32 = &ksmp->sgl[0].sge32[0];
+ response_xferlen = sge32[0].length;
+ request_xferlen = sge32[1].length;
+ con_log(CL_ANN, (CE_NOTE, "issue_mfi_smp: "
+ "response_xferlen = %x, request_xferlen = %x",
+ response_xferlen, request_xferlen));
+
+ response_ubuf = (void *)(ulong_t)sge32[0].phys_addr;
+ request_ubuf = (void *)(ulong_t)sge32[1].phys_addr;
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: "
+ "response_ubuf = %p, request_ubuf = %p",
+ response_ubuf, request_ubuf));
+ } else {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: DDI_MODEL_ILP32"));
+
+ sge32 = &ksmp->sgl[0].sge32[0];
+ response_xferlen = sge32[0].length;
+ request_xferlen = sge32[1].length;
+ con_log(CL_ANN, (CE_NOTE, "issue_mfi_smp: "
+ "response_xferlen = %x, request_xferlen = %x",
+ response_xferlen, request_xferlen));
+
+ response_ubuf = (void *)(ulong_t)sge32[0].phys_addr;
+ request_ubuf = (void *)(ulong_t)sge32[1].phys_addr;
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: "
+ "response_ubuf = %p, request_ubuf = %p",
+ response_ubuf, request_ubuf));
+#else
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: DDI_MODEL_LP64"));
+
+ sge64 = &ksmp->sgl[0].sge64[0];
+ response_xferlen = sge64[0].length;
+ request_xferlen = sge64[1].length;
+
+ response_ubuf = (void *)(ulong_t)sge64[0].phys_addr;
+ request_ubuf = (void *)(ulong_t)sge64[1].phys_addr;
+#endif
+ }
+ if (request_xferlen) {
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ request_dma_obj.size = request_xferlen;
+ request_dma_obj.dma_attr = drsas_generic_dma_attr;
+ request_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ request_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ request_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ request_dma_obj.dma_attr.dma_attr_align = 1;
+
+ /* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &request_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ for (i = 0; i < request_xferlen; i++) {
+ if (ddi_copyin((uint8_t *)request_ubuf + i,
+ (uint8_t *)request_dma_obj.buffer + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ if (response_xferlen) {
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ response_dma_obj.size = response_xferlen;
+ response_dma_obj.dma_attr = drsas_generic_dma_attr;
+ response_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ response_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ response_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ response_dma_obj.dma_attr.dma_attr_align = 1;
+
+ /* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &response_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ for (i = 0; i < response_xferlen; i++) {
+ if (ddi_copyin((uint8_t *)response_ubuf + i,
+ (uint8_t *)response_dma_obj.buffer + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ ddi_put8(acc_handle, &smp->cmd, ksmp->cmd);
+ ddi_put8(acc_handle, &smp->cmd_status, 0);
+ ddi_put8(acc_handle, &smp->connection_status, 0);
+ ddi_put8(acc_handle, &smp->sge_count, ksmp->sge_count);
+ /* smp->context = ksmp->context; */
+ ddi_put16(acc_handle, &smp->timeout, ksmp->timeout);
+ ddi_put32(acc_handle, &smp->data_xfer_len, ksmp->data_xfer_len);
+
+ bcopy((void *)&ksmp->sas_addr, (void *)&tmp_sas_addr,
+ sizeof (uint64_t));
+ ddi_put64(acc_handle, &smp->sas_addr, tmp_sas_addr);
+
+ ddi_put16(acc_handle, &smp->flags, ksmp->flags & ~MFI_FRAME_SGL64);
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE,
+ "handle_drv_ioctl: DDI_MODEL_ILP32"));
+
+ sge32 = &smp->sgl[0].sge32[0];
+ ddi_put32(acc_handle, &sge32[0].length, response_xferlen);
+ ddi_put32(acc_handle, &sge32[0].phys_addr,
+ response_dma_obj.dma_cookie[0].dmac_address);
+ ddi_put32(acc_handle, &sge32[1].length, request_xferlen);
+ ddi_put32(acc_handle, &sge32[1].phys_addr,
+ request_dma_obj.dma_cookie[0].dmac_address);
+ } else {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE,
+ "handle_drv_ioctl: DDI_MODEL_ILP32"));
+ sge32 = &smp->sgl[0].sge32[0];
+ ddi_put32(acc_handle, &sge32[0].length, response_xferlen);
+ ddi_put32(acc_handle, &sge32[0].phys_addr,
+ response_dma_obj.dma_cookie[0].dmac_address);
+ ddi_put32(acc_handle, &sge32[1].length, request_xferlen);
+ ddi_put32(acc_handle, &sge32[1].phys_addr,
+ request_dma_obj.dma_cookie[0].dmac_address);
+#else
+ con_log(CL_ANN1, (CE_NOTE,
+ "issue_mfi_smp: DDI_MODEL_LP64"));
+ sge64 = &smp->sgl[0].sge64[0];
+ ddi_put32(acc_handle, &sge64[0].length, response_xferlen);
+ ddi_put64(acc_handle, &sge64[0].phys_addr,
+ response_dma_obj.dma_cookie[0].dmac_address);
+ ddi_put32(acc_handle, &sge64[1].length, request_xferlen);
+ ddi_put64(acc_handle, &sge64[1].phys_addr,
+ request_dma_obj.dma_cookie[0].dmac_address);
+#endif
+ }
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp : "
+ "smp->response_xferlen = %d, smp->request_xferlen = %d "
+ "smp->data_xfer_len = %d", ddi_get32(acc_handle, &sge32[0].length),
+ ddi_get32(acc_handle, &sge32[1].length),
+ ddi_get32(acc_handle, &smp->data_xfer_len)));
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_smp: fw_ioctl failed"));
+ } else {
+ con_log(CL_ANN1, (CE_NOTE,
+ "issue_mfi_smp: copy to user space"));
+
+ if (request_xferlen) {
+ for (i = 0; i < request_xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)request_dma_obj.buffer +
+ i, (uint8_t *)request_ubuf + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_smp : copy to user space"
+ " failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ if (response_xferlen) {
+ for (i = 0; i < response_xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)response_dma_obj.buffer
+ + i, (uint8_t *)response_ubuf
+ + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_smp : copy to "
+ "user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+ }
+
+ ksmp->cmd_status = ddi_get8(acc_handle, &smp->cmd_status);
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: smp->cmd_status = %d",
+ ddi_get8(acc_handle, &smp->cmd_status)));
+
+
+ if (request_xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, request_dma_obj) !=
+ DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ if (response_xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, response_dma_obj) !=
+ DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * issue_mfi_stp
+ */
+static int
+issue_mfi_stp(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ struct drsas_cmd *cmd, int mode)
+{
+ void *fis_ubuf;
+ void *data_ubuf;
+ uint32_t fis_xferlen = 0;
+ uint32_t data_xferlen = 0;
+ uint_t model;
+ dma_obj_t fis_dma_obj;
+ dma_obj_t data_dma_obj;
+ struct drsas_stp_frame *kstp;
+ struct drsas_stp_frame *stp;
+ ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle;
+ int i;
+
+ stp = &cmd->frame->stp;
+ kstp = (struct drsas_stp_frame *)&ioctl->frame[0];
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_stp: DDI_MODEL_ILP32"));
+
+ fis_xferlen = kstp->sgl.sge32[0].length;
+ data_xferlen = kstp->sgl.sge32[1].length;
+
+ fis_ubuf = (void *)(ulong_t)kstp->sgl.sge32[0].phys_addr;
+ data_ubuf = (void *)(ulong_t)kstp->sgl.sge32[1].phys_addr;
+ }
+ else
+ {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_stp: DDI_MODEL_ILP32"));
+
+ fis_xferlen = kstp->sgl.sge32[0].length;
+ data_xferlen = kstp->sgl.sge32[1].length;
+
+ fis_ubuf = (void *)(ulong_t)kstp->sgl.sge32[0].phys_addr;
+ data_ubuf = (void *)(ulong_t)kstp->sgl.sge32[1].phys_addr;
+#else
+ con_log(CL_ANN1, (CE_NOTE, "issue_mfi_stp: DDI_MODEL_LP64"));
+
+ fis_xferlen = kstp->sgl.sge64[0].length;
+ data_xferlen = kstp->sgl.sge64[1].length;
+
+ fis_ubuf = (void *)(ulong_t)kstp->sgl.sge64[0].phys_addr;
+ data_ubuf = (void *)(ulong_t)kstp->sgl.sge64[1].phys_addr;
+#endif
+ }
+
+
+ if (fis_xferlen) {
+ con_log(CL_ANN, (CE_NOTE, "issue_mfi_stp: "
+ "fis_ubuf = %p fis_xferlen = %x", fis_ubuf, fis_xferlen));
+
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ fis_dma_obj.size = fis_xferlen;
+ fis_dma_obj.dma_attr = drsas_generic_dma_attr;
+ fis_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ fis_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ fis_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ fis_dma_obj.dma_attr.dma_attr_align = 1;
+
+ /* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &fis_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_stp : "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ for (i = 0; i < fis_xferlen; i++) {
+ if (ddi_copyin((uint8_t *)fis_ubuf + i,
+ (uint8_t *)fis_dma_obj.buffer + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ if (data_xferlen) {
+ con_log(CL_ANN, (CE_NOTE, "issue_mfi_stp: data_ubuf = %p "
+ "data_xferlen = %x", data_ubuf, data_xferlen));
+
+ /* means IOCTL requires DMA */
+ /* allocate the data transfer buffer */
+ data_dma_obj.size = data_xferlen;
+ data_dma_obj.dma_attr = drsas_generic_dma_attr;
+ data_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU;
+ data_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU;
+ data_dma_obj.dma_attr.dma_attr_sgllen = 1;
+ data_dma_obj.dma_attr.dma_attr_align = 1;
+
+/* allocate kernel buffer for DMA */
+ if (drsas_alloc_dma_obj(instance, &data_dma_obj,
+ (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: "
+ "could not allocate data transfer buffer."));
+ return (DDI_FAILURE);
+ }
+
+ /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */
+ for (i = 0; i < data_xferlen; i++) {
+ if (ddi_copyin((uint8_t *)data_ubuf + i,
+ (uint8_t *)data_dma_obj.buffer + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: "
+ "copy from user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ ddi_put8(acc_handle, &stp->cmd, kstp->cmd);
+ ddi_put8(acc_handle, &stp->cmd_status, 0);
+ ddi_put8(acc_handle, &stp->connection_status, 0);
+ ddi_put8(acc_handle, &stp->target_id, kstp->target_id);
+ ddi_put8(acc_handle, &stp->sge_count, kstp->sge_count);
+
+ ddi_put16(acc_handle, &stp->timeout, kstp->timeout);
+ ddi_put32(acc_handle, &stp->data_xfer_len, kstp->data_xfer_len);
+
+ ddi_rep_put8(acc_handle, (uint8_t *)kstp->fis, (uint8_t *)stp->fis, 10,
+ DDI_DEV_AUTOINCR);
+
+ ddi_put16(acc_handle, &stp->flags, kstp->flags & ~MFI_FRAME_SGL64);
+ ddi_put32(acc_handle, &stp->stp_flags, kstp->stp_flags);
+ ddi_put32(acc_handle, &stp->sgl.sge32[0].length, fis_xferlen);
+ ddi_put32(acc_handle, &stp->sgl.sge32[0].phys_addr,
+ fis_dma_obj.dma_cookie[0].dmac_address);
+ ddi_put32(acc_handle, &stp->sgl.sge32[1].length, data_xferlen);
+ ddi_put32(acc_handle, &stp->sgl.sge32[1].phys_addr,
+ data_dma_obj.dma_cookie[0].dmac_address);
+
+ cmd->sync_cmd = DRSAS_TRUE;
+ cmd->frame_count = 1;
+
+ if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) {
+ con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: fw_ioctl failed"));
+ } else {
+
+ if (fis_xferlen) {
+ for (i = 0; i < fis_xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)fis_dma_obj.buffer + i,
+ (uint8_t *)fis_ubuf + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_stp : copy to "
+ "user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+ }
+ if (data_xferlen) {
+ for (i = 0; i < data_xferlen; i++) {
+ if (ddi_copyout(
+ (uint8_t *)data_dma_obj.buffer + i,
+ (uint8_t *)data_ubuf + i, 1, mode)) {
+ con_log(CL_ANN, (CE_WARN,
+ "issue_mfi_stp : copy to"
+ " user space failed"));
+ return (DDI_FAILURE);
+ }
+ }
+ }
+
+ kstp->cmd_status = ddi_get8(acc_handle, &stp->cmd_status);
+
+ if (fis_xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, fis_dma_obj) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ if (data_xferlen) {
+ /* free kernel buffer */
+ if (drsas_free_dma_obj(instance, data_dma_obj) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * fill_up_drv_ver
+ */
+static void
+fill_up_drv_ver(struct drsas_drv_ver *dv)
+{
+ (void) memset(dv, 0, sizeof (struct drsas_drv_ver));
+
+ (void) memcpy(dv->signature, "$LSI LOGIC$", strlen("$LSI LOGIC$"));
+ (void) memcpy(dv->os_name, "Solaris", strlen("Solaris"));
+ (void) memcpy(dv->drv_name, "dr_sas", strlen("dr_sas"));
+ (void) memcpy(dv->drv_ver, DRSAS_VERSION, strlen(DRSAS_VERSION));
+ (void) memcpy(dv->drv_rel_date, DRSAS_RELDATE,
+ strlen(DRSAS_RELDATE));
+}
+
+/*
+ * handle_drv_ioctl
+ */
+static int
+handle_drv_ioctl(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ int mode)
+{
+ int i;
+ int rval = DDI_SUCCESS;
+ int *props = NULL;
+ void *ubuf;
+
+ uint8_t *pci_conf_buf;
+ uint32_t xferlen;
+ uint32_t num_props;
+ uint_t model;
+ struct drsas_dcmd_frame *kdcmd;
+ struct drsas_drv_ver dv;
+ struct drsas_pci_information pi;
+
+ kdcmd = (struct drsas_dcmd_frame *)&ioctl->frame[0];
+
+ model = ddi_model_convert_from(mode & FMODELS);
+ if (model == DDI_MODEL_ILP32) {
+ con_log(CL_ANN1, (CE_NOTE,
+ "handle_drv_ioctl: DDI_MODEL_ILP32"));
+
+ xferlen = kdcmd->sgl.sge32[0].length;
+
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr;
+ } else {
+#ifdef _ILP32
+ con_log(CL_ANN1, (CE_NOTE,
+ "handle_drv_ioctl: DDI_MODEL_ILP32"));
+ xferlen = kdcmd->sgl.sge32[0].length;
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr;
+#else
+ con_log(CL_ANN1, (CE_NOTE,
+ "handle_drv_ioctl: DDI_MODEL_LP64"));
+ xferlen = kdcmd->sgl.sge64[0].length;
+ ubuf = (void *)(ulong_t)kdcmd->sgl.sge64[0].phys_addr;
+#endif
+ }
+ con_log(CL_ANN1, (CE_NOTE, "handle_drv_ioctl: "
+ "dataBuf=%p size=%d bytes", ubuf, xferlen));
+
+ switch (kdcmd->opcode) {
+ case DRSAS_DRIVER_IOCTL_DRIVER_VERSION:
+ con_log(CL_ANN1, (CE_NOTE, "handle_drv_ioctl: "
+ "DRSAS_DRIVER_IOCTL_DRIVER_VERSION"));
+
+ fill_up_drv_ver(&dv);
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyout((uint8_t *)&dv + i, (uint8_t *)ubuf + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+ "DRSAS_DRIVER_IOCTL_DRIVER_VERSION"
+ " : copy to user space failed"));
+ kdcmd->cmd_status = 1;
+ rval = DDI_FAILURE;
+ break;
+ }
+ }
+ if (i == xferlen)
+ kdcmd->cmd_status = 0;
+ break;
+ case DRSAS_DRIVER_IOCTL_PCI_INFORMATION:
+ con_log(CL_ANN1, (CE_NOTE, "handle_drv_ioctl: "
+ "DRSAS_DRIVER_IOCTL_PCI_INFORMAITON"));
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, instance->dip,
+ 0, "reg", &props, &num_props)) {
+ con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+ "DRSAS_DRIVER_IOCTL_PCI_INFORMATION : "
+ "ddi_prop_look_int_array failed"));
+ rval = DDI_FAILURE;
+ } else {
+
+ pi.busNumber = (props[0] >> 16) & 0xFF;
+ pi.deviceNumber = (props[0] >> 11) & 0x1f;
+ pi.functionNumber = (props[0] >> 8) & 0x7;
+ ddi_prop_free((void *)props);
+ }
+
+ pci_conf_buf = (uint8_t *)&pi.pciHeaderInfo;
+
+ for (i = 0; i < (sizeof (struct drsas_pci_information) -
+ offsetof(struct drsas_pci_information, pciHeaderInfo));
+ i++) {
+ pci_conf_buf[i] =
+ pci_config_get8(instance->pci_handle, i);
+ }
+ for (i = 0; i < xferlen; i++) {
+ if (ddi_copyout((uint8_t *)&pi + i, (uint8_t *)ubuf + i,
+ 1, mode)) {
+ con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+ "DRSAS_DRIVER_IOCTL_PCI_INFORMATION"
+ " : copy to user space failed"));
+ kdcmd->cmd_status = 1;
+ rval = DDI_FAILURE;
+ break;
+ }
+ }
+
+ if (i == xferlen)
+ kdcmd->cmd_status = 0;
+
+ break;
+ default:
+ con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+ "invalid driver specific IOCTL opcode = 0x%x",
+ kdcmd->opcode));
+ kdcmd->cmd_status = 1;
+ rval = DDI_FAILURE;
+ break;
+ }
+
+ return (rval);
+}
+
+/*
+ * handle_mfi_ioctl
+ */
+static int
+handle_mfi_ioctl(struct drsas_instance *instance, struct drsas_ioctl *ioctl,
+ int mode)
+{
+ int rval = DDI_SUCCESS;
+
+ struct drsas_header *hdr;
+ struct drsas_cmd *cmd;
+
+ cmd = get_mfi_pkt(instance);
+
+ if (!cmd) {
+ con_log(CL_ANN, (CE_WARN, "dr_sas: "
+ "failed to get a cmd packet"));
+ return (DDI_FAILURE);
+ }
+
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ hdr = (struct drsas_header *)&ioctl->frame[0];
+
+ switch (hdr->cmd) {
+ case MFI_CMD_OP_DCMD:
+ rval = issue_mfi_dcmd(instance, ioctl, cmd, mode);
+ break;
+ case MFI_CMD_OP_SMP:
+ rval = issue_mfi_smp(instance, ioctl, cmd, mode);
+ break;
+ case MFI_CMD_OP_STP:
+ rval = issue_mfi_stp(instance, ioctl, cmd, mode);
+ break;
+ case MFI_CMD_OP_LD_SCSI:
+ case MFI_CMD_OP_PD_SCSI:
+ rval = issue_mfi_pthru(instance, ioctl, cmd, mode);
+ break;
+ default:
+ con_log(CL_ANN, (CE_WARN, "handle_mfi_ioctl: "
+ "invalid mfi ioctl hdr->cmd = %d", hdr->cmd));
+ rval = DDI_FAILURE;
+ break;
+ }
+
+
+ return_mfi_pkt(instance, cmd);
+ if (drsas_common_check(instance, cmd) != DDI_SUCCESS)
+ rval = DDI_FAILURE;
+ return (rval);
+}
+
+/*
+ * AEN
+ */
+static int
+handle_mfi_aen(struct drsas_instance *instance, struct drsas_aen *aen)
+{
+ int rval = 0;
+
+ rval = register_mfi_aen(instance, instance->aen_seq_num,
+ aen->class_locale_word);
+
+ aen->cmd_status = (uint8_t)rval;
+
+ return (rval);
+}
+
+static int
+register_mfi_aen(struct drsas_instance *instance, uint32_t seq_num,
+ uint32_t class_locale_word)
+{
+ int ret_val;
+
+ struct drsas_cmd *cmd, *aen_cmd;
+ struct drsas_dcmd_frame *dcmd;
+ union drsas_evt_class_locale curr_aen;
+ union drsas_evt_class_locale prev_aen;
+
+ /*
+ * If there an AEN pending already (aen_cmd), check if the
+ * class_locale of that pending AEN is inclusive of the new
+ * AEN request we currently have. If it is, then we don't have
+ * to do anything. In other words, whichever events the current
+ * AEN request is subscribing to, have already been subscribed
+ * to.
+ *
+ * If the old_cmd is _not_ inclusive, then we have to abort
+ * that command, form a class_locale that is superset of both
+ * old and current and re-issue to the FW
+ */
+
+ curr_aen.word = class_locale_word;
+ aen_cmd = instance->aen_cmd;
+ if (aen_cmd) {
+ prev_aen.word = ddi_get32(aen_cmd->frame_dma_obj.acc_handle,
+ &aen_cmd->frame->dcmd.mbox.w[1]);
+
+ /*
+ * A class whose enum value is smaller is inclusive of all
+ * higher values. If a PROGRESS (= -1) was previously
+ * registered, then a new registration requests for higher
+ * classes need not be sent to FW. They are automatically
+ * included.
+ *
+ * Locale numbers don't have such hierarchy. They are bitmap
+ * values
+ */
+ if ((prev_aen.members.class <= curr_aen.members.class) &&
+ !((prev_aen.members.locale & curr_aen.members.locale) ^
+ curr_aen.members.locale)) {
+ /*
+ * Previously issued event registration includes
+ * current request. Nothing to do.
+ */
+
+ return (0);
+ } else {
+ curr_aen.members.locale |= prev_aen.members.locale;
+
+ if (prev_aen.members.class < curr_aen.members.class)
+ curr_aen.members.class = prev_aen.members.class;
+
+ ret_val = abort_aen_cmd(instance, aen_cmd);
+
+ if (ret_val) {
+ con_log(CL_ANN, (CE_WARN, "register_mfi_aen: "
+ "failed to abort prevous AEN command"));
+
+ return (ret_val);
+ }
+ }
+ } else {
+ curr_aen.word = class_locale_word;
+ }
+
+ cmd = get_mfi_pkt(instance);
+
+ if (!cmd)
+ return (ENOMEM);
+ /* Clear the frame buffer and assign back the context id */
+ (void) memset((char *)&cmd->frame[0], 0, sizeof (union drsas_frame));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context,
+ cmd->index);
+
+ dcmd = &cmd->frame->dcmd;
+
+ /* for(i = 0; i < DCMD_MBOX_SZ; i++) dcmd->mbox.b[i] = 0; */
+ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ);
+
+ (void) memset(instance->mfi_evt_detail_obj.buffer, 0,
+ sizeof (struct drsas_evt_detail));
+
+ /* Prepare DCMD for aen registration */
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0x0);
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags,
+ MFI_FRAME_DIR_READ);
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len,
+ sizeof (struct drsas_evt_detail));
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode,
+ DR_DCMD_CTRL_EVENT_WAIT);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[0], seq_num);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[1],
+ curr_aen.word);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr,
+ instance->mfi_evt_detail_obj.dma_cookie[0].dmac_address);
+ ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length,
+ sizeof (struct drsas_evt_detail));
+
+ instance->aen_seq_num = seq_num;
+
+
+ /*
+ * Store reference to the cmd used to register for AEN. When an
+ * application wants us to register for AEN, we have to abort this
+ * cmd and re-register with a new EVENT LOCALE supplied by that app
+ */
+ instance->aen_cmd = cmd;
+
+ cmd->frame_count = 1;
+
+ /* Issue the aen registration frame */
+ /* atomic_add_16 (&instance->fw_outstanding, 1); */
+ instance->func_ptr->issue_cmd(cmd, instance);
+
+ return (0);
+}
+
+static void
+display_scsi_inquiry(caddr_t scsi_inq)
+{
+#define MAX_SCSI_DEVICE_CODE 14
+ int i;
+ char inquiry_buf[256] = {0};
+ int len;
+ const char *const scsi_device_types[] = {
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications ",
+ "Unknown ",
+ "Unknown ",
+ "Unknown ",
+ "Enclosure ",
+ };
+
+ len = 0;
+
+ len += snprintf(inquiry_buf + len, 265 - len, " Vendor: ");
+ for (i = 8; i < 16; i++) {
+ len += snprintf(inquiry_buf + len, 265 - len, "%c",
+ scsi_inq[i]);
+ }
+
+ len += snprintf(inquiry_buf + len, 265 - len, " Model: ");
+
+ for (i = 16; i < 32; i++) {
+ len += snprintf(inquiry_buf + len, 265 - len, "%c",
+ scsi_inq[i]);
+ }
+
+ len += snprintf(inquiry_buf + len, 265 - len, " Rev: ");
+
+ for (i = 32; i < 36; i++) {
+ len += snprintf(inquiry_buf + len, 265 - len, "%c",
+ scsi_inq[i]);
+ }
+
+ len += snprintf(inquiry_buf + len, 265 - len, "\n");
+
+
+ i = scsi_inq[0] & 0x1f;
+
+
+ len += snprintf(inquiry_buf + len, 265 - len, " Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] :
+ "Unknown ");
+
+
+ len += snprintf(inquiry_buf + len, 265 - len,
+ " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07);
+
+ if ((scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1) {
+ len += snprintf(inquiry_buf + len, 265 - len, " CCS\n");
+ } else {
+ len += snprintf(inquiry_buf + len, 265 - len, "\n");
+ }
+
+ con_log(CL_ANN1, (CE_CONT, inquiry_buf));
+}
+
+static int
+read_fw_status_reg_ppc(struct drsas_instance *instance)
+{
+ return ((int)RD_OB_SCRATCH_PAD_0(instance));
+}
+
+static void
+issue_cmd_ppc(struct drsas_cmd *cmd, struct drsas_instance *instance)
+{
+ atomic_add_16(&instance->fw_outstanding, 1);
+
+ /* Issue the command to the FW */
+ WR_IB_QPORT((cmd->frame_phys_addr) |
+ (((cmd->frame_count - 1) << 1) | 1), instance);
+}
+
+/*
+ * issue_cmd_in_sync_mode
+ */
+static int
+issue_cmd_in_sync_mode_ppc(struct drsas_instance *instance,
+ struct drsas_cmd *cmd)
+{
+ int i;
+ uint32_t msecs = MFI_POLL_TIMEOUT_SECS * (10 * MILLISEC);
+
+ con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_sync_mode_ppc: called"));
+
+ cmd->cmd_status = ENODATA;
+
+ WR_IB_QPORT((cmd->frame_phys_addr) |
+ (((cmd->frame_count - 1) << 1) | 1), instance);
+
+ mutex_enter(&instance->int_cmd_mtx);
+
+ for (i = 0; i < msecs && (cmd->cmd_status == ENODATA); i++) {
+ cv_wait(&instance->int_cmd_cv, &instance->int_cmd_mtx);
+ }
+
+ mutex_exit(&instance->int_cmd_mtx);
+
+ con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_sync_mode_ppc: done"));
+
+ if (i < (msecs -1)) {
+ return (DDI_SUCCESS);
+ } else {
+ return (DDI_FAILURE);
+ }
+}
+
+/*
+ * issue_cmd_in_poll_mode
+ */
+static int
+issue_cmd_in_poll_mode_ppc(struct drsas_instance *instance,
+ struct drsas_cmd *cmd)
+{
+ int i;
+ uint16_t flags;
+ uint32_t msecs = MFI_POLL_TIMEOUT_SECS * MILLISEC;
+ struct drsas_header *frame_hdr;
+
+ con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_poll_mode_ppc: called"));
+
+ frame_hdr = (struct drsas_header *)cmd->frame;
+ ddi_put8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status,
+ MFI_CMD_STATUS_POLL_MODE);
+ flags = ddi_get16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags);
+ flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+
+ ddi_put16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags, flags);
+
+ /* issue the frame using inbound queue port */
+ WR_IB_QPORT((cmd->frame_phys_addr) |
+ (((cmd->frame_count - 1) << 1) | 1), instance);
+
+ /* wait for cmd_status to change from 0xFF */
+ for (i = 0; i < msecs && (
+ ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status)
+ == MFI_CMD_STATUS_POLL_MODE); i++) {
+ drv_usecwait(MILLISEC); /* wait for 1000 usecs */
+ }
+
+ if (ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status)
+ == MFI_CMD_STATUS_POLL_MODE) {
+ con_log(CL_ANN, (CE_NOTE, "issue_cmd_in_poll_mode: "
+ "cmd polling timed out"));
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static void
+enable_intr_ppc(struct drsas_instance *instance)
+{
+ uint32_t mask;
+
+ con_log(CL_ANN1, (CE_NOTE, "enable_intr_ppc: called"));
+
+ /* WR_OB_DOORBELL_CLEAR(0xFFFFFFFF, instance); */
+ WR_OB_DOORBELL_CLEAR(OB_DOORBELL_CLEAR_MASK, instance);
+
+ /* WR_OB_INTR_MASK(~0x80000000, instance); */
+ WR_OB_INTR_MASK(~(MFI_REPLY_2108_MESSAGE_INTR_MASK), instance);
+
+ /* dummy read to force PCI flush */
+ mask = RD_OB_INTR_MASK(instance);
+
+ con_log(CL_ANN1, (CE_NOTE, "enable_intr_ppc: "
+ "outbound_intr_mask = 0x%x", mask));
+}
+
+static void
+disable_intr_ppc(struct drsas_instance *instance)
+{
+ uint32_t mask;
+
+ con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: called"));
+
+ con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: before : "
+ "outbound_intr_mask = 0x%x", RD_OB_INTR_MASK(instance)));
+
+ /* WR_OB_INTR_MASK(0xFFFFFFFF, instance); */
+ WR_OB_INTR_MASK(OB_INTR_MASK, instance);
+
+ con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: after : "
+ "outbound_intr_mask = 0x%x", RD_OB_INTR_MASK(instance)));
+
+ /* dummy read to force PCI flush */
+ mask = RD_OB_INTR_MASK(instance);
+#ifdef lint
+ mask = mask;
+#endif
+}
+
+static int
+intr_ack_ppc(struct drsas_instance *instance)
+{
+ uint32_t status;
+
+ con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: called"));
+
+ /* check if it is our interrupt */
+ status = RD_OB_INTR_STATUS(instance);
+
+ con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: status = 0x%x", status));
+
+ if (!(status & MFI_REPLY_2108_MESSAGE_INTR)) {
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* clear the interrupt by writing back the same value */
+ WR_OB_DOORBELL_CLEAR(status, instance);
+
+ /* dummy READ */
+ status = RD_OB_INTR_STATUS(instance);
+
+ con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: interrupt cleared"));
+
+ return (DDI_INTR_CLAIMED);
+}
+
+static int
+drsas_common_check(struct drsas_instance *instance,
+ struct drsas_cmd *cmd)
+{
+ int ret = DDI_SUCCESS;
+
+ if (drsas_check_dma_handle(cmd->frame_dma_obj.dma_handle) !=
+ DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+ if (cmd->pkt != NULL) {
+ cmd->pkt->pkt_reason = CMD_TRAN_ERR;
+ cmd->pkt->pkt_statistics = 0;
+ }
+ ret = DDI_FAILURE;
+ }
+ if (drsas_check_dma_handle(instance->mfi_internal_dma_obj.dma_handle)
+ != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+ if (cmd->pkt != NULL) {
+ cmd->pkt->pkt_reason = CMD_TRAN_ERR;
+ cmd->pkt->pkt_statistics = 0;
+ }
+ ret = DDI_FAILURE;
+ }
+ if (drsas_check_dma_handle(instance->mfi_evt_detail_obj.dma_handle) !=
+ DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+ if (cmd->pkt != NULL) {
+ cmd->pkt->pkt_reason = CMD_TRAN_ERR;
+ cmd->pkt->pkt_statistics = 0;
+ }
+ ret = DDI_FAILURE;
+ }
+ if (drsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) {
+ ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+
+ ddi_fm_acc_err_clear(instance->regmap_handle, DDI_FME_VER0);
+
+ if (cmd->pkt != NULL) {
+ cmd->pkt->pkt_reason = CMD_TRAN_ERR;
+ cmd->pkt->pkt_statistics = 0;
+ }
+ ret = DDI_FAILURE;
+ }
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+drsas_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
+{
+ /*
+ * as the driver can always deal with an error in any dma or
+ * access handle, we can just return the fme_status value.
+ */
+ pci_ereport_post(dip, err, NULL);
+ return (err->fme_status);
+}
+
+static void
+drsas_fm_init(struct drsas_instance *instance)
+{
+ /* Need to change iblock to priority for new MSI intr */
+ ddi_iblock_cookie_t fm_ibc;
+
+ /* Only register with IO Fault Services if we have some capability */
+ if (instance->fm_capabilities) {
+ /* Adjust access and dma attributes for FMA */
+ endian_attr.devacc_attr_access = DDI_FLAGERR_ACC;
+ drsas_generic_dma_attr.dma_attr_flags = DDI_DMA_FLAGERR;
+
+ /*
+ * Register capabilities with IO Fault Services.
+ * fm_capabilities will be updated to indicate
+ * capabilities actually supported (not requested.)
+ */
+
+ ddi_fm_init(instance->dip, &instance->fm_capabilities, &fm_ibc);
+
+ /*
+ * Initialize pci ereport capabilities if ereport
+ * capable (should always be.)
+ */
+
+ if (DDI_FM_EREPORT_CAP(instance->fm_capabilities) ||
+ DDI_FM_ERRCB_CAP(instance->fm_capabilities)) {
+ pci_ereport_setup(instance->dip);
+ }
+
+ /*
+ * Register error callback if error callback capable.
+ */
+ if (DDI_FM_ERRCB_CAP(instance->fm_capabilities)) {
+ ddi_fm_handler_register(instance->dip,
+ drsas_fm_error_cb, (void*) instance);
+ }
+ } else {
+ endian_attr.devacc_attr_access = DDI_DEFAULT_ACC;
+ drsas_generic_dma_attr.dma_attr_flags = 0;
+ }
+}
+
+static void
+drsas_fm_fini(struct drsas_instance *instance)
+{
+ /* Only unregister FMA capabilities if registered */
+ if (instance->fm_capabilities) {
+ /*
+ * Un-register error callback if error callback capable.
+ */
+ if (DDI_FM_ERRCB_CAP(instance->fm_capabilities)) {
+ ddi_fm_handler_unregister(instance->dip);
+ }
+
+ /*
+ * Release any resources allocated by pci_ereport_setup()
+ */
+ if (DDI_FM_EREPORT_CAP(instance->fm_capabilities) ||
+ DDI_FM_ERRCB_CAP(instance->fm_capabilities)) {
+ pci_ereport_teardown(instance->dip);
+ }
+
+ /* Unregister from IO Fault Services */
+ ddi_fm_fini(instance->dip);
+
+ /* Adjust access and dma attributes for FMA */
+ endian_attr.devacc_attr_access = DDI_DEFAULT_ACC;
+ drsas_generic_dma_attr.dma_attr_flags = 0;
+ }
+}
+
+int
+drsas_check_acc_handle(ddi_acc_handle_t handle)
+{
+ ddi_fm_error_t de;
+
+ if (handle == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION);
+
+ return (de.fme_status);
+}
+
+int
+drsas_check_dma_handle(ddi_dma_handle_t handle)
+{
+ ddi_fm_error_t de;
+
+ if (handle == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION);
+
+ return (de.fme_status);
+}
+
+void
+drsas_fm_ereport(struct drsas_instance *instance, char *detail)
+{
+ uint64_t ena;
+ char buf[FM_MAX_CLASS];
+
+ (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
+ ena = fm_ena_generate(0, FM_ENA_FMT1);
+ if (DDI_FM_EREPORT_CAP(instance->fm_capabilities)) {
+ ddi_fm_ereport_post(instance->dip, buf, ena, DDI_NOSLEEP,
+ FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERSION, NULL);
+ }
+}
+
+static int
+drsas_add_intrs(struct drsas_instance *instance, int intr_type)
+{
+
+ dev_info_t *dip = instance->dip;
+ int avail, actual, count;
+ int i, flag, ret;
+
+ con_log(CL_DLEVEL1, (CE_WARN, "drsas_add_intrs: intr_type = %x",
+ intr_type));
+
+ /* Get number of interrupts */
+ ret = ddi_intr_get_nintrs(dip, intr_type, &count);
+ if ((ret != DDI_SUCCESS) || (count == 0)) {
+ con_log(CL_ANN, (CE_WARN, "ddi_intr_get_nintrs() failed:"
+ "ret %d count %d", ret, count));
+
+ return (DDI_FAILURE);
+ }
+
+ con_log(CL_DLEVEL1, (CE_WARN, "drsas_add_intrs: count = %d ", count));
+
+ /* Get number of available interrupts */
+ ret = ddi_intr_get_navail(dip, intr_type, &avail);
+ if ((ret != DDI_SUCCESS) || (avail == 0)) {
+ con_log(CL_ANN, (CE_WARN, "ddi_intr_get_navail() failed:"
+ "ret %d avail %d", ret, avail));
+
+ return (DDI_FAILURE);
+ }
+ con_log(CL_DLEVEL1, (CE_WARN, "drsas_add_intrs: avail = %d ", avail));
+
+ /* Only one interrupt routine. So limit the count to 1 */
+ if (count > 1) {
+ count = 1;
+ }
+
+ /*
+ * Allocate an array of interrupt handlers. Currently we support
+ * only one interrupt. The framework can be extended later.
+ */
+ instance->intr_size = count * sizeof (ddi_intr_handle_t);
+ instance->intr_htable = kmem_zalloc(instance->intr_size, KM_SLEEP);
+ ASSERT(instance->intr_htable);
+
+ flag = ((intr_type == DDI_INTR_TYPE_MSI) || (intr_type ==
+ DDI_INTR_TYPE_MSIX)) ? DDI_INTR_ALLOC_STRICT:DDI_INTR_ALLOC_NORMAL;
+
+ /* Allocate interrupt */
+ ret = ddi_intr_alloc(dip, instance->intr_htable, intr_type, 0,
+ count, &actual, flag);
+
+ if ((ret != DDI_SUCCESS) || (actual == 0)) {
+ con_log(CL_ANN, (CE_WARN, "drsas_add_intrs: "
+ "avail = %d", avail));
+ kmem_free(instance->intr_htable, instance->intr_size);
+ return (DDI_FAILURE);
+ }
+ if (actual < count) {
+ con_log(CL_ANN, (CE_WARN, "drsas_add_intrs: "
+ "Requested = %d Received = %d", count, actual));
+ }
+ instance->intr_cnt = actual;
+
+ /*
+ * Get the priority of the interrupt allocated.
+ */
+ if ((ret = ddi_intr_get_pri(instance->intr_htable[0],
+ &instance->intr_pri)) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "drsas_add_intrs: "
+ "get priority call failed"));
+
+ for (i = 0; i < actual; i++) {
+ (void) ddi_intr_free(instance->intr_htable[i]);
+ }
+ kmem_free(instance->intr_htable, instance->intr_size);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Test for high level mutex. we don't support them.
+ */
+ if (instance->intr_pri >= ddi_intr_get_hilevel_pri()) {
+ con_log(CL_ANN, (CE_WARN, "drsas_add_intrs: "
+ "High level interrupts not supported."));
+
+ for (i = 0; i < actual; i++) {
+ (void) ddi_intr_free(instance->intr_htable[i]);
+ }
+ kmem_free(instance->intr_htable, instance->intr_size);
+ return (DDI_FAILURE);
+ }
+
+ con_log(CL_DLEVEL1, (CE_NOTE, "drsas_add_intrs: intr_pri = 0x%x ",
+ instance->intr_pri));
+
+ /* Call ddi_intr_add_handler() */
+ for (i = 0; i < actual; i++) {
+ ret = ddi_intr_add_handler(instance->intr_htable[i],
+ (ddi_intr_handler_t *)drsas_isr, (caddr_t)instance,
+ (caddr_t)(uintptr_t)i);
+
+ if (ret != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "drsas_add_intrs:"
+ "failed %d", ret));
+
+ for (i = 0; i < actual; i++) {
+ (void) ddi_intr_free(instance->intr_htable[i]);
+ }
+ kmem_free(instance->intr_htable, instance->intr_size);
+ return (DDI_FAILURE);
+ }
+
+ }
+
+ con_log(CL_DLEVEL1, (CE_WARN, " ddi_intr_add_handler done"));
+
+ if ((ret = ddi_intr_get_cap(instance->intr_htable[0],
+ &instance->intr_cap)) != DDI_SUCCESS) {
+ con_log(CL_ANN, (CE_WARN, "ddi_intr_get_cap() failed %d",
+ ret));
+
+ /* Free already allocated intr */
+ for (i = 0; i < actual; i++) {
+ (void) ddi_intr_remove_handler(
+ instance->intr_htable[i]);
+ (void) ddi_intr_free(instance->intr_htable[i]);
+ }
+ kmem_free(instance->intr_htable, instance->intr_size);
+ return (DDI_FAILURE);
+ }
+
+ if (instance->intr_cap & DDI_INTR_FLAG_BLOCK) {
+ con_log(CL_ANN, (CE_WARN, "Calling ddi_intr_block _enable"));
+
+ (void) ddi_intr_block_enable(instance->intr_htable,
+ instance->intr_cnt);
+ } else {
+ con_log(CL_ANN, (CE_NOTE, " calling ddi_intr_enable"));
+
+ for (i = 0; i < instance->intr_cnt; i++) {
+ (void) ddi_intr_enable(instance->intr_htable[i]);
+ con_log(CL_ANN, (CE_NOTE, "ddi intr enable returns "
+ "%d", i));
+ }
+ }
+
+ return (DDI_SUCCESS);
+
+}
+
+
+static void
+drsas_rem_intrs(struct drsas_instance *instance)
+{
+ int i;
+
+ con_log(CL_ANN, (CE_NOTE, "drsas_rem_intrs called"));
+
+ /* Disable all interrupts first */
+ if (instance->intr_cap & DDI_INTR_FLAG_BLOCK) {
+ (void) ddi_intr_block_disable(instance->intr_htable,
+ instance->intr_cnt);
+ } else {
+ for (i = 0; i < instance->intr_cnt; i++) {
+ (void) ddi_intr_disable(instance->intr_htable[i]);
+ }
+ }
+
+ /* Remove all the handlers */
+
+ for (i = 0; i < instance->intr_cnt; i++) {
+ (void) ddi_intr_remove_handler(instance->intr_htable[i]);
+ (void) ddi_intr_free(instance->intr_htable[i]);
+ }
+
+ kmem_free(instance->intr_htable, instance->intr_size);
+}
+
+static int
+drsas_tran_bus_config(dev_info_t *parent, uint_t flags,
+ ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+ struct drsas_instance *instance;
+ int config;
+ int rval;
+
+ char *ptr = NULL;
+ int tgt, lun;
+
+ con_log(CL_ANN1, (CE_NOTE, "Bus config called for op = %x", op));
+
+ if ((instance = ddi_get_soft_state(drsas_state,
+ ddi_get_instance(parent))) == NULL) {
+ return (NDI_FAILURE);
+ }
+
+ /* Hold nexus during bus_config */
+ ndi_devi_enter(parent, &config);
+ switch (op) {
+ case BUS_CONFIG_ONE: {
+
+ /* parse wwid/target name out of name given */
+ if ((ptr = strchr((char *)arg, '@')) == NULL) {
+ rval = NDI_FAILURE;
+ break;
+ }
+ ptr++;
+
+ if (drsas_parse_devname(arg, &tgt, &lun) != 0) {
+ rval = NDI_FAILURE;
+ break;
+ }
+
+ if (lun == 0) {
+ rval = drsas_config_ld(instance, tgt, lun, childp);
+ } else {
+ rval = NDI_FAILURE;
+ }
+
+ break;
+ }
+ case BUS_CONFIG_DRIVER:
+ case BUS_CONFIG_ALL: {
+
+ rval = drsas_config_all_devices(instance);
+
+ rval = NDI_SUCCESS;
+ break;
+ }
+ }
+
+ if (rval == NDI_SUCCESS) {
+ rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
+
+ }
+ ndi_devi_exit(parent, config);
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_tran_bus_config: rval = %x",
+ rval));
+ return (rval);
+}
+
+static int
+drsas_config_all_devices(struct drsas_instance *instance)
+{
+ int rval, tgt;
+
+ for (tgt = 0; tgt < MRDRV_MAX_LD; tgt++) {
+ (void) drsas_config_ld(instance, tgt, 0, NULL);
+
+ }
+
+ rval = NDI_SUCCESS;
+ return (rval);
+}
+
+static int
+drsas_parse_devname(char *devnm, int *tgt, int *lun)
+{
+ char devbuf[SCSI_MAXNAMELEN];
+ char *addr;
+ char *p, *tp, *lp;
+ long num;
+
+ /* Parse dev name and address */
+ (void) strcpy(devbuf, devnm);
+ addr = "";
+ for (p = devbuf; *p != '\0'; p++) {
+ if (*p == '@') {
+ addr = p + 1;
+ *p = '\0';
+ } else if (*p == ':') {
+ *p = '\0';
+ break;
+ }
+ }
+
+ /* Parse target and lun */
+ for (p = tp = addr, lp = NULL; *p != '\0'; p++) {
+ if (*p == ',') {
+ lp = p + 1;
+ *p = '\0';
+ break;
+ }
+ }
+ if (tgt && tp) {
+ if (ddi_strtol(tp, NULL, 0x10, &num)) {
+ return (DDI_FAILURE); /* Can declare this as constant */
+ }
+ *tgt = (int)num;
+ }
+ if (lun && lp) {
+ if (ddi_strtol(lp, NULL, 0x10, &num)) {
+ return (DDI_FAILURE);
+ }
+ *lun = (int)num;
+ }
+ return (DDI_SUCCESS); /* Success case */
+}
+
+static int
+drsas_config_ld(struct drsas_instance *instance, uint16_t tgt,
+ uint8_t lun, dev_info_t **ldip)
+{
+ struct scsi_device *sd;
+ dev_info_t *child;
+ int rval;
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_config_ld: t = %d l = %d",
+ tgt, lun));
+
+ if ((child = drsas_find_child(instance, tgt, lun)) != NULL) {
+ if (ldip) {
+ *ldip = child;
+ }
+ con_log(CL_ANN1, (CE_NOTE,
+ "drsas_config_ld: Child = %p found t = %d l = %d",
+ (void *)child, tgt, lun));
+ return (NDI_SUCCESS);
+ }
+
+ sd = kmem_zalloc(sizeof (struct scsi_device), KM_SLEEP);
+ sd->sd_address.a_hba_tran = instance->tran;
+ sd->sd_address.a_target = (uint16_t)tgt;
+ sd->sd_address.a_lun = (uint8_t)lun;
+
+ if (scsi_hba_probe(sd, NULL) == SCSIPROBE_EXISTS)
+ rval = drsas_config_scsi_device(instance, sd, ldip);
+ else
+ rval = NDI_FAILURE;
+
+ /* sd_unprobe is blank now. Free buffer manually */
+ if (sd->sd_inq) {
+ kmem_free(sd->sd_inq, SUN_INQSIZE);
+ sd->sd_inq = (struct scsi_inquiry *)NULL;
+ }
+
+ kmem_free(sd, sizeof (struct scsi_device));
+ con_log(CL_ANN1, (CE_NOTE, "drsas_config_ld: return rval = %d",
+ rval));
+ return (rval);
+}
+
+static int
+drsas_config_scsi_device(struct drsas_instance *instance,
+ struct scsi_device *sd, dev_info_t **dipp)
+{
+ char *nodename = NULL;
+ char **compatible = NULL;
+ int ncompatible = 0;
+ char *childname;
+ dev_info_t *ldip = NULL;
+ int tgt = sd->sd_address.a_target;
+ int lun = sd->sd_address.a_lun;
+ int dtype = sd->sd_inq->inq_dtype & DTYPE_MASK;
+ int rval;
+
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: scsi_device t%dL%d", tgt, lun));
+ scsi_hba_nodename_compatible_get(sd->sd_inq, NULL, dtype,
+ NULL, &nodename, &compatible, &ncompatible);
+
+ if (nodename == NULL) {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: Found no compatible driver "
+ "for t%dL%d", tgt, lun));
+ rval = NDI_FAILURE;
+ goto finish;
+ }
+
+ childname = (dtype == DTYPE_DIRECT) ? "sd" : nodename;
+ con_log(CL_ANN1, (CE_WARN,
+ "dr_sas: Childname = %2s nodename = %s", childname, nodename));
+
+ /* Create a dev node */
+ rval = ndi_devi_alloc(instance->dip, childname, DEVI_SID_NODEID, &ldip);
+ con_log(CL_ANN1, (CE_WARN,
+ "dr_sas_config_scsi_device: ndi_devi_alloc rval = %x", rval));
+ if (rval == NDI_SUCCESS) {
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "target", tgt) !=
+ DDI_PROP_SUCCESS) {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: unable to create "
+ "property for t%dl%d target", tgt, lun));
+ rval = NDI_FAILURE;
+ goto finish;
+ }
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "lun", lun) !=
+ DDI_PROP_SUCCESS) {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: unable to create "
+ "property for t%dl%d lun", tgt, lun));
+ rval = NDI_FAILURE;
+ goto finish;
+ }
+
+ if (ndi_prop_update_string_array(DDI_DEV_T_NONE, ldip,
+ "compatible", compatible, ncompatible) !=
+ DDI_PROP_SUCCESS) {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: unable to create "
+ "property for t%dl%d compatible", tgt, lun));
+ rval = NDI_FAILURE;
+ goto finish;
+ }
+
+ rval = ndi_devi_online(ldip, NDI_ONLINE_ATTACH);
+ if (rval != NDI_SUCCESS) {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: unable to online "
+ "t%dl%d", tgt, lun));
+ ndi_prop_remove_all(ldip);
+ (void) ndi_devi_free(ldip);
+ } else {
+ con_log(CL_ANN1, (CE_WARN, "dr_sas: online Done :"
+ "0 t%dl%d", tgt, lun));
+ }
+
+ }
+finish:
+ if (dipp) {
+ *dipp = ldip;
+ }
+
+ con_log(CL_DLEVEL1, (CE_WARN,
+ "dr_sas: config_scsi_device rval = %d t%dL%d",
+ rval, tgt, lun));
+ scsi_hba_nodename_compatible_free(nodename, compatible);
+ return (rval);
+}
+
+/*ARGSUSED*/
+static int
+drsas_service_evt(struct drsas_instance *instance, int tgt, int lun, int event,
+ uint64_t wwn)
+{
+ struct drsas_eventinfo *mrevt = NULL;
+
+ con_log(CL_ANN1, (CE_NOTE,
+ "drsas_service_evt called for t%dl%d event = %d",
+ tgt, lun, event));
+
+ if ((instance->taskq == NULL) || (mrevt =
+ kmem_zalloc(sizeof (struct drsas_eventinfo), KM_NOSLEEP)) == NULL) {
+ return (ENOMEM);
+ }
+
+ mrevt->instance = instance;
+ mrevt->tgt = tgt;
+ mrevt->lun = lun;
+ mrevt->event = event;
+
+ if ((ddi_taskq_dispatch(instance->taskq,
+ (void (*)(void *))drsas_issue_evt_taskq, mrevt, DDI_NOSLEEP)) !=
+ DDI_SUCCESS) {
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: Event task failed for t%dl%d event = %d",
+ tgt, lun, event));
+ kmem_free(mrevt, sizeof (struct drsas_eventinfo));
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+}
+
+static void
+drsas_issue_evt_taskq(struct drsas_eventinfo *mrevt)
+{
+ struct drsas_instance *instance = mrevt->instance;
+ dev_info_t *dip, *pdip;
+ int circ1 = 0;
+ char *devname;
+
+ con_log(CL_ANN1, (CE_NOTE, "drsas_issue_evt_taskq: called for"
+ " tgt %d lun %d event %d",
+ mrevt->tgt, mrevt->lun, mrevt->event));
+
+ if (mrevt->tgt < MRDRV_MAX_LD && mrevt->lun == 0) {
+ dip = instance->dr_ld_list[mrevt->tgt].dip;
+ } else {
+ return;
+ }
+
+ ndi_devi_enter(instance->dip, &circ1);
+ switch (mrevt->event) {
+ case DRSAS_EVT_CONFIG_TGT:
+ if (dip == NULL) {
+
+ if (mrevt->lun == 0) {
+ (void) drsas_config_ld(instance, mrevt->tgt,
+ 0, NULL);
+ }
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: EVT_CONFIG_TGT called:"
+ " for tgt %d lun %d event %d",
+ mrevt->tgt, mrevt->lun, mrevt->event));
+
+ } else {
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: EVT_CONFIG_TGT dip != NULL:"
+ " for tgt %d lun %d event %d",
+ mrevt->tgt, mrevt->lun, mrevt->event));
+ }
+ break;
+ case DRSAS_EVT_UNCONFIG_TGT:
+ if (dip) {
+ if (i_ddi_devi_attached(dip)) {
+
+ pdip = ddi_get_parent(dip);
+
+ devname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
+ (void) ddi_deviname(dip, devname);
+
+ (void) devfs_clean(pdip, devname + 1,
+ DV_CLEAN_FORCE);
+ kmem_free(devname, MAXNAMELEN + 1);
+ }
+ (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: EVT_UNCONFIG_TGT called:"
+ " for tgt %d lun %d event %d",
+ mrevt->tgt, mrevt->lun, mrevt->event));
+ } else {
+ con_log(CL_ANN1, (CE_NOTE,
+ "dr_sas: EVT_UNCONFIG_TGT dip == NULL:"
+ " for tgt %d lun %d event %d",
+ mrevt->tgt, mrevt->lun, mrevt->event));
+ }
+ break;
+ }
+ kmem_free(mrevt, sizeof (struct drsas_eventinfo));
+ ndi_devi_exit(instance->dip, circ1);
+}
+
+static int
+drsas_mode_sense_build(struct scsi_pkt *pkt)
+{
+ union scsi_cdb *cdbp;
+ uint16_t page_code;
+ struct scsa_cmd *acmd;
+ struct buf *bp;
+ struct mode_header *modehdrp;
+
+ cdbp = (void *)pkt->pkt_cdbp;
+ page_code = cdbp->cdb_un.sg.scsi[0];
+ acmd = PKT2CMD(pkt);
+ bp = acmd->cmd_buf;
+ if ((!bp) && bp->b_un.b_addr && bp->b_bcount && acmd->cmd_dmacount) {
+ con_log(CL_ANN1, (CE_WARN, "Failing MODESENSE Command"));
+ /* ADD pkt statistics as Command failed. */
+ return (NULL);
+ }
+
+ bp_mapin(bp);
+ bzero(bp->b_un.b_addr, bp->b_bcount);
+
+ switch (page_code) {
+ case 0x3: {
+ struct mode_format *page3p = NULL;
+ modehdrp = (struct mode_header *)(bp->b_un.b_addr);
+ modehdrp->bdesc_length = MODE_BLK_DESC_LENGTH;
+
+ page3p = (void *)((caddr_t)modehdrp +
+ MODE_HEADER_LENGTH + MODE_BLK_DESC_LENGTH);
+ page3p->mode_page.code = 0x3;
+ page3p->mode_page.length =
+ (uchar_t)(sizeof (struct mode_format));
+ page3p->data_bytes_sect = 512;
+ page3p->sect_track = 63;
+ break;
+ }
+ case 0x4: {
+ struct mode_geometry *page4p = NULL;
+ modehdrp = (struct mode_header *)(bp->b_un.b_addr);
+ modehdrp->bdesc_length = MODE_BLK_DESC_LENGTH;
+
+ page4p = (void *)((caddr_t)modehdrp +
+ MODE_HEADER_LENGTH + MODE_BLK_DESC_LENGTH);
+ page4p->mode_page.code = 0x4;
+ page4p->mode_page.length =
+ (uchar_t)(sizeof (struct mode_geometry));
+ page4p->heads = 255;
+ page4p->rpm = 10000;
+ break;
+ }
+ default:
+ break;
+ }
+ return (NULL);
+}
diff --git a/usr/src/uts/common/io/dr_sas/dr_sas.conf b/usr/src/uts/common/io/dr_sas/dr_sas.conf
new file mode 100644
index 0000000000..3792f43ca4
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/dr_sas.conf
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2008-2009, LSI Logic Corporation.
+# All rights reserved.
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# dr_sas.conf for sol 10 (and later) for all supported architectures
+#
+# global definitions
+
+# MSI specific flag. user can uncomment this line and set flag "yes" to enable MSI
+#drsas-enable-msi="yes";
diff --git a/usr/src/uts/common/io/dr_sas/dr_sas.h b/usr/src/uts/common/io/dr_sas/dr_sas.h
new file mode 100644
index 0000000000..8f78658edf
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/dr_sas.h
@@ -0,0 +1,1766 @@
+/*
+ * dr_sas.h: header for dr_sas
+ *
+ * Solaris MegaRAID driver for SAS2.0 controllers
+ * Copyright (c) 2008-2009, LSI Logic Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DR_SAS_H_
+#define _DR_SAS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/scsi/scsi.h>
+#include "dr_sas_list.h"
+
+/*
+ * MegaRAID SAS2.0 Driver meta data
+ */
+#define DRSAS_VERSION "LSIv2.0"
+#define DRSAS_RELDATE "Jan 9, 2009"
+
+#define DRSAS_TRUE 1
+#define DRSAS_FALSE 0
+
+/*
+ * MegaRAID SAS2.0 device id conversion definitions.
+ */
+#define INST2LSIRDCTL(x) ((x) << INST_MINOR_SHIFT)
+
+/*
+ * MegaRAID SAS2.0 supported controllers
+ */
+#define PCI_DEVICE_ID_LSI_2108VDE 0x0078
+#define PCI_DEVICE_ID_LSI_2108V 0x0079
+
+/*
+ * Register Index for 2108 Controllers.
+ */
+#define REGISTER_SET_IO_2108 (2)
+
+#define DRSAS_MAX_SGE_CNT 0x50
+
+#define DRSAS_IOCTL_DRIVER 0x12341234
+#define DRSAS_IOCTL_FIRMWARE 0x12345678
+#define DRSAS_IOCTL_AEN 0x87654321
+
+#define DRSAS_1_SECOND 1000000
+
+/* Dynamic Enumeration Flags */
+#define DRSAS_PD_LUN 1
+#define DRSAS_LD_LUN 0
+#define DRSAS_PD_TGT_MAX 255
+#define DRSAS_GET_PD_MAX(s) ((s)->dr_pd_max)
+#define WWN_STRLEN 17
+
+/*
+ * =====================================
+ * MegaRAID SAS2.0 MFI firmware definitions
+ * =====================================
+ */
+/*
+ * MFI stands for MegaRAID SAS2.0 FW Interface. This is just a moniker for
+ * protocol between the software and firmware. Commands are issued using
+ * "message frames"
+ */
+
+/*
+ * FW posts its state in upper 4 bits of outbound_msg_0 register
+ */
+#define MFI_STATE_SHIFT 28
+#define MFI_STATE_MASK ((uint32_t)0xF<<MFI_STATE_SHIFT)
+#define MFI_STATE_UNDEFINED ((uint32_t)0x0<<MFI_STATE_SHIFT)
+#define MFI_STATE_BB_INIT ((uint32_t)0x1<<MFI_STATE_SHIFT)
+#define MFI_STATE_FW_INIT ((uint32_t)0x4<<MFI_STATE_SHIFT)
+#define MFI_STATE_WAIT_HANDSHAKE ((uint32_t)0x6<<MFI_STATE_SHIFT)
+#define MFI_STATE_FW_INIT_2 ((uint32_t)0x7<<MFI_STATE_SHIFT)
+#define MFI_STATE_DEVICE_SCAN ((uint32_t)0x8<<MFI_STATE_SHIFT)
+#define MFI_STATE_BOOT_MESSAGE_PENDING ((uint32_t)0x9<<MFI_STATE_SHIFT)
+#define MFI_STATE_FLUSH_CACHE ((uint32_t)0xA<<MFI_STATE_SHIFT)
+#define MFI_STATE_READY ((uint32_t)0xB<<MFI_STATE_SHIFT)
+#define MFI_STATE_OPERATIONAL ((uint32_t)0xC<<MFI_STATE_SHIFT)
+#define MFI_STATE_FAULT ((uint32_t)0xF<<MFI_STATE_SHIFT)
+
+#define MRMFI_FRAME_SIZE 64
+
+/*
+ * During FW init, clear pending cmds & reset state using inbound_msg_0
+ *
+ * ABORT : Abort all pending cmds
+ * READY : Move from OPERATIONAL to READY state; discard queue info
+ * MFIMODE : Discard (possible) low MFA posted in 64-bit mode (??)
+ * CLR_HANDSHAKE: FW is waiting for HANDSHAKE from BIOS or Driver
+ */
+#define MFI_INIT_ABORT 0x00000001
+#define MFI_INIT_READY 0x00000002
+#define MFI_INIT_MFIMODE 0x00000004
+#define MFI_INIT_CLEAR_HANDSHAKE 0x00000008
+#define MFI_INIT_HOTPLUG 0x00000010
+#define MFI_STOP_ADP 0x00000020
+#define MFI_RESET_FLAGS MFI_INIT_READY|MFI_INIT_MFIMODE|MFI_INIT_ABORT
+
+/*
+ * MFI frame flags
+ */
+#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000
+#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001
+#define MFI_FRAME_SGL32 0x0000
+#define MFI_FRAME_SGL64 0x0002
+#define MFI_FRAME_SENSE32 0x0000
+#define MFI_FRAME_SENSE64 0x0004
+#define MFI_FRAME_DIR_NONE 0x0000
+#define MFI_FRAME_DIR_WRITE 0x0008
+#define MFI_FRAME_DIR_READ 0x0010
+#define MFI_FRAME_DIR_BOTH 0x0018
+
+/*
+ * Definition for cmd_status
+ */
+#define MFI_CMD_STATUS_POLL_MODE 0xFF
+#define MFI_CMD_STATUS_SYNC_MODE 0xFF
+
+/*
+ * MFI command opcodes
+ */
+#define MFI_CMD_OP_INIT 0x00
+#define MFI_CMD_OP_LD_READ 0x01
+#define MFI_CMD_OP_LD_WRITE 0x02
+#define MFI_CMD_OP_LD_SCSI 0x03
+#define MFI_CMD_OP_PD_SCSI 0x04
+#define MFI_CMD_OP_DCMD 0x05
+#define MFI_CMD_OP_ABORT 0x06
+#define MFI_CMD_OP_SMP 0x07
+#define MFI_CMD_OP_STP 0x08
+
+#define DR_DCMD_CTRL_GET_INFO 0x01010000
+
+#define DR_DCMD_CTRL_CACHE_FLUSH 0x01101000
+#define DR_FLUSH_CTRL_CACHE 0x01
+#define DR_FLUSH_DISK_CACHE 0x02
+
+#define DR_DCMD_CTRL_SHUTDOWN 0x01050000
+#define DRSAS_ENABLE_DRIVE_SPINDOWN 0x01
+
+#define DR_DCMD_CTRL_EVENT_GET_INFO 0x01040100
+#define DR_DCMD_CTRL_EVENT_GET 0x01040300
+#define DR_DCMD_CTRL_EVENT_WAIT 0x01040500
+#define DR_DCMD_LD_GET_PROPERTIES 0x03030000
+#define DR_DCMD_PD_GET_INFO 0x02020000
+
+/*
+ * Solaris Specific MAX values
+ */
+#define MAX_SGL 24
+/*
+ * MFI command completion codes
+ */
+enum MFI_STAT {
+ MFI_STAT_OK = 0x00,
+ MFI_STAT_INVALID_CMD = 0x01,
+ MFI_STAT_INVALID_DCMD = 0x02,
+ MFI_STAT_INVALID_PARAMETER = 0x03,
+ MFI_STAT_INVALID_SEQUENCE_NUMBER = 0x04,
+ MFI_STAT_ABORT_NOT_POSSIBLE = 0x05,
+ MFI_STAT_APP_HOST_CODE_NOT_FOUND = 0x06,
+ MFI_STAT_APP_IN_USE = 0x07,
+ MFI_STAT_APP_NOT_INITIALIZED = 0x08,
+ MFI_STAT_ARRAY_INDEX_INVALID = 0x09,
+ MFI_STAT_ARRAY_ROW_NOT_EMPTY = 0x0a,
+ MFI_STAT_CONFIG_RESOURCE_CONFLICT = 0x0b,
+ MFI_STAT_DEVICE_NOT_FOUND = 0x0c,
+ MFI_STAT_DRIVE_TOO_SMALL = 0x0d,
+ MFI_STAT_FLASH_ALLOC_FAIL = 0x0e,
+ MFI_STAT_FLASH_BUSY = 0x0f,
+ MFI_STAT_FLASH_ERROR = 0x10,
+ MFI_STAT_FLASH_IMAGE_BAD = 0x11,
+ MFI_STAT_FLASH_IMAGE_INCOMPLETE = 0x12,
+ MFI_STAT_FLASH_NOT_OPEN = 0x13,
+ MFI_STAT_FLASH_NOT_STARTED = 0x14,
+ MFI_STAT_FLUSH_FAILED = 0x15,
+ MFI_STAT_HOST_CODE_NOT_FOUNT = 0x16,
+ MFI_STAT_LD_CC_IN_PROGRESS = 0x17,
+ MFI_STAT_LD_INIT_IN_PROGRESS = 0x18,
+ MFI_STAT_LD_LBA_OUT_OF_RANGE = 0x19,
+ MFI_STAT_LD_MAX_CONFIGURED = 0x1a,
+ MFI_STAT_LD_NOT_OPTIMAL = 0x1b,
+ MFI_STAT_LD_RBLD_IN_PROGRESS = 0x1c,
+ MFI_STAT_LD_RECON_IN_PROGRESS = 0x1d,
+ MFI_STAT_LD_WRONG_RAID_LEVEL = 0x1e,
+ MFI_STAT_MAX_SPARES_EXCEEDED = 0x1f,
+ MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20,
+ MFI_STAT_MFC_HW_ERROR = 0x21,
+ MFI_STAT_NO_HW_PRESENT = 0x22,
+ MFI_STAT_NOT_FOUND = 0x23,
+ MFI_STAT_NOT_IN_ENCL = 0x24,
+ MFI_STAT_PD_CLEAR_IN_PROGRESS = 0x25,
+ MFI_STAT_PD_TYPE_WRONG = 0x26,
+ MFI_STAT_PR_DISABLED = 0x27,
+ MFI_STAT_ROW_INDEX_INVALID = 0x28,
+ MFI_STAT_SAS_CONFIG_INVALID_ACTION = 0x29,
+ MFI_STAT_SAS_CONFIG_INVALID_DATA = 0x2a,
+ MFI_STAT_SAS_CONFIG_INVALID_PAGE = 0x2b,
+ MFI_STAT_SAS_CONFIG_INVALID_TYPE = 0x2c,
+ MFI_STAT_SCSI_DONE_WITH_ERROR = 0x2d,
+ MFI_STAT_SCSI_IO_FAILED = 0x2e,
+ MFI_STAT_SCSI_RESERVATION_CONFLICT = 0x2f,
+ MFI_STAT_SHUTDOWN_FAILED = 0x30,
+ MFI_STAT_TIME_NOT_SET = 0x31,
+ MFI_STAT_WRONG_STATE = 0x32,
+ MFI_STAT_LD_OFFLINE = 0x33,
+ /* UNUSED: 0x34 to 0xfe */
+ MFI_STAT_INVALID_STATUS = 0xFF
+};
+
+enum DR_EVT_CLASS {
+ DR_EVT_CLASS_DEBUG = -2,
+ DR_EVT_CLASS_PROGRESS = -1,
+ DR_EVT_CLASS_INFO = 0,
+ DR_EVT_CLASS_WARNING = 1,
+ DR_EVT_CLASS_CRITICAL = 2,
+ DR_EVT_CLASS_FATAL = 3,
+ DR_EVT_CLASS_DEAD = 4
+};
+
+enum DR_EVT_LOCALE {
+ DR_EVT_LOCALE_LD = 0x0001,
+ DR_EVT_LOCALE_PD = 0x0002,
+ DR_EVT_LOCALE_ENCL = 0x0004,
+ DR_EVT_LOCALE_BBU = 0x0008,
+ DR_EVT_LOCALE_SAS = 0x0010,
+ DR_EVT_LOCALE_CTRL = 0x0020,
+ DR_EVT_LOCALE_CONFIG = 0x0040,
+ DR_EVT_LOCALE_CLUSTER = 0x0080,
+ DR_EVT_LOCALE_ALL = 0xffff
+};
+
+#define DR_EVT_CFG_CLEARED 0x0004
+#define DR_EVT_LD_CREATED 0x008a
+#define DR_EVT_LD_DELETED 0x008b
+#define DR_EVT_PD_REMOVED_EXT 0x00f8
+#define DR_EVT_PD_INSERTED_EXT 0x00f7
+
+enum LD_STATE {
+ LD_OFFLINE = 0,
+ LD_PARTIALLY_DEGRADED = 1,
+ LD_DEGRADED = 2,
+ LD_OPTIMAL = 3,
+ LD_INVALID = 0xFF
+};
+
+enum DRSAS_EVT {
+ DRSAS_EVT_CONFIG_TGT = 0,
+ DRSAS_EVT_UNCONFIG_TGT = 1,
+ DRSAS_EVT_UNCONFIG_SMP = 2
+};
+
+#define DMA_OBJ_ALLOCATED 1
+#define DMA_OBJ_REALLOCATED 2
+#define DMA_OBJ_FREED 3
+
+/*
+ * dma_obj_t - Our DMA object
+ * @param buffer : kernel virtual address
+ * @param size : size of the data to be allocated
+ * @param acc_handle : access handle
+ * @param dma_handle : dma handle
+ * @param dma_cookie : scatter-gather list
+ * @param dma_attr : dma attributes for this buffer
+ * Our DMA object. The caller must initialize the size and dma attributes
+ * (dma_attr) fields before allocating the resources.
+ */
+typedef struct {
+ caddr_t buffer;
+ uint32_t size;
+ ddi_acc_handle_t acc_handle;
+ ddi_dma_handle_t dma_handle;
+ ddi_dma_cookie_t dma_cookie[DRSAS_MAX_SGE_CNT];
+ ddi_dma_attr_t dma_attr;
+ uint8_t status;
+ uint8_t reserved[3];
+} dma_obj_t;
+
+struct drsas_eventinfo {
+ struct drsas_instance *instance;
+ int tgt;
+ int lun;
+ int event;
+};
+
+struct drsas_ld {
+ dev_info_t *dip;
+ uint8_t lun_type;
+ uint8_t reserved[3];
+};
+
+struct drsas_pd {
+ dev_info_t *dip;
+ uint8_t lun_type;
+ uint8_t dev_id;
+ uint8_t flags;
+ uint8_t reserved;
+};
+
+struct drsas_pd_info {
+ uint16_t deviceId;
+ uint16_t seqNum;
+ uint8_t inquiryData[96];
+ uint8_t vpdPage83[64];
+ uint8_t notSupported;
+ uint8_t scsiDevType;
+ uint8_t a;
+ uint8_t device_speed;
+ uint32_t mediaerrcnt;
+ uint32_t other;
+ uint32_t pred;
+ uint32_t lastpred;
+ uint16_t fwState;
+ uint8_t disabled;
+ uint8_t linkspwwd;
+ uint32_t ddfType;
+ struct {
+ uint8_t count;
+ uint8_t isPathBroken;
+ uint8_t connectorIndex[2];
+ uint8_t reserved[4];
+ uint64_t sasAddr[2];
+ uint8_t reserved2[16];
+ } pathInfo;
+};
+
+typedef struct drsas_instance {
+ uint32_t *producer;
+ uint32_t *consumer;
+
+ uint32_t *reply_queue;
+ dma_obj_t mfi_internal_dma_obj;
+
+ uint8_t init_id;
+ uint8_t reserved[3];
+
+ uint16_t max_num_sge;
+ uint16_t max_fw_cmds;
+ uint32_t max_sectors_per_req;
+
+ struct drsas_cmd **cmd_list;
+
+ mlist_t cmd_pool_list;
+ kmutex_t cmd_pool_mtx;
+
+ mlist_t cmd_pend_list;
+ kmutex_t cmd_pend_mtx;
+
+ dma_obj_t mfi_evt_detail_obj;
+ struct drsas_cmd *aen_cmd;
+
+ uint32_t aen_seq_num;
+ uint32_t aen_class_locale_word;
+
+ scsi_hba_tran_t *tran;
+
+ kcondvar_t int_cmd_cv;
+ kmutex_t int_cmd_mtx;
+
+ kcondvar_t aen_cmd_cv;
+ kmutex_t aen_cmd_mtx;
+
+ kcondvar_t abort_cmd_cv;
+ kmutex_t abort_cmd_mtx;
+
+ dev_info_t *dip;
+ ddi_acc_handle_t pci_handle;
+
+ timeout_id_t timeout_id;
+ uint32_t unique_id;
+ uint16_t fw_outstanding;
+ caddr_t regmap;
+ ddi_acc_handle_t regmap_handle;
+ uint8_t isr_level;
+ ddi_iblock_cookie_t iblock_cookie;
+ ddi_iblock_cookie_t soft_iblock_cookie;
+ ddi_softintr_t soft_intr_id;
+ uint8_t softint_running;
+ kmutex_t completed_pool_mtx;
+ mlist_t completed_pool_list;
+
+ caddr_t internal_buf;
+ uint32_t internal_buf_dmac_add;
+ uint32_t internal_buf_size;
+
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t subsysvid;
+ uint16_t subsysid;
+ int instance;
+ int baseaddress;
+ char iocnode[16];
+
+ int fm_capabilities;
+
+ struct drsas_func_ptr *func_ptr;
+ /* MSI interrupts specific */
+ ddi_intr_handle_t *intr_htable;
+ int intr_type;
+ int intr_cnt;
+ size_t intr_size;
+ uint_t intr_pri;
+ int intr_cap;
+
+ ddi_taskq_t *taskq;
+ struct drsas_ld *dr_ld_list;
+} drsas_t;
+
+struct drsas_func_ptr {
+ int (*read_fw_status_reg)(struct drsas_instance *);
+ void (*issue_cmd)(struct drsas_cmd *, struct drsas_instance *);
+ int (*issue_cmd_in_sync_mode)(struct drsas_instance *,
+ struct drsas_cmd *);
+ int (*issue_cmd_in_poll_mode)(struct drsas_instance *,
+ struct drsas_cmd *);
+ void (*enable_intr)(struct drsas_instance *);
+ void (*disable_intr)(struct drsas_instance *);
+ int (*intr_ack)(struct drsas_instance *);
+};
+
+/*
+ * ### Helper routines ###
+ */
+
+/*
+ * con_log() - console log routine
+ * @param level : indicates the severity of the message.
+ * @fparam mt : format string
+ *
+ * con_log displays the error messages on the console based on the current
+ * debug level. Also it attaches the appropriate kernel severity level with
+ * the message.
+ *
+ *
+ * console messages debug levels
+ */
+#define CL_NONE 0 /* No debug information */
+#define CL_ANN 1 /* print unconditionally, announcements */
+#define CL_ANN1 2 /* No o/p */
+#define CL_DLEVEL1 3 /* debug level 1, informative */
+#define CL_DLEVEL2 4 /* debug level 2, verbose */
+#define CL_DLEVEL3 5 /* debug level 3, very verbose */
+
+#ifdef __SUNPRO_C
+#define __func__ ""
+#endif
+
+#define con_log(level, fmt) { if (debug_level_g >= level) cmn_err fmt; }
+
+/*
+ * ### SCSA definitions ###
+ */
+#define PKT2TGT(pkt) ((pkt)->pkt_address.a_target)
+#define PKT2LUN(pkt) ((pkt)->pkt_address.a_lun)
+#define PKT2TRAN(pkt) ((pkt)->pkt_adress.a_hba_tran)
+#define ADDR2TRAN(ap) ((ap)->a_hba_tran)
+
+#define TRAN2MR(tran) (struct drsas_instance *)(tran)->tran_hba_private)
+#define ADDR2MR(ap) (TRAN2MR(ADDR2TRAN(ap))
+
+#define PKT2CMD(pkt) ((struct scsa_cmd *)(pkt)->pkt_ha_private)
+#define CMD2PKT(sp) ((sp)->cmd_pkt)
+#define PKT2REQ(pkt) (&(PKT2CMD(pkt)->request))
+
+#define CMD2ADDR(cmd) (&CMD2PKT(cmd)->pkt_address)
+#define CMD2TRAN(cmd) (CMD2PKT(cmd)->pkt_address.a_hba_tran)
+#define CMD2MR(cmd) (TRAN2MR(CMD2TRAN(cmd)))
+
+#define CFLAG_DMAVALID 0x0001 /* requires a dma operation */
+#define CFLAG_DMASEND 0x0002 /* Transfer from the device */
+#define CFLAG_CONSISTENT 0x0040 /* consistent data transfer */
+
+/*
+ * ### Data structures for ioctl inteface and internal commands ###
+ */
+
+/*
+ * Data direction flags
+ */
+#define UIOC_RD 0x00001
+#define UIOC_WR 0x00002
+
+#define SCP2HOST(scp) (scp)->device->host /* to host */
+#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata /* to soft state */
+#define SCP2CHANNEL(scp) (scp)->device->channel /* to channel */
+#define SCP2TARGET(scp) (scp)->device->id /* to target */
+#define SCP2LUN(scp) (scp)->device->lun /* to LUN */
+
+#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0])
+#define SCP2ADAPTER(scp) \
+ (struct drsas_instance *)SCSIHOST2ADAP(SCP2HOST(scp))
+
+#define MRDRV_IS_LOGICAL_SCSA(instance, acmd) \
+ (acmd->device_id < MRDRV_MAX_LD) ? 1 : 0
+#define MRDRV_IS_LOGICAL(ap) \
+ ((ap->a_target < MRDRV_MAX_LD) && (ap->a_lun == 0)) ? 1 : 0
+#define MAP_DEVICE_ID(instance, ap) \
+ (ap->a_target)
+
+#define HIGH_LEVEL_INTR 1
+#define NORMAL_LEVEL_INTR 0
+
+/*
+ * scsa_cmd - Per-command mr private data
+ * @param cmd_dmahandle : dma handle
+ * @param cmd_dmacookies : current dma cookies
+ * @param cmd_pkt : scsi_pkt reference
+ * @param cmd_dmacount : dma count
+ * @param cmd_cookie : next cookie
+ * @param cmd_ncookies : cookies per window
+ * @param cmd_cookiecnt : cookies per sub-win
+ * @param cmd_nwin : number of dma windows
+ * @param cmd_curwin : current dma window
+ * @param cmd_dma_offset : current window offset
+ * @param cmd_dma_len : current window length
+ * @param cmd_flags : private flags
+ * @param cmd_cdblen : length of cdb
+ * @param cmd_scblen : length of scb
+ * @param cmd_buf : command buffer
+ * @param channel : channel for scsi sub-system
+ * @param target : target for scsi sub-system
+ * @param lun : LUN for scsi sub-system
+ *
+ * - Allocated at same time as scsi_pkt by scsi_hba_pkt_alloc(9E)
+ * - Pointed to by pkt_ha_private field in scsi_pkt
+ */
+struct scsa_cmd {
+ ddi_dma_handle_t cmd_dmahandle;
+ ddi_dma_cookie_t cmd_dmacookies[DRSAS_MAX_SGE_CNT];
+ struct scsi_pkt *cmd_pkt;
+ ulong_t cmd_dmacount;
+ uint_t cmd_cookie;
+ uint_t cmd_ncookies;
+ uint_t cmd_cookiecnt;
+ uint_t cmd_nwin;
+ uint_t cmd_curwin;
+ off_t cmd_dma_offset;
+ ulong_t cmd_dma_len;
+ ulong_t cmd_flags;
+ uint_t cmd_cdblen;
+ uint_t cmd_scblen;
+ struct buf *cmd_buf;
+ ushort_t device_id;
+ uchar_t islogical;
+ uchar_t lun;
+ struct drsas_device *drsas_dev;
+};
+
+
+struct drsas_cmd {
+ union drsas_frame *frame;
+ uint32_t frame_phys_addr;
+ uint8_t *sense;
+ uint32_t sense_phys_addr;
+ dma_obj_t frame_dma_obj;
+ uint8_t frame_dma_obj_status;
+
+ uint32_t index;
+ uint8_t sync_cmd;
+ uint8_t cmd_status;
+ uint16_t abort_aen;
+ mlist_t list;
+ uint32_t frame_count;
+ struct scsa_cmd *cmd;
+ struct scsi_pkt *pkt;
+};
+
+#define MAX_MGMT_ADAPTERS 1024
+#define IOC_SIGNATURE "MR-SAS"
+
+#define IOC_CMD_FIRMWARE 0x0
+#define DRSAS_DRIVER_IOCTL_COMMON 0xF0010000
+#define DRSAS_DRIVER_IOCTL_DRIVER_VERSION 0xF0010100
+#define DRSAS_DRIVER_IOCTL_PCI_INFORMATION 0xF0010200
+#define DRSAS_DRIVER_IOCTL_MRRAID_STATISTICS 0xF0010300
+
+
+#define DRSAS_MAX_SENSE_LENGTH 32
+
+struct drsas_mgmt_info {
+
+ uint16_t count;
+ struct drsas_instance *instance[MAX_MGMT_ADAPTERS];
+ uint16_t map[MAX_MGMT_ADAPTERS];
+ int max_index;
+};
+
+#pragma pack(1)
+
+/*
+ * SAS controller properties
+ */
+struct drsas_ctrl_prop {
+ uint16_t seq_num;
+ uint16_t pred_fail_poll_interval;
+ uint16_t intr_throttle_count;
+ uint16_t intr_throttle_timeouts;
+
+ uint8_t rebuild_rate;
+ uint8_t patrol_read_rate;
+ uint8_t bgi_rate;
+ uint8_t cc_rate;
+ uint8_t recon_rate;
+
+ uint8_t cache_flush_interval;
+
+ uint8_t spinup_drv_count;
+ uint8_t spinup_delay;
+
+ uint8_t cluster_enable;
+ uint8_t coercion_mode;
+ uint8_t disk_write_cache_disable;
+ uint8_t alarm_enable;
+
+ uint8_t reserved[44];
+};
+
+/*
+ * SAS controller information
+ */
+struct drsas_ctrl_info {
+ /* PCI device information */
+ struct {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t sub_vendor_id;
+ uint16_t sub_device_id;
+ uint8_t reserved[24];
+ } pci;
+
+ /* Host interface information */
+ struct {
+ uint8_t PCIX : 1;
+ uint8_t PCIE : 1;
+ uint8_t iSCSI : 1;
+ uint8_t SAS_3G : 1;
+ uint8_t reserved_0 : 4;
+ uint8_t reserved_1[6];
+ uint8_t port_count;
+ uint64_t port_addr[8];
+ } host_interface;
+
+ /* Device (backend) interface information */
+ struct {
+ uint8_t SPI : 1;
+ uint8_t SAS_3G : 1;
+ uint8_t SATA_1_5G : 1;
+ uint8_t SATA_3G : 1;
+ uint8_t reserved_0 : 4;
+ uint8_t reserved_1[6];
+ uint8_t port_count;
+ uint64_t port_addr[8];
+ } device_interface;
+
+ /* List of components residing in flash. All str are null terminated */
+ uint32_t image_check_word;
+ uint32_t image_component_count;
+
+ struct {
+ char name[8];
+ char version[32];
+ char build_date[16];
+ char built_time[16];
+ } image_component[8];
+
+ /*
+ * List of flash components that have been flashed on the card, but
+ * are not in use, pending reset of the adapter. This list will be
+ * empty if a flash operation has not occurred. All stings are null
+ * terminated
+ */
+ uint32_t pending_image_component_count;
+
+ struct {
+ char name[8];
+ char version[32];
+ char build_date[16];
+ char build_time[16];
+ } pending_image_component[8];
+
+ uint8_t max_arms;
+ uint8_t max_spans;
+ uint8_t max_arrays;
+ uint8_t max_lds;
+
+ char product_name[80];
+ char serial_no[32];
+
+ /*
+ * Other physical/controller/operation information. Indicates the
+ * presence of the hardware
+ */
+ struct {
+ uint32_t bbu : 1;
+ uint32_t alarm : 1;
+ uint32_t nvram : 1;
+ uint32_t uart : 1;
+ uint32_t reserved : 28;
+ } hw_present;
+
+ uint32_t current_fw_time;
+
+ /* Maximum data transfer sizes */
+ uint16_t max_concurrent_cmds;
+ uint16_t max_sge_count;
+ uint32_t max_request_size;
+
+ /* Logical and physical device counts */
+ uint16_t ld_present_count;
+ uint16_t ld_degraded_count;
+ uint16_t ld_offline_count;
+
+ uint16_t pd_present_count;
+ uint16_t pd_disk_present_count;
+ uint16_t pd_disk_pred_failure_count;
+ uint16_t pd_disk_failed_count;
+
+ /* Memory size information */
+ uint16_t nvram_size;
+ uint16_t memory_size;
+ uint16_t flash_size;
+
+ /* Error counters */
+ uint16_t mem_correctable_error_count;
+ uint16_t mem_uncorrectable_error_count;
+
+ /* Cluster information */
+ uint8_t cluster_permitted;
+ uint8_t cluster_active;
+ uint8_t reserved_1[2];
+
+ /* Controller capabilities structures */
+ struct {
+ uint32_t raid_level_0 : 1;
+ uint32_t raid_level_1 : 1;
+ uint32_t raid_level_5 : 1;
+ uint32_t raid_level_1E : 1;
+ uint32_t reserved : 28;
+ } raid_levels;
+
+ struct {
+ uint32_t rbld_rate : 1;
+ uint32_t cc_rate : 1;
+ uint32_t bgi_rate : 1;
+ uint32_t recon_rate : 1;
+ uint32_t patrol_rate : 1;
+ uint32_t alarm_control : 1;
+ uint32_t cluster_supported : 1;
+ uint32_t bbu : 1;
+ uint32_t spanning_allowed : 1;
+ uint32_t dedicated_hotspares : 1;
+ uint32_t revertible_hotspares : 1;
+ uint32_t foreign_config_import : 1;
+ uint32_t self_diagnostic : 1;
+ uint32_t reserved : 19;
+ } adapter_operations;
+
+ struct {
+ uint32_t read_policy : 1;
+ uint32_t write_policy : 1;
+ uint32_t io_policy : 1;
+ uint32_t access_policy : 1;
+ uint32_t reserved : 28;
+ } ld_operations;
+
+ struct {
+ uint8_t min;
+ uint8_t max;
+ uint8_t reserved[2];
+ } stripe_size_operations;
+
+ struct {
+ uint32_t force_online : 1;
+ uint32_t force_offline : 1;
+ uint32_t force_rebuild : 1;
+ uint32_t reserved : 29;
+ } pd_operations;
+
+ struct {
+ uint32_t ctrl_supports_sas : 1;
+ uint32_t ctrl_supports_sata : 1;
+ uint32_t allow_mix_in_encl : 1;
+ uint32_t allow_mix_in_ld : 1;
+ uint32_t allow_sata_in_cluster : 1;
+ uint32_t reserved : 27;
+ } pd_mix_support;
+
+ /* Include the controller properties (changeable items) */
+ uint8_t reserved_2[12];
+ struct drsas_ctrl_prop properties;
+
+ uint8_t pad[0x800 - 0x640];
+};
+
+/*
+ * ==================================
+ * MegaRAID SAS2.0 driver definitions
+ * ==================================
+ */
+#define MRDRV_MAX_NUM_CMD 1024
+
+#define MRDRV_MAX_PD_CHANNELS 2
+#define MRDRV_MAX_LD_CHANNELS 2
+#define MRDRV_MAX_CHANNELS (MRDRV_MAX_PD_CHANNELS + \
+ MRDRV_MAX_LD_CHANNELS)
+#define MRDRV_MAX_DEV_PER_CHANNEL 128
+#define MRDRV_DEFAULT_INIT_ID -1
+#define MRDRV_MAX_CMD_PER_LUN 1000
+#define MRDRV_MAX_LUN 1
+#define MRDRV_MAX_LD 64
+
+#define MRDRV_RESET_WAIT_TIME 300
+#define MRDRV_RESET_NOTICE_INTERVAL 5
+
+#define DRSAS_IOCTL_CMD 0
+
+/*
+ * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit
+ * SGLs based on the size of dma_addr_t
+ */
+#define IS_DMA64 (sizeof (dma_addr_t) == 8)
+
+#define IB_MSG_0_OFF 0x10 /* XScale */
+#define OB_MSG_0_OFF 0x18 /* XScale */
+#define IB_DOORBELL_OFF 0x20 /* XScale & ROC */
+#define OB_INTR_STATUS_OFF 0x30 /* XScale & ROC */
+#define OB_INTR_MASK_OFF 0x34 /* XScale & ROC */
+#define IB_QPORT_OFF 0x40 /* XScale & ROC */
+#define OB_DOORBELL_CLEAR_OFF 0xA0 /* ROC */
+#define OB_SCRATCH_PAD_0_OFF 0xB0 /* ROC */
+#define OB_INTR_MASK 0xFFFFFFFF
+#define OB_DOORBELL_CLEAR_MASK 0xFFFFFFFF
+
+/*
+ * All MFI register set macros accept drsas_register_set*
+ */
+#define WR_IB_MSG_0(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + IB_MSG_0_OFF), (v))
+
+#define RD_OB_MSG_0(instance) ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_MSG_0_OFF))
+
+#define WR_IB_DOORBELL(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + IB_DOORBELL_OFF), (v))
+
+#define RD_IB_DOORBELL(instance) ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + IB_DOORBELL_OFF))
+
+#define WR_OB_INTR_STATUS(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_STATUS_OFF), (v))
+
+#define RD_OB_INTR_STATUS(instance) ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_STATUS_OFF))
+
+#define WR_OB_INTR_MASK(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_MASK_OFF), (v))
+
+#define RD_OB_INTR_MASK(instance) ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_MASK_OFF))
+
+#define WR_IB_QPORT(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + IB_QPORT_OFF), (v))
+
+#define WR_OB_DOORBELL_CLEAR(v, instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_DOORBELL_CLEAR_OFF), \
+ (v))
+
+#define RD_OB_SCRATCH_PAD_0(instance) ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_SCRATCH_PAD_0_OFF))
+
+/*
+ * When FW is in MFI_STATE_READY or MFI_STATE_OPERATIONAL, the state data
+ * of Outbound Msg Reg 0 indicates max concurrent cmds supported, max SGEs
+ * supported per cmd and if 64-bit MFAs (M64) is enabled or disabled.
+ */
+#define MFI_OB_INTR_STATUS_MASK 0x00000002
+
+/*
+ * This MFI_REPLY_2108_MESSAGE_INTR flag is used also
+ * in enable_intr_ppc also. Hence bit 2, i.e. 0x4 has
+ * been set in this flag along with bit 1.
+ */
+#define MFI_REPLY_2108_MESSAGE_INTR 0x00000001
+#define MFI_REPLY_2108_MESSAGE_INTR_MASK 0x00000005
+
+#define MFI_POLL_TIMEOUT_SECS 60
+
+#define MFI_ENABLE_INTR(instance) ddi_put32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_MASK_OFF), 1)
+#define MFI_DISABLE_INTR(instance) \
+{ \
+ uint32_t disable = 1; \
+ uint32_t mask = ddi_get32((instance)->regmap_handle, \
+ (uint32_t *)((uintptr_t)(instance)->regmap + OB_INTR_MASK_OFF));\
+ mask &= ~disable; \
+ ddi_put32((instance)->regmap_handle, (uint32_t *) \
+ (uintptr_t)((instance)->regmap + OB_INTR_MASK_OFF), mask); \
+}
+
+/* By default, the firmware programs for 8 Kbytes of memory */
+#define DEFAULT_MFI_MEM_SZ 8192
+#define MINIMUM_MFI_MEM_SZ 4096
+
+/* DCMD Message Frame MAILBOX0-11 */
+#define DCMD_MBOX_SZ 12
+
+
+struct drsas_register_set {
+ uint32_t reserved_0[4];
+
+ uint32_t inbound_msg_0;
+ uint32_t inbound_msg_1;
+ uint32_t outbound_msg_0;
+ uint32_t outbound_msg_1;
+
+ uint32_t inbound_doorbell;
+ uint32_t inbound_intr_status;
+ uint32_t inbound_intr_mask;
+
+ uint32_t outbound_doorbell;
+ uint32_t outbound_intr_status;
+ uint32_t outbound_intr_mask;
+
+ uint32_t reserved_1[2];
+
+ uint32_t inbound_queue_port;
+ uint32_t outbound_queue_port;
+
+ uint32_t reserved_2[22];
+
+ uint32_t outbound_doorbell_clear;
+
+ uint32_t reserved_3[3];
+
+ uint32_t outbound_scratch_pad;
+
+ uint32_t reserved_4[3];
+
+ uint32_t inbound_low_queue_port;
+
+ uint32_t inbound_high_queue_port;
+
+ uint32_t reserved_5;
+ uint32_t index_registers[820];
+};
+
+struct drsas_sge32 {
+ uint32_t phys_addr;
+ uint32_t length;
+};
+
+struct drsas_sge64 {
+ uint64_t phys_addr;
+ uint32_t length;
+};
+
+union drsas_sgl {
+ struct drsas_sge32 sge32[1];
+ struct drsas_sge64 sge64[1];
+};
+
+struct drsas_header {
+ uint8_t cmd;
+ uint8_t sense_len;
+ uint8_t cmd_status;
+ uint8_t scsi_status;
+
+ uint8_t target_id;
+ uint8_t lun;
+ uint8_t cdb_len;
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+ uint32_t data_xferlen;
+};
+
+union drsas_sgl_frame {
+ struct drsas_sge32 sge32[8];
+ struct drsas_sge64 sge64[5];
+};
+
+struct drsas_init_frame {
+ uint8_t cmd;
+ uint8_t reserved_0;
+ uint8_t cmd_status;
+
+ uint8_t reserved_1;
+ uint32_t reserved_2;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t reserved_3;
+ uint32_t data_xfer_len;
+
+ uint32_t queue_info_new_phys_addr_lo;
+ uint32_t queue_info_new_phys_addr_hi;
+ uint32_t queue_info_old_phys_addr_lo;
+ uint32_t queue_info_old_phys_addr_hi;
+
+ uint32_t reserved_4[6];
+};
+
+struct drsas_init_queue_info {
+ uint32_t init_flags;
+ uint32_t reply_queue_entries;
+
+ uint32_t reply_queue_start_phys_addr_lo;
+ uint32_t reply_queue_start_phys_addr_hi;
+ uint32_t producer_index_phys_addr_lo;
+ uint32_t producer_index_phys_addr_hi;
+ uint32_t consumer_index_phys_addr_lo;
+ uint32_t consumer_index_phys_addr_hi;
+};
+
+struct drsas_io_frame {
+ uint8_t cmd;
+ uint8_t sense_len;
+ uint8_t cmd_status;
+ uint8_t scsi_status;
+
+ uint8_t target_id;
+ uint8_t access_byte;
+ uint8_t reserved_0;
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+ uint32_t lba_count;
+
+ uint32_t sense_buf_phys_addr_lo;
+ uint32_t sense_buf_phys_addr_hi;
+
+ uint32_t start_lba_lo;
+ uint32_t start_lba_hi;
+
+ union drsas_sgl sgl;
+};
+
+struct drsas_pthru_frame {
+ uint8_t cmd;
+ uint8_t sense_len;
+ uint8_t cmd_status;
+ uint8_t scsi_status;
+
+ uint8_t target_id;
+ uint8_t lun;
+ uint8_t cdb_len;
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+ uint32_t data_xfer_len;
+
+ uint32_t sense_buf_phys_addr_lo;
+ uint32_t sense_buf_phys_addr_hi;
+
+ uint8_t cdb[16];
+ union drsas_sgl sgl;
+};
+
+struct drsas_dcmd_frame {
+ uint8_t cmd;
+ uint8_t reserved_0;
+ uint8_t cmd_status;
+ uint8_t reserved_1[4];
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+
+ uint32_t data_xfer_len;
+ uint32_t opcode;
+
+ union {
+ uint8_t b[DCMD_MBOX_SZ];
+ uint16_t s[6];
+ uint32_t w[3];
+ } mbox;
+
+ union drsas_sgl sgl;
+};
+
+struct drsas_abort_frame {
+ uint8_t cmd;
+ uint8_t reserved_0;
+ uint8_t cmd_status;
+
+ uint8_t reserved_1;
+ uint32_t reserved_2;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t reserved_3;
+ uint32_t reserved_4;
+
+ uint32_t abort_context;
+ uint32_t pad_1;
+
+ uint32_t abort_mfi_phys_addr_lo;
+ uint32_t abort_mfi_phys_addr_hi;
+
+ uint32_t reserved_5[6];
+};
+
+struct drsas_smp_frame {
+ uint8_t cmd;
+ uint8_t reserved_1;
+ uint8_t cmd_status;
+ uint8_t connection_status;
+
+ uint8_t reserved_2[3];
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+
+ uint32_t data_xfer_len;
+
+ uint64_t sas_addr;
+
+ union drsas_sgl sgl[2];
+};
+
+struct drsas_stp_frame {
+ uint8_t cmd;
+ uint8_t reserved_1;
+ uint8_t cmd_status;
+ uint8_t connection_status;
+
+ uint8_t target_id;
+ uint8_t reserved_2[2];
+ uint8_t sge_count;
+
+ uint32_t context;
+ uint8_t req_id;
+ uint8_t msgvector;
+ uint16_t pad_0;
+
+ uint16_t flags;
+ uint16_t timeout;
+
+ uint32_t data_xfer_len;
+
+ uint16_t fis[10];
+ uint32_t stp_flags;
+ union drsas_sgl sgl;
+};
+
+union drsas_frame {
+ struct drsas_header hdr;
+ struct drsas_init_frame init;
+ struct drsas_io_frame io;
+ struct drsas_pthru_frame pthru;
+ struct drsas_dcmd_frame dcmd;
+ struct drsas_abort_frame abort;
+ struct drsas_smp_frame smp;
+ struct drsas_stp_frame stp;
+
+ uint8_t raw_bytes[64];
+};
+
+typedef struct drsas_pd_address {
+ uint16_t device_id;
+ uint16_t encl_id;
+
+ union {
+ struct {
+ uint8_t encl_index;
+ uint8_t slot_number;
+ } pd_address;
+ struct {
+ uint8_t encl_position;
+ uint8_t encl_connector_index;
+ } encl_address;
+ }address;
+
+ uint8_t scsi_dev_type;
+
+ union {
+ uint8_t port_bitmap;
+ uint8_t port_numbers;
+ } connected;
+
+ uint64_t sas_addr[2];
+} drsas_pd_address_t;
+
+union drsas_evt_class_locale {
+ struct {
+ uint16_t locale;
+ uint8_t reserved;
+ int8_t class;
+ } members;
+
+ uint32_t word;
+};
+
+struct drsas_evt_log_info {
+ uint32_t newest_seq_num;
+ uint32_t oldest_seq_num;
+ uint32_t clear_seq_num;
+ uint32_t shutdown_seq_num;
+ uint32_t boot_seq_num;
+};
+
+struct drsas_progress {
+ uint16_t progress;
+ uint16_t elapsed_seconds;
+};
+
+struct drsas_evtarg_ld {
+ uint16_t target_id;
+ uint8_t ld_index;
+ uint8_t reserved;
+};
+
+struct drsas_evtarg_pd {
+ uint16_t device_id;
+ uint8_t encl_index;
+ uint8_t slot_number;
+};
+
+struct drsas_evt_detail {
+ uint32_t seq_num;
+ uint32_t time_stamp;
+ uint32_t code;
+ union drsas_evt_class_locale cl;
+ uint8_t arg_type;
+ uint8_t reserved1[15];
+
+ union {
+ struct {
+ struct drsas_evtarg_pd pd;
+ uint8_t cdb_length;
+ uint8_t sense_length;
+ uint8_t reserved[2];
+ uint8_t cdb[16];
+ uint8_t sense[64];
+ } cdbSense;
+
+ struct drsas_evtarg_ld ld;
+
+ struct {
+ struct drsas_evtarg_ld ld;
+ uint64_t count;
+ } ld_count;
+
+ struct {
+ uint64_t lba;
+ struct drsas_evtarg_ld ld;
+ } ld_lba;
+
+ struct {
+ struct drsas_evtarg_ld ld;
+ uint32_t prevOwner;
+ uint32_t newOwner;
+ } ld_owner;
+
+ struct {
+ uint64_t ld_lba;
+ uint64_t pd_lba;
+ struct drsas_evtarg_ld ld;
+ struct drsas_evtarg_pd pd;
+ } ld_lba_pd_lba;
+
+ struct {
+ struct drsas_evtarg_ld ld;
+ struct drsas_progress prog;
+ } ld_prog;
+
+ struct {
+ struct drsas_evtarg_ld ld;
+ uint32_t prev_state;
+ uint32_t new_state;
+ } ld_state;
+
+ struct {
+ uint64_t strip;
+ struct drsas_evtarg_ld ld;
+ } ld_strip;
+
+ struct drsas_evtarg_pd pd;
+
+ struct {
+ struct drsas_evtarg_pd pd;
+ uint32_t err;
+ } pd_err;
+
+ struct {
+ uint64_t lba;
+ struct drsas_evtarg_pd pd;
+ } pd_lba;
+
+ struct {
+ uint64_t lba;
+ struct drsas_evtarg_pd pd;
+ struct drsas_evtarg_ld ld;
+ } pd_lba_ld;
+
+ struct {
+ struct drsas_evtarg_pd pd;
+ struct drsas_progress prog;
+ } pd_prog;
+
+ struct {
+ struct drsas_evtarg_pd pd;
+ uint32_t prevState;
+ uint32_t newState;
+ } pd_state;
+
+ struct {
+ uint16_t vendorId;
+ uint16_t deviceId;
+ uint16_t subVendorId;
+ uint16_t subDeviceId;
+ } pci;
+
+ uint32_t rate;
+ char str[96];
+
+ struct {
+ uint32_t rtc;
+ uint32_t elapsedSeconds;
+ } time;
+
+ struct {
+ uint32_t ecar;
+ uint32_t elog;
+ char str[64];
+ } ecc;
+
+ drsas_pd_address_t pd_addr;
+
+ uint8_t b[96];
+ uint16_t s[48];
+ uint32_t w[24];
+ uint64_t d[12];
+ } args;
+
+ char description[128];
+
+};
+
+/* only 63 are usable by the application */
+#define MAX_LOGICAL_DRIVES 64
+/* only 255 physical devices may be used */
+#define MAX_PHYSICAL_DEVICES 256
+#define MAX_PD_PER_ENCLOSURE 64
+/* maximum disks per array */
+#define MAX_ROW_SIZE 32
+/* maximum spans per logical drive */
+#define MAX_SPAN_DEPTH 8
+/* maximum number of arrays a hot spare may be dedicated to */
+#define MAX_ARRAYS_DEDICATED 16
+/* maximum number of arrays which may exist */
+#define MAX_ARRAYS 128
+/* maximum number of foreign configs that may ha managed at once */
+#define MAX_FOREIGN_CONFIGS 8
+/* maximum spares (global and dedicated combined) */
+#define MAX_SPARES_FOR_THE_CONTROLLER MAX_PHYSICAL_DEVICES
+/* maximum possible Target IDs (i.e. 0 to 63) */
+#define MAX_TARGET_ID 63
+/* maximum number of supported enclosures */
+#define MAX_ENCLOSURES 32
+/* maximum number of PHYs per controller */
+#define MAX_PHYS_PER_CONTROLLER 16
+/* maximum number of LDs per array (due to DDF limitations) */
+#define MAX_LDS_PER_ARRAY 16
+
+/*
+ * -----------------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
+ *
+ * Logical Drive commands
+ *
+ * -----------------------------------------------------------------------------
+ * -----------------------------------------------------------------------------
+ */
+#define DR_DCMD_LD 0x03000000, /* Logical Device (LD) opcodes */
+
+/*
+ * Input: dcmd.opcode - DR_DCMD_LD_GET_LIST
+ * dcmd.mbox - reserved
+ * dcmd.sge IN - ptr to returned DR_LD_LIST structure
+ * Desc: Return the logical drive list structure
+ * Status: No error
+ */
+
+/*
+ * defines the logical drive reference structure
+ */
+typedef union _DR_LD_REF { /* LD reference structure */
+ struct {
+ uint8_t targetId; /* LD target id (0 to MAX_TARGET_ID) */
+ uint8_t reserved; /* reserved for in line with DR_PD_REF */
+ uint16_t seqNum; /* Sequence Number */
+ } ld_ref;
+ uint32_t ref; /* shorthand reference to full 32-bits */
+} DR_LD_REF; /* 4 bytes */
+
+/*
+ * defines the logical drive list structure
+ */
+typedef struct _DR_LD_LIST {
+ uint32_t ldCount; /* number of LDs */
+ uint32_t reserved; /* pad to 8-byte boundary */
+ struct {
+ DR_LD_REF ref; /* LD reference */
+ uint8_t state; /* current LD state (DR_LD_STATE) */
+ uint8_t reserved[3]; /* pad to 8-byte boundary */
+ uint64_t size; /* LD size */
+ } ldList[MAX_LOGICAL_DRIVES];
+} DR_LD_LIST;
+
+struct drsas_drv_ver {
+ uint8_t signature[12];
+ uint8_t os_name[16];
+ uint8_t os_ver[12];
+ uint8_t drv_name[20];
+ uint8_t drv_ver[32];
+ uint8_t drv_rel_date[20];
+};
+
+#define PCI_TYPE0_ADDRESSES 6
+#define PCI_TYPE1_ADDRESSES 2
+#define PCI_TYPE2_ADDRESSES 5
+
+struct drsas_pci_common_header {
+ uint16_t vendorID; /* (ro) */
+ uint16_t deviceID; /* (ro) */
+ uint16_t command; /* Device control */
+ uint16_t status;
+ uint8_t revisionID; /* (ro) */
+ uint8_t progIf; /* (ro) */
+ uint8_t subClass; /* (ro) */
+ uint8_t baseClass; /* (ro) */
+ uint8_t cacheLineSize; /* (ro+) */
+ uint8_t latencyTimer; /* (ro+) */
+ uint8_t headerType; /* (ro) */
+ uint8_t bist; /* Built in self test */
+
+ union {
+ struct {
+ uint32_t baseAddresses[PCI_TYPE0_ADDRESSES];
+ uint32_t cis;
+ uint16_t subVendorID;
+ uint16_t subSystemID;
+ uint32_t romBaseAddress;
+ uint8_t capabilitiesPtr;
+ uint8_t reserved1[3];
+ uint32_t reserved2;
+ uint8_t interruptLine;
+ uint8_t interruptPin; /* (ro) */
+ uint8_t minimumGrant; /* (ro) */
+ uint8_t maximumLatency; /* (ro) */
+ } type_0;
+
+ struct {
+ uint32_t baseAddresses[PCI_TYPE1_ADDRESSES];
+ uint8_t primaryBus;
+ uint8_t secondaryBus;
+ uint8_t subordinateBus;
+ uint8_t secondaryLatency;
+ uint8_t ioBase;
+ uint8_t ioLimit;
+ uint16_t secondaryStatus;
+ uint16_t memoryBase;
+ uint16_t memoryLimit;
+ uint16_t prefetchBase;
+ uint16_t prefetchLimit;
+ uint32_t prefetchBaseUpper32;
+ uint32_t prefetchLimitUpper32;
+ uint16_t ioBaseUpper16;
+ uint16_t ioLimitUpper16;
+ uint8_t capabilitiesPtr;
+ uint8_t reserved1[3];
+ uint32_t romBaseAddress;
+ uint8_t interruptLine;
+ uint8_t interruptPin;
+ uint16_t bridgeControl;
+ } type_1;
+
+ struct {
+ uint32_t socketRegistersBaseAddress;
+ uint8_t capabilitiesPtr;
+ uint8_t reserved;
+ uint16_t secondaryStatus;
+ uint8_t primaryBus;
+ uint8_t secondaryBus;
+ uint8_t subordinateBus;
+ uint8_t secondaryLatency;
+ struct {
+ uint32_t base;
+ uint32_t limit;
+ } range[PCI_TYPE2_ADDRESSES-1];
+ uint8_t interruptLine;
+ uint8_t interruptPin;
+ uint16_t bridgeControl;
+ } type_2;
+ } header;
+};
+
+struct drsas_pci_link_capability {
+ union {
+ struct {
+ uint32_t linkSpeed :4;
+ uint32_t linkWidth :6;
+ uint32_t aspmSupport :2;
+ uint32_t losExitLatency :3;
+ uint32_t l1ExitLatency :3;
+ uint32_t rsvdp :6;
+ uint32_t portNumber :8;
+ } bits;
+
+ uint32_t asUlong;
+ } cap;
+
+};
+
+struct drsas_pci_link_status_capability {
+ union {
+ struct {
+ uint16_t linkSpeed :4;
+ uint16_t negotiatedLinkWidth :6;
+ uint16_t linkTrainingError :1;
+ uint16_t linkTraning :1;
+ uint16_t slotClockConfig :1;
+ uint16_t rsvdZ :3;
+ } bits;
+
+ uint16_t asUshort;
+ } stat_cap;
+
+ uint16_t reserved;
+
+};
+
+struct drsas_pci_capabilities {
+ struct drsas_pci_link_capability linkCapability;
+ struct drsas_pci_link_status_capability linkStatusCapability;
+};
+
+struct drsas_pci_information
+{
+ uint32_t busNumber;
+ uint8_t deviceNumber;
+ uint8_t functionNumber;
+ uint8_t interruptVector;
+ uint8_t reserved;
+ struct drsas_pci_common_header pciHeaderInfo;
+ struct drsas_pci_capabilities capability;
+ uint8_t reserved2[32];
+};
+
+struct drsas_ioctl {
+ uint16_t version;
+ uint16_t controller_id;
+ uint8_t signature[8];
+ uint32_t reserved_1;
+ uint32_t control_code;
+ uint32_t reserved_2[2];
+ uint8_t frame[64];
+ union drsas_sgl_frame sgl_frame;
+ uint8_t sense_buff[DRSAS_MAX_SENSE_LENGTH];
+ uint8_t data[1];
+};
+
+struct drsas_aen {
+ uint16_t host_no;
+ uint16_t cmd_status;
+ uint32_t seq_num;
+ uint32_t class_locale_word;
+};
+#pragma pack()
+
+#ifndef DDI_VENDOR_LSI
+#define DDI_VENDOR_LSI "LSI"
+#endif /* DDI_VENDOR_LSI */
+
+static int drsas_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int drsas_attach(dev_info_t *, ddi_attach_cmd_t);
+static int drsas_reset(dev_info_t *, ddi_reset_cmd_t);
+static int drsas_detach(dev_info_t *, ddi_detach_cmd_t);
+static int drsas_open(dev_t *, int, int, cred_t *);
+static int drsas_close(dev_t, int, int, cred_t *);
+static int drsas_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+static int drsas_tran_tgt_init(dev_info_t *, dev_info_t *,
+ scsi_hba_tran_t *, struct scsi_device *);
+static struct scsi_pkt *drsas_tran_init_pkt(struct scsi_address *, register
+ struct scsi_pkt *, struct buf *, int, int, int, int,
+ int (*)(), caddr_t);
+static int drsas_tran_start(struct scsi_address *,
+ register struct scsi_pkt *);
+static int drsas_tran_abort(struct scsi_address *, struct scsi_pkt *);
+static int drsas_tran_reset(struct scsi_address *, int);
+static int drsas_tran_getcap(struct scsi_address *, char *, int);
+static int drsas_tran_setcap(struct scsi_address *, char *, int, int);
+static void drsas_tran_destroy_pkt(struct scsi_address *,
+ struct scsi_pkt *);
+static void drsas_tran_dmafree(struct scsi_address *, struct scsi_pkt *);
+static void drsas_tran_sync_pkt(struct scsi_address *, struct scsi_pkt *);
+static uint_t drsas_isr();
+static uint_t drsas_softintr();
+
+static int init_mfi(struct drsas_instance *);
+static int drsas_free_dma_obj(struct drsas_instance *, dma_obj_t);
+static int drsas_alloc_dma_obj(struct drsas_instance *, dma_obj_t *,
+ uchar_t);
+static struct drsas_cmd *get_mfi_pkt(struct drsas_instance *);
+static void return_mfi_pkt(struct drsas_instance *,
+ struct drsas_cmd *);
+
+static void free_space_for_mfi(struct drsas_instance *);
+static void free_additional_dma_buffer(struct drsas_instance *);
+static int alloc_additional_dma_buffer(struct drsas_instance *);
+static int read_fw_status_reg_ppc(struct drsas_instance *);
+static void issue_cmd_ppc(struct drsas_cmd *, struct drsas_instance *);
+static int issue_cmd_in_poll_mode_ppc(struct drsas_instance *,
+ struct drsas_cmd *);
+static int issue_cmd_in_sync_mode_ppc(struct drsas_instance *,
+ struct drsas_cmd *);
+static void enable_intr_ppc(struct drsas_instance *);
+static void disable_intr_ppc(struct drsas_instance *);
+static int intr_ack_ppc(struct drsas_instance *);
+static int mfi_state_transition_to_ready(struct drsas_instance *);
+static void destroy_mfi_frame_pool(struct drsas_instance *);
+static int create_mfi_frame_pool(struct drsas_instance *);
+static int drsas_dma_alloc(struct drsas_instance *, struct scsi_pkt *,
+ struct buf *, int, int (*)());
+static int drsas_dma_move(struct drsas_instance *,
+ struct scsi_pkt *, struct buf *);
+static void flush_cache(struct drsas_instance *instance);
+static void display_scsi_inquiry(caddr_t);
+static int start_mfi_aen(struct drsas_instance *instance);
+static int handle_drv_ioctl(struct drsas_instance *instance,
+ struct drsas_ioctl *ioctl, int mode);
+static int handle_mfi_ioctl(struct drsas_instance *instance,
+ struct drsas_ioctl *ioctl, int mode);
+static int handle_mfi_aen(struct drsas_instance *instance,
+ struct drsas_aen *aen);
+static void fill_up_drv_ver(struct drsas_drv_ver *dv);
+static struct drsas_cmd *build_cmd(struct drsas_instance *instance,
+ struct scsi_address *ap, struct scsi_pkt *pkt,
+ uchar_t *cmd_done);
+static int register_mfi_aen(struct drsas_instance *instance,
+ uint32_t seq_num, uint32_t class_locale_word);
+static int issue_mfi_pthru(struct drsas_instance *instance, struct
+ drsas_ioctl *ioctl, struct drsas_cmd *cmd, int mode);
+static int issue_mfi_dcmd(struct drsas_instance *instance, struct
+ drsas_ioctl *ioctl, struct drsas_cmd *cmd, int mode);
+static int issue_mfi_smp(struct drsas_instance *instance, struct
+ drsas_ioctl *ioctl, struct drsas_cmd *cmd, int mode);
+static int issue_mfi_stp(struct drsas_instance *instance, struct
+ drsas_ioctl *ioctl, struct drsas_cmd *cmd, int mode);
+static int abort_aen_cmd(struct drsas_instance *instance,
+ struct drsas_cmd *cmd_to_abort);
+
+static int drsas_common_check(struct drsas_instance *instance,
+ struct drsas_cmd *cmd);
+static void drsas_fm_init(struct drsas_instance *instance);
+static void drsas_fm_fini(struct drsas_instance *instance);
+static int drsas_fm_error_cb(dev_info_t *, ddi_fm_error_t *,
+ const void *);
+static void drsas_fm_ereport(struct drsas_instance *instance,
+ char *detail);
+static int drsas_check_dma_handle(ddi_dma_handle_t handle);
+static int drsas_check_acc_handle(ddi_acc_handle_t handle);
+
+static void drsas_rem_intrs(struct drsas_instance *instance);
+static int drsas_add_intrs(struct drsas_instance *instance, int intr_type);
+
+static void drsas_tran_tgt_free(dev_info_t *, dev_info_t *,
+ scsi_hba_tran_t *, struct scsi_device *);
+static int drsas_tran_bus_config(dev_info_t *, uint_t,
+ ddi_bus_config_op_t, void *, dev_info_t **);
+static int drsas_parse_devname(char *, int *, int *);
+static int drsas_config_all_devices(struct drsas_instance *);
+static int drsas_config_scsi_device(struct drsas_instance *,
+ struct scsi_device *, dev_info_t **);
+static int drsas_config_ld(struct drsas_instance *, uint16_t,
+ uint8_t, dev_info_t **);
+static dev_info_t *drsas_find_child(struct drsas_instance *, uint16_t,
+ uint8_t);
+static int drsas_name_node(dev_info_t *, char *, int);
+static void drsas_issue_evt_taskq(struct drsas_eventinfo *);
+static int drsas_service_evt(struct drsas_instance *, int, int, int,
+ uint64_t);
+static int drsas_mode_sense_build(struct scsi_pkt *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DR_SAS_H_ */
diff --git a/usr/src/uts/common/io/dr_sas/dr_sas_list.h b/usr/src/uts/common/io/dr_sas/dr_sas_list.h
new file mode 100644
index 0000000000..4154a77796
--- /dev/null
+++ b/usr/src/uts/common/io/dr_sas/dr_sas_list.h
@@ -0,0 +1,212 @@
+/*
+ * dr_sas_list.h: header for dr_sas
+ *
+ * Solaris MegaRAID driver for SAS2.0 controllers
+ * Copyright (c) 2008-2009, LSI Logic Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DR_SAS_LIST_H_
+#define _DR_SAS_LIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct mlist_head {
+ struct mlist_head *next, *prev;
+};
+
+typedef struct mlist_head mlist_t;
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct mlist_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+}
+
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static void __list_add(struct mlist_head *new,
+ struct mlist_head *prev,
+ struct mlist_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+
+/*
+ * mlist_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static void mlist_add(struct mlist_head *new, struct mlist_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+
+/*
+ * mlist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static void mlist_add_tail(struct mlist_head *new, struct mlist_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static void __list_del(struct mlist_head *prev,
+ struct mlist_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+
+/*
+ * mlist_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static void mlist_del_init(struct mlist_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+
+/*
+ * mlist_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static int mlist_empty(struct mlist_head *head)
+{
+ return (head->next == head);
+}
+
+
+/*
+ * mlist_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static void mlist_splice(struct mlist_head *list, struct mlist_head *head)
+{
+ struct mlist_head *first = list->next;
+
+ if (first != list) {
+ struct mlist_head *last = list->prev;
+ struct mlist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+
+/*
+ * mlist_entry - get the struct for this entry
+ * @ptr: the &struct mlist_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define mlist_entry(ptr, type, member) \
+ ((type *)((size_t)(ptr) - offsetof(type, member)))
+
+
+/*
+ * mlist_for_each - iterate over a list
+ * @pos: the &struct mlist_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define mlist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+
+/*
+ * mlist_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct mlist_head to use as a loop counter.
+ * @n: another &struct mlist_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define mlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DR_SAS_LIST_H_ */
diff --git a/usr/src/uts/common/io/eventfd.c b/usr/src/uts/common/io/eventfd.c
index e5082b49b6..ac2f75885c 100644
--- a/usr/src/uts/common/io/eventfd.c
+++ b/usr/src/uts/common/io/eventfd.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -37,6 +37,7 @@ struct eventfd_state {
kcondvar_t efd_cv; /* condvar */
pollhead_t efd_pollhd; /* poll head */
uint64_t efd_value; /* value */
+ size_t efd_bwriters; /* count of blocked writers */
eventfd_state_t *efd_next; /* next state on global list */
};
@@ -126,10 +127,21 @@ eventfd_read(dev_t dev, uio_t *uio, cred_t *cr)
err = uiomove(&val, sizeof (val), UIO_READ, uio);
+ /*
+ * Wake any writers blocked on this eventfd as this read operation may
+ * have created adequate capacity for their values.
+ */
+ if (state->efd_bwriters != 0) {
+ cv_broadcast(&state->efd_cv);
+ }
mutex_exit(&state->efd_lock);
+ /*
+ * It is necessary to emit POLLOUT events only when the eventfd
+ * transitions from EVENTFD_VALMAX to a lower value. At all other
+ * times, it is already considered writable by poll.
+ */
if (oval == EVENTFD_VALMAX) {
- cv_broadcast(&state->efd_cv);
pollwakeup(&state->efd_pollhd, POLLWRNORM | POLLOUT);
}
@@ -164,10 +176,13 @@ eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
return (EAGAIN);
}
+ state->efd_bwriters++;
if (!cv_wait_sig_swap(&state->efd_cv, &state->efd_lock)) {
+ state->efd_bwriters--;
mutex_exit(&state->efd_lock);
return (EINTR);
}
+ state->efd_bwriters--;
}
/*
@@ -175,10 +190,19 @@ eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
*/
state->efd_value = (oval = state->efd_value) + val;
+ /*
+ * If the value was previously "empty", notify blocked readers that
+ * data is available.
+ */
+ if (oval == 0) {
+ cv_broadcast(&state->efd_cv);
+ }
mutex_exit(&state->efd_lock);
+ /*
+ * Notify pollers as well if the eventfd is now readable.
+ */
if (oval == 0) {
- cv_broadcast(&state->efd_cv);
pollwakeup(&state->efd_pollhd, POLLRDNORM | POLLIN);
}
diff --git a/usr/src/uts/common/io/fibre-channel/impl/fctl.c b/usr/src/uts/common/io/fibre-channel/impl/fctl.c
index 634de6c6dd..87105e779d 100644
--- a/usr/src/uts/common/io/fibre-channel/impl/fctl.c
+++ b/usr/src/uts/common/io/fibre-channel/impl/fctl.c
@@ -24,6 +24,7 @@
*/
/*
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
*/
/*
* Fibre channel Transport Library (fctl)
@@ -5500,6 +5501,11 @@ fc_ulp_get_adapter_paths(char *pathList, int count)
maxPorts ++;
}
+ if (maxPorts == 0) {
+ mutex_exit(&fctl_port_lock);
+ return (0);
+ }
+
/* Now allocate a buffer to store all the pointers for comparisons */
portList = kmem_zalloc(sizeof (fc_local_port_t *) * maxPorts, KM_SLEEP);
diff --git a/usr/src/uts/common/io/gsqueue/gsqueue.c b/usr/src/uts/common/io/gsqueue/gsqueue.c
new file mode 100644
index 0000000000..b484b16142
--- /dev/null
+++ b/usr/src/uts/common/io/gsqueue/gsqueue.c
@@ -0,0 +1,612 @@
+/*
+ * 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) 2014 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Serialization queues are a technique used in illumos to provide what's
+ * commonly known as a 'vertical' perimeter. The idea (described a bit in
+ * uts/common/inet/squeue.c) is to provide a means to make sure that message
+ * blocks (mblk_t) are processed in a specific order. Subsystems like ip and vnd
+ * consume these on different policies, ip on a conn_t basis, vnd on a per
+ * device basis, and use this to ensure that only one packet is being processed
+ * at a given time.
+ *
+ * Serialization queues were originally used by ip. As part of that
+ * implementation, many of the details of ip were baked into it. That includes
+ * things like conn_t, ip receive attributes, and the notion of sets. While an
+ * individual serialization queue, or gsqueue_t, is a useful level of
+ * abstraction, it isn't the basis on which monst consumers want to manage them.
+ * Instead, we have the notion of a set of serialization queues. These sets are
+ * DR (CPU Dynamic reconfiguration) aware, and allow consumers to have a
+ * gsqueue_t per CPU to fanout on without managing them all itself. In the
+ * original implementation, this existed, but they were heavily tied into the
+ * infrastructure of IP, and its notion of polling on the underlying MAC
+ * devices.
+ *
+ * The result of that past is a new interface to serialization queues and a
+ * similar, but slightly different, abstraction to sets of these
+ * (gsqueue_set_t). When designing this there are two different approaches that
+ * one could consider. The first is that the system has one gsqueue_set_t that
+ * the entire world shares, whether IP or some other consumer. The other is that
+ * every consumer has their own set.
+ *
+ * The trade offs between these two failure modes are the pathological failure
+ * modes. There is no guarantee that any two consumers here are equivalent. In
+ * fact, they very likely have very different latency profiles. If they are
+ * being processed in the same queue, that can lead to very odd behaviors. More
+ * generally, if we have a series of processing functions from one consumer
+ * which are generally short, and another which are generally long, that'll
+ * cause undue latency that's harder to observe. If we instead take the approach
+ * that each consumer should have its own set that it fans out over then we
+ * won't end up with the problem that a given serialization queue will have
+ * multiple latency profiles, but instead we'll see cpu contention for the bound
+ * gsqueue_t worker thread. Keep in mind though, that only the gsqueue_t worker
+ * thread is bound and it is in fact possible for it to be processed by other
+ * threads on other CPUs.
+ *
+ * We've opted to go down the second path, so each consumer has its own
+ * independent set of serialization queues that it is bound over.
+ *
+ * Structure Hierarchies
+ * ---------------------
+ *
+ * At the top level, we have a single list of gsqueue_set_t. The gsqueue_set_t
+ * encapsulates all the per-CPU gsqueue_t that exist in the form of
+ * gsqueue_cpu_t. The gsqueue_cpu_t has been designed such that it could
+ * accommodate more than one gsqueue_t, but today there is a one to one mapping.
+ *
+ * We maintain two different lists of gsqueue_cpu_t, the active and defunct
+ * sets. The active set is maintained in the array `gs_cpus`. There are NCPU
+ * entries available in `gs_cpus` with the total number of currently active cpus
+ * described in `gs_ncpus`. The ordering of `gs_cpus` is unimportant. When
+ * there is no longer a need for a given binding (see the following section for
+ * more explanation on when this is the case) then we move the entry to the
+ * `gs_defunct` list which is just a singly linked list of gsqueue_cpu_t.
+ *
+ * In addition, each gsqueue_set_t can have a series of callbacks registered
+ * with it. These are described in the following section. Graphically, a given
+ * gsqueue_set_t looks roughly like the following:
+ *
+ * +---------------+
+ * | gsqueue_set_t |
+ * +---------------+
+ * | | |
+ * | | * . . . gs_cpus
+ * | | |
+ * | | | +-------------------------------------------------+
+ * | | +----->| gsqueue_cpu_t || gsqueue_cpu_t || gsqueue_cpu_t |...
+ * | | +-------------------------------------------------+
+ * | |
+ * | * . . . gs_defunct
+ * | |
+ * | | +---------------+ +---------------+ +---------------+
+ * | +--->| gsqueue_cpu_t |-->| gsqueue_cpu_t |-->| gsqueue_cpu_t |...
+ * | +---------------+ +---------------+ +---------------+
+ * * . . . gs_cbs
+ * |
+ * | +--------------+ +--------------+ +--------------+
+ * +--->| gsqueue_cb_t |-->| gsqueue_cb_t |->| gsqueue_cb_t |...
+ * +--------------+ +--------------+ +--------------+
+ *
+ * CPU DR, gsqueue_t, and gsqueue_t
+ * --------------------------------
+ *
+ * Recall, that every serialization queue (gsqueue_t or squeue_t) has a worker
+ * thread that may end up doing work. As part of supporting fanout, we have one
+ * gsqueue_t per CPU, and its worker thread is bound to that CPU. Because of
+ * this binding, we need to deal with CPU DR changes.
+ *
+ * The gsqueue driver maintains a single CPU DR callback that is used for the
+ * entire sub-system. We break down CPU DR events into three groups. Offline
+ * events, online events, and events we can ignore. When the first group occurs,
+ * we need to go through every gsqueue_t, find the gsqueue_cpu_t that
+ * corresponds to that processor id, and unbind all of its gsqueue_t's. It's
+ * rather important that we only unbind the gsqueue_t's and not actually destroy
+ * them. When this happens, they could very easily have data queued inside of
+ * them and it's unreasonable to just throw out everything in them at this
+ * point. The data remains intact and service continues uinterrupted.
+ *
+ * When we receive an online event, we do the opposite. We try to find a
+ * gsqueue_cpu_t that previously was bound to this CPU (by leaving its gqc_cpuid
+ * field intact) in the defunct list. If we find one, we remove it from the
+ * defunct list and add it to the active list as well as binding the gsqueue_t
+ * to the CPU in question. If we don't find one, then we create a new one.
+ *
+ * To deal with these kinds of situations, we allow a consumer to register
+ * callbacks for the gsqueue_t that they are interested in. These callbacks will
+ * fire whenever we are handling a topology change. The design of the callbacks
+ * is not that the user can take any administrative action during them, but
+ * rather set something for them to do asynchronously. It is illegal to make any
+ * calls into the gsqueue system while you are in a callback.
+ *
+ * Locking
+ * -------
+ *
+ * The lock ordering here is fairly straightforward. Due to our use of CPU
+ * binding and the CPU DR callbacks, we have an additional lock to consider
+ * cpu_lock. Because of that, the following are the rules for locking:
+ *
+ *
+ * o If performing binding operations, you must grab cpu_lock. cpu_lock is
+ * also at the top of the order.
+ *
+ * o cpu_lock > gsqueue_lock > gsqueue_t`gs_lock > squeue_t`sq_lock
+ * If you need to take multiple locks, you must take the greatest
+ * (left-most) one first.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/kmem.h>
+#include <sys/stream.h>
+#include <sys/modctl.h>
+#include <sys/cpuvar.h>
+#include <sys/list.h>
+#include <sys/sysmacros.h>
+
+#include <sys/gsqueue.h>
+#include <sys/squeue_impl.h>
+
+typedef struct gsqueue_cb {
+ struct gsqueue_cb *gcb_next;
+ gsqueue_cb_f gcb_func;
+ void *gcb_arg;
+} gsqueue_cb_t;
+
+typedef struct gsqueue_cpu {
+ struct gsqueue_cpu *gqc_next;
+ squeue_t *gqc_head;
+ processorid_t gqc_cpuid;
+} gsqueue_cpu_t;
+
+struct gsqueue_set {
+ list_node_t gs_next;
+ uint_t gs_wwait;
+ pri_t gs_wpri;
+ kmutex_t gs_lock;
+ int gs_ncpus;
+ gsqueue_cpu_t **gs_cpus;
+ gsqueue_cpu_t *gs_defunct;
+ gsqueue_cb_t *gs_cbs;
+};
+
+static kmutex_t gsqueue_lock;
+static list_t gsqueue_list;
+static kmem_cache_t *gsqueue_cb_cache;
+static kmem_cache_t *gsqueue_cpu_cache;
+static kmem_cache_t *gsqueue_set_cache;
+
+static gsqueue_cpu_t *
+gsqueue_cpu_create(uint_t wwait, pri_t wpri, processorid_t cpuid)
+{
+ gsqueue_cpu_t *scp;
+
+ scp = kmem_cache_alloc(gsqueue_cpu_cache, KM_SLEEP);
+
+ scp->gqc_next = NULL;
+ scp->gqc_cpuid = cpuid;
+ scp->gqc_head = squeue_create(wwait, wpri, B_FALSE);
+ scp->gqc_head->sq_state = SQS_DEFAULT;
+ squeue_bind(scp->gqc_head, cpuid);
+
+ return (scp);
+}
+
+static void
+gsqueue_cpu_destroy(gsqueue_cpu_t *scp)
+{
+ squeue_destroy(scp->gqc_head);
+ kmem_cache_free(gsqueue_cpu_cache, scp);
+}
+
+gsqueue_set_t *
+gsqueue_set_create(uint_t wwait, pri_t wpri)
+{
+ int i;
+ gsqueue_set_t *gssp;
+
+ gssp = kmem_cache_alloc(gsqueue_set_cache, KM_SLEEP);
+ gssp->gs_wwait = wwait;
+ gssp->gs_wpri = wpri;
+ gssp->gs_ncpus = 0;
+
+ /*
+ * We're grabbing CPU lock. Once we let go of it we have to ensure all
+ * set up of the gsqueue_set_t is complete, as it'll be in there for the
+ * various CPU DR bits.
+ */
+ mutex_enter(&cpu_lock);
+
+ for (i = 0; i < NCPU; i++) {
+ gsqueue_cpu_t *scp;
+ cpu_t *cp = cpu_get(i);
+ if (cp != NULL && CPU_ACTIVE(cp) &&
+ cp->cpu_flags & CPU_EXISTS) {
+ scp = gsqueue_cpu_create(wwait, wpri, cp->cpu_id);
+ gssp->gs_cpus[gssp->gs_ncpus] = scp;
+ gssp->gs_ncpus++;
+ }
+ }
+
+ /* Finally we can add it to our global list and be done */
+ mutex_enter(&gsqueue_lock);
+ list_insert_tail(&gsqueue_list, gssp);
+ mutex_exit(&gsqueue_lock);
+ mutex_exit(&cpu_lock);
+
+ return (gssp);
+}
+
+void
+gsqueue_set_destroy(gsqueue_set_t *gssp)
+{
+ int i;
+ gsqueue_cpu_t *scp;
+
+ /*
+ * Go through and unbind all of the squeues while cpu_lock is held and
+ * move them to the defunct list. Once that's done, we don't need to do
+ * anything else with cpu_lock.
+ */
+ mutex_enter(&cpu_lock);
+ mutex_enter(&gsqueue_lock);
+ list_remove(&gsqueue_list, gssp);
+ mutex_exit(&gsqueue_lock);
+
+ mutex_enter(&gssp->gs_lock);
+
+ for (i = 0; i < gssp->gs_ncpus; i++) {
+ scp = gssp->gs_cpus[i];
+ squeue_unbind(scp->gqc_head);
+ scp->gqc_next = gssp->gs_defunct;
+ gssp->gs_defunct = scp;
+ gssp->gs_cpus[i] = NULL;
+ }
+ gssp->gs_ncpus = 0;
+
+ mutex_exit(&gssp->gs_lock);
+ mutex_exit(&cpu_lock);
+
+ while (gssp->gs_defunct != NULL) {
+ gsqueue_cpu_t *scp;
+
+ scp = gssp->gs_defunct;
+ gssp->gs_defunct = scp->gqc_next;
+ gsqueue_cpu_destroy(scp);
+ }
+
+ while (gssp->gs_cbs != NULL) {
+ gsqueue_cb_t *cbp;
+
+ cbp = gssp->gs_cbs;
+ gssp->gs_cbs = cbp->gcb_next;
+ kmem_cache_free(gsqueue_cb_cache, cbp);
+ }
+
+ ASSERT(gssp->gs_ncpus == 0);
+ ASSERT(gssp->gs_defunct == NULL);
+ ASSERT(gssp->gs_cbs == NULL);
+ kmem_cache_free(gsqueue_set_cache, gssp);
+}
+
+gsqueue_t *
+gsqueue_set_get(gsqueue_set_t *gssp, uint_t index)
+{
+ squeue_t *sqp;
+ gsqueue_cpu_t *scp;
+
+ mutex_enter(&gssp->gs_lock);
+ scp = gssp->gs_cpus[index % gssp->gs_ncpus];
+ sqp = scp->gqc_head;
+ mutex_exit(&gssp->gs_lock);
+ return ((gsqueue_t *)sqp);
+}
+
+uintptr_t
+gsqueue_set_cb_add(gsqueue_set_t *gssp, gsqueue_cb_f cb, void *arg)
+{
+ gsqueue_cb_t *cbp;
+
+ cbp = kmem_cache_alloc(gsqueue_cb_cache, KM_SLEEP);
+ cbp->gcb_func = cb;
+ cbp->gcb_arg = arg;
+
+ mutex_enter(&gssp->gs_lock);
+ cbp->gcb_next = gssp->gs_cbs;
+ gssp->gs_cbs = cbp;
+ mutex_exit(&gssp->gs_lock);
+ return ((uintptr_t)cbp);
+}
+
+int
+gsqueue_set_cb_remove(gsqueue_set_t *gssp, uintptr_t id)
+{
+ gsqueue_cb_t *cbp, *prev;
+ mutex_enter(&gssp->gs_lock);
+ cbp = gssp->gs_cbs;
+ prev = NULL;
+ while (cbp != NULL) {
+ if ((uintptr_t)cbp != id) {
+ prev = cbp;
+ cbp = cbp->gcb_next;
+ continue;
+ }
+
+ if (prev == NULL) {
+ gssp->gs_cbs = cbp->gcb_next;
+ } else {
+ prev->gcb_next = cbp->gcb_next;
+ }
+
+ mutex_exit(&gssp->gs_lock);
+ kmem_cache_free(gsqueue_cb_cache, cbp);
+ return (0);
+ }
+ mutex_exit(&gssp->gs_lock);
+ return (-1);
+}
+
+void
+gsqueue_enter_one(gsqueue_t *gsp, mblk_t *mp, gsqueue_proc_f func, void *arg,
+ int flags, uint8_t tag)
+{
+ squeue_t *sqp = (squeue_t *)gsp;
+
+ ASSERT(mp->b_next == NULL);
+ ASSERT(mp->b_prev == NULL);
+ mp->b_queue = (queue_t *)func;
+ mp->b_prev = arg;
+ sqp->sq_enter(sqp, mp, mp, 1, NULL, flags, tag);
+}
+
+static void
+gsqueue_notify(gsqueue_set_t *gssp, squeue_t *sqp, boolean_t online)
+{
+ gsqueue_cb_t *cbp;
+
+ ASSERT(MUTEX_HELD(&gssp->gs_lock));
+ cbp = gssp->gs_cbs;
+ while (cbp != NULL) {
+ cbp->gcb_func(gssp, (gsqueue_t *)sqp, cbp->gcb_arg, online);
+ cbp = cbp->gcb_next;
+ }
+
+}
+
+/*
+ * When we online a processor we need to go through and either bind a defunct
+ * squeue or create a new one. We'll try to reuse a gsqueue_cpu_t from the
+ * defunct list that used to be on that processor. If no such gsqueue_cpu_t
+ * exists, then we'll create a new one. We'd rather avoid taking over an
+ * existing defunct one that used to be on another CPU, as its not unreasonable
+ * to believe that its CPU will come back. More CPUs are offlined and onlined by
+ * the administrator or by creating cpu sets than actually get offlined by FMA.
+ */
+static void
+gsqueue_handle_online(processorid_t id)
+{
+ gsqueue_set_t *gssp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ mutex_enter(&gsqueue_lock);
+ for (gssp = list_head(&gsqueue_list); gssp != NULL;
+ gssp = list_next(&gsqueue_list, gssp)) {
+ gsqueue_cpu_t *scp;
+
+ mutex_enter(&gssp->gs_lock);
+ scp = gssp->gs_defunct;
+ while (scp != NULL) {
+ if (scp->gqc_cpuid == id)
+ break;
+ scp = scp->gqc_next;
+ }
+
+ if (scp == NULL) {
+ scp = gsqueue_cpu_create(gssp->gs_wwait,
+ gssp->gs_wpri, id);
+ } else {
+ squeue_bind(scp->gqc_head, id);
+ }
+ ASSERT(gssp->gs_ncpus < NCPU);
+ gssp->gs_cpus[gssp->gs_ncpus] = scp;
+ gssp->gs_ncpus++;
+ gsqueue_notify(gssp, scp->gqc_head, B_TRUE);
+ mutex_exit(&gssp->gs_lock);
+ }
+ mutex_exit(&gsqueue_lock);
+}
+
+static void
+gsqueue_handle_offline(processorid_t id)
+{
+ gsqueue_set_t *gssp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ mutex_enter(&gsqueue_lock);
+ for (gssp = list_head(&gsqueue_list); gssp != NULL;
+ gssp = list_next(&gsqueue_list, gssp)) {
+ int i;
+ gsqueue_cpu_t *scp = NULL;
+
+ mutex_enter(&gssp->gs_lock);
+ for (i = 0; i < gssp->gs_ncpus; i++) {
+ if (gssp->gs_cpus[i]->gqc_cpuid == id) {
+ scp = gssp->gs_cpus[i];
+ break;
+ }
+ }
+
+ if (scp != NULL) {
+ squeue_unbind(scp->gqc_head);
+ scp->gqc_next = gssp->gs_defunct;
+ gssp->gs_defunct = scp;
+ gssp->gs_cpus[i] = gssp->gs_cpus[gssp->gs_ncpus-1];
+ gssp->gs_ncpus--;
+ gsqueue_notify(gssp, scp->gqc_head, B_FALSE);
+ }
+ mutex_exit(&gssp->gs_lock);
+ }
+ mutex_exit(&gsqueue_lock);
+}
+
+/* ARGSUSED */
+static int
+gsqueue_cpu_setup(cpu_setup_t what, int id, void *unused)
+{
+ cpu_t *cp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ cp = cpu_get(id);
+ switch (what) {
+ case CPU_CONFIG:
+ case CPU_ON:
+ case CPU_INIT:
+ case CPU_CPUPART_IN:
+ if (cp != NULL && CPU_ACTIVE(cp) && cp->cpu_flags & CPU_EXISTS)
+ gsqueue_handle_online(cp->cpu_id);
+ break;
+ case CPU_UNCONFIG:
+ case CPU_OFF:
+ case CPU_CPUPART_OUT:
+ gsqueue_handle_offline(cp->cpu_id);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+
+/* ARGSUSED */
+static int
+gsqueue_set_cache_construct(void *buf, void *arg, int kmflags)
+{
+ gsqueue_set_t *gssp = buf;
+
+ gssp->gs_cpus = kmem_alloc(sizeof (gsqueue_cpu_t *) * NCPU, kmflags);
+ if (gssp->gs_cpus == NULL)
+ return (-1);
+
+ mutex_init(&gssp->gs_lock, NULL, MUTEX_DRIVER, NULL);
+ gssp->gs_ncpus = 0;
+ gssp->gs_defunct = NULL;
+ gssp->gs_cbs = NULL;
+
+ return (0);
+}
+
+static void
+gsqueue_set_cache_destruct(void *buf, void *arg)
+{
+ gsqueue_set_t *gssp = buf;
+
+ kmem_free(gssp->gs_cpus, sizeof (gsqueue_cpu_t *) * NCPU);
+ gssp->gs_cpus = NULL;
+ mutex_destroy(&gssp->gs_lock);
+}
+
+static void
+gsqueue_ddiinit(void)
+{
+ list_create(&gsqueue_list, sizeof (gsqueue_set_t),
+ offsetof(gsqueue_set_t, gs_next));
+ mutex_init(&gsqueue_lock, NULL, MUTEX_DRIVER, NULL);
+
+ gsqueue_cb_cache = kmem_cache_create("gsqueue_cb_cache",
+ sizeof (gsqueue_cb_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ gsqueue_cpu_cache = kmem_cache_create("gsqueue_cpu_cache",
+ sizeof (gsqueue_cpu_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ gsqueue_set_cache = kmem_cache_create("squeue_set_cache",
+ sizeof (gsqueue_set_t),
+ 0, gsqueue_set_cache_construct, gsqueue_set_cache_destruct,
+ NULL, NULL, NULL, 0);
+
+
+ mutex_enter(&cpu_lock);
+ register_cpu_setup_func(gsqueue_cpu_setup, NULL);
+ mutex_exit(&cpu_lock);
+}
+
+static int
+gsqueue_ddifini(void)
+{
+ mutex_enter(&gsqueue_lock);
+ if (list_is_empty(&gsqueue_list) == 0) {
+ mutex_exit(&gsqueue_lock);
+ return (EBUSY);
+ }
+ list_destroy(&gsqueue_list);
+ mutex_exit(&gsqueue_lock);
+
+ mutex_enter(&cpu_lock);
+ register_cpu_setup_func(gsqueue_cpu_setup, NULL);
+ mutex_exit(&cpu_lock);
+
+ kmem_cache_destroy(gsqueue_set_cache);
+ kmem_cache_destroy(gsqueue_cpu_cache);
+ kmem_cache_destroy(gsqueue_cb_cache);
+
+ mutex_destroy(&gsqueue_lock);
+
+ return (0);
+}
+
+static struct modlmisc gsqueue_modmisc = {
+ &mod_miscops,
+ "gsqueue"
+};
+
+static struct modlinkage gsqueue_modlinkage = {
+ MODREV_1,
+ &gsqueue_modmisc,
+ NULL
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ gsqueue_ddiinit();
+ if ((ret = mod_install(&gsqueue_modlinkage)) != 0) {
+ VERIFY(gsqueue_ddifini() == 0);
+ return (ret);
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&gsqueue_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = gsqueue_ddifini()) != 0)
+ return (ret);
+
+ if ((ret = mod_remove(&gsqueue_modlinkage)) != 0)
+ return (ret);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/inotify.c b/usr/src/uts/common/io/inotify.c
new file mode 100644
index 0000000000..baa36cfc8d
--- /dev/null
+++ b/usr/src/uts/common/io/inotify.c
@@ -0,0 +1,1504 @@
+/*
+ * 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) 2015 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015 The MathWorks, Inc. All rights reserved.
+ */
+
+/*
+ * Support for the inotify facility, a Linux-borne facility for asynchronous
+ * notification of certain events on specified files or directories. Our
+ * implementation broadly leverages the file event monitoring facility, and
+ * would actually be quite straightforward were it not for a very serious
+ * blunder in the inotify interface: in addition to allowing for one to be
+ * notified on events on a particular file or directory, inotify also allows
+ * for one to be notified on certain events on files _within_ a watched
+ * directory -- even though those events have absolutely nothing to do with
+ * the directory itself. This leads to all sorts of madness because file
+ * operations are (of course) not undertaken on paths but rather on open
+ * files -- and the relationships between open files and the paths that resolve
+ * to those files are neither static nor isomorphic. We implement this
+ * concept by having _child watches_ when directories are watched with events
+ * in IN_CHILD_EVENTS. We add child watches when a watch on a directory is
+ * first added, and we modify those child watches dynamically as files are
+ * created, deleted, moved into or moved out of the specified directory. This
+ * mechanism works well, absent hard links. Hard links, unfortunately, break
+ * this rather badly, and the user is warned that watches on directories that
+ * have multiple directory entries referring to the same file may behave
+ * unexpectedly.
+ */
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/inotify.h>
+#include <sys/fem.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vmem.h>
+#include <sys/avl.h>
+#include <sys/sysmacros.h>
+#include <sys/cyclic.h>
+#include <sys/filio.h>
+
+struct inotify_state;
+struct inotify_kevent;
+
+typedef struct inotify_watch inotify_watch_t;
+typedef struct inotify_state inotify_state_t;
+typedef struct inotify_kevent inotify_kevent_t;
+
+struct inotify_watch {
+ kmutex_t inw_lock; /* lock protecting ref count */
+ int inw_refcnt; /* reference count */
+ uint8_t inw_zombie:1; /* boolean: is zombie */
+ uint8_t inw_fired:1; /* boolean: fired one-shot */
+ uint8_t inw_active:1; /* boolean: watch is active */
+ uint8_t inw_orphaned:1; /* boolean: orphaned */
+ kcondvar_t inw_cv; /* condvar for zombifier */
+ uint32_t inw_mask; /* mask of watch */
+ int32_t inw_wd; /* watch descriptor */
+ vnode_t *inw_vp; /* underlying vnode */
+ inotify_watch_t *inw_parent; /* parent, if a child */
+ avl_node_t inw_byvp; /* watches by vnode */
+ avl_node_t inw_bywd; /* watches by descriptor */
+ avl_tree_t inw_children; /* children, if a parent */
+ char *inw_name; /* name, if a child */
+ list_node_t inw_orphan; /* orphan list */
+ cred_t *inw_cred; /* cred, if orphaned */
+ inotify_state_t *inw_state; /* corresponding state */
+};
+
+struct inotify_kevent {
+ inotify_kevent_t *ine_next; /* next event in queue */
+ struct inotify_event ine_event; /* event (variable size) */
+};
+
+#define INOTIFY_EVENT_LENGTH(ev) \
+ (sizeof (inotify_kevent_t) + (ev)->ine_event.len)
+
+struct inotify_state {
+ kmutex_t ins_lock; /* lock protecting state */
+ avl_tree_t ins_byvp; /* watches by vnode */
+ avl_tree_t ins_bywd; /* watches by descriptor */
+ vmem_t *ins_wds; /* watch identifier arena */
+ int ins_maxwatches; /* maximum number of watches */
+ int ins_maxevents; /* maximum number of events */
+ int ins_nevents; /* current # of events */
+ int32_t ins_size; /* total size of events */
+ inotify_kevent_t *ins_head; /* head of event queue */
+ inotify_kevent_t *ins_tail; /* tail of event queue */
+ pollhead_t ins_pollhd; /* poll head */
+ kcondvar_t ins_cv; /* condvar for reading */
+ list_t ins_orphans; /* orphan list */
+ ddi_periodic_t ins_cleaner; /* cyclic for cleaning */
+ inotify_watch_t *ins_zombies; /* zombie watch list */
+ cred_t *ins_cred; /* creator's credentials */
+ inotify_state_t *ins_next; /* next state on global list */
+};
+
+/*
+ * Tunables (exported read-only in lx-branded zones via /proc).
+ */
+int inotify_maxwatches = 8192; /* max watches per instance */
+int inotify_maxevents = 16384; /* max events */
+int inotify_maxinstances = 128; /* max instances per user */
+
+/*
+ * Internal global variables.
+ */
+static kmutex_t inotify_lock; /* lock protecting state */
+static dev_info_t *inotify_devi; /* device info */
+static fem_t *inotify_femp; /* FEM pointer */
+static vmem_t *inotify_minor; /* minor number arena */
+static void *inotify_softstate; /* softstate pointer */
+static inotify_state_t *inotify_state; /* global list if state */
+
+static void inotify_watch_event(inotify_watch_t *, uint64_t, char *);
+static void inotify_watch_insert(inotify_watch_t *, vnode_t *, char *);
+static void inotify_watch_delete(inotify_watch_t *, uint32_t);
+static void inotify_watch_remove(inotify_state_t *state,
+ inotify_watch_t *watch);
+
+static int
+inotify_fop_close(femarg_t *vf, int flag, int count, offset_t offset,
+ cred_t *cr, caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_close(vf, flag, count, offset, cr, ct)) == 0) {
+ inotify_watch_event(watch, flag & FWRITE ?
+ IN_CLOSE_WRITE : IN_CLOSE_NOWRITE, NULL);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
+ int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_create(vf, name, vap, excl, mode,
+ vpp, cr, flag, ct, vsecp)) == 0) {
+ inotify_watch_insert(watch, *vpp, name);
+ inotify_watch_event(watch, IN_CREATE, name);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_link(vf, svp, tnm, cr, ct, flags)) == 0) {
+ inotify_watch_insert(watch, svp, tnm);
+ inotify_watch_event(watch, IN_CREATE, tnm);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_mkdir(femarg_t *vf, char *name, vattr_t *vap, vnode_t **vpp,
+ cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_mkdir(vf, name, vap, vpp, cr,
+ ct, flags, vsecp)) == 0) {
+ inotify_watch_insert(watch, *vpp, name);
+ inotify_watch_event(watch, IN_CREATE | IN_ISDIR, name);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_open(vf, mode, cr, ct)) == 0)
+ inotify_watch_event(watch, IN_OPEN, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_read(vf, uiop, ioflag, cr, ct);
+ inotify_watch_event(watch, IN_ACCESS, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
+ inotify_watch_event(watch, IN_ACCESS | IN_ISDIR, NULL);
+
+ return (rval);
+}
+
+int
+inotify_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_remove(vf, nm, cr, ct, flags)) == 0)
+ inotify_watch_event(watch, IN_DELETE, nm);
+
+ return (rval);
+}
+
+int
+inotify_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_rmdir(vf, nm, cdir, cr, ct, flags)) == 0)
+ inotify_watch_event(watch, IN_DELETE | IN_ISDIR, nm);
+
+ return (rval);
+}
+
+static int
+inotify_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_setattr(vf, vap, flags, cr, ct)) == 0)
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_write(vf, uiop, ioflag, cr, ct);
+ inotify_watch_event(watch, IN_MODIFY, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+
+ switch (vnevent) {
+ case VE_RENAME_SRC:
+ inotify_watch_event(watch, IN_MOVE_SELF, NULL);
+ inotify_watch_delete(watch, IN_MOVE_SELF);
+ break;
+ case VE_REMOVE:
+ /*
+ * Linux will apparently fire an IN_ATTRIB event when the link
+ * count changes (including when it drops to 0 on a remove).
+ * This is merely somewhat odd; what is amazing is that this
+ * IN_ATTRIB event is not visible on an inotify watch on the
+ * parent directory. (IN_ATTRIB events are normally sent to
+ * watches on the parent directory). While it's hard to
+ * believe that this constitutes desired semantics, ltp
+ * unfortunately tests this case (if implicitly); in the name
+ * of bug-for-bug compatibility, we fire IN_ATTRIB iff we are
+ * explicitly watching the file that has been removed.
+ */
+ if (watch->inw_parent == NULL)
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+ /*FALLTHROUGH*/
+ case VE_RENAME_DEST:
+ inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+ inotify_watch_delete(watch, IN_DELETE_SELF);
+ break;
+ case VE_RMDIR:
+ /*
+ * It seems that IN_ISDIR should really be OR'd in here, but
+ * Linux doesn't seem to do that in this case; for the sake of
+ * bug-for-bug compatibility, we don't do it either.
+ */
+ inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+ inotify_watch_delete(watch, IN_DELETE_SELF);
+ break;
+ case VE_CREATE:
+ case VE_TRUNCATE:
+ case VE_RESIZE:
+ inotify_watch_event(watch, IN_MODIFY | IN_ATTRIB, NULL);
+ break;
+ case VE_LINK:
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+ break;
+ case VE_RENAME_SRC_DIR:
+ inotify_watch_event(watch, IN_MOVED_FROM, name);
+ break;
+ case VE_RENAME_DEST_DIR:
+ if (name == NULL)
+ name = dvp->v_path;
+
+ inotify_watch_insert(watch, dvp, name);
+ inotify_watch_event(watch, IN_MOVED_TO, name);
+ break;
+ case VE_SUPPORT:
+ case VE_MOUNTEDOVER:
+ case VE_PRE_RENAME_SRC:
+ case VE_PRE_RENAME_DEST:
+ case VE_PRE_RENAME_DEST_DIR:
+ break;
+ }
+
+ return (vnext_vnevent(vf, vnevent, dvp, name, ct));
+}
+
+const fs_operation_def_t inotify_vnodesrc_template[] = {
+ VOPNAME_CLOSE, { .femop_close = inotify_fop_close },
+ VOPNAME_CREATE, { .femop_create = inotify_fop_create },
+ VOPNAME_LINK, { .femop_link = inotify_fop_link },
+ VOPNAME_MKDIR, { .femop_mkdir = inotify_fop_mkdir },
+ VOPNAME_OPEN, { .femop_open = inotify_fop_open },
+ VOPNAME_READ, { .femop_read = inotify_fop_read },
+ VOPNAME_READDIR, { .femop_readdir = inotify_fop_readdir },
+ VOPNAME_REMOVE, { .femop_remove = inotify_fop_remove },
+ VOPNAME_RMDIR, { .femop_rmdir = inotify_fop_rmdir },
+ VOPNAME_SETATTR, { .femop_setattr = inotify_fop_setattr },
+ VOPNAME_WRITE, { .femop_write = inotify_fop_write },
+ VOPNAME_VNEVENT, { .femop_vnevent = inotify_fop_vnevent },
+ NULL, NULL
+};
+
+static int
+inotify_watch_cmpwd(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+ if (lhs->inw_wd < rhs->inw_wd)
+ return (-1);
+
+ if (lhs->inw_wd > rhs->inw_wd)
+ return (1);
+
+ return (0);
+}
+
+static int
+inotify_watch_cmpvp(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+ uintptr_t lvp = (uintptr_t)lhs->inw_vp, rvp = (uintptr_t)rhs->inw_vp;
+
+ if (lvp < rvp)
+ return (-1);
+
+ if (lvp > rvp)
+ return (1);
+
+ return (0);
+}
+
+static void
+inotify_watch_hold(inotify_watch_t *watch)
+{
+ mutex_enter(&watch->inw_lock);
+ VERIFY(watch->inw_refcnt > 0);
+ watch->inw_refcnt++;
+ mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_release(inotify_watch_t *watch)
+{
+ mutex_enter(&watch->inw_lock);
+ VERIFY(watch->inw_refcnt > 1);
+
+ if (--watch->inw_refcnt == 1 && watch->inw_zombie) {
+ /*
+ * We're down to our last reference; kick anyone that might be
+ * waiting.
+ */
+ cv_signal(&watch->inw_cv);
+ }
+
+ mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_event(inotify_watch_t *watch, uint64_t mask, char *name)
+{
+ inotify_kevent_t *event, *tail;
+ inotify_state_t *state = watch->inw_state;
+ uint32_t wd = watch->inw_wd, cookie = 0, len;
+ boolean_t removal = mask & IN_REMOVAL ? B_TRUE : B_FALSE;
+ inotify_watch_t *source = watch;
+
+ if (!(mask &= watch->inw_mask) || mask == IN_ISDIR)
+ return;
+
+ if (watch->inw_parent != NULL) {
+ /*
+ * This is an event on the child; if this isn't a valid child
+ * event, return. Otherwise, we move our watch to be our
+ * parent (which we know is around because we have a hold on
+ * it) and continue.
+ */
+ if (!(mask & IN_CHILD_EVENTS))
+ return;
+
+ name = watch->inw_name;
+ watch = watch->inw_parent;
+ wd = watch->inw_wd;
+ }
+
+ if (!removal) {
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie ||
+ watch->inw_fired || !watch->inw_active) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+ } else {
+ if (!watch->inw_active)
+ return;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ }
+
+ /*
+ * If this is an operation on a directory and it's a child event
+ * (event if it's not on a child), we specify IN_ISDIR.
+ */
+ if (source->inw_vp->v_type == VDIR && (mask & IN_CHILD_EVENTS))
+ mask |= IN_ISDIR;
+
+ if (mask & (IN_MOVED_FROM | IN_MOVED_TO))
+ cookie = (uint32_t)curthread->t_did;
+
+ if (state->ins_nevents >= state->ins_maxevents) {
+ /*
+ * We're at our maximum number of events -- turn our event
+ * into an IN_Q_OVERFLOW event, which will be coalesced if
+ * it's already the tail event.
+ */
+ mask = IN_Q_OVERFLOW;
+ wd = (uint32_t)-1;
+ cookie = 0;
+ len = 0;
+ }
+
+ if ((tail = state->ins_tail) != NULL && tail->ine_event.wd == wd &&
+ tail->ine_event.mask == mask && tail->ine_event.cookie == cookie &&
+ ((tail->ine_event.len == 0 && len == 0) ||
+ (name != NULL && tail->ine_event.len != 0 &&
+ strcmp(tail->ine_event.name, name) == 0))) {
+ /*
+ * This is an implicitly coalesced event; we're done.
+ */
+ if (!removal)
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (name != NULL) {
+ len = strlen(name) + 1;
+ len = roundup(len, sizeof (struct inotify_event));
+ } else {
+ len = 0;
+ }
+
+ event = kmem_zalloc(sizeof (inotify_kevent_t) + len, KM_SLEEP);
+ event->ine_event.wd = wd;
+ event->ine_event.mask = (uint32_t)mask;
+ event->ine_event.cookie = cookie;
+ event->ine_event.len = len;
+
+ if (name != NULL)
+ strcpy(event->ine_event.name, name);
+
+ if (tail != NULL) {
+ tail->ine_next = event;
+ } else {
+ VERIFY(state->ins_head == NULL);
+ state->ins_head = event;
+ cv_broadcast(&state->ins_cv);
+ }
+
+ state->ins_tail = event;
+ state->ins_nevents++;
+ state->ins_size += sizeof (event->ine_event) + len;
+
+ if (removal)
+ return;
+
+ if ((watch->inw_mask & IN_ONESHOT) && !watch->inw_fired) {
+ /*
+ * If this is a one-shot, we need to remove the watch. (Note
+ * that this will recurse back into inotify_watch_event() to
+ * fire the IN_IGNORED event -- but with "removal" set.)
+ */
+ watch->inw_fired = 1;
+ inotify_watch_remove(state, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ pollwakeup(&state->ins_pollhd, POLLRDNORM | POLLIN);
+}
+
+/*
+ * Destroy a watch. By the time we're in here, the watch must have exactly
+ * one reference.
+ */
+static void
+inotify_watch_destroy(inotify_watch_t *watch)
+{
+ VERIFY(MUTEX_HELD(&watch->inw_lock));
+
+ if (watch->inw_name != NULL)
+ kmem_free(watch->inw_name, strlen(watch->inw_name) + 1);
+
+ kmem_free(watch, sizeof (inotify_watch_t));
+}
+
+/*
+ * Zombify a watch. By the time we come in here, it must be true that the
+ * watch has already been fem_uninstall()'d -- the only reference should be
+ * in the state's data structure. If we can get away with freeing it, we'll
+ * do that -- but if the reference count is greater than one due to an active
+ * vnode operation, we'll put this watch on the zombie list on the state
+ * structure.
+ */
+static void
+inotify_watch_zombify(inotify_watch_t *watch)
+{
+ inotify_state_t *state = watch->inw_state;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ VERIFY(!watch->inw_zombie);
+
+ watch->inw_zombie = 1;
+
+ if (watch->inw_parent != NULL) {
+ inotify_watch_release(watch->inw_parent);
+ } else {
+ avl_remove(&state->ins_byvp, watch);
+ avl_remove(&state->ins_bywd, watch);
+ vmem_free(state->ins_wds, (void *)(uintptr_t)watch->inw_wd, 1);
+ watch->inw_wd = -1;
+ }
+
+ mutex_enter(&watch->inw_lock);
+
+ if (watch->inw_refcnt == 1) {
+ /*
+ * There are no operations in flight and there is no way
+ * for anyone to discover this watch -- we can destroy it.
+ */
+ inotify_watch_destroy(watch);
+ } else {
+ /*
+ * There are operations in flight; we will need to enqueue
+ * this for later destruction.
+ */
+ watch->inw_parent = state->ins_zombies;
+ state->ins_zombies = watch;
+ mutex_exit(&watch->inw_lock);
+ }
+}
+
+static inotify_watch_t *
+inotify_watch_add(inotify_state_t *state, inotify_watch_t *parent,
+ const char *name, vnode_t *vp, uint32_t mask)
+{
+ inotify_watch_t *watch;
+ int err;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+
+ watch = kmem_zalloc(sizeof (inotify_watch_t), KM_SLEEP);
+
+ watch->inw_vp = vp;
+ watch->inw_mask = mask;
+ watch->inw_state = state;
+ watch->inw_refcnt = 1;
+
+ if (parent == NULL) {
+ watch->inw_wd = (int)(uintptr_t)vmem_alloc(state->ins_wds,
+ 1, VM_BESTFIT | VM_SLEEP);
+ avl_add(&state->ins_byvp, watch);
+ avl_add(&state->ins_bywd, watch);
+
+ avl_create(&watch->inw_children,
+ (int(*)(const void *, const void *))inotify_watch_cmpvp,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_byvp));
+ } else {
+ VERIFY(name != NULL);
+ inotify_watch_hold(parent);
+ watch->inw_mask &= IN_CHILD_EVENTS;
+ watch->inw_parent = parent;
+ watch->inw_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
+ strcpy(watch->inw_name, name);
+
+ avl_add(&parent->inw_children, watch);
+ }
+
+ /*
+ * Add our monitor to the vnode. We must not have the watch lock held
+ * when we do this, as it will immediately hold our watch.
+ */
+ err = fem_install(vp, inotify_femp, watch, OPARGUNIQ,
+ (void (*)(void *))inotify_watch_hold,
+ (void (*)(void *))inotify_watch_release);
+
+ VERIFY(err == 0);
+
+ return (watch);
+}
+
+/*
+ * Remove a (non-child) watch. This is called from either synchronous context
+ * via inotify_rm_watch() or monitor context via either a vnevent or a
+ * one-shot.
+ */
+static void
+inotify_watch_remove(inotify_state_t *state, inotify_watch_t *watch)
+{
+ inotify_watch_t *child;
+ int err;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ VERIFY(watch->inw_parent == NULL);
+
+ err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+ VERIFY(err == 0);
+
+ /*
+ * If we have children, we're going to remove them all and set them
+ * all to be zombies.
+ */
+ while ((child = avl_first(&watch->inw_children)) != NULL) {
+ VERIFY(child->inw_parent == watch);
+ avl_remove(&watch->inw_children, child);
+
+ err = fem_uninstall(child->inw_vp, inotify_femp, child);
+ VERIFY(err == 0);
+
+ /*
+ * If this child watch has been orphaned, remove it from the
+ * state's list of orphans.
+ */
+ if (child->inw_orphaned) {
+ list_remove(&state->ins_orphans, child);
+ crfree(child->inw_cred);
+ }
+
+ VN_RELE(child->inw_vp);
+
+ /*
+ * We're down (or should be down) to a single reference to
+ * this child watch; it's safe to zombify it.
+ */
+ inotify_watch_zombify(child);
+ }
+
+ inotify_watch_event(watch, IN_IGNORED | IN_REMOVAL, NULL);
+ VN_RELE(watch->inw_vp);
+
+ /*
+ * It's now safe to zombify the watch -- we know that the only reference
+ * can come from operations in flight.
+ */
+ inotify_watch_zombify(watch);
+}
+
+/*
+ * Delete a watch. Should only be called from VOP context.
+ */
+static void
+inotify_watch_delete(inotify_watch_t *watch, uint32_t event)
+{
+ inotify_state_t *state = watch->inw_state;
+ inotify_watch_t cmp = { .inw_vp = watch->inw_vp }, *parent;
+ int err;
+
+ if (event != IN_DELETE_SELF && !(watch->inw_mask & IN_CHILD_EVENTS))
+ return;
+
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if ((parent = watch->inw_parent) == NULL) {
+ if (event == IN_DELETE_SELF) {
+ /*
+ * If we're here because we're being deleted and we
+ * are not a child watch, we need to delete the entire
+ * watch, children and all.
+ */
+ inotify_watch_remove(state, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ return;
+ } else {
+ if (event == IN_DELETE_SELF &&
+ !(parent->inw_mask & IN_EXCL_UNLINK)) {
+ /*
+ * This is a child watch for a file that is being
+ * removed and IN_EXCL_UNLINK has not been specified;
+ * indicate that it is orphaned and add it to the list
+ * of orphans. (This list will be checked by the
+ * cleaning cyclic to determine when the watch has
+ * become the only hold on the vnode, at which point
+ * the watch can be zombified.) Note that we check
+ * if the watch is orphaned before we orphan it: hard
+ * links make it possible for VE_REMOVE to be called
+ * multiple times on the same vnode. (!)
+ */
+ if (!watch->inw_orphaned) {
+ watch->inw_orphaned = 1;
+ watch->inw_cred = CRED();
+ crhold(watch->inw_cred);
+ list_insert_head(&state->ins_orphans, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (watch->inw_orphaned) {
+ /*
+ * If we're here, a file was orphaned and then later
+ * moved -- which almost certainly means that hard
+ * links are on the scene. We choose the orphan over
+ * the move because we don't want to spuriously
+ * drop events if we can avoid it.
+ */
+ crfree(watch->inw_cred);
+ list_remove(&state->ins_orphans, watch);
+ }
+ }
+
+ if (avl_find(&parent->inw_children, &cmp, NULL) == NULL) {
+ /*
+ * This watch has already been deleted from the parent.
+ */
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ avl_remove(&parent->inw_children, watch);
+ err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+ VERIFY(err == 0);
+
+ VN_RELE(watch->inw_vp);
+
+ /*
+ * It's now safe to zombify the watch -- which won't actually delete
+ * it as we know that the reference count is greater than 1.
+ */
+ inotify_watch_zombify(watch);
+ mutex_exit(&state->ins_lock);
+}
+
+/*
+ * Insert a new child watch. Should only be called from VOP context when
+ * a child is created in a watched directory.
+ */
+static void
+inotify_watch_insert(inotify_watch_t *watch, vnode_t *vp, char *name)
+{
+ inotify_state_t *state = watch->inw_state;
+ inotify_watch_t cmp = { .inw_vp = vp };
+
+ if (!(watch->inw_mask & IN_CHILD_EVENTS))
+ return;
+
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie || watch->inw_parent != NULL || vp == NULL) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ VN_HOLD(vp);
+ watch = inotify_watch_add(state, watch, name, vp, watch->inw_mask);
+ VERIFY(watch != NULL);
+
+ mutex_exit(&state->ins_lock);
+}
+
+
+static int
+inotify_add_watch(inotify_state_t *state, vnode_t *vp, uint32_t mask,
+ int32_t *wdp)
+{
+ inotify_watch_t *watch, cmp = { .inw_vp = vp };
+ uint32_t set;
+
+ set = (mask & (IN_ALL_EVENTS | IN_MODIFIERS)) | IN_UNMASKABLE;
+
+ /*
+ * Lookup our vnode to determine if we already have a watch on it.
+ */
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+ /*
+ * We don't have this watch; allocate a new one, provided that
+ * we have fewer than our limit.
+ */
+ if (avl_numnodes(&state->ins_bywd) >= state->ins_maxwatches) {
+ mutex_exit(&state->ins_lock);
+ return (ENOSPC);
+ }
+
+ VN_HOLD(vp);
+ watch = inotify_watch_add(state, NULL, NULL, vp, set);
+ *wdp = watch->inw_wd;
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+ }
+
+ VERIFY(!watch->inw_zombie);
+
+ if (!(mask & IN_MASK_ADD)) {
+ /*
+ * Note that if we're resetting our event mask and we're
+ * transitioning from an event mask that includes child events
+ * to one that doesn't, there will be potentially some stale
+ * child watches. This is basically fine: they won't fire,
+ * and they will correctly be removed when the watch is
+ * removed.
+ */
+ watch->inw_mask = 0;
+ }
+
+ watch->inw_mask |= set;
+
+ *wdp = watch->inw_wd;
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+static int
+inotify_add_child(inotify_state_t *state, vnode_t *vp, char *name)
+{
+ inotify_watch_t *watch, cmp = { .inw_vp = vp };
+ vnode_t *cvp;
+ int err;
+
+ /*
+ * Verify that the specified child doesn't have a directory component
+ * within it.
+ */
+ if (strchr(name, '/') != NULL)
+ return (EINVAL);
+
+ /*
+ * Lookup the underlying file. Note that this will succeed even if
+ * we don't have permissions to actually read the file.
+ */
+ if ((err = lookupnameat(name,
+ UIO_SYSSPACE, NO_FOLLOW, NULL, &cvp, vp)) != 0) {
+ return (err);
+ }
+
+ /*
+ * Use our vnode to find our watch, and then add our child watch to it.
+ */
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+ /*
+ * This is unexpected -- it means that we don't have the
+ * watch that we thought we had.
+ */
+ mutex_exit(&state->ins_lock);
+ VN_RELE(cvp);
+ return (ENXIO);
+ }
+
+ /*
+ * Now lookup the child vnode in the watch; we'll only add it if it
+ * isn't already there.
+ */
+ cmp.inw_vp = cvp;
+
+ if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+ mutex_exit(&state->ins_lock);
+ VN_RELE(cvp);
+ return (0);
+ }
+
+ watch = inotify_watch_add(state, watch, name, cvp, watch->inw_mask);
+ VERIFY(watch != NULL);
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+static int
+inotify_rm_watch(inotify_state_t *state, int32_t wd)
+{
+ inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+ mutex_exit(&state->ins_lock);
+ return (EINVAL);
+ }
+
+ inotify_watch_remove(state, watch);
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+static int
+inotify_activate(inotify_state_t *state, int32_t wd)
+{
+ inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+ mutex_exit(&state->ins_lock);
+ return (EINVAL);
+ }
+
+ watch->inw_active = 1;
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+/*
+ * Called periodically as a cyclic to process the orphans and zombies.
+ */
+static void
+inotify_clean(void *arg)
+{
+ inotify_state_t *state = arg;
+ inotify_watch_t *watch, *parent, *next, **prev;
+ cred_t *savecred;
+ int err;
+
+ mutex_enter(&state->ins_lock);
+
+ for (watch = list_head(&state->ins_orphans);
+ watch != NULL; watch = next) {
+ next = list_next(&state->ins_orphans, watch);
+
+ VERIFY(!watch->inw_zombie);
+ VERIFY((parent = watch->inw_parent) != NULL);
+
+ if (watch->inw_vp->v_count > 1)
+ continue;
+
+ avl_remove(&parent->inw_children, watch);
+ err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+ VERIFY(err == 0);
+
+ list_remove(&state->ins_orphans, watch);
+
+ /*
+ * For purposes of releasing the vnode, we need to switch our
+ * cred to be the cred of the orphaning thread (which we held
+ * at the time this watch was orphaned).
+ */
+ savecred = curthread->t_cred;
+ curthread->t_cred = watch->inw_cred;
+ VN_RELE(watch->inw_vp);
+ crfree(watch->inw_cred);
+ curthread->t_cred = savecred;
+
+ inotify_watch_zombify(watch);
+ }
+
+ prev = &state->ins_zombies;
+
+ while ((watch = *prev) != NULL) {
+ mutex_enter(&watch->inw_lock);
+
+ if (watch->inw_refcnt == 1) {
+ *prev = watch->inw_parent;
+ inotify_watch_destroy(watch);
+ continue;
+ }
+
+ prev = &watch->inw_parent;
+ mutex_exit(&watch->inw_lock);
+ }
+
+ mutex_exit(&state->ins_lock);
+}
+
+/*ARGSUSED*/
+static int
+inotify_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ inotify_state_t *state;
+ major_t major = getemajor(*devp);
+ minor_t minor = getminor(*devp);
+ int instances = 0;
+ char c[64];
+
+ if (minor != INOTIFYMNRN_INOTIFY)
+ return (ENXIO);
+
+ mutex_enter(&inotify_lock);
+
+ for (state = inotify_state; state != NULL; state = state->ins_next) {
+ if (state->ins_cred == cred_p)
+ instances++;
+ }
+
+ if (instances >= inotify_maxinstances) {
+ mutex_exit(&inotify_lock);
+ return (EMFILE);
+ }
+
+ minor = (minor_t)(uintptr_t)vmem_alloc(inotify_minor, 1,
+ VM_BESTFIT | VM_SLEEP);
+
+ if (ddi_soft_state_zalloc(inotify_softstate, minor) != DDI_SUCCESS) {
+ vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+ mutex_exit(&inotify_lock);
+ return (NULL);
+ }
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+ *devp = makedevice(major, minor);
+
+ crhold(cred_p);
+ state->ins_cred = cred_p;
+ state->ins_next = inotify_state;
+ inotify_state = state;
+
+ (void) snprintf(c, sizeof (c), "inotify_watchid_%d", minor);
+ state->ins_wds = vmem_create(c, (void *)1, UINT32_MAX, 1,
+ NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
+
+ avl_create(&state->ins_bywd,
+ (int(*)(const void *, const void *))inotify_watch_cmpwd,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_bywd));
+
+ avl_create(&state->ins_byvp,
+ (int(*)(const void *, const void *))inotify_watch_cmpvp,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_byvp));
+
+ list_create(&state->ins_orphans, sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_orphan));
+
+ state->ins_maxwatches = inotify_maxwatches;
+ state->ins_maxevents = inotify_maxevents;
+
+ mutex_exit(&inotify_lock);
+
+ state->ins_cleaner = ddi_periodic_add(inotify_clean,
+ state, NANOSEC, DDI_IPL_0);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_read(dev_t dev, uio_t *uio, cred_t *cr)
+{
+ inotify_state_t *state;
+ inotify_kevent_t *event;
+ minor_t minor = getminor(dev);
+ int err = 0, nevents = 0;
+ size_t len;
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ mutex_enter(&state->ins_lock);
+
+ while (state->ins_head == NULL) {
+ if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
+ mutex_exit(&state->ins_lock);
+ return (EAGAIN);
+ }
+
+ if (!cv_wait_sig_swap(&state->ins_cv, &state->ins_lock)) {
+ mutex_exit(&state->ins_lock);
+ return (EINTR);
+ }
+ }
+
+ /*
+ * We have events and we have our lock; return as many as we can.
+ */
+ while ((event = state->ins_head) != NULL) {
+ len = sizeof (event->ine_event) + event->ine_event.len;
+
+ if (uio->uio_resid < len) {
+ if (nevents == 0)
+ err = EINVAL;
+ break;
+ }
+
+ nevents++;
+
+ if ((err = uiomove(&event->ine_event, len, UIO_READ, uio)) != 0)
+ break;
+
+ VERIFY(state->ins_nevents > 0);
+ state->ins_nevents--;
+
+ VERIFY(state->ins_size > 0);
+ state->ins_size -= len;
+
+ if ((state->ins_head = event->ine_next) == NULL) {
+ VERIFY(event == state->ins_tail);
+ VERIFY(state->ins_nevents == 0);
+ state->ins_tail = NULL;
+ }
+
+ kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+ }
+
+ mutex_exit(&state->ins_lock);
+
+ return (err);
+}
+
+/*ARGSUSED*/
+static int
+inotify_poll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ inotify_state_t *state;
+ minor_t minor = getminor(dev);
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ mutex_enter(&state->ins_lock);
+
+ if (state->ins_head != NULL) {
+ *reventsp = events & (POLLRDNORM | POLLIN);
+ } else {
+ *reventsp = 0;
+
+ if (!anyyet)
+ *phpp = &state->ins_pollhd;
+ }
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ inotify_state_t *state;
+ minor_t minor = getminor(dev);
+ file_t *fp;
+ int rval;
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ switch (cmd) {
+ case INOTIFYIOC_ADD_WATCH: {
+ inotify_addwatch_t addwatch;
+ file_t *fp;
+
+ if (copyin((void *)arg, &addwatch, sizeof (addwatch)) != 0)
+ return (EFAULT);
+
+ if ((fp = getf(addwatch.inaw_fd)) == NULL)
+ return (EBADF);
+
+ rval = inotify_add_watch(state, fp->f_vnode,
+ addwatch.inaw_mask, rv);
+
+ releasef(addwatch.inaw_fd);
+ return (rval);
+ }
+
+ case INOTIFYIOC_ADD_CHILD: {
+ inotify_addchild_t addchild;
+ char name[MAXPATHLEN];
+
+ if (copyin((void *)arg, &addchild, sizeof (addchild)) != 0)
+ return (EFAULT);
+
+ if (copyinstr(addchild.inac_name, name, MAXPATHLEN, NULL) != 0)
+ return (EFAULT);
+
+ if ((fp = getf(addchild.inac_fd)) == NULL)
+ return (EBADF);
+
+ rval = inotify_add_child(state, fp->f_vnode, name);
+
+ releasef(addchild.inac_fd);
+ return (rval);
+ }
+
+ case INOTIFYIOC_RM_WATCH:
+ return (inotify_rm_watch(state, arg));
+
+ case INOTIFYIOC_ACTIVATE:
+ return (inotify_activate(state, arg));
+
+ case FIONREAD: {
+ int32_t size;
+
+ mutex_enter(&state->ins_lock);
+ size = state->ins_size;
+ mutex_exit(&state->ins_lock);
+
+ if (copyout(&size, (void *)arg, sizeof (size)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+inotify_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ inotify_state_t *state, **sp;
+ inotify_watch_t *watch, *zombies;
+ inotify_kevent_t *event;
+ minor_t minor = getminor(dev);
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ if (state->ins_pollhd.ph_list != NULL) {
+ pollwakeup(&state->ins_pollhd, POLLERR);
+ pollhead_clean(&state->ins_pollhd);
+ }
+
+ mutex_enter(&state->ins_lock);
+
+ /*
+ * First, destroy all of our watches.
+ */
+ while ((watch = avl_first(&state->ins_bywd)) != NULL)
+ inotify_watch_remove(state, watch);
+
+ /*
+ * And now destroy our event queue.
+ */
+ while ((event = state->ins_head) != NULL) {
+ state->ins_head = event->ine_next;
+ kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+ }
+
+ zombies = state->ins_zombies;
+ state->ins_zombies = NULL;
+ mutex_exit(&state->ins_lock);
+
+ /*
+ * Now that our state lock is dropped, we can synchronously wait on
+ * any zombies.
+ */
+ while ((watch = zombies) != NULL) {
+ zombies = zombies->inw_parent;
+
+ mutex_enter(&watch->inw_lock);
+
+ while (watch->inw_refcnt > 1)
+ cv_wait(&watch->inw_cv, &watch->inw_lock);
+
+ inotify_watch_destroy(watch);
+ }
+
+ if (state->ins_cleaner != NULL) {
+ ddi_periodic_delete(state->ins_cleaner);
+ state->ins_cleaner = NULL;
+ }
+
+ mutex_enter(&inotify_lock);
+
+ /*
+ * Remove our state from our global list, and release our hold on
+ * the cred.
+ */
+ for (sp = &inotify_state; *sp != state; sp = &((*sp)->ins_next))
+ VERIFY(*sp != NULL);
+
+ *sp = (*sp)->ins_next;
+ crfree(state->ins_cred);
+ vmem_destroy(state->ins_wds);
+
+ ddi_soft_state_free(inotify_softstate, minor);
+ vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+
+ mutex_exit(&inotify_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ mutex_enter(&inotify_lock);
+
+ if (ddi_soft_state_init(&inotify_softstate,
+ sizeof (inotify_state_t), 0) != 0) {
+ cmn_err(CE_NOTE, "/dev/inotify failed to create soft state");
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "inotify", S_IFCHR,
+ INOTIFYMNRN_INOTIFY, DDI_PSEUDO, NULL) == DDI_FAILURE) {
+ cmn_err(CE_NOTE, "/dev/inotify couldn't create minor node");
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (fem_create("inotify_fem",
+ inotify_vnodesrc_template, &inotify_femp) != 0) {
+ cmn_err(CE_NOTE, "/dev/inotify couldn't create FEM state");
+ ddi_remove_minor_node(devi, NULL);
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ inotify_devi = devi;
+
+ inotify_minor = vmem_create("inotify_minor", (void *)INOTIFYMNRN_CLONE,
+ UINT32_MAX - INOTIFYMNRN_CLONE, 1, NULL, NULL, NULL, 0,
+ VM_SLEEP | VMC_IDENTIFIER);
+
+ mutex_exit(&inotify_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&inotify_lock);
+ fem_free(inotify_femp);
+ vmem_destroy(inotify_minor);
+
+ ddi_remove_minor_node(inotify_devi, NULL);
+ inotify_devi = NULL;
+
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)inotify_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+static struct cb_ops inotify_cb_ops = {
+ inotify_open, /* open */
+ inotify_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ inotify_read, /* read */
+ nodev, /* write */
+ inotify_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ inotify_poll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops inotify_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ inotify_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ inotify_attach, /* attach */
+ inotify_detach, /* detach */
+ nodev, /* reset */
+ &inotify_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "inotify support", /* name of module */
+ &inotify_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/io/inotify.conf b/usr/src/uts/common/io/inotify.conf
new file mode 100644
index 0000000000..ce9da6180f
--- /dev/null
+++ b/usr/src/uts/common/io/inotify.conf
@@ -0,0 +1,16 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+name="inotify" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_main.c b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
index 6b7f385678..c77bfece05 100644
--- a/usr/src/uts/common/io/ixgbe/ixgbe_main.c
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
@@ -273,7 +273,7 @@ static adapter_info_t ixgbe_82599eb_cap = {
128, /* default number of rx queues */
64, /* maximum number of rx groups */
1, /* minimum number of rx groups */
- 1, /* default number of rx groups */
+ 32, /* default number of rx groups */
128, /* maximum number of tx queues */
1, /* minimum number of tx queues */
8, /* default number of tx queues */
@@ -304,7 +304,7 @@ static adapter_info_t ixgbe_X540_cap = {
128, /* default number of rx queues */
64, /* maximum number of rx groups */
1, /* minimum number of rx groups */
- 1, /* default number of rx groups */
+ 32, /* default number of rx groups */
128, /* maximum number of tx queues */
1, /* minimum number of tx queues */
8, /* default number of tx queues */
@@ -1956,6 +1956,7 @@ ixgbe_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
void *arg1, void *arg2)
{
ixgbe_t *ixgbe = (ixgbe_t *)arg1;
+ int prev = ixgbe->intr_cnt;
switch (cbaction) {
/* IRM callback */
@@ -1969,7 +1970,8 @@ ixgbe_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
if (ixgbe_intr_adjust(ixgbe, cbaction, count) !=
DDI_SUCCESS) {
ixgbe_error(ixgbe,
- "IRM CB: Failed to adjust interrupts");
+ "IRM CB: Failed to adjust interrupts [%d %d %d]",
+ cbaction, count, prev);
goto cb_fail;
}
break;
diff --git a/usr/src/uts/common/io/ksocket/ksocket.c b/usr/src/uts/common/io/ksocket/ksocket.c
index 8944fcbff3..25da45be39 100644
--- a/usr/src/uts/common/io/ksocket/ksocket.c
+++ b/usr/src/uts/common/io/ksocket/ksocket.c
@@ -22,7 +22,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/file.h>
@@ -932,3 +932,15 @@ ksocket_rele(ksocket_t ks)
cv_signal(&so->so_closing_cv);
}
}
+
+int
+ksocket_krecv_set(ksocket_t ks, ksocket_krecv_f cb, void *arg)
+{
+ return (so_krecv_set(KSTOSO(ks), (so_krecv_f)cb, arg));
+}
+
+void
+ksocket_krecv_unblock(ksocket_t ks)
+{
+ return (so_krecv_unblock(KSTOSO(ks)));
+}
diff --git a/usr/src/uts/common/io/ksocket/ksocket_impl.h b/usr/src/uts/common/io/ksocket/ksocket_impl.h
index ac5251540f..516a68d358 100644
--- a/usr/src/uts/common/io/ksocket/ksocket_impl.h
+++ b/usr/src/uts/common/io/ksocket/ksocket_impl.h
@@ -22,11 +22,17 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _INET_KSOCKET_KSOCKET_IMPL_H
#define _INET_KSOCKET_KSOCKET_IMPL_H
+/*
+ * Note that if this relationship ever changes, the logic in ksocket_krecv_set
+ * must be updated and we must maintain local state about this on whatever the
+ * new ksocket object is.
+ */
#define KSTOSO(ks) ((struct sonode *)(ks))
#define SOTOKS(so) ((ksocket_t)(uintptr_t)(so))
diff --git a/usr/src/uts/common/io/ksyms.c b/usr/src/uts/common/io/ksyms.c
index c9f0c63b69..5233fcd0b4 100644
--- a/usr/src/uts/common/io/ksyms.c
+++ b/usr/src/uts/common/io/ksyms.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
@@ -219,6 +220,14 @@ ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
char *addr;
void *hptr = NULL;
ksyms_buflist_hdr_t hdr;
+
+ /*
+ * This device should never be visible in a zone, but if it somehow
+ * does get created we refuse to allow the zone to use it.
+ */
+ if (crgetzoneid(cred) != GLOBAL_ZONEID)
+ return (EACCES);
+
bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
list_create(&hdr.blist, PAGESIZE,
offsetof(ksyms_buflist_t, buflist_node));
diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c
index 1d30dc3478..1bf49a5b44 100644
--- a/usr/src/uts/common/io/mac/mac.c
+++ b/usr/src/uts/common/io/mac/mac.c
@@ -3141,6 +3141,9 @@ mac_prop_check_size(mac_prop_id_t id, uint_t valsize, boolean_t is_range)
case MAC_PROP_WL_MLME:
minsize = sizeof (wl_mlme_t);
break;
+ case MAC_PROP_VN_PROMISC_FILTERED:
+ minsize = sizeof (boolean_t);
+ break;
}
return (valsize >= minsize);
diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c
index 8f0ec9eb67..18a6613424 100644
--- a/usr/src/uts/common/io/mac/mac_client.c
+++ b/usr/src/uts/common/io/mac/mac_client.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/*
@@ -3263,6 +3263,11 @@ mac_promisc_add(mac_client_handle_t mch, mac_client_promisc_type_t type,
mac_cb_info_t *mcbi;
int rc;
+ if ((flags & MAC_PROMISC_FLAGS_NO_COPY) &&
+ (flags & MAC_PROMISC_FLAGS_DO_FIXUPS)) {
+ return (EINVAL);
+ }
+
i_mac_perim_enter(mip);
if ((rc = mac_start((mac_handle_t)mip)) != 0) {
@@ -3271,7 +3276,8 @@ mac_promisc_add(mac_client_handle_t mch, mac_client_promisc_type_t type,
}
if ((mcip->mci_state_flags & MCIS_IS_VNIC) &&
- type == MAC_CLIENT_PROMISC_ALL) {
+ type == MAC_CLIENT_PROMISC_ALL &&
+ (mcip->mci_protect_flags & MPT_FLAG_PROMISC_FILTERED)) {
/*
* The function is being invoked by the upper MAC client
* of a VNIC. The VNIC should only see the traffic
@@ -3308,6 +3314,7 @@ mac_promisc_add(mac_client_handle_t mch, mac_client_promisc_type_t type,
mpip->mpi_strip_vlan_tag =
((flags & MAC_PROMISC_FLAGS_VLAN_TAG_STRIP) != 0);
mpip->mpi_no_copy = ((flags & MAC_PROMISC_FLAGS_NO_COPY) != 0);
+ mpip->mpi_do_fixups = ((flags & MAC_PROMISC_FLAGS_DO_FIXUPS) != 0);
mcbi = &mip->mi_promisc_cb_info;
mutex_enter(mcbi->mcbi_lockp);
@@ -3944,15 +3951,22 @@ mac_client_get_effective_resources(mac_client_handle_t mch,
static void
mac_promisc_dispatch_one(mac_promisc_impl_t *mpip, mblk_t *mp,
- boolean_t loopback)
+ boolean_t loopback, boolean_t local)
{
mblk_t *mp_copy, *mp_next;
- if (!mpip->mpi_no_copy || mpip->mpi_strip_vlan_tag) {
+ if (!mpip->mpi_no_copy || mpip->mpi_strip_vlan_tag ||
+ (mpip->mpi_do_fixups && local)) {
mp_copy = copymsg(mp);
if (mp_copy == NULL)
return;
+ if (mpip->mpi_do_fixups && local) {
+ mp_copy = mac_fix_cksum(mp_copy);
+ if (mp_copy == NULL)
+ return;
+ }
+
if (mpip->mpi_strip_vlan_tag) {
mp_copy = mac_strip_vlan_tag_chain(mp_copy);
if (mp_copy == NULL)
@@ -4009,7 +4023,7 @@ mac_is_mcast(mac_impl_t *mip, mblk_t *mp)
*/
void
mac_promisc_dispatch(mac_impl_t *mip, mblk_t *mp_chain,
- mac_client_impl_t *sender)
+ mac_client_impl_t *sender, boolean_t local)
{
mac_promisc_impl_t *mpip;
mac_cb_t *mcb;
@@ -4049,8 +4063,10 @@ mac_promisc_dispatch(mac_impl_t *mip, mblk_t *mp_chain,
if (is_sender ||
mpip->mpi_type == MAC_CLIENT_PROMISC_ALL ||
- is_mcast)
- mac_promisc_dispatch_one(mpip, mp, is_sender);
+ is_mcast) {
+ mac_promisc_dispatch_one(mpip, mp, is_sender,
+ local);
+ }
}
}
MAC_PROMISC_WALKER_DCR(mip);
@@ -4079,7 +4095,8 @@ mac_promisc_client_dispatch(mac_client_impl_t *mcip, mblk_t *mp_chain)
mpip = (mac_promisc_impl_t *)mcb->mcb_objp;
if (mpip->mpi_type == MAC_CLIENT_PROMISC_FILTERED &&
!is_mcast) {
- mac_promisc_dispatch_one(mpip, mp, B_FALSE);
+ mac_promisc_dispatch_one(mpip, mp, B_FALSE,
+ B_FALSE);
}
}
}
@@ -4150,16 +4167,15 @@ mac_info_get(const char *name, mac_info_t *minfop)
/*
* To get the capabilities that MAC layer cares about, such as rings, factory
* mac address, vnic or not, it should directly invoke this function. If the
- * link is part of a bridge, then the only "capability" it has is the inability
- * to do zero copy.
+ * link is part of a bridge, then the link is unable to do zero copy.
*/
boolean_t
i_mac_capab_get(mac_handle_t mh, mac_capab_t cap, void *cap_data)
{
mac_impl_t *mip = (mac_impl_t *)mh;
- if (mip->mi_bridge_link != NULL)
- return (cap == MAC_CAPAB_NO_ZCOPY);
+ if (mip->mi_bridge_link != NULL && cap == MAC_CAPAB_NO_ZCOPY)
+ return (B_TRUE);
else if (mip->mi_callbacks->mc_callbacks & MC_GETCAPAB)
return (mip->mi_getcapab(mip->mi_driver, cap, cap_data));
else
@@ -4338,7 +4354,13 @@ mac_addr_len(mac_handle_t mh)
boolean_t
mac_is_vnic(mac_handle_t mh)
{
- return (((mac_impl_t *)mh)->mi_state_flags & MIS_IS_VNIC);
+ return ((((mac_impl_t *)mh)->mi_state_flags & MIS_IS_VNIC) != 0);
+}
+
+boolean_t
+mac_is_overlay(mac_handle_t mh)
+{
+ return ((((mac_impl_t *)mh)->mi_state_flags & MIS_IS_OVERLAY) != 0);
}
mac_handle_t
@@ -5552,3 +5574,23 @@ mac_client_set_rings(mac_client_handle_t mch, int rxrings, int txrings)
mrp->mrp_ntxrings = txrings;
}
}
+
+boolean_t
+mac_get_promisc_filtered(mac_client_handle_t mch)
+{
+ mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
+
+ return (mcip->mci_protect_flags & MPT_FLAG_PROMISC_FILTERED);
+}
+
+void
+mac_set_promisc_filtered(mac_client_handle_t mch, boolean_t enable)
+{
+ mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
+
+ ASSERT(MAC_PERIM_HELD((mac_handle_t)mcip->mci_mip));
+ if (enable)
+ mcip->mci_protect_flags |= MPT_FLAG_PROMISC_FILTERED;
+ else
+ mcip->mci_protect_flags &= ~MPT_FLAG_PROMISC_FILTERED;
+}
diff --git a/usr/src/uts/common/io/mac/mac_datapath_setup.c b/usr/src/uts/common/io/mac/mac_datapath_setup.c
index 14d94981cd..c2d09868e0 100644
--- a/usr/src/uts/common/io/mac/mac_datapath_setup.c
+++ b/usr/src/uts/common/io/mac/mac_datapath_setup.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -603,6 +604,7 @@ mac_srs_cpu_setup(cpu_setup_t what, int id, void *arg)
*
* TODO: Cleanup and tighten some of the assumptions.
*/
+boolean_t mac_check_overlay = B_TRUE;
boolean_t mac_use_bw_heuristic = B_TRUE;
static int
mac_compute_soft_ring_count(flow_entry_t *flent, int rx_srs_cnt, int maxcpus)
@@ -610,6 +612,7 @@ mac_compute_soft_ring_count(flow_entry_t *flent, int rx_srs_cnt, int maxcpus)
uint64_t cpu_speed, bw = 0;
int srings = 0;
boolean_t bw_enabled = B_FALSE;
+ mac_client_impl_t *mcip = flent->fe_mcip;
ASSERT(!(flent->fe_type & FLOW_USER));
if (flent->fe_resource_props.mrp_mask & MRP_MAXBW &&
@@ -637,7 +640,16 @@ mac_compute_soft_ring_count(flow_entry_t *flent, int rx_srs_cnt, int maxcpus)
*/
if (mac_soft_ring_enable)
srings = srings * 2;
+ } else if (mac_check_overlay == B_TRUE &&
+ (mcip->mci_state_flags & MCIS_IS_VNIC) != 0) {
+ /* Is this a VNIC on an overlay? */
+ mac_handle_t mh = (mac_handle_t)mcip->mci_mip;
+ if (mac_is_overlay(mh) == B_TRUE) {
+ srings = mac_rx_soft_ring_10gig_count;
+ }
}
+
+
} else {
/*
* Soft ring computation using CPU speed and specified
@@ -2233,7 +2245,14 @@ mac_srs_create(mac_client_impl_t *mcip, flow_entry_t *flent, uint32_t srs_type,
mac_srs->srs_state |= SRS_SOFTRING_QUEUE;
}
- mac_srs->srs_worker = thread_create(NULL, 0,
+ /*
+ * Create the srs_worker with twice the stack of a normal kernel thread
+ * to reduce the likelihood of stack overflows in receive-side
+ * processing. (The larger stacks are not the only precaution taken
+ * against stack overflows; see the use of the MAC_RX_SRS_TOODEEP
+ * macro for details.)
+ */
+ mac_srs->srs_worker = thread_create(NULL, default_stksize << 1,
mac_srs_worker, mac_srs, 0, &p0, TS_RUN, mac_srs->srs_pri);
if (is_tx_srs) {
diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c
index 805b5d36f9..da83dc643e 100644
--- a/usr/src/uts/common/io/mac/mac_protect.c
+++ b/usr/src/uts/common/io/mac/mac_protect.c
@@ -2576,6 +2576,9 @@ mac_protect_init(mac_client_impl_t *mcip)
sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node));
avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip,
sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node));
+
+ if (mcip->mci_state_flags & MCIS_IS_VNIC)
+ mcip->mci_protect_flags |= MPT_FLAG_PROMISC_FILTERED;
}
void
diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c
index 57d1996d84..98b770786a 100644
--- a/usr/src/uts/common/io/mac/mac_provider.c
+++ b/usr/src/uts/common/io/mac/mac_provider.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -350,6 +351,9 @@ mac_register(mac_register_t *mregp, mac_handle_t *mhp)
if (i_mac_capab_get((mac_handle_t)mip, MAC_CAPAB_AGGR, NULL))
mip->mi_state_flags |= MIS_IS_AGGR;
+ if (i_mac_capab_get((mac_handle_t)mip, MAC_CAPAB_OVERLAY, NULL))
+ mip->mi_state_flags |= MIS_IS_OVERLAY;
+
mac_addr_factory_init(mip);
/*
@@ -670,7 +674,7 @@ mac_trill_snoop(mac_handle_t mh, mblk_t *mp)
mac_impl_t *mip = (mac_impl_t *)mh;
if (mip->mi_promisc_list != NULL)
- mac_promisc_dispatch(mip, mp, NULL);
+ mac_promisc_dispatch(mip, mp, NULL, B_FALSE);
}
/*
@@ -691,7 +695,7 @@ mac_rx_common(mac_handle_t mh, mac_resource_handle_t mrh, mblk_t *mp_chain)
* this MAC, pass them a copy if appropriate.
*/
if (mip->mi_promisc_list != NULL)
- mac_promisc_dispatch(mip, mp_chain, NULL);
+ mac_promisc_dispatch(mip, mp_chain, NULL, B_FALSE);
if (mr != NULL) {
/*
diff --git a/usr/src/uts/common/io/mac/mac_sched.c b/usr/src/uts/common/io/mac/mac_sched.c
index 148f739d52..746ee45664 100644
--- a/usr/src/uts/common/io/mac/mac_sched.c
+++ b/usr/src/uts/common/io/mac/mac_sched.c
@@ -1367,11 +1367,11 @@ int mac_srs_worker_wakeup_ticks = 0;
* can occur in situ (in the interrupt thread) or if it should be left to a
* worker thread. Note that the constant used to make this determination is
* not entirely made-up, and is a result of some emprical validation. That
- * said, the constant is left as a static variable to allow it to be
+ * said, the constant is left as a global variable to allow it to be
* dynamically tuned in the field if and as needed.
*/
-static uintptr_t mac_rx_srs_stack_needed = 10240;
-static uint_t mac_rx_srs_stack_toodeep;
+uintptr_t mac_rx_srs_stack_needed = 14336;
+uint_t mac_rx_srs_stack_toodeep;
#ifndef STACK_GROWTH_DOWN
#error Downward stack growth assumed.
@@ -1379,7 +1379,7 @@ static uint_t mac_rx_srs_stack_toodeep;
#define MAC_RX_SRS_TOODEEP() (STACK_BIAS + (uintptr_t)getfp() - \
(uintptr_t)curthread->t_stkbase < mac_rx_srs_stack_needed && \
- ++mac_rx_srs_stack_toodeep)
+ (++mac_rx_srs_stack_toodeep || (mac_rx_srs_stack_toodeep = 1)))
/*
@@ -2310,7 +2310,7 @@ check_again:
if (smcip->mci_mip->mi_promisc_list != NULL) {
mutex_exit(lock);
mac_promisc_dispatch(smcip->mci_mip,
- head, NULL);
+ head, NULL, B_FALSE);
mutex_enter(lock);
}
}
@@ -4450,8 +4450,10 @@ mac_tx_send(mac_client_handle_t mch, mac_ring_handle_t ring, mblk_t *mp_chain,
* check is done inside the MAC_TX()
* macro.
*/
- if (mip->mi_promisc_list != NULL)
- mac_promisc_dispatch(mip, mp, src_mcip);
+ if (mip->mi_promisc_list != NULL) {
+ mac_promisc_dispatch(mip, mp, src_mcip,
+ B_TRUE);
+ }
do_switch = ((src_mcip->mci_state_flags &
dst_mcip->mci_state_flags &
diff --git a/usr/src/uts/common/io/mac/mac_stat.c b/usr/src/uts/common/io/mac/mac_stat.c
index 31972f94d8..c1a5c9c069 100644
--- a/usr/src/uts/common/io/mac/mac_stat.c
+++ b/usr/src/uts/common/io/mac/mac_stat.c
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2013 Joyent, Inc. All rights reserved.
*/
/*
@@ -390,8 +391,8 @@ i_mac_stat_create(void *handle, const char *modname, const char *statname,
kstat_t *ksp;
kstat_named_t *knp;
- ksp = kstat_create(modname, 0, statname, "net",
- KSTAT_TYPE_NAMED, count, 0);
+ ksp = kstat_create_zone(modname, 0, statname, "net",
+ KSTAT_TYPE_NAMED, count, 0, getzoneid());
if (ksp == NULL)
return (NULL);
@@ -948,9 +949,9 @@ mac_driver_stat_create(mac_impl_t *mip)
major_t major = getmajor(mip->mi_phy_dev);
count = MAC_MOD_NKSTAT + MAC_NKSTAT + mip->mi_type->mt_statcount;
- ksp = kstat_create((const char *)ddi_major_to_name(major),
+ ksp = kstat_create_zone((const char *)ddi_major_to_name(major),
getminor(mip->mi_phy_dev) - 1, MAC_KSTAT_NAME,
- MAC_KSTAT_CLASS, KSTAT_TYPE_NAMED, count, 0);
+ MAC_KSTAT_CLASS, KSTAT_TYPE_NAMED, count, 0, getzoneid());
if (ksp == NULL)
return;
diff --git a/usr/src/uts/common/io/mem.c b/usr/src/uts/common/io/mem.c
index cdbeb0d422..8955b3d935 100644
--- a/usr/src/uts/common/io/mem.c
+++ b/usr/src/uts/common/io/mem.c
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/*
@@ -221,10 +221,19 @@ mmopen(dev_t *devp, int flag, int typ, struct cred *cred)
switch (getminor(*devp)) {
case M_NULL:
case M_ZERO:
+ /* standard devices */
+ break;
+
case M_MEM:
case M_KMEM:
case M_ALLKMEM:
- /* standard devices */
+ /*
+ * These devices should never be visible in a zone, but if they
+ * somehow do get created we refuse to allow the zone to use
+ * them.
+ */
+ if (crgetzoneid(cred) != GLOBAL_ZONEID)
+ return (EACCES);
break;
default:
diff --git a/usr/src/uts/common/io/mr_sas/mr_sas.conf b/usr/src/uts/common/io/mr_sas/mr_sas.conf
index cfda434e23..6c585c6a42 100644
--- a/usr/src/uts/common/io/mr_sas/mr_sas.conf
+++ b/usr/src/uts/common/io/mr_sas/mr_sas.conf
@@ -13,3 +13,11 @@
# Fast-Path specific flag. Default is "yes".
# mrsas-enable-fp="yes";
+flow_control="dmult" queue="qsort" tape="sctp";
+
+# MSI specific flag. To enable MSI modify the flag value to "yes"
+mrsas-enable-msi="yes";
+
+# Fast-Path specific flag. To enable Fast-Path modify the flag value to "yes"
+mrsas-enable-fp="yes";
+
diff --git a/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..187088ff34
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014, Thales UK Limited
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..cde8b65b37
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+NFAST CRYPTO ACCELERATOR DRIVER
diff --git a/usr/src/uts/common/io/nfp/autoversion.h b/usr/src/uts/common/io/nfp/autoversion.h
new file mode 100644
index 0000000000..b9021942b2
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/autoversion.h
@@ -0,0 +1,21 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/* AUTOGENERATED - DO NOT EDIT */
+#ifndef AUTOVERSION_H
+#define AUTOVERSION_H
+
+#define VERSION_RELEASEMAJOR 2
+#define VERSION_RELEASEMINOR 26
+#define VERSION_RELEASEPATCH 40
+#define VERSION_NO "2.26.40cam999"
+#define VERSION_COMPNAME "nfdrv"
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/drvlist.c b/usr/src/uts/common/io/nfp/drvlist.c
new file mode 100644
index 0000000000..a04b1fd5b0
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/drvlist.c
@@ -0,0 +1,19 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_cmd.h"
+
+const nfpcmd_dev *nfp_drvlist[] = {
+ &i21285_cmddev,
+ &i21555_cmddev,
+ NULL
+};
+
diff --git a/usr/src/uts/common/io/nfp/hostif.c b/usr/src/uts/common/io/nfp/hostif.c
new file mode 100644
index 0000000000..684be703ea
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/hostif.c
@@ -0,0 +1,1192 @@
+/*
+
+hostif.c: nFast PCI driver for Solaris 2.5, 2.6, 2.7 and 2.8
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+06/05/1998 jsh Original solaris 2.6
+21/05/1999 jsh added support for solaris 2.5
+10/06/1999 jsh added support for solaris 2.7 (32 and 64 bit)
+??/??/2001 jsh added support for solaris 2.8 (32 and 64 bit)
+16/10/2001 jsh moved from nfast to new structure in nfdrv
+12/02/2002 jsh added high level interrupt support
+
+*/
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/map.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/open.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+
+#include "nfp_common.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "nfp_cmd.h"
+
+#include "nfp.h"
+
+/* mapped memory attributes, no-swap endianess (done in higher level) */
+static struct ddi_device_acc_attr nosw_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+/* dma attributes */
+static ddi_dma_attr_t dma_attrs = {
+ DMA_ATTR_V0, /* version number */
+ (uint64_t)0x0, /* low address */
+ (uint64_t)0xffffffff, /* high address */
+ (uint64_t)0xffffff, /* DMA counter max */
+ (uint64_t)0x1, /* alignment */
+ 0x0c, /* burst sizes */
+ 0x1, /* minimum transfer size */
+ (uint64_t)0x3ffffff, /* maximum transfer size */
+ (uint64_t)0x7fff, /* maximum segment size */
+ 1, /* no scatter/gather lists */
+ 1, /* granularity */
+ 0 /* DMA flags */
+};
+
+/*
+ * Debug message control
+ * Debug Levels:
+ * 0 = no messages
+ * 1 = Errors
+ * 2 = Subroutine calls & control flow
+ * 3 = I/O Data (verbose!)
+ * Can be set with adb or in the /etc/system file with
+ * "set nfp:nfp_debug=<value>"
+ */
+
+int nfp_debug= 1;
+
+static void *state_head; /* opaque handle top of state structs */
+
+static int nfp_open(dev_t *dev, int openflags, int otyp, cred_t *credp);
+static int nfp_close(dev_t dev, int openflags, int otyp, cred_t *credp);
+static int nfp_release_dev( dev_info_t *dip );
+
+static int nfp_read(dev_t dev, struct uio *uiop, cred_t *credp);
+static int nfp_write(dev_t dev, struct uio *uiop, cred_t *credp);
+static int nfp_strategy(struct buf *bp);
+
+static int nfp_ioctl(dev_t dev, int cmd, ioctlptr_t arg, int mode, cred_t *credp, int *rvalp);
+static int nfp_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp);
+
+static void nfp_wrtimeout (void *pdev);
+static void nfp_rdtimeout (void *pdev);
+
+static int nfp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
+static int nfp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int nfp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static void nfp_read_complete_final(nfp_dev *pdev, int ok);
+static void nfp_write_complete_final(nfp_dev *pdev, int ok);
+
+/* nfp file ops --------------------------------------------------- */
+
+static struct cb_ops nfp_cb_ops = {
+ nfp_open,
+ nfp_close,
+ nodev, /* no nfp_strategy */
+ nodev, /* no print routine */
+ nodev, /* no dump routine */
+ nfp_read,
+ nfp_write,
+ nfp_ioctl,
+ nodev, /* no devmap routine */
+ nodev, /* no mmap routine */
+ nodev, /* no segmap routine */
+ nfp_chpoll,
+ ddi_prop_op,
+ 0, /* not a STREAMS driver, no cb_str routine */
+ D_NEW | D_MP | EXTRA_CB_FLAGS, /* must be safe for multi-thread/multi-processor */
+ CB_REV,
+ nodev, /* aread */
+ nodev /* awrite */
+};
+
+static struct dev_ops nfp_ops = {
+ DEVO_REV, /* DEVO_REV indicated by manual */
+ 0, /* device reference count */
+ nfp_getinfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ nfp_attach,
+ nfp_detach,
+ nodev, /* device reset routine */
+ &nfp_cb_ops,
+ (struct bus_ops *)0, /* bus operations */
+};
+
+extern struct mod_ops mod_driverops;
+static struct modldrv modldrv = {
+ &mod_driverops,
+ NFP_DRVNAME,
+ &nfp_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, /* MODREV_1 indicated by manual */
+ (void *)&modldrv,
+ NULL, /* termination of list of linkage structures */
+};
+
+/* interface resource allocation */
+
+int nfp_alloc_pci_push( nfp_dev *pdev ) {
+ /* allocate resources needed for PCI Push,
+ * if not already allocated.
+ * return True if successful
+ */
+ nfp_err ret;
+ uint_t cookie_count;
+ size_t real_length;
+
+ if(!pdev->read_buf) {
+ /* allocate read buffer */
+ pdev->read_buf = kmem_zalloc( NFP_READBUF_SIZE, KM_NOSLEEP );
+ }
+ if(!pdev->read_buf) {
+ nfp_log( NFP_DBG1, "nfp_attach: kmem_zalloc read buffer failed");
+ pdev->read_buf = NULL;
+ return 0;
+ }
+
+ if(!pdev->rd_dma_ok) {
+ /* allocate dma handle for read buffer */
+ ret = ddi_dma_alloc_handle( pdev->dip,
+ &dma_attrs,
+ DDI_DMA_DONTWAIT,
+ NULL,
+ &pdev->read_dma_handle );
+ if( ret != DDI_SUCCESS ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: ddi_dma_alloc_handle failed (%d)",
+ ret );
+ return 0;
+ }
+
+ /* Allocate the memory for dma transfers */
+ ret = ddi_dma_mem_alloc(pdev->read_dma_handle, NFP_READBUF_SIZE, &nosw_attr,
+ DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
+ (caddr_t*)&pdev->read_buf, &real_length, &pdev->acchandle);
+ if (ret != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_alloc_pci_push: ddi_dma_mem_alloc failed (%d)", ret);
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+
+ ret = ddi_dma_addr_bind_handle( pdev->read_dma_handle,
+ NULL, /* kernel address space */
+ (caddr_t)pdev->read_buf, real_length,
+ DDI_DMA_READ | DDI_DMA_CONSISTENT, /* dma flags */
+ DDI_DMA_DONTWAIT, NULL,
+ &pdev->read_dma_cookie, &cookie_count );
+ if( ret != DDI_DMA_MAPPED ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: ddi_dma_addr_bind_handle failed (%d)",
+ ret);
+ ddi_dma_mem_free(&pdev->acchandle);
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+ if( cookie_count > 1 ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: error:"
+ " ddi_dma_addr_bind_handle wants %d transfers",
+ cookie_count);
+ ddi_dma_mem_free(&pdev->acchandle);
+ (void) ddi_dma_unbind_handle( pdev->read_dma_handle );
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+ pdev->rd_dma_ok = 1;
+ }
+ return pdev->rd_dma_ok;
+}
+
+void nfp_free_pci_push( nfp_dev *pdev ) {
+ /* free resources allocated to PCI Push */
+ if( pdev->rd_dma_ok ) {
+ (void) ddi_dma_sync(pdev->read_dma_handle,0,0,DDI_DMA_SYNC_FORKERNEL);
+ ddi_dma_mem_free(&pdev->acchandle);
+ (void) ddi_dma_unbind_handle( pdev->read_dma_handle );
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ pdev->rd_dma_ok = 0;
+ }
+ if( pdev->read_buf ) {
+ kmem_free( pdev->read_buf, NFP_READBUF_SIZE );
+ pdev->read_buf = NULL;
+ }
+}
+
+/* include definition of nfp_set_ifvers() */
+#define nfp_ifvers NFDEV_IF_PCI_PUSH
+#include "nfp_ifvers.c"
+#undef nfp_ifvers
+
+/*--------------------*/
+/* nfp_isr */
+/*--------------------*/
+
+static u_int nfp_isr( char *pdev_in ) {
+ /* LINTED: alignment */
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+ nfp_err ne;
+ int handled;
+
+ nfp_log( NFP_DBG3, "nfp_isr: entered");
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_isr: cannot find dev");
+ return DDI_INTR_UNCLAIMED;
+ }
+
+ /* The isr needs to be mutex'ed - an SMP can call us while we're still
+ * running!
+ */
+ mutex_enter(&pdev->low_mutex);
+ ne= pdev->cmddev->isr( pdev->common.cmdctx, &handled );
+ mutex_exit(&pdev->low_mutex);
+
+ if( !ne && handled )
+ return DDI_INTR_CLAIMED;
+ if (ne)
+ nfp_log( NFP_DBG1, "nfp_isr: failed");
+ else
+ nfp_log( NFP_DBG3, "nfp_isr: unclaimed");
+ return DDI_INTR_UNCLAIMED;
+}
+
+static u_int nfp_soft_isr( char *pdev_in ) {
+ /* LINTED: alignment */
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+ int rd, wr;
+
+ nfp_log( NFP_DBG3, "nfp_soft_isr: entered");
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_soft_isr: cannot find dev");
+ return DDI_INTR_UNCLAIMED;
+ }
+ rd= wr= 0;
+
+ mutex_enter(&pdev->high_mutex);
+ if(pdev->high_read) {
+ pdev->high_read= 0;
+ mutex_exit(&pdev->high_mutex);
+ rd= 1;
+ }
+ if(pdev->high_write) {
+ pdev->high_write= 0;
+ wr= 1;
+ }
+ mutex_exit(&pdev->high_mutex);
+
+ if(rd) {
+ nfp_log( NFP_DBG3, "nfp_soft_isr: read done");
+ nfp_read_complete_final(pdev, pdev->rd_ok);
+ }
+ if(wr) {
+ nfp_log( NFP_DBG3, "nfp_soft_isr: write done");
+ nfp_write_complete_final(pdev, pdev->wr_ok);
+ }
+ if( rd || wr )
+ return DDI_INTR_CLAIMED;
+
+ nfp_log( NFP_DBG2, "nfp_isr: unclaimed");
+ return DDI_INTR_UNCLAIMED;
+}
+
+
+/*-------------------------*/
+/* nfp_read */
+/*-------------------------*/
+
+void nfp_read_complete(nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_read_complete: entering");
+
+ if(pdev->high_intr) {
+ nfp_log(NFP_DBG2, "nfp_read_complete: high_intr");
+ mutex_enter(&pdev->high_mutex);
+ nfp_log(NFP_DBG3, "nfp_read_complete: high_mutex entered");
+ if(pdev->high_read)
+ nfp_log(NFP_DBG1, "nfp_read_complete: high_read allread set!");
+ pdev->high_read= 1;
+ pdev->rd_ok= ok;
+ nfp_log(NFP_DBG3, "nfp_read_complete: exiting high_mutex");
+ mutex_exit(&pdev->high_mutex);
+ ddi_trigger_softintr(pdev->soft_int_id);
+ } else
+ nfp_read_complete_final( pdev, ok );
+ nfp_log( NFP_DBG2,"nfp_read_complete: exiting");
+}
+
+static void nfp_read_complete_final(nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: entering");
+ if(pdev->rdtimeout)
+ (void) untimeout(pdev->rdtimeout);
+ if(!pdev->rd_outstanding) {
+ nfp_log( NFP_DBG1,"nfp_read_complete_final: !pdev->rd_outstanding");
+ }
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: pdev->rd_outstanding=0, ok %d", ok);
+ mutex_enter(&pdev->isr_mutex);
+ pdev->rd_outstanding= 0;
+ pdev->rd_ready= 1;
+ pdev->rd_ok= ok;
+ cv_broadcast(&pdev->rd_cv);
+ mutex_exit(&pdev->isr_mutex);
+ pollwakeup (&pdev->pollhead, POLLRDNORM);
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: exiting");
+}
+
+static void nfp_rdtimeout( void *pdev_in )
+{
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+
+ nfp_log( NFP_DBG1, "nfp_rdtimeout: read timed out");
+
+ if (!pdev) {
+ nfp_log( NFP_DBG1, "nfp_rdtimeout: NULL pdev." );
+ return;
+ }
+ pdev->rdtimeout= 0;
+ nfp_read_complete_final(pdev, 0);
+}
+
+/* ARGSUSED */
+static int nfp_read(dev_t dev, struct uio *uiop, cred_t *credp) {
+ int ret;
+ nfp_log( NFP_DBG2, "nfp_read: entered" );
+ if (ddi_get_soft_state(state_head, getminor(dev)) != NULL) {
+ nfp_log( NFP_DBG1, "nfp_read: unable to get nfp_dev");
+ return (ENODEV);
+ }
+ nfp_log( NFP_DBG2, "nfp_read: about to physio." );
+ ret = physio(nfp_strategy, (struct buf *)0, dev, B_READ, minphys, uiop );
+ if(ret)
+ nfp_log( NFP_DBG1, "nfp_read: physio returned %x.", ret );
+ return ret;
+}
+
+/*-------------------------*/
+/* nfp_write */
+/*-------------------------*/
+
+void nfp_write_complete( nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_write_complete: entering");
+
+ if(pdev->high_intr) {
+ mutex_enter(&pdev->high_mutex);
+ if(pdev->high_write)
+ nfp_log(NFP_DBG1, "nfp_write_complete: high_write allread set!");
+ pdev->high_write= 1;
+ pdev->wr_ok= ok;
+ mutex_exit(&pdev->high_mutex);
+ ddi_trigger_softintr(pdev->soft_int_id);
+ } else
+ nfp_write_complete_final( pdev, ok );
+ nfp_log( NFP_DBG2,"nfp_write_complete: exiting");
+}
+
+static void nfp_write_complete_final( nfp_dev *pdev, int ok) {
+ struct buf *local_wr_bp;
+ nfp_log( NFP_DBG2,"nfp_write_complete_final: entering");
+ if(pdev->wrtimeout)
+ (void) untimeout(pdev->wrtimeout);
+
+ if (!pdev->wr_bp) {
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: write: wr_bp == NULL." );
+ return;
+ }
+
+ bp_mapout(pdev->wr_bp);
+ pdev->wr_bp->b_resid = ok ? 0 : pdev->wr_bp->b_bcount;
+ /* Make sure we set wr_ready before calling biodone to avoid a race */
+ pdev->wr_ready = 1;
+ bioerror(pdev->wr_bp, ok ? 0 : ENXIO);
+ local_wr_bp = pdev->wr_bp;
+ pdev->wr_bp = 0;
+ biodone(local_wr_bp);
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: isr_mutex extited");
+ pollwakeup (&pdev->pollhead, POLLWRNORM);
+
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: leaving");
+}
+
+static void nfp_wrtimeout( void *pdev_in )
+{
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+
+ nfp_log( NFP_DBG1, "nfp_wrtimeout: write timed out");
+
+ if (!pdev) {
+ nfp_log( NFP_DBG1, "nfp_wrtimeout: NULL pdev." );
+ return;
+ }
+ pdev->wrtimeout= 0;
+ nfp_write_complete_final(pdev, 0);
+}
+
+/* ARGSUSED */
+static int nfp_write(dev_t dev, struct uio *uiop, cred_t *credp) {
+ int ret;
+ nfp_log( NFP_DBG2, "nfp_write: entered." );
+ if (ddi_get_soft_state(state_head, getminor(dev)) == NULL) {
+ nfp_log( NFP_DBG1, "nfp_chread: unable to get nfp_dev.");
+ return (ENODEV);
+ }
+ nfp_log( NFP_DBG2, "nfp_write: about to physio." );
+ ret = physio(nfp_strategy, (struct buf *)0, dev, B_WRITE, minphys, uiop );
+ if(ret)
+ nfp_log( NFP_DBG1, "nfp_write: physio returned %x.", ret );
+ return ret;
+}
+
+/*-------------------------*/
+/* nfp_strategy */
+/*-------------------------*/
+
+#define NFP_STRAT_ERR(thebp,err,txt) \
+ nfp_log( NFP_DBG1, "nfp_strategy: " txt ".\n"); \
+ (thebp)->b_resid = (thebp)->b_bcount; \
+ bioerror ((thebp), err); \
+ biodone ((thebp));
+
+static int nfp_strategy(struct buf *bp) {
+ register struct nfp_dev *pdev;
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "nfp_strategy: entered." );
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(bp->b_edev)))) {
+ NFP_STRAT_ERR (bp, ENXIO, "unable to get nfp_dev");
+ return (0);
+ }
+
+ if (bp->b_flags & B_READ) {
+ int count;
+ /* read */
+ if (!pdev->rd_ready) {
+ NFP_STRAT_ERR (bp,ENXIO,"read called when not ready");
+ return (0);
+ }
+ pdev->rd_ready=0;
+ pdev->rd_pending = 0;
+ if( !pdev->rd_ok) {
+ NFP_STRAT_ERR (bp,ENXIO,"read failed");
+ return (0);
+ }
+ /* copy data from module */
+ if(pdev->ifvers >= NFDEV_IF_PCI_PUSH) {
+ nfp_log( NFP_DBG3, "nfp_strategy: copying kernel read buffer");
+ if( ddi_dma_sync(pdev->read_dma_handle,0,0,DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS )
+ {
+ NFP_STRAT_ERR(bp,ENXIO,"ddi_dma_sync(read_dma_handle) failed");
+ return (0);
+ }
+ /* LINTED: alignment */
+ count= *(unsigned int *)(pdev->read_buf+4);
+ count= FROM_LE32_MEM(&count);
+ nfp_log( NFP_DBG3, "nfp_strategy: read count %d", count);
+ if(count<0 || count>bp->b_bcount) {
+ NFP_STRAT_ERR(bp,ENXIO,"bad read byte count from device");
+ nfp_log( NFP_DBG1, "nfp_strategy: bad read byte count (%d) from device", count);
+ return (0);
+ }
+ bp_mapin (bp);
+ bcopy( pdev->read_buf + 8, bp->b_un.b_addr, count );
+ bp_mapout (bp);
+ } else {
+ bp_mapin (bp);
+ ne= pdev->cmddev->read_block( bp->b_un.b_addr, bp->b_bcount, pdev->common.cmdctx, &count );
+ bp_mapout (bp);
+ if( ne != NFP_SUCCESS) {
+ NFP_STRAT_ERR (bp,nfp_oserr(ne),"read_block failed");
+ return (0);
+ }
+ }
+ bioerror(bp, 0);
+ bp->b_resid = 0;
+ biodone (bp);
+ } else {
+ /* write */
+ if (!pdev->wr_ready) {
+ NFP_STRAT_ERR (bp,ENXIO,"write called when not ready");
+ return (0);
+ }
+ if (pdev->wr_bp) {
+ NFP_STRAT_ERR (bp,ENXIO,"wr_bp != NULL");
+ return (0);
+ }
+ pdev->wrtimeout= timeout(nfp_wrtimeout, (caddr_t)pdev, NFP_TIMEOUT_SEC * drv_usectohz(1000000));
+ pdev->wr_bp = bp;
+ pdev->wr_ready = 0;
+ bp_mapin (bp);
+ ne= pdev->cmddev->write_block( bp->b_un.b_addr, bp->b_bcount, pdev->common.cmdctx);
+ if( ne != NFP_SUCCESS ) {
+ bp_mapout (bp);
+ (void) untimeout(pdev->wrtimeout);
+ pdev->wr_bp = 0;
+ pdev->wr_ready = 1;
+ NFP_STRAT_ERR (bp,nfp_oserr(ne),"write failed");
+ return (0);
+ }
+ }
+ nfp_log( NFP_DBG2, "nfp_strategy: leaving");
+
+ return (0);
+}
+
+
+/*--------------------*/
+/* poll / select */
+/*--------------------*/
+
+static int nfp_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp) {
+ nfp_dev *pdev;
+ short revents;
+
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(dev)))) {
+ nfp_log( NFP_DBG1, "nfp_chpoll: unable to get nfp_dev");
+ *reventsp=0;
+ return (0);
+ }
+ nfp_log( NFP_DBG2, "nfp_chpoll: entered %x", events);
+
+ revents=0;
+ if (events&POLLWRNORM) {
+ if (pdev->wr_ready) {
+ nfp_log( NFP_DBG2, "nfp_chpoll: write ready");
+ revents|=POLLWRNORM;
+ }
+ }
+
+ if (events&POLLRDNORM) {
+ if (pdev->rd_ready) {
+ nfp_log( NFP_DBG2, "nfp_chpoll: read ready");
+ revents|=POLLRDNORM;
+ }
+ }
+
+ if (!revents && !anyyet) {
+ *phpp=&pdev->pollhead;
+ }
+ *reventsp=revents;
+
+ nfp_log( NFP_DBG2, "nfp_chpoll: leaving");
+ return (0);
+}
+
+
+/*--------------------*/
+/* ioctl */
+/*--------------------*/
+
+/* ARGSUSED */
+static int nfp_ioctl(dev_t dev, int cmd, ioctlptr_t arg, int mode, cred_t *credp, int *rvalp) {
+ register struct nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: entered." );
+
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(dev)))) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: unable to get nfp dev.");
+ return (ENXIO);
+ }
+
+ switch (cmd) {
+ case NFDEV_IOCTL_ENQUIRY:
+ {
+ long *outp;
+ int outlen;
+ nfdev_enquiry_str enq_data;
+
+ enq_data.busno = (unsigned int)-1;
+ enq_data.slotno = (unsigned char)-1;
+
+ /* get our bus and slot num */
+ if (ddi_getlongprop (DDI_DEV_T_NONE,
+ pdev->dip, 0, "reg",
+ (caddr_t)&outp, &outlen) != DDI_PROP_NOT_FOUND) {
+ nfp_log( NFP_DBG2, "ddi_getlongprop('reg') ok." );
+ if( outlen > 0 ) {
+ enq_data.busno = ((*outp)>>16) & 0xff;
+ enq_data.slotno = ((*outp)>>11) & 0x1f;
+ nfp_log( NFP_DBG2, "busno %d, slotno %d.",
+ enq_data.busno, enq_data.slotno );
+ }
+ } else
+ nfp_log( NFP_DBG1, "ddi_getlongprop('reg') failed." );
+
+ if( ddi_copyout( (char *)&enq_data, (void *)arg, sizeof(enq_data), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyout() failed." );
+ return EFAULT;
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_ENSUREREADING:
+ {
+ unsigned int addr, len;
+ nfp_err ret;
+ if( ddi_copyin( (void *)arg, (char *)&len, sizeof(unsigned int), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyin() failed." );
+ return (EFAULT);
+ }
+ /* signal a read to the module */
+ nfp_log( NFP_DBG2, "nfp_ioctl: signalling read request to module, len = %x.", len );
+ if (len>8192) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: len >8192 = %x.", len );
+ return EINVAL;
+ }
+ if (pdev->rd_outstanding==1) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: not about to call read with read outstanding.");
+ return EIO;
+ }
+
+ addr= 0;
+ if(pdev->ifvers >= NFDEV_IF_PCI_PUSH) {
+ if( len > NFP_READBUF_SIZE ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: len > NFP_READBUF_SIZE = %x.", len );
+ return EINVAL;
+ }
+ addr= pdev->read_dma_cookie.dmac_address;
+ }
+
+ pdev->rd_outstanding = 1;
+ nfp_log( NFP_DBG2,"nfp_ioctl: pdev->rd_outstanding=1");
+
+ /* setup timeout timer */
+ pdev->rdtimeout= timeout(nfp_rdtimeout, (caddr_t)pdev, NFP_TIMEOUT_SEC * drv_usectohz(1000000));
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: read request");
+ ret = pdev->cmddev->ensure_reading(addr, len, pdev->common.cmdctx);
+ if ( ret != NFP_SUCCESS ) {
+ (void) untimeout(pdev->rdtimeout);
+ pdev->rdtimeout = 0;
+ pdev->rd_outstanding = 0;
+ nfp_log( NFP_DBG1, "nfp_ioctl : cmddev->ensure_reading failed ");
+ return nfp_oserr( ret );
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_PCI_IFVERS:
+ {
+ int vers;
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: NFDEV_IOCTL_PCI_IFVERS");
+
+ if( ddi_copyin( (void *)arg, (char *)&vers, sizeof(vers), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyin() failed." );
+ return (EFAULT);
+ }
+
+ if( pdev->rd_outstanding ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: can't set ifvers %d as read outstanding", vers);
+ return EIO;
+ }
+
+ nfp_set_ifvers(pdev, vers);
+ if( pdev->ifvers != vers ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: can't set ifvers %d", vers);
+ return EIO;
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_STATS:
+ {
+ if( ddi_copyout( (char *)&(pdev->common.stats),
+ (void *)arg,
+ sizeof(nfdev_stats_str),
+ mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyout() failed." );
+ return EFAULT;
+ }
+ }
+ break;
+
+ default:
+ nfp_log( NFP_DBG1, "nfp_ioctl: unknown ioctl." );
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/*-------------------------*/
+/* nfp_open */
+/*-------------------------*/
+
+/* ARGSUSED */
+int nfp_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
+{
+ nfp_err ret;
+ register struct nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "entered nfp_open." );
+
+ pdev = (nfp_dev *)ddi_get_soft_state(state_head, getminor(*dev));
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_open: unable to get nfp dev.");
+ return (ENODEV);
+ }
+
+ if( otyp != OTYP_CHR ) {
+ nfp_log( NFP_DBG1, "nfp_open: not opened as character device");
+ return (EINVAL);
+ }
+
+ mutex_enter(&pdev->busy_mutex);
+
+ if (pdev->busy) {
+ mutex_exit(&pdev->busy_mutex);
+ nfp_log( NFP_DBG1, "nfp_open: device busy");
+ return EBUSY;
+ }
+ pdev->busy= 1;
+ mutex_exit(&pdev->busy_mutex);
+
+ /* use oldest possible interface until told otherwise */
+ pdev->ifvers= NFDEV_IF_STANDARD;
+ nfp_log( NFP_DBG3, "nfp_open: setting ifvers %d", pdev->ifvers);
+ pdev->rd_ready= 0; /* drop any old data */
+
+ ret = pdev->cmddev->open(pdev->common.cmdctx);
+ if( ret != NFP_SUCCESS ) {
+ nfp_log( NFP_DBG1, "nfp_open : cmddev->open failed ");
+ return nfp_oserr( ret );
+ }
+
+ nfp_log( NFP_DBG2, "nfp_open: done");
+
+ return 0;
+}
+
+/*--------------------*/
+/* nfp_close */
+/*--------------------*/
+
+/* ARGSUSED */
+static int nfp_close(dev_t dev, int openflags, int otyp, cred_t *credp) {
+ nfp_dev *pdev;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_close: entered");
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, getminor(dev));
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_close: cannot find dev.");
+ return ENODEV;
+ }
+
+ mutex_enter(&pdev->isr_mutex);
+ if(pdev->rd_outstanding) {
+ int lbolt, err;
+ nfp_get_lbolt(&lbolt, err);
+ if(!err)
+ (void) cv_timedwait(&pdev->rd_cv, &pdev->isr_mutex, lbolt + (NFP_TIMEOUT_SEC * drv_usectohz(1000000)) );
+ }
+ mutex_exit(&pdev->isr_mutex);
+ ret = pdev->cmddev->close(pdev->common.cmdctx);
+ if (ret != NFP_SUCCESS ) {
+ nfp_log( NFP_DBG1, " nfp_close : cmddev->close failed");
+ return nfp_oserr( ret );
+ }
+
+ mutex_enter(&pdev->busy_mutex);
+ pdev->busy= 0;
+ mutex_exit(&pdev->busy_mutex);
+
+ return 0;
+}
+
+/****************************************************************************
+
+ nfp driver config
+
+ ****************************************************************************/
+
+/*-------------------------*/
+/* nfp_getinfo */
+/*-------------------------*/
+
+/* ARGSUSED */
+static int nfp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) {
+ int error;
+ nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "nfp_getinfo: entered" );
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, getminor((dev_t)arg));
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_close: cannot find dev.");
+ return ENODEV;
+ }
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (pdev == NULL) {
+ *result = NULL;
+ error = DDI_FAILURE;
+ } else {
+ /*
+ * don't need to use a MUTEX even though we are
+ * accessing our instance structure; dev->dip
+ * never changes.
+ */
+ *result = pdev->dip;
+ error = DDI_SUCCESS;
+ }
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)getminor((dev_t)arg);
+ error = DDI_SUCCESS;
+ break;
+ default:
+ *result = NULL;
+ error = DDI_FAILURE;
+ }
+
+ nfp_log( NFP_DBG2, "nfp_getinfo: leaving." );
+ return (error);
+}
+
+/*-------------------------*/
+/* nfp_release */
+/*-------------------------*/
+
+static int nfp_release_dev( dev_info_t *dip ) {
+ nfp_dev *pdev;
+ int instance, i;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_release_dev: entering" );
+
+ instance = ddi_get_instance(dip);
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, instance);
+ if (pdev) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing device" );
+
+ nfp_free_pci_push(pdev);
+
+ if( pdev->cmddev ) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: destroying cmd dev" );
+ ret = pdev->cmddev->destroy(pdev->common.cmdctx);
+ if (ret != NFP_SUCCESS) {
+ nfp_log( NFP_DBG1, " nfp_release_dev : cmddev->destroy failed ");
+ return nfp_oserr( ret );
+ }
+ }
+
+ if(pdev->high_iblock_cookie) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing high and soft irq" );
+ ddi_remove_softintr(pdev->soft_int_id);
+ ddi_remove_intr(pdev->dip, 0, pdev->high_iblock_cookie);
+ mutex_destroy( &pdev->busy_mutex );
+ cv_destroy( &pdev->rd_cv );
+ mutex_destroy( &pdev->isr_mutex );
+ mutex_destroy( &pdev->high_mutex );
+ } else if(pdev->iblock_cookie) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing irq" );
+ ddi_remove_intr(pdev->dip, 0, pdev->iblock_cookie);
+ mutex_destroy( &pdev->busy_mutex );
+ cv_destroy( &pdev->rd_cv );
+ mutex_destroy( &pdev->isr_mutex );
+ }
+ if(pdev->low_iblock_cookie) {
+ ddi_remove_intr(pdev->dip, 0, pdev->low_iblock_cookie);
+ mutex_destroy( &pdev->low_mutex);
+ }
+
+ for(i=0;i<6;i++) {
+ if( pdev->common.extra[i] ) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: unmapping BAR %d", i );
+ ddi_regs_map_free ((ddi_acc_handle_t *)&pdev->common.extra[i]);
+ }
+ }
+
+ ddi_remove_minor_node(dip, NULL);
+
+ if (pdev->conf_handle)
+ pci_config_teardown( &pdev->conf_handle );
+
+ ddi_soft_state_free(state_head, instance);
+ }
+ nfp_log( NFP_DBG2, "nfp_release: finished" );
+
+ return DDI_SUCCESS;
+}
+
+
+/*-------------------------*/
+/* nfp_attach */
+/*-------------------------*/
+
+static int nfp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) {
+ int instance;
+ nfp_dev *pdev = NULL;
+ int intres;
+ uint16_t device, vendor, sub_device, sub_vendor;
+ long *outp;
+ nfpcmd_dev const *cmddev;
+ int index, i;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_attach: entered." );
+
+ if (cmd != DDI_ATTACH) {
+ nfp_log( NFP_DBG1, "nfp_attach: bad command." );
+ goto bailout;
+ }
+
+ instance = ddi_get_instance(dip);
+
+ if (ddi_soft_state_zalloc(state_head, instance) != 0) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_soft_state_zalloc() failed." );
+ goto bailout;
+ }
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, instance);
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_attach: cannot find dev.");
+ return ENODEV;
+ }
+ pdev->dip = dip;
+
+ /* map in pci config registers */
+ if (pci_config_setup(dip, &pdev->conf_handle)) {
+ nfp_log( NFP_DBG1, "nfp_attach: pci_config_setup() failed." );
+ goto bailout;
+ }
+
+ /* find out what we have got */
+ vendor= PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_VENID );
+ device = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_DEVID );
+ sub_vendor = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_SUBVENID );
+ sub_device = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_SUBSYSID );
+
+ index= 0;
+ while( (cmddev = nfp_drvlist[index++]) != NULL ) {
+ if( cmddev->vendorid == vendor &&
+ cmddev->deviceid == device &&
+ cmddev->sub_vendorid == sub_vendor &&
+ cmddev->sub_deviceid == sub_device )
+ break;
+ }
+ if( !cmddev ) {
+ nfp_log( NFP_DBG1, "nfp_attach: unknonw device." );
+ goto bailout;
+ }
+
+ /* map BARs */
+ for( i=0; i<6; i++ ) {
+ if( cmddev->bar_sizes[i] ) {
+ off_t size;
+ if( ddi_dev_regsize(dip, i+1, &size) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_dev_regsize() failed for BAR %d", i );
+ goto bailout;
+ }
+ if( size < (cmddev->bar_sizes[i] & ~NFP_MEMBAR_MASK) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: BAR %d too small %x (%x)", i, size, (cmddev->bar_sizes[i] & ~0xF) );
+ goto bailout;
+ }
+ if (ddi_regs_map_setup(dip, i+1, (caddr_t *)&pdev->common.bar[i],
+ 0, cmddev->bar_sizes[i] & ~NFP_MEMBAR_MASK, &nosw_attr, (ddi_acc_handle_t *)&pdev->common.extra[i] )) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_regs_map_setup() failed for BAR %d", i );
+ goto bailout;
+ }
+ nfp_log( NFP_DBG3, "nfp_attach: BAR[%d] mapped to %x (%x)", i, pdev->common.bar[i], size );
+ }
+ }
+
+ pdev->read_buf = NULL;
+ pdev->rd_dma_ok = 0;
+
+ /* attach to minor node */
+ if (ddi_create_minor_node(dip, "nfp", S_IFCHR, instance, (char *)cmddev->name, 0) == DDI_FAILURE) {
+ ddi_remove_minor_node(dip, NULL);
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_create_minor_node() failed." );
+ goto bailout;
+ }
+
+ pdev->wr_ready = 1;
+ pdev->rd_ready = 0;
+ pdev->rd_pending = 0;
+ pdev->rd_outstanding = 0;
+ pdev->busy=0;
+ pdev->cmddev= cmddev;
+
+ ret = pdev->cmddev->create(&pdev->common);
+ if( ret != NFP_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: failed to create command device");
+ goto bailout;
+ }
+ pdev->common.dev= pdev;
+
+ if (ddi_intr_hilevel(dip, 0) != 0){
+ nfp_log( NFP_DBG2, "nfp_attach: high-level interrupt");
+ if( ddi_get_iblock_cookie(dip, 0, &pdev->high_iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(high) failed." );
+ goto bailout;
+ }
+ if( ddi_get_iblock_cookie(dip, 0, &pdev->low_iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(low) failed." );
+ goto bailout;
+ }
+ mutex_init(&pdev->high_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->high_iblock_cookie);
+ mutex_init(&pdev->low_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->low_iblock_cookie);
+ if (ddi_add_intr(dip, 0, NULL,
+ NULL, nfp_isr,
+ (caddr_t)pdev) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_add_intr(high) failed." );
+ goto bailout;
+ }
+ if( ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HIGH,
+ &pdev->iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(soft) failed." );
+ goto bailout;
+ }
+ mutex_init(&pdev->isr_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->iblock_cookie);
+ if (ddi_add_softintr(dip, DDI_SOFTINT_HIGH, &pdev->soft_int_id,
+ &pdev->iblock_cookie, NULL,
+ nfp_soft_isr, (caddr_t)pdev) != DDI_SUCCESS)
+ goto bailout;
+ pdev->high_intr= 1;
+ } else {
+ nfp_log( NFP_DBG2, "nfp_attach: low-level interrupt");
+
+ if (ddi_get_iblock_cookie (dip, 0, &pdev->iblock_cookie)) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie() failed." );
+ goto bailout;
+ }
+
+ mutex_init(&pdev->isr_mutex, "nfp isr mutex", MUTEX_DRIVER, (void *)pdev->iblock_cookie);
+
+ if (ddi_add_intr(dip, 0, NULL,
+ (ddi_idevice_cookie_t *)NULL, nfp_isr,
+ (caddr_t)pdev) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_add_intr() failed." );
+ goto bailout;
+ }
+ }
+ mutex_init(&pdev->busy_mutex, "nfp busy mutex", MUTEX_DRIVER, NULL );
+ cv_init(&pdev->rd_cv, "nfp read condvar", CV_DRIVER, NULL );
+
+ /* get our bus and slot num */
+ if (ddi_getlongprop (DDI_DEV_T_NONE,
+ pdev->dip, 0, "reg",
+ (caddr_t)&outp, &intres) != DDI_PROP_NOT_FOUND) {
+ nfp_log( NFP_DBG2, "nfp_attach: ddi_getlongprop('reg') ok." );
+ if( intres > 0 ) {
+ nfp_log( NFP_DBG1, "nfp_attach: found PCI nfast bus %x slot %x.",
+ ((*outp)>>16) & 0xff, ((*outp)>>11) & 0x1f );
+ }
+ }
+
+ nfp_log( NFP_DBG2, "nfp_attach: attach succeeded." );
+ return DDI_SUCCESS;
+
+bailout:
+ (void) nfp_release_dev( dip );
+
+ return DDI_FAILURE;
+}
+
+/*-------------------------*/
+/* nfp_detach */
+/*-------------------------*/
+
+/*
+ * When our driver is unloaded, nfp_detach cleans up and frees the resources
+ * we allocated in nfp_attach.
+ */
+static int nfp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) {
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ (void) nfp_release_dev(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*-------------------------*/
+/* _init */
+/*-------------------------*/
+
+int _init(void) {
+ register int error;
+
+ nfp_log( NFP_DBG2, "_init: entered" );
+
+ if ((error = ddi_soft_state_init(&state_head, sizeof (struct nfp_dev), 1)) != 0) {
+ nfp_log( NFP_DBG1, "_init: soft_state_init() failed" );
+ return (error);
+ }
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ nfp_log( NFP_DBG1, "_init: mod_install() failed" );
+ ddi_soft_state_fini(&state_head);
+ }
+
+ nfp_log( NFP_DBG2, "_init: leaving" );
+ return (error);
+}
+
+/*-------------------------*/
+/* _info */
+/*-------------------------*/
+
+int _info(struct modinfo *modinfop) {
+ nfp_log( NFP_DBG2, "_info: entered" );
+
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*-------------------------*/
+/* _fini */
+/*-------------------------*/
+
+int _fini(void) {
+ int status;
+
+ nfp_log( NFP_DBG2, "_fini: entered" );
+
+ if ((status = mod_remove(&modlinkage)) != 0) {
+ nfp_log( NFP_DBG2, "_fini: mod_remove() failed." );
+ return (status);
+ }
+
+ ddi_soft_state_fini(&state_head);
+
+ nfp_log( NFP_DBG2, "_fini: leaving" );
+
+ return (status);
+}
+
diff --git a/usr/src/uts/common/io/nfp/i21285.c b/usr/src/uts/common/io/nfp/i21285.c
new file mode 100644
index 0000000000..f51a09188d
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21285.c
@@ -0,0 +1,310 @@
+/*
+
+i21285.c: nCipher PCI HSM intel/digital 21285 command driver
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+
+history
+
+09/10/2001 jsh Original
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "i21285.h"
+#include "nfp_cmd.h"
+#include "nfpci.h"
+
+/* create ------------------------------------------------------- */
+
+static nfp_err i21285_create( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_create: entered");
+ pdev->cmdctx= pdev; /* set our context to just be a pointer to our nfp_cdev */
+
+ nfp_log( NFP_DBG2, "i21285_create: enable doorbell");
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_create: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, DOORBELL_ENABLE | POSTLIST_ENABLE);
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_INTERRUPT_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* stop ------------------------------------------------------- */
+
+static nfp_err i21285_destroy( void * ctx ) {
+ nfp_cdev *pdev;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_destroy: entered");
+
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21285_destroy: NULL pdev");
+ return NFP_ENODEV;
+ }
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_destroy: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, DOORBELL_DISABLE | POSTLIST_DISABLE );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_INTERRUPT_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* open ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_open( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21285_open: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* close ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_close( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21285_close: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* isr ------------------------------------------------------- */
+
+static nfp_err i21285_isr( void *ctx, int *handled ) {
+ nfp_cdev *pdev;
+ unsigned int doorbell;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG3, "i21285_isr: entered");
+
+ *handled= 0;
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21285_isr: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ doorbell= nfp_inl( pdev, IOBAR, I21285_OFFSET_DOORBELL);
+ doorbell= FROM_LE32_IO(&doorbell) & 0xffff;
+ while( doorbell && doorbell != 0xffff) {
+ *handled= 1;
+ /* service interrupts */
+ if( doorbell & (NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ TO_LE32_IO( &tmp32, NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED);
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log(NFP_DBG2, "i21285_isr: write done interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+
+ nfp_write_complete(pdev->dev, doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+ }
+
+ if( doorbell & (NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED)) {
+ TO_LE32_IO( &tmp32, NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log(NFP_DBG2, "i21285_isr: read ack interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0 );
+ nfp_read_complete( pdev->dev, doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0);
+ }
+
+ if( doorbell & ~(NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED |
+ NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ nfp_log( NFP_DBG1, "i21285_isr: unexpected interrupt %x", doorbell );
+ TO_LE32_IO( &tmp32, 0xffff & doorbell );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+ }
+ doorbell= nfp_inl( pdev, IOBAR, I21285_OFFSET_DOORBELL);
+ doorbell= FROM_LE32_IO(&doorbell) & 0xffff;
+ }
+ return 0;
+}
+
+/* write ------------------------------------------------------- */
+
+static nfp_err i21285_write( const char *block, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ nfp_err ne;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_write: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_write: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ nfp_log(NFP_DBG2, "i21285_write: pdev->bar[ MEMBAR ]= %x\n", cdev->bar[ MEMBAR ]);
+ nfp_log(NFP_DBG2, "i21285_write: pdev->bar[ IOBAR ]= %x\n", cdev->bar[ IOBAR ]);
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_write: null BAR[%d]", MEMBAR );
+ return NFP_ENOMEM;
+ }
+ ne= nfp_copy_from_user_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_DATA, block, len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_from_user_to_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_WR_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM( &tmp32, len );
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21285_write: length not written");
+ return NFP_EIO;
+ }
+
+ TO_LE32_IO( &tmp32, NFAST_INT_HOST_WRITE_REQUEST);
+
+ nfp_outl( cdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log( NFP_DBG2, "i21285_write: done");
+ return NFP_SUCCESS;
+}
+
+/* read ------------------------------------------------------- */
+
+static nfp_err i21285_read( char *block, int len, void *ctx, int *rcount) {
+ nfp_cdev *cdev;
+ nfp_err ne;
+ int count;
+
+ nfp_log( NFP_DBG2, "i21285_read: entered, len %d", len);
+ *rcount= 0;
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_read: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_read: null BAR[%d]", MEMBAR );
+ return NFP_ENOMEM;
+ }
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)&count, 4);
+ if(ne) {
+ nfp_log( NFP_DBG1, "i21285_read: nfp_copy_from_dev failed.");
+ return ne;
+ }
+ count= FROM_LE32_MEM(&count);
+ if(count<0 || count>len) {
+ nfp_log( NFP_DBG1, "i21285_read: bad byte count (%d) from device", count);
+ return NFP_EIO;
+ }
+ ne= nfp_copy_to_user_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_DATA, block, count);
+ if( ne ) {
+ nfp_log( NFP_DBG1, "i21285_read: nfp_copy_to_user_from_dev failed.");
+ return ne;
+ }
+ nfp_log( NFP_DBG2, "i21285_read: done");
+ *rcount= count;
+ return NFP_SUCCESS;
+}
+
+/* chupdate ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_chupdate( char *data, int len, void *ctx ) {
+ nfp_log( NFP_DBG1, "i21285_chupdate: NYI");
+ return NFP_SUCCESS;
+}
+
+/* ensure reading -------------------------------------------------- */
+
+static nfp_err i21285_ensure_reading( unsigned int addr, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ unsigned int tmp32;
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "i21285_ensure_reading: entered");
+
+ if(addr) {
+ nfp_log( NFP_DBG2, "i21285_ensure_reading: bad addr");
+ return -NFP_EINVAL;
+ }
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: null BAR[%d]", MEMBAR );
+ return NFP_ENXIO;
+ }
+ nfp_log( NFP_DBG3, "i21285_ensure_reading: pdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21285_ensure_reading: pdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ TO_LE32_MEM( &hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM( &hdr[1], len);
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_RD_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: nfp_copy_to_dev failed");
+ return ne;
+ }
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: nfp_copy_from_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM( &tmp32, len );
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: len not written");
+ return NFP_EIO;
+ };
+ TO_LE32_IO( &tmp32, NFAST_INT_HOST_READ_REQUEST );
+ nfp_outl( cdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* command device structure ------------------------------------- */
+
+
+const nfpcmd_dev i21285_cmddev = {
+ "nCipher Gen 1 PCI",
+ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285,
+ PCI_VENDOR_ID_NCIPHER, PCI_DEVICE_ID_NFAST_GEN1,
+ { 0, IOSIZE | PCI_BASE_ADDRESS_SPACE_IO, NFPCI_RAM_MINSIZE, 0, 0, 0 },
+ NFP_CMD_FLG_NEED_IOBUF,
+ i21285_create,
+ i21285_destroy,
+ i21285_open,
+ i21285_close,
+ i21285_isr,
+ i21285_write,
+ i21285_read,
+ i21285_chupdate,
+ i21285_ensure_reading,
+ 0, /* no debug */
+};
+
diff --git a/usr/src/uts/common/io/nfp/i21285.h b/usr/src/uts/common/io/nfp/i21285.h
new file mode 100644
index 0000000000..4ea1d853ec
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21285.h
@@ -0,0 +1,43 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef NFP_I21285_H
+#define NFP_I21285_H
+
+#ifndef PCI_VENDOR_ID_DEC
+#define PCI_VENDOR_ID_DEC 0x1011
+#endif
+#ifndef PCI_DEVICE_ID_DEC_21285
+#define PCI_DEVICE_ID_DEC_21285 0x1065
+#endif
+#ifndef PCI_VENDOR_ID_NCIPHER
+#define PCI_VENDOR_ID_NCIPHER 0x0100
+#endif
+
+#ifndef PCI_DEVICE_ID_NFAST_GEN1
+#define PCI_DEVICE_ID_NFAST_GEN1 0x0100
+#endif
+
+#define I21285_OFFSET_DOORBELL 0x60
+#define I21285_OFFSET_INTERRUPT_MASK 0x34
+
+#define DOORBELL_ENABLE 0x0
+#define DOORBELL_DISABLE 0x4
+
+#define POSTLIST_ENABLE 0x0
+#define POSTLIST_DISABLE 0x8
+
+#define IOBAR 1
+#define MEMBAR 2
+
+#define IOSIZE 0x80
+#define MEMSIZE 0x100000
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/i21555.c b/usr/src/uts/common/io/nfp/i21555.c
new file mode 100644
index 0000000000..82024dc800
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555.c
@@ -0,0 +1,423 @@
+/*
+
+i21555.c: nCipher PCI HSM intel 21555 command driver
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+09/10/2001 jsh Original
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "i21555.h"
+#include "nfp_cmd.h"
+#include "nfpci.h"
+
+/* started ------------------------------------------------------
+ *
+ * Check that device is ready to talk, by checking that
+ * the i21555 has master enabled on its secondary interface
+ */
+
+static nfp_err i21555_started( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+#ifdef CONFIGSPACE_DEBUG
+ unsigned int reg32[64];
+ int i;
+#endif
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "i21555_started: entered");
+
+#ifdef CONFIGSPACE_DEBUG
+ /* Suck up all the registers */
+ for (i=0; i < 64; i++) {
+ ne = nfp_config_inl( pdev, i*4, &reg32[i] );
+ }
+
+ for (i=0; i < 16; i++) {
+ int j = i * 4;
+ nfp_log( NFP_DBG3, "i21555 config reg %2x: %08x %08x %08x %08x", j*4,
+ reg32[j], reg32[j+1], reg32[j+2], reg32[j+3]);
+ }
+#endif
+
+ ne = nfp_config_inl( pdev, I21555_CFG_SEC_CMD_STATUS, &tmp32 );
+ if (ne) {
+ /* succeed if PCI config reads are not implemented */
+ if (ne == NFP_EUNKNOWN)
+ return NFP_SUCCESS;
+ nfp_log( NFP_DBG1, "i21555_started: nfp_config_inl failed");
+ return ne;
+ }
+
+ tmp32= FROM_LE32_IO(&tmp32) & 0xffff;
+
+ if ( tmp32 & CFG_CMD_MASTER ) {
+ nfp_log( NFP_DBG3, "i21555_started: Yes %x", tmp32);
+ return NFP_SUCCESS;
+ } else {
+ nfp_log( NFP_DBG1, "i21555_started: device not started yet %x", tmp32);
+ return NFP_ESTARTING;
+ }
+}
+
+/* create ------------------------------------------------------- */
+
+static nfp_err i21555_create( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_create: entered");
+ pdev->cmdctx= pdev; /* set our context to just be a pointer to our nfp_cdev */
+
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_create: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ nfp_log( NFP_DBG2, "i21555_create: enable doorbell");
+ TO_LE32_IO( &tmp32, I21555_DOORBELL_PRI_ENABLE );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET_MASK, tmp32 );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK, tmp32 );
+ return NFP_SUCCESS;
+}
+
+/* stop ------------------------------------------------------- */
+
+static nfp_err i21555_destroy( void * ctx ) {
+ nfp_cdev *pdev;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_destroy: entered");
+
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21555_destroy: NULL pdev");
+ return NFP_ENODEV;
+ }
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_destroy: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, I21555_DOORBELL_PRI_DISABLE );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET_MASK, tmp32 );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* open ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_open( void * ctx ) {
+
+ nfp_log( NFP_DBG2, "i21555_open: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* close ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_close( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21555_close: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* isr ------------------------------------------------------- */
+
+static nfp_err i21555_isr( void *ctx, int *handled ) {
+ nfp_cdev *pdev;
+ nfp_err ne;
+ unsigned short doorbell;
+ unsigned short tmp16;
+
+ nfp_log( NFP_DBG3, "i21555_isr: entered");
+
+ *handled= 0;
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21555_isr: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ pdev->stats.isr++;
+
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_isr: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ /* This interrupt may not be from our module, so check that it actually is
+ * us before handling it.
+ */
+ ne = i21555_started( pdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_isr: i21555_started failed");
+ }
+ return ne;
+ }
+
+ doorbell= nfp_inw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET);
+ doorbell= FROM_LE16_IO(&doorbell);
+ while( doorbell && doorbell != 0xffff) {
+ *handled= 1;
+ /* service interrupts */
+ if( doorbell & (NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ pdev->stats.isr_write++;
+ TO_LE16_IO(&tmp16,NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+
+ nfp_log( NFP_DBG2, "i21555_isr: write done interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+
+ nfp_write_complete(pdev->dev, doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+ }
+
+ if( doorbell & (NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED)) {
+ pdev->stats.isr_read++;
+ TO_LE16_IO(&tmp16,NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+
+ nfp_log( NFP_DBG2, "i21555_isr: read ack interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0 );
+ nfp_read_complete( pdev->dev, doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0);
+ }
+
+ if( doorbell & ~(NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED |
+ NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ TO_LE16_IO(&tmp16,doorbell);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+ nfp_log( NFP_DBG1, "i21555_isr: unexpected interrupt %x", doorbell );
+ }
+ doorbell= nfp_inw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET);
+ doorbell= FROM_LE16_IO(&doorbell);
+ }
+ nfp_log( NFP_DBG3, "i21555_isr: exiting");
+ return 0;
+}
+
+/* write ------------------------------------------------------- */
+
+static nfp_err i21555_write( const char *block, int len, void *ctx) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ nfp_err ne;
+ unsigned short tmp16;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_write: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_write: NULL cdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.write_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_write: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne = i21555_started( cdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_write: i21555_started failed");
+ }
+ return ne;
+ }
+
+ nfp_log( NFP_DBG3, "i21555_write: cdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21555_write: cdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ nfp_log( NFP_DBG3, "i21555_write: block len %d", len );
+ ne= nfp_copy_from_user_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_DATA, block, len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_from_user_to_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_WR_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM(&tmp32, len);
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21555_write: length not written");
+ return NFP_EIO;
+ }
+ TO_LE16_IO(&tmp16, NFAST_INT_HOST_WRITE_REQUEST >> 16);
+ nfp_outw( cdev, IOBAR, I21555_OFFSET_DOORBELL_SEC_SET, tmp16);
+
+ cdev->stats.write_fail--;
+ cdev->stats.write_block++;
+ cdev->stats.write_byte += len;
+
+ nfp_log( NFP_DBG2, "i21555_write: done");
+ return NFP_SUCCESS;
+}
+
+/* read ------------------------------------------------------- */
+
+static nfp_err i21555_read( char *block, int len, void *ctx, int *rcount) {
+ nfp_cdev *cdev;
+ nfp_err ne;
+ int count;
+
+ nfp_log( NFP_DBG2, "i21555_read: entered");
+ *rcount= 0;
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_read: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.read_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_read: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)&count, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_read: nfp_copy_from_dev failed.");
+ return ne;
+ }
+ count= FROM_LE32_MEM(&count);
+ if(count<0 || count>len) {
+ nfp_log( NFP_DBG1, "i21555_read: bad byte count (%d) from device", count);
+ return NFP_EIO;
+ }
+ ne= nfp_copy_to_user_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_DATA, block, count);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_read: nfp_copy_to_user failed.");
+ return ne;
+ }
+ nfp_log( NFP_DBG2, "i21555_read: done");
+ *rcount= count;
+ cdev->stats.read_fail--;
+ cdev->stats.read_block++;
+ cdev->stats.read_byte += len;
+ return NFP_SUCCESS;
+}
+
+/* chupdate ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_chupdate( char *data, int len, void *ctx ) {
+ nfp_log( NFP_DBG1, "i21555_chupdate: NYI");
+ return NFP_SUCCESS;
+}
+
+/* ensure reading -------------------------------------------------- */
+
+static nfp_err i21555_ensure_reading( unsigned int addr, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[3];
+ unsigned short tmp16;
+ unsigned int tmp32;
+ nfp_err ne;
+ int hdr_len;
+
+ nfp_log( NFP_DBG2, "i21555_ensure_reading: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.ensure_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne = i21555_started( cdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: i21555_started failed");
+ }
+ return ne;
+ }
+
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: pdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: pdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ if(addr) {
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: new format, addr %x", addr);
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL_PCI_PUSH);
+ TO_LE32_MEM(&hdr[1], len);
+ TO_LE32_MEM(&hdr[2], addr);
+ hdr_len= 12;
+ } else {
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+ hdr_len= 8;
+ }
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_RD_CONTROL, (const char *)hdr, hdr_len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM(&tmp32, len);
+
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: len not written");
+ return NFP_EIO;
+ }
+ TO_LE16_IO( &tmp16, NFAST_INT_HOST_READ_REQUEST >> 16);
+ nfp_outw( cdev, IOBAR, I21555_OFFSET_DOORBELL_SEC_SET, tmp16);
+
+ cdev->stats.ensure_fail--;
+ cdev->stats.ensure++;
+
+ return NFP_SUCCESS;
+}
+
+/* command device structure ------------------------------------- */
+
+const nfpcmd_dev i21555_cmddev = {
+ "nCipher Gen 2 PCI",
+ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_21555,
+ PCI_VENDOR_ID_NCIPHER, PCI_SUBSYSTEM_ID_NFAST_REV1,
+ { 0, IOSIZE | PCI_BASE_ADDRESS_SPACE_IO, NFPCI_RAM_MINSIZE_JOBS, 0, 0, 0 },
+ NFP_CMD_FLG_NEED_IOBUF,
+ i21555_create,
+ i21555_destroy,
+ i21555_open,
+ i21555_close,
+ i21555_isr,
+ i21555_write,
+ i21555_read,
+ i21555_chupdate,
+ i21555_ensure_reading,
+ i21555_debug,
+};
diff --git a/usr/src/uts/common/io/nfp/i21555.h b/usr/src/uts/common/io/nfp/i21555.h
new file mode 100644
index 0000000000..d8f3965938
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555.h
@@ -0,0 +1,51 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef I21555_H
+#define I21555_H
+
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_21555
+#define PCI_DEVICE_ID_INTEL_21555 0xb555
+#endif
+
+#ifndef PCI_VENDOR_ID_NCIPHER
+#define PCI_VENDOR_ID_NCIPHER 0x0100
+#endif
+
+#ifndef PCI_SUBSYSTEM_ID_NFAST_REV1
+#define PCI_SUBSYSTEM_ID_NFAST_REV1 0x0100
+#endif
+
+#define I21555_OFFSET_DOORBELL_PRI_SET 0x9C
+#define I21555_OFFSET_DOORBELL_SEC_SET 0x9E
+#define I21555_OFFSET_DOORBELL_PRI_CLEAR 0x98
+
+#define I21555_OFFSET_DOORBELL_PRI_SET_MASK 0xA4
+#define I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK 0xA0
+
+#define I21555_DOORBELL_PRI_ENABLE 0x0000
+#define I21555_DOORBELL_PRI_DISABLE 0xFFFF
+
+#define I21555_CFG_SEC_CMD_STATUS 0x44
+
+#define CFG_CMD_MASTER 0x0004
+
+#define IOBAR 1
+#define MEMBAR 2
+
+#define IOSIZE 0x100
+
+extern nfp_err i21555_debug( int cmd, void *ctx );
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/i21555d.c b/usr/src/uts/common/io/nfp/i21555d.c
new file mode 100644
index 0000000000..183ace8275
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555d.c
@@ -0,0 +1,28 @@
+/*
+
+i21555d.c: nCipher PCI HSM intel 21555 debug ioctl
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+
+history
+
+15/05/2002 jsh Original, does nothing
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_osif.h"
+#include "i21555.h"
+
+/* ARGSUSED */
+nfp_err i21555_debug( int cmd, void *ctx) {
+ nfp_log( NFP_DBG1, "i21555_debug: entered");
+
+ return NFP_EUNKNOWN;
+}
diff --git a/usr/src/uts/common/io/nfp/nfdev-common.h b/usr/src/uts/common/io/nfp/nfdev-common.h
new file mode 100644
index 0000000000..8a97bf2c63
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfdev-common.h
@@ -0,0 +1,141 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+/** \file nfdev-common.h
+ *
+ * \brief nFast device driver (not generic SCSI) ioctl struct definition file
+ * include NFDEV-$(system) for ioctl number definitions
+ *
+ * 1998.07.13 jsh Started
+ *
+ *
+ */
+
+#ifndef NFDEV_COMMON_H
+#define NFDEV_COMMON_H
+
+/**
+ * Result of the ENQUIRY ioctl.
+ */
+typedef struct nfdev_enquiry_str {
+ unsigned int busno; /**< Which bus is the PCI device on. */
+ unsigned char slotno; /**< Which slot is the PCI device in. */
+ unsigned char reserved[3]; /**< for consistant struct alignment */
+} nfdev_enquiry_str;
+
+/**
+ * Result of the STATS ioctl.
+ */
+typedef struct nfdev_stats_str {
+ unsigned long isr; /**< Count interrupts. */
+ unsigned long isr_read; /**< Count read interrupts. */
+ unsigned long isr_write; /**< Count write interrupts. */
+ unsigned long write_fail; /**< Count write failures. */
+ unsigned long write_block; /**< Count blocks written. */
+ unsigned long write_byte; /**< Count bytes written. */
+ unsigned long read_fail; /**< Count read failures. */
+ unsigned long read_block; /**< Count blocks read. */
+ unsigned long read_byte; /**< Count bytes read. */
+ unsigned long ensure_fail; /**< Count read request failures. */
+ unsigned long ensure; /**< Count read requests. */
+} nfdev_stats_str;
+
+/**
+ * Input to the CONTROL ioctl.
+ */
+typedef struct nfdev_control_str {
+ unsigned control; /**< Control flags. */
+} nfdev_control_str;
+
+/** Control bit indicating host supports MOI control */
+#define NFDEV_CONTROL_HOST_MOI 0x0001
+
+/** Index of control bits indicating desired mode
+ *
+ * Desired mode follows the M_ModuleMode enumeration.
+ */
+#define NFDEV_CONTROL_MODE_SHIFT 1
+
+/** Detect a backwards-compatible control value
+ *
+ * Returns true if the request control value "makes no difference", i.e.
+ * and the failure of an attempt to set it is therefore uninteresting.
+ */
+#define NFDEV_CONTROL_HARMLESS(c) ((c) <= 1)
+
+/**
+ * Result of the STATUS ioctl.
+ */
+typedef struct nfdev_status_str {
+ unsigned status; /**< Status flags. */
+ char error[8]; /**< Error string. */
+} nfdev_status_str;
+
+/** Monitor firmware supports MOI control and error reporting */
+#define NFDEV_STATUS_MONITOR_MOI 0x0001
+
+/** Application firmware supports MOI control and error reporting */
+#define NFDEV_STATUS_APPLICATION_MOI 0x0002
+
+/** Application firmware running and supports error reporting */
+#define NFDEV_STATUS_APPLICATION_RUNNING 0x0004
+
+/** HSM failed
+ *
+ * Consult error[] for additional information.
+ */
+#define NFDEV_STATUS_FAILED 0x0008
+
+/** Standard PCI interface. */
+#define NFDEV_IF_STANDARD 0x01
+
+/** PCI interface with results pushed from device
+ * via DMA.
+ */
+#define NFDEV_IF_PCI_PUSH 0x02
+
+/* platform independant base ioctl numbers */
+
+/** Enquiry ioctl.
+ * \return nfdev_enquiry_str describing the attached device. */
+#define NFDEV_IOCTL_NUM_ENQUIRY 0x01
+/** Channel Update ioctl.
+ * \deprecated */
+#define NFDEV_IOCTL_NUM_CHUPDATE 0x02
+/** Ensure Reading ioctl.
+ * Signal a read request to the device.
+ * \param (unsigned int) Length of data to be read.
+ */
+#define NFDEV_IOCTL_NUM_ENSUREREADING 0x03
+/** Device Count ioctl.
+ * Not implemented for on all platforms.
+ * \return (int) the number of attached devices. */
+#define NFDEV_IOCTL_NUM_DEVCOUNT 0x04
+/** Internal Debug ioctl.
+ * Not implemented in release drivers. */
+#define NFDEV_IOCTL_NUM_DEBUG 0x05
+/** PCI Interface Version ioctl.
+ * \param (int) Maximum PCI interface version
+ * supported by the user of the device. */
+#define NFDEV_IOCTL_NUM_PCI_IFVERS 0x06
+/** Statistics ioctl.
+ * \return nfdev_enquiry_str describing the attached device. */
+#define NFDEV_IOCTL_NUM_STATS 0x07
+
+/** Module control ioctl
+ * \param (nfdev_control_str) Value to write to HSM control register
+ */
+#define NFDEV_IOCTL_NUM_CONTROL 0x08
+
+/** Module state ioctl
+ * \return (nfdev_status_str) Values read from HSM status/error registers
+ */
+#define NFDEV_IOCTL_NUM_STATUS 0x09
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfdev-solaris.h b/usr/src/uts/common/io/nfp/nfdev-solaris.h
new file mode 100644
index 0000000000..923b902e46
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfdev-solaris.h
@@ -0,0 +1,37 @@
+/*
+
+nfdev-solaris.h: nFast solaris specific device ioctl interface.
+
+(C) Copyright nCipher Corporation Ltd 1998-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+14/07/1998 jsh Original
+
+*/
+
+#ifndef NFDEV_SOLARIS_H
+#define NFDEV_SOLARIS_H
+
+#include "nfdev-common.h"
+
+#define NFDEV_IOCTL_TYPE ('n'<<8)
+
+#define NFDEV_IOCTL_ENQUIRY ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_ENQUIRY )
+#define NFDEV_IOCTL_ENSUREREADING ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_ENSUREREADING )
+#define NFDEV_IOCTL_DEVCOUNT ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_DEVCOUNT )
+#define NFDEV_IOCTL_DEBUG ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_DEBUG )
+#define NFDEV_IOCTL_PCI_IFVERS ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_PCI_IFVERS )
+#define NFDEV_IOCTL_STATS ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_STATS )
+
+#endif /* NFDEV_SOLARIS_H */
diff --git a/usr/src/uts/common/io/nfp/nfp.h b/usr/src/uts/common/io/nfp/nfp.h
new file mode 100644
index 0000000000..9704f04fbc
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp.h
@@ -0,0 +1,113 @@
+/*
+
+nfp.h: nFast PCI driver for Solaris 2.5, 2.6 and 2.7
+
+(C) Copyright nCipher Corporation Ltd 2001-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+06/05/1998 jsh Original solaris 2.6
+21/05/1999 jsh added support for solaris 2.5
+10/06/1999 jsh added support for solaris 2.7 (32 and 64 bit)
+16/10/2001 jsh moved from nfast to new structure in nfdrv
+
+*/
+
+#ifndef NFP_H
+#define NFP_H
+
+#ifndef _KERNEL
+#error Hello? this is a driver, please compile with -D_KERNEL
+#endif
+
+#if ( CH_KERNELVER < 260 )
+typedef int ioctlptr_t;
+typedef unsigned short uint16_t;
+#define DDI_GET32 ddi_getl
+#define DDI_PUT32 ddi_putl
+#define DDI_GET16 ddi_getw
+#define DDI_PUT16 ddi_putw
+#define DDI_REP_GET8 ddi_rep_getb
+#define DDI_REP_PUT8 ddi_rep_putb
+#define DDI_REP_GET32 ddi_rep_getl
+#define DDI_REP_PUT32 ddi_rep_putl
+#define PCI_CONFIG_GET16 pci_config_getw
+#else /* ( CH_KERNELVER >= 260 ) */
+typedef intptr_t ioctlptr_t;
+#define DDI_GET32 ddi_get32
+#define DDI_PUT32 ddi_put32
+#define DDI_GET16 ddi_get16
+#define DDI_PUT16 ddi_put16
+#define DDI_REP_GET8 ddi_rep_get8
+#define DDI_REP_PUT8 ddi_rep_put8
+#define DDI_REP_GET32 ddi_rep_get32
+#define DDI_REP_PUT32 ddi_rep_put32
+#define PCI_CONFIG_GET16 pci_config_get16
+#endif
+
+#if ( CH_KERNELVER < 270 )
+typedef int nfp_timeout_t;
+#define EXTRA_CB_FLAGS 0
+#define VSXPRINTF(s, n, format, ap) vsprintf (s, format, ap)
+#else /* ( CH_KERNELVER >= 270 ) */
+typedef timeout_id_t nfp_timeout_t;
+#define EXTRA_CB_FLAGS D_64BIT
+#define VSXPRINTF(s, n, format, ap) vsnprintf(s, n, format, ap)
+#endif
+
+typedef struct nfp_dev {
+ int rd_ok;
+ int wr_ok;
+
+ int ifvers;
+
+ /* for PCI push read interface */
+ unsigned char *read_buf;
+ ddi_dma_handle_t read_dma_handle;
+ ddi_dma_cookie_t read_dma_cookie;
+
+ ddi_acc_handle_t acchandle;
+
+ int rd_dma_ok;
+
+ nfp_timeout_t wrtimeout;
+ nfp_timeout_t rdtimeout;
+
+ struct buf *wr_bp;
+ int wr_ready;
+ int rd_ready;
+ int rd_pending;
+ int rd_outstanding;
+ kcondvar_t rd_cv;
+
+ struct pollhead pollhead;
+ dev_info_t *dip;
+
+ ddi_iblock_cookie_t high_iblock_cookie; /* for mutex */
+ ddi_iblock_cookie_t low_iblock_cookie; /* for mutex */
+ kmutex_t high_mutex;
+ kmutex_t low_mutex;
+ int high_intr;
+ ddi_softintr_t soft_int_id;
+ int high_read;
+ int high_write;
+
+ ddi_iblock_cookie_t iblock_cookie; /* for mutex */
+ kmutex_t isr_mutex;
+
+ kmutex_t busy_mutex;
+ int busy;
+
+ ddi_acc_handle_t conf_handle;
+
+ nfp_cdev common;
+ const nfpcmd_dev *cmddev;
+} nfp_dev;
+
+extern struct nfp_dev *nfp_dev_list[];
+
+#endif /* NFP_H */
diff --git a/usr/src/uts/common/io/nfp/nfp_cmd.h b/usr/src/uts/common/io/nfp/nfp_cmd.h
new file mode 100644
index 0000000000..db8af0b2f9
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_cmd.h
@@ -0,0 +1,68 @@
+/*
+
+nfp_cmd.h: nCipher PCI HSM command driver decalrations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFPCMD_H
+#define NFPCMD_H
+
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+
+/* read and write called with userspace buffer */
+
+typedef struct nfpcmd_dev {
+ const char *name;
+ unsigned short vendorid, deviceid,
+ sub_vendorid, sub_deviceid;
+ unsigned int bar_sizes[6]; /* includes IO bit */
+ unsigned int flags;
+ nfp_err (*create)(struct nfp_cdev *pdev);
+ nfp_err (*destroy)(void * ctx);
+ nfp_err (*open)(void * ctx);
+ nfp_err (*close)(void * ctx);
+ nfp_err (*isr)(void *ctx, int *handled);
+ nfp_err (*write_block)( const char *ublock, int len, void *ctx );
+ nfp_err (*read_block)( char *ublock, int len, void *ctx, int *rcount);
+ nfp_err (*channel_update)( char *data, int len, void *ctx);
+ nfp_err (*ensure_reading)( unsigned int addr, int len, void *ctx );
+ nfp_err (*debug)( int cmd, void *ctx);
+} nfpcmd_dev;
+
+#define NFP_CMD_FLG_NEED_IOBUF 0x1
+
+/* list of all supported drivers ---------------------------------------- */
+
+extern const nfpcmd_dev *nfp_drvlist[];
+
+extern const nfpcmd_dev i21285_cmddev;
+extern const nfpcmd_dev i21555_cmddev;
+extern const nfpcmd_dev bcm5820_cmddev;
+
+#ifndef PCI_BASE_ADDRESS_SPACE_IO
+#define PCI_BASE_ADDRESS_SPACE_IO 0x1
+#endif
+
+#define NFP_MAXDEV 16
+
+
+#define NFP_MEMBAR_MASK ~0xf
+#define NFP_IOBAR_MASK ~0x3
+/*
+ This masks off the bottom bits of the PCI_CSR_BAR which signify that the
+ BAR is an IO BAR rather than a MEM BAR
+*/
+
+#endif
+
diff --git a/usr/src/uts/common/io/nfp/nfp_common.h b/usr/src/uts/common/io/nfp/nfp_common.h
new file mode 100644
index 0000000000..d1d2100fea
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_common.h
@@ -0,0 +1,68 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef NFP_COMMON_H
+#define NFP_COMMON_H
+
+#include <sys/types.h>
+#include <sys/conf.h>
+
+typedef uint32_t UINT32;
+typedef uint8_t BYTE;
+
+#define DEFINE_NFPCI_PACKED_STRUCTS
+#include "nfpci.h"
+#include "nfdev-solaris.h"
+
+typedef int oserr_t;
+
+#if CH_BIGENDIAN
+
+/* Big Endian Sparc */
+
+#define SWP32(x) \
+( (((unsigned int)(x)>>24)&0xff) | (((unsigned int)(x)>>8)&0xff00) | (((unsigned int)(x)<<8)&0xff0000) | (((unsigned int)(x)<<24)&0xff000000) )
+
+#define SWP16(x) ( (((x)>>8)&0xff) | (((x)<<8)&0xff00) )
+
+#define FROM_LE32_IO(x) SWP32(*x)
+#define TO_LE32_IO(x,y) *x=SWP32(y)
+
+#define FROM_LE32_MEM(x) SWP32(*x)
+#define TO_LE32_MEM(x,y) *x=SWP32(y)
+
+#define FROM_LE16_IO(x) SWP16(*x)
+#define TO_LE16_IO(x,y) *x=SWP16(y)
+
+#else
+
+/* Little Endian x86 */
+
+#define FROM_LE32_IO(x) (*x)
+#define TO_LE32_IO(x,y) (*x=y)
+
+#define FROM_LE32_MEM(x) (*x)
+#define TO_LE32_MEM(x,y) (*x=y)
+
+#define FROM_LE16_IO(x) (*x)
+#define TO_LE16_IO(x,y) (*x=y)
+
+#endif /* !CH_BIGENDIAN */
+
+#include <sys/types.h>
+
+#if CH_KERNELVER == 260
+#define nfp_get_lbolt( lbolt, err ) err= drv_getparm( LBOLT, lbolt )
+#else
+#define nfp_get_lbolt( lbolt, err ) { *lbolt= ddi_get_lbolt(); err= 0; }
+#endif
+
+#endif
+
diff --git a/usr/src/uts/common/io/nfp/nfp_error.h b/usr/src/uts/common/io/nfp/nfp_error.h
new file mode 100644
index 0000000000..d64cb78fd4
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_error.h
@@ -0,0 +1,48 @@
+/*
+
+nfp_error.h: nCipher PCI HSM error handling
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+05/12/2001 jsh Original
+
+*/
+
+#ifndef NFP_ERROR_H
+#define NFP_ERROR_H
+
+#include "nfp_common.h"
+
+#define NFP_SUCCESS 0x0
+#define NFP_EFAULT 0x1
+#define NFP_ENOMEM 0x2
+#define NFP_EINVAL 0x3
+#define NFP_EIO 0x4
+#define NFP_ENXIO 0x5
+#define NFP_ENODEV 0x6
+#define NFP_EINTR 0x7
+#define NFP_ESTARTING 0x8
+#define NFP_EAGAIN 0x9
+#define NFP_EUNKNOWN 0x100
+
+typedef int nfp_err;
+
+extern oserr_t nfp_oserr( nfp_err nerr );
+extern nfp_err nfp_error( oserr_t oerr );
+
+#define nfr( x) \
+ return nfp_error((x))
+
+#define nfer(x, fn, msg) \
+ { oserr_t err=(x); if(err) { nfp_log( NFP_DBG1, #fn ": " msg); return nfp_error(err); } }
+
+#define er(x, fn, msg ) \
+{ nfp_err err=(x); if(err) { nfp_log( NFP_DBG1, #fn ": " msg); return err; } }
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfp_hostif.h b/usr/src/uts/common/io/nfp/nfp_hostif.h
new file mode 100644
index 0000000000..3e7d8187e5
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_hostif.h
@@ -0,0 +1,54 @@
+/*
+
+nfp_hostif.h: nCipher PCI HSM host interface declarations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFP_HOSTIF_H
+#define NFP_HOSTIF_H
+
+#include "nfdev-common.h"
+
+struct nfp_dev;
+
+/* common device structure */
+
+typedef struct nfp_cdev {
+ unsigned char *bar[6];
+ void *extra[6];
+
+ int busno;
+ int slotno;
+
+ void *cmdctx;
+
+ char *iobuf;
+
+ struct nfp_dev* dev;
+
+ struct nfdev_stats_str stats;
+
+} nfp_cdev;
+
+/* callbacks from command drivers -------------------------------------- */
+
+void nfp_read_complete( struct nfp_dev *pdev, int ok);
+void nfp_write_complete( struct nfp_dev *pdev, int ok);
+
+#define NFP_READ_MAX (8 * 1024)
+#define NFP_READBUF_SIZE (NFP_READ_MAX + 8)
+#define NFP_TIMEOUT_SEC 10
+
+#define NFP_DRVNAME "nCipher nFast PCI driver"
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfp_ifvers.c b/usr/src/uts/common/io/nfp/nfp_ifvers.c
new file mode 100644
index 0000000000..807b4f24c5
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_ifvers.c
@@ -0,0 +1,51 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/*
+ * nfp_ifervs.c - common pci interface versioning
+ *
+ * uses:
+ *
+ * int pdev->ifvers
+ * device interface version
+ *
+ * int nfp_ifvers
+ * interface version limit
+ *
+ * int nfp_alloc_pci_push( nfp_dev *pdev )
+ * allocates resources needed for PCI Push,
+ * if not already allocated, and return True if successful
+ *
+ * void nfp_free_pci_push( nfp_dev *pdev ) {
+ * frees any resources allocated to PCI Push
+ */
+
+void nfp_set_ifvers( nfp_dev *pdev, int vers ) {
+ if( nfp_ifvers != 0 && vers > nfp_ifvers ) {
+ nfp_log( NFP_DBG2,
+ "nfp_set_ifvers: can't set ifvers %d"
+ " as nfp_ifvers wants max ifvers %d",
+ vers, nfp_ifvers);
+ return;
+ }
+ if( vers >= NFDEV_IF_PCI_PUSH ) {
+ if(!nfp_alloc_pci_push(pdev)) {
+ nfp_log( NFP_DBG1,
+ "nfp_set_ifvers: can't set ifvers %d"
+ " as resources not available",
+ vers);
+ return;
+ }
+ } else {
+ nfp_free_pci_push(pdev);
+ }
+ pdev->ifvers= vers;
+ nfp_log( NFP_DBG3, "nfp_set_ifvers: setting ifvers %d", vers);
+}
diff --git a/usr/src/uts/common/io/nfp/nfp_osif.h b/usr/src/uts/common/io/nfp/nfp_osif.h
new file mode 100644
index 0000000000..17ffe469ce
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_osif.h
@@ -0,0 +1,105 @@
+/*
+
+nfp_osif.h: nCipher PCI HSM OS interface declarations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFP_OSIF_H
+#define NFP_OSIF_H
+
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+
+/* general typedefs ----------------------------------------------- */
+
+typedef volatile unsigned int reg32;
+typedef volatile unsigned short reg16;
+typedef volatile unsigned char reg8;
+
+/* sempaphores, mutexs and events --------------------------------- */
+
+#if 0
+extern nfp_err nfp_sema_init( nfp_sema *sema, int initial);
+extern void nfp_sema_destroy( nfp_sema *sema );
+extern void nfp_sema_post( nfp_sema *sema );
+extern void nfp_sema_wait( nfp_sema *sema );
+extern int nfp_sema_wait_sig( nfp_sema *sema );
+
+extern nfp_err nfp_mutex_init( nfp_mutex *mutex );
+extern void nfp_mutex_destroy( nfp_mutex *mutex );
+extern void nfp_mutex_enter( nfp_mutex *mutex );
+extern void nfp_mutex_exit( nfp_mutex *mutex );
+
+extern nfp_err nfp_event_init( nfp_event *event );
+extern void nfp_event_destroy( nfp_event *event );
+extern void nfp_event_set( nfp_event *event );
+extern void nfp_event_clear( nfp_event *event );
+extern void nfp_event_wait( nfp_event *event );
+extern void nfp_event_wait_sig( nfp_event *event );
+
+#endif
+
+/* timeouts ------------------------------------------------------ */
+
+extern void nfp_sleep( int ms );
+
+/* memory handling ----------------------------------------------- */
+
+#define KMALLOC_DMA 0
+#define KMALLOC_CACHED 1
+
+extern void *nfp_kmalloc( int size, int flags );
+extern void *nfp_krealloc( void *ptr, int size, int flags );
+extern void nfp_kfree( void * );
+
+/* config space access ------------------------------------------------ */
+
+/* return Little Endian 32 bit config register */
+extern nfp_err nfp_config_inl( nfp_cdev *pdev, int offset, unsigned int *res );
+
+/* io space access ------------------------------------------------ */
+
+extern unsigned int nfp_inl( nfp_cdev *pdev, int bar, int offset );
+extern unsigned short nfp_inw( nfp_cdev *pdev, int bar, int offset );
+extern void nfp_outl( nfp_cdev *pdev, int bar, int offset, unsigned int data );
+extern void nfp_outw( nfp_cdev *pdev, int bar, int offset, unsigned short data );
+
+/* user and device memory space access ---------------------------- */
+
+/* NB these 2 functions are not guarenteed to be re-entrant for a given device */
+extern nfp_err nfp_copy_from_user_to_dev( nfp_cdev *cdev, int bar, int offset, const char *ubuf, int len);
+extern nfp_err nfp_copy_to_user_from_dev( nfp_cdev *cdev, int bar, int offset, char *ubuf, int len);
+
+extern nfp_err nfp_copy_from_user( char *kbuf, const char *ubuf, int len );
+extern nfp_err nfp_copy_to_user( char *ubuf, const char *kbuf, int len );
+
+extern nfp_err nfp_copy_from_dev( nfp_cdev *cdev, int bar, int offset, char *kbuf, int len );
+extern nfp_err nfp_copy_to_dev( nfp_cdev *cdev, int bar, int offset, const char *kbuf, int len);
+
+/* debug ------------------------------------------------------------ */
+
+#define NFP_DBG1 1
+#define NFP_DBGE NFP_DBG1
+#define NFP_DBG2 2
+#define NFP_DBG3 3
+#define NFP_DBG4 4
+
+#ifdef STRANGE_VARARGS
+extern void nfp_log();
+#else
+extern void nfp_log( int severity, const char *format, ...);
+#endif
+
+extern int nfp_debug;
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfpci.h b/usr/src/uts/common/io/nfp/nfpci.h
new file mode 100644
index 0000000000..793f5995e6
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfpci.h
@@ -0,0 +1,171 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/*
+*
+* NFPCI.H - nFast PCI interface definition file
+*
+*
+*
+* 1998.06.09 IH Started
+*
+* The interface presented by nFast PCI devices consists of:
+*
+* A region of shared RAM used for data transfer & control information
+* A doorbell interrupt register, so both sides can give each other interrupts
+* A number of DMA channels for transferring data
+*/
+
+#ifndef NFPCI_H
+#define NFPCI_H
+
+/* Sizes of some regions */
+#define NFPCI_RAM_MINSIZE 0x00100000
+/* This is the minimum size of shared RAM. In future it may be possible to
+ negotiate larger sizes of shared RAM or auto-detect how big it is */
+#define NFPCI_RAM_MINSIZE_JOBS 0x00020000 /* standard jobs only */
+#define NFPCI_RAM_MINSIZE_KERN 0x00040000 /* standard and kernel jobs */
+
+/* Offsets within shared memory space.
+ The following main regions are:
+ jobs input area
+ jobs output area
+ kernel jobs input area
+ kernel output area
+*/
+
+#define NFPCI_OFFSET_JOBS 0x00000000
+#define NFPCI_OFFSET_JOBS_WR 0x00000000
+#define NFPCI_OFFSET_JOBS_RD 0x00010000
+#define NFPCI_OFFSET_KERN 0x00020000
+#define NFPCI_OFFSET_KERN_WR 0x00020000
+#define NFPCI_OFFSET_KERN_RD 0x00030000
+
+/* Interrupts, defined by bit position in doorbell register */
+
+/* Interrupts from device to host */
+#define NFAST_INT_DEVICE_WRITE_OK 0x00000001
+#define NFAST_INT_DEVICE_WRITE_FAILED 0x00000002
+#define NFAST_INT_DEVICE_READ_OK 0x00000004
+#define NFAST_INT_DEVICE_READ_FAILED 0x00000008
+#define NFAST_INT_DEVICE_KERN_WRITE_OK 0x00000010
+#define NFAST_INT_DEVICE_KERN_WRITE_FAILED 0x00000020
+#define NFAST_INT_DEVICE_KERN_READ_OK 0x00000040
+#define NFAST_INT_DEVICE_KERN_READ_FAILED 0x00000080
+
+/* Interrupts from host to device */
+#define NFAST_INT_HOST_WRITE_REQUEST 0x00010000
+#define NFAST_INT_HOST_READ_REQUEST 0x00020000
+#define NFAST_INT_HOST_DEBUG 0x00040000
+#define NFAST_INT_HOST_KERN_WRITE_REQUEST 0x00080000
+#define NFAST_INT_HOST_KERN_READ_REQUEST 0x00100000
+
+/* Ordinary job submission ------------------------ */
+
+/* The NFPCI_OFFSET_JOBS_WR and NFPCI_OFFSET_JOBS_RD regions are defined
+ by the following (byte) address offsets... */
+
+#define NFPCI_OFFSET_CONTROL 0x0
+#define NFPCI_OFFSET_LENGTH 0x4
+#define NFPCI_OFFSET_DATA 0x8
+#define NFPCI_OFFSET_PUSH_ADDR 0x8
+
+#define NFPCI_JOBS_WR_CONTROL (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_CONTROL)
+#define NFPCI_JOBS_WR_LENGTH (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_LENGTH)
+#define NFPCI_JOBS_WR_DATA (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_DATA)
+#define NFPCI_MAX_JOBS_WR_LEN (0x0000FFF8)
+
+#define NFPCI_JOBS_RD_CONTROL (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_CONTROL)
+#define NFPCI_JOBS_RD_LENGTH (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_LENGTH)
+#define NFPCI_JOBS_RD_DATA (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_DATA)
+/* address in PCI space of host buffer for NFPCI_JOB_CONTROL_PCI_PUSH */
+#define NFPCI_JOBS_RD_PUSH_ADDR (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_PUSH_ADDR)
+#define NFPCI_MAX_JOBS_RD_LEN (0x000FFF8)
+
+/* Kernel inferface job submission ---------------- */
+
+#define NFPCI_KERN_WR_CONTROL (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_CONTROL)
+#define NFPCI_KERN_WR_LENGTH (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_LENGTH)
+#define NFPCI_KERN_WR_DATA (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_DATA)
+#define NFPCI_MAX_KERN_WR_LEN (0x0000FFF8)
+
+#define NFPCI_KERN_RD_CONTROL (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_CONTROL)
+#define NFPCI_KERN_RD_LENGTH (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_LENGTH)
+#define NFPCI_KERN_RD_DATA (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_DATA)
+/* address in PCI space of host buffer for NFPCI_JOB_CONTROL_PCI_PUSH */
+#define NFPCI_KERN_RD_ADDR (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_PUSH_ADDR)
+#define NFPCI_MAX_KERN_RD_LEN (0x000FFF8)
+
+#ifdef DEFINE_NFPCI_PACKED_STRUCTS
+typedef struct
+{
+ UINT32 controlword;
+ UINT32 length; /* length of data to follow */
+ union {
+ BYTE data[1];
+ UINT32 addr;
+ } uu;
+}
+ NFPCI_JOBS_BLOCK;
+#endif
+
+
+#define NFPCI_JOB_CONTROL 0x00000001
+#define NFPCI_JOB_CONTROL_PCI_PUSH 0x00000002
+/*
+ The 'Control' word is analogous to the SCSI read/write address;
+ 1 = standard push/pull IO
+ 2 = push/push IO
+
+ To submit a block of job data, the host:
+ - sets the (32-bit, little-endian) word at NFPCI_JOBS_WR_CONTROL to NFPCI_JOB_CONTROL
+ - sets the word at NFPCI_JOBS_WR_LENGTH to the length of the data
+ - copies the data to NFPCI_JOBS_WR_DATA
+ - sets interrupt NFAST_INT_HOST_WRITE_REQUEST in the doorbell register
+ - awaits the NFAST_INT_DEVICE_WRITE_OK (or _FAILED) interrupts back
+
+ To read a block of jobs back, the host:
+ - sets the word at NFPCI_JOBS_RD_CONTROL to NFPCI_JOB_CONTROL
+ - sets the word at NFPCI_JOBS_RD_LENGTH to the max length for returned data
+ - sets interrupt NFAST_INT_HOST_READ_REQUEST
+ - awaits the NFAST_INT_DEVICE_READ_OK (or _FAILED) interrupt
+ - reads the data from NFPCI_JOBS_RD_DATA; the module will set the word at
+ NFPCI_JOBS_RD_LENGTH to its actual length.
+
+ Optionally the host can request the PCI read data to be pushed to host PCI mapped ram:
+ - allocates a contiguous PCI addressable buffer for a NFPCI_JOBS_BLOCK of max
+ size NFPCI_MAX_JOBS_RD_LEN (or NFPCI_MAX_KERN_RD_LEN) + 8
+ - sets the word at NFPCI_JOBS_RD_CONTROL to NFPCI_JOB_CONTROL_PCI_PUSH
+ - sets the word at NFPCI_JOBS_RD_LENGTH to the max length for returned data
+ - sets the word at NFPCI_JOBS_RD_PUSH_ADDR to be the host PCI address of
+ the buffer
+ - sets interrupt NFAST_INT_HOST_READ_REQUEST
+ - awaits the NFAST_INT_DEVICE_READ_OK (or _FAILED) interrupt
+ - reads the data from the buffer at NFPCI_OFFSET_DATA in the buffer. The
+ module will set NFPCI_OFFSET_LENGTH to the actual length.
+*/
+
+#define NFPCI_SCRATCH_CONTROL 0
+
+#define NFPCI_SCRATCH_CONTROL_HOST_MOI (1<<0)
+#define NFPCI_SCRATCH_CONTROL_MODE_SHIFT 1
+#define NFPCI_SCRATCH_CONTROL_MODE_MASK (3<<NFPCI_SCRATCH_CONTROL_MODE_SHIFT)
+
+#define NFPCI_SCRATCH_STATUS 1
+
+#define NFPCI_SCRATCH_STATUS_MONITOR_MOI (1<<0)
+#define NFPCI_SCRATCH_STATUS_APPLICATION_MOI (1<<1)
+#define NFPCI_SCRATCH_STATUS_APPLICATION_RUNNING (1<<2)
+#define NFPCI_SCRATCH_STATUS_ERROR (1<<3)
+
+#define NFPCI_SCRATCH_ERROR_LO 2
+#define NFPCI_SCRATCH_ERROR_HI 3
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/osif.c b/usr/src/uts/common/io/nfp/osif.c
new file mode 100644
index 0000000000..fba62f9a37
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/osif.c
@@ -0,0 +1,184 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/map.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/open.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+
+#include "nfp_common.h"
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+#include "nfp_osif.h"
+#include "nfp_cmd.h"
+#include "nfp.h"
+#include "autoversion.h"
+
+/* config space access ---------------------------------- */
+
+nfp_err nfp_config_inl( nfp_cdev *pdev, int offset, unsigned int *res ) {
+ unsigned int tmp32;
+ if ( !pdev || !pdev->dev || !pdev->dev->conf_handle )
+ return NFP_ENODEV;
+
+/* pci_config_get32() does byte swapping, so put back to LE */
+ tmp32 = pci_config_get32( pdev->dev->conf_handle, offset );
+ TO_LE32_IO(res, tmp32);
+
+ return NFP_SUCCESS;
+}
+
+/* user space memory access ---------------------------------- */
+
+nfp_err nfp_copy_from_user( char *kbuf, const char *ubuf, int len) {
+ bcopy(ubuf, kbuf, len);
+ return 0;
+}
+
+nfp_err nfp_copy_to_user( char *ubuf, const char *kbuf, int len) {
+ bcopy(kbuf, ubuf, len);
+ return 0;
+}
+
+nfp_err nfp_copy_from_user_to_dev( nfp_cdev *cdev, int bar, int offset, const char *ubuf, int len) {
+ /* dirty hack on Solaris, as we are called from strategy we are, in fact, copying from kernel mem */
+ return nfp_copy_to_dev( cdev, bar, offset, ubuf, len );
+}
+
+nfp_err nfp_copy_to_user_from_dev( nfp_cdev *cdev, int bar, int offset, char *ubuf, int len) {
+ /* dirty hack on Solaris, as we are called from strategy we are, in fact, copying to kernel mem */
+ return nfp_copy_from_dev( cdev, bar, offset, ubuf, len );
+}
+
+nfp_err nfp_copy_from_dev( nfp_cdev *cdev, int bar, int offset, char *kbuf, int len) {
+ if( len & 0x3 || offset & 0x3 )
+ DDI_REP_GET8( cdev->extra[bar], (unsigned char *)kbuf, cdev->bar[bar] + offset, len, DDI_DEV_AUTOINCR);
+ else
+ /* LINTED: alignment */
+ DDI_REP_GET32( cdev->extra[bar], (unsigned int *)kbuf, (unsigned int *)(cdev->bar[bar] + offset), len / 4, DDI_DEV_AUTOINCR);
+ return NFP_SUCCESS;
+}
+
+nfp_err nfp_copy_to_dev( nfp_cdev *cdev, int bar, int offset, const char *kbuf, int len) {
+ if( len & 0x3 || offset & 0x3 )
+ DDI_REP_PUT8( cdev->extra[bar], (unsigned char *)kbuf, cdev->bar[bar] + offset, len, DDI_DEV_AUTOINCR );
+ else
+ /* LINTED: alignment */
+ DDI_REP_PUT32( cdev->extra[bar], (unsigned int *)kbuf, (unsigned int *)(cdev->bar[bar] + offset), len / 4, DDI_DEV_AUTOINCR );
+ return NFP_SUCCESS;
+}
+
+/* pci io space access --------------------------------------- */
+
+unsigned int nfp_inl( nfp_cdev *pdev, int bar, int offset ) {
+ nfp_log( NFP_DBG3, "nfp_inl: addr %x", (uintptr_t) pdev->bar[bar] + offset);
+ /* LINTED: alignment */
+ return DDI_GET32( pdev->extra[bar], (uint32_t *)(pdev->bar[bar] + offset) );
+}
+
+unsigned short nfp_inw( nfp_cdev *pdev, int bar, int offset ) {
+ nfp_log( NFP_DBG3, "nfp_inw: addr %x", (uintptr_t) pdev->bar[bar] + offset);
+ /* LINTED: alignment */
+ return DDI_GET16( pdev->extra[bar], (unsigned short *)(pdev->bar[ bar ] + offset) );
+}
+
+void nfp_outl( nfp_cdev *pdev, int bar, int offset, unsigned int data ) {
+ nfp_log( NFP_DBG3, "nfp_outl: addr %x, data %x", (uintptr_t) pdev->bar[bar] + offset, data);
+ /* LINTED: alignment */
+ DDI_PUT32( pdev->extra[bar], (uint32_t *)(pdev->bar[ bar ] + offset), data );
+}
+
+void nfp_outw( nfp_cdev *pdev, int bar, int offset, unsigned short data ) {
+ nfp_log( NFP_DBG3, "nfp_outl: addr %x, data %x", (uintptr_t) pdev->bar[bar] + offset, data);
+ /* LINTED: alignment */
+ DDI_PUT16( pdev->extra[bar], (unsigned short *)(pdev->bar[ bar ] + offset), data );
+}
+
+/* logging ---------------------------------------------------- */
+
+void nfp_log( int level, const char *fmt, ...)
+{
+ auto char buf[256];
+ va_list ap;
+
+ switch (level) {
+ case NFP_DBG4: if (nfp_debug < 4) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG3: if (nfp_debug < 3) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG2: if (nfp_debug < 2) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG1: if (nfp_debug < 1) break;
+ /*FALLTHROUGH*/
+ default:
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, 256, fmt, ap);
+ va_end(ap);
+ cmn_err(CE_CONT, "!" VERSION_COMPNAME " " VERSION_NO ": %s\n", buf);
+ break;
+ }
+}
+
+struct errstr {
+ int oserr;
+ nfp_err nferr;
+};
+
+
+static struct errstr errtab[] = {
+ { EFAULT, NFP_EFAULT },
+ { ENOMEM, NFP_ENOMEM },
+ { EINVAL, NFP_EINVAL },
+ { EIO, NFP_EIO },
+ { ENXIO, NFP_ENXIO },
+ { ENODEV, NFP_ENODEV },
+ { EINVAL, NFP_EUNKNOWN },
+ { 0, 0 }
+};
+
+nfp_err nfp_error( int oserr )
+{
+ struct errstr *perr;
+ if(!oserr)
+ return 0;
+ perr= errtab;
+ while(perr->nferr) {
+ if(perr->oserr == oserr)
+ return perr->nferr;
+ perr++;
+ }
+ return NFP_EUNKNOWN;
+}
+
+int nfp_oserr( nfp_err nferr )
+{
+ struct errstr *perr;
+ if(nferr == NFP_SUCCESS)
+ return 0;
+ perr= errtab;
+ while(perr->nferr) {
+ if(perr->nferr == nferr)
+ return perr->oserr;
+ perr++;
+ }
+ return EIO;
+}
diff --git a/usr/src/uts/common/io/overlay/overlay.c b/usr/src/uts/common/io/overlay/overlay.c
new file mode 100644
index 0000000000..3f34ec3b58
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay.c
@@ -0,0 +1,2184 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Overlay Devices
+ *
+ * Overlay devices provide a means for creating overlay networks, a means of
+ * multiplexing multiple logical, isolated, and discrete layer two and layer
+ * three networks on top of one physical network.
+ *
+ * In general, these overlay devices encapsulate the logic to answer two
+ * different questions:
+ *
+ * 1) How should I transform a packet to put it on the wire?
+ * 2) Where should I send a transformed packet?
+ *
+ * Each overlay device is presented to the user as a GLDv3 device. While the
+ * link itself cannot have an IP interface created on top of it, it allows for
+ * additional GLDv3 devices, such as a VNIC, to be created on top of it which
+ * can be plumbed up with IP interfaces.
+ *
+ *
+ * --------------------
+ * General Architecture
+ * --------------------
+ *
+ * The logical overlay device that a user sees in dladm(1M) is a combination of
+ * two different components that work together. The first component is this
+ * kernel module, which is responsible for answering question one -- how should
+ * I transform a packet to put it on the wire.
+ *
+ * The second component is what we call the virtual ARP daemon, or varpd. It is
+ * a userland component that is responsible for answering the second question --
+ * Where should I send a transformed packet. Instances of the kernel overlay
+ * GLDv3 device ask varpd the question of where should a packet go.
+ *
+ * The split was done for a few reasons. Importantly, we wanted to keep the act
+ * of generating encapsulated packets in the kernel so as to ensure that the
+ * general data path was fast and also kept simple. On the flip side, while the
+ * question of where should something go may be simple, it may often be
+ * complicated and need to interface with several different external or
+ * distributed systems. In those cases, it's simpler to allow for the full
+ * flexibility of userland to be brought to bear to solve that problem and in
+ * general, the path isn't very common.
+ *
+ * The following is what makes up the logical overlay device that a user would
+ * create with dladm(1M).
+ *
+ * Kernel Userland
+ * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ * . +--------+ +--------+ +--------+ . . .
+ * . | VNIC 0 | | VNIC 1 | | VNIC 2 | . . .
+ * . +--------+ +--------+ +--------+ . . .
+ * . | | | . . .
+ * . | | | . . .
+ * . +------------+-----------+ . . .
+ * . | . . /dev/overlay .
+ * . +--------------+ . . . +------------+ .
+ * . | | . . . | | .
+ * . | Overlay |======*=================| Virtual | .
+ * . | GLDv3 Device |========================| ARP Daemon | .
+ * . | | . . | | .
+ * . +--------------+ . . +------------+ .
+ * . | . . | .
+ * . | . . | .
+ * . +----------------+ . . +--------+ .
+ * . | Overlay | . . | varpd | .
+ * . | Encapsulation | . . | Lookup | .
+ * . | Plugin | . . | Plugin | .
+ * . +----------------+ . . +--------+ .
+ * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ *
+ *
+ * This image shows the two different components and where they live.
+ * Importantly, it also shows that both the kernel overlay device and the
+ * userland varpd both support plugins. The plugins actually implement the
+ * things that users care about and the APIs have been designed to try to
+ * minimize the amount of things that a module writer needs to worry about it.
+ *
+ * IDENTIFIERS
+ *
+ * Every overlay device is defined by a unique identifier which is the overlay
+ * identifier. Its purpose is similar to that of a VLAN identifier, it's a
+ * unique number that is used to differentiate between different entries on the
+ * wire.
+ *
+ * ENCAPSULATION
+ *
+ * An overlay encapsulation plugin is a kernel miscellaneous module whose
+ * purpose is to contain knowledge about how to transform packets to put them
+ * onto the wire and to take them off. An example of an encapsulation plugin is
+ * vxlan. It's also how support for things like nvgre or geneve would be brought
+ * into the system.
+ *
+ * Each encapsulation plugins defines a series of operation vectors and
+ * properties. For the full details on everything they should provide, please
+ * read uts/common/sys/overlay_plugin.h. The encapsulation plugin is responsible
+ * for telling the system what information is required to send a packet. For
+ * example, vxlan is defined to send everything over a UDP packet and therefore
+ * requires a port and an IP address, while nvgre on the other hand is its own
+ * IP type and therefore just requires an IP address. In addition, it also
+ * provides information about the kind of socket that should be created. This is
+ * used by the kernel multiplexor, more of that in the Kernel Components
+ * section.
+ *
+ * LOOKUPS
+ *
+ * The kernel communicates requests for lookups over the character device
+ * /dev/overlay. varpd is responsible for listening for requests on that device
+ * and answering them. The character device is specific to the target path and
+ * varpd.
+ *
+ * Much as the kernel overlay module handles the bulk of the scaffolding but
+ * leaves the important work to the encapsulation plugin, varpd provides a
+ * similar role and leaves the full brunt of lookups to a userland dynamic
+ * shared object which implements the logic of lookups.
+ *
+ * Each lookup plugin defines a series of operation vectors and properties. For
+ * the full details on everything that they should provide, please read
+ * lib/varpd/libvarpd/libvarpd_provider.h. Essentially, they are given a MAC
+ * address and asked to give an address on the physical network that it should
+ * be sent to. In addition, they handle questions related to how to handle
+ * things like broadcast and multicast traffic, etc.
+ *
+ * ----------
+ * Properties
+ * ----------
+ *
+ * A device from a dladm perspective has a unique set of properties that are
+ * combined from three different sources:
+ *
+ * 1) Generic properties that every overlay device has
+ * 2) Properties that are specific to the encapsulation plugin
+ * 3) Properties that are specific to the lookup plugin
+ *
+ * All of these are exposed in a single set of properties in dladm. Note that
+ * these are not necessarily traditional link properties. However, if something
+ * is both a traditional GLDv3 link property, say the MTU of a device, and a
+ * specific property here, than the driver ensures that all existing GLDv3
+ * specific means of manipulating it are used and wraps up its private property
+ * interfaces to ensure that works.
+ *
+ * Properties in the second and third category are prefixed with the name of
+ * their module. For example, the vxlan encapsulation module has a property
+ * called the 'listen_ip'. This property would show up in dladm as
+ * 'vxlan/listen_ip'. This allows different plugins to both use similar names
+ * for similar properties and to also have independent name spaces so that
+ * overlapping names do not conflict with anything else.
+ *
+ * While the kernel combines both sets one and two into a single coherent view,
+ * it does not do anything with respect to the properties that are owned by the
+ * lookup plugin -- those are owned wholly by varpd. Instead, libdladm is in
+ * charge of bridging these two worlds into one magical experience for the user.
+ * It carries the burden of knowing about both overlay specific and varpd
+ * specific properties. Importantly, we want to maintain this distinction. We
+ * don't want to treat the kernel as an arbitrary key/value store for varpd and
+ * we want the kernel to own its own data and not have to ask userland for
+ * information that it owns.
+ *
+ * Every property in the system has the following attributes:
+ *
+ * o A name
+ * o A type
+ * o A size
+ * o Permissions
+ * o Default value
+ * o Valid value ranges
+ * o A value
+ *
+ * Everything except for the value is obtained by callers through the propinfo
+ * callbacks and a property has a maximum size of OVERLAY_PROP_SIZEMAX,
+ * currently 256 bytes.
+ *
+ * The following are the supported types of properties:
+ *
+ * OVERLAY_PROP_T_INT
+ *
+ * A signed integer, its length is 8 bytes, corresponding to a
+ * int64_t.
+ *
+ * OVERLAY_PROP_T_UINT
+ *
+ * An unsigned integer, its length is 8 bytes, corresponding to a
+ * uint64_t.
+ *
+ * OVERLAY_PROP_T_IP
+ *
+ * A struct in6_addr, it has a fixed size.
+ *
+ * OVERLAY_PROP_T_STRING
+ *
+ * A null-terminated character string encoded in either ASCII or
+ * UTF-8. Note that the size of the string includes the null
+ * terminator.
+ *
+ * The next thing that we apply to a property is its permission. The permissions
+ * are put together by the bitwise or of the following flags and values.
+ *
+ * OVERLAY_PROP_PERM_REQ
+ *
+ * This indicates a required property. A property that is required
+ * must be set by a consumer before the device can be created. If a
+ * required property has a default property, this constraint is
+ * loosened because the default property defines the value.
+ *
+ * OVERLAY_PORP_PERM_READ
+ *
+ * This indicates that a property can be read. All properties will
+ * have this value set.
+ *
+ * OVERLAY_PROP_PERM_WRITE
+ *
+ * This indicates that a property can be written to and thus
+ * updated by userland. Properties that are only intended to
+ * display information, will not have OVERLAY_PROP_PERM_WRITE set.
+ *
+ * In addition, a few additional values are defined as a convenience to
+ * consumers. The first, OVERLAY_PROP_PERM_RW, is a combination of
+ * OVERLAY_PROP_PERM_READ and OVERLAY_PERM_PROP_WRITE. The second,
+ * OVERLAY_PROP_PERM_RRW, is a combination of OVERLAY_PROP_PERM_REQ,
+ * OVERLAY_PROP_PERM_READ, and OVERLAY_PROP_PERM_WRITE. The protection mode of a
+ * property should generally be a constant across its lifetime.
+ *
+ * A property may optionally have a default value. If it does have a default
+ * value, and that property is not set to be a different value, then the default
+ * value is inherited automatically. It also means that if the default value is
+ * acceptable, there is no need to set the value for a required property. For
+ * example, the vxlan module has the vxlan/listen_port property which is
+ * required, but has a default value of 4789 (the IANA assigned port). Because
+ * of that default value, there is no need for it to be set.
+ *
+ * Finally, a property may declare a list of valid values. These valid values
+ * are used for display purposes, they are not enforced by the broader system,
+ * but merely allow a means for the information to be communicated to the user
+ * through dladm(1M). Like a default value, this is optional.
+ *
+ * The general scaffolding does not do very much with respect to the getting and
+ * setting of properties. That is really owned by the individual plugins
+ * themselves.
+ *
+ * -----------------------------
+ * Destinations and Plugin Types
+ * -----------------------------
+ *
+ * Both encapsulation and lookup plugins define the kinds of destinations that
+ * they know how to support. There are three different pieces of information
+ * that can be used to address to a destination currently, all of which is
+ * summarized in the type overlay_point_t. Any combination of these is
+ * supported.
+ *
+ * OVERLAY_PLUGIN_D_ETHERNET
+ *
+ * An Ethernet MAC address is required.
+ *
+ * OVERLAY_PLUGIN_D_IP
+ *
+ * An IP address is required. All IP addresses used by the overlay
+ * system are transmitted as IPv6 addresses. IPv4 addresses can be
+ * represented by using IPv4-mapped IPv6 addresses.
+ *
+ * OVERLAY_PLUGIN_D_PORT
+ *
+ * A TCP/UDP port is required.
+ *
+ * A kernel encapsulation plugin declares which of these that it requires, it's
+ * a static set. On the other hand, a userland lookup plugin can be built to
+ * support all of these or any combination thereof. It gets passed the required
+ * destination type, based on the kernel encapsulation method, and then it makes
+ * the determination as to whether or not it supports it. For example, the
+ * direct plugin can support either an IP or both an IP and a port, it simply
+ * doesn't display the direct/dest_port property in the cases where a port is
+ * not required to support this.
+ *
+ * The user lookup plugins have two different modes of operation which
+ * determines how they interact with the broader system and how look ups are
+ * performed. These types are:
+ *
+ * OVERLAY_TARGET_POINT
+ *
+ * A point to point plugin has a single static definition for where
+ * to send all traffic. Every packet in the system always gets sent
+ * to the exact same destination which is programmed into the
+ * kernel when the general device is activated.
+ *
+ * OVERLAY_TARGET_DYNAMIC
+ *
+ * A dynamic plugin does not have a single static definition.
+ * Instead, for each destination, the kernel makes an asynchronous
+ * request to varpd to determine where the packet should be routed,
+ * and if a specific destination is found, then that destination is
+ * cached in the overlay device's target cache.
+ *
+ * This distinction, while important for the general overlay device's operation,
+ * is not important to the encapsulation plugins. They don't need to know about
+ * any of these pieces. It's just a concern for varpd, the userland plugin, and
+ * the general overlay scaffolding.
+ *
+ * When an overlay device is set to OVERLAY_TARGET_POINT, then it does not
+ * maintain a target cache, and instead just keeps track of the destination and
+ * always sends encapsulated packets to that address. When the target type is of
+ * OVERLAY_TARGET_DYNAMIC, then the kernel maintains a cache of all such
+ * destinations. These destinations are kept around in an instance of a
+ * reference hash that is specific to the given overlay device. Entries in the
+ * cache can be invalidated and replaced by varpd and its lookup plugins.
+ *
+ * ----------------------------------
+ * Kernel Components and Architecture
+ * ----------------------------------
+ *
+ * There are multiple pieces inside the kernel that work together, there is the
+ * general overlay_dev_t structure, which is the logical GLDv3 device, but it
+ * itself has references to things like an instance of an encapsulation plugin,
+ * a pointer to a mux and a target cache. It can roughly be summarized in the
+ * following image:
+ *
+ * +------------------+
+ * | global |
+ * | overlay list |
+ * | overlay_dev_list |
+ * +------------------+
+ * |
+ * | +-----------------------+ +---------------+
+ * +->| GLDv3 Device |----------->| GLDv3 Device | -> ...
+ * | overlay_dev_t | | overlay_dev_t |
+ * | | +---------------+
+ * | |
+ * | mac_handle_t -----+---> GLDv3 handle to MAC
+ * | datalink_id_t -----+---> Datalink ID used by DLS
+ * | overlay_dev_flag_t ---+---> Device state
+ * | uint_t -----+---> Curent device MTU
+ * | uint_t -----+---> In-progress RX operations
+ * | uint_t -----+---> In-progress TX operations
+ * | char[] -----+---> FMA degraded message
+ * | void * -----+---> plugin private data
+ * | overlay_target_t * ---+---------------------+
+ * | overlay_plugin_t * ---+---------+ |
+ * +-----------------------+ | |
+ * ^ | |
+ * +--------------------+ | | |
+ * | Kernel Socket | | | |
+ * | Multiplexor | | | |
+ * | overlay_mux_t | | | |
+ * | | | | |
+ * | avl_tree_t -+--+ | |
+ * | uint_t -+--> socket family | |
+ * | uint_t -+--> socket type | |
+ * | uint_t -+--> socket protocol | |
+ * | ksocket_t -+--> I/O socket | |
+ * | struct sockaddr * -+--> ksocket address | |
+ * | overlay_plugin_t --+--------+ | |
+ * +--------------------+ | | |
+ * | | |
+ * +-------------------------+ | | |
+ * | Encap Plugin |<--+-----------+ |
+ * | overlay_plugin_t | |
+ * | | |
+ * | char * ---+--> plugin name |
+ * | overlay_plugin_ops_t * -+--> plugin downcalls |
+ * | char ** (props) ---+--> property list |
+ * | uint_t ---+--> id length |
+ * | overlay_plugin_flags_t -+--> plugin flags |
+ * | overlay_plugin_dest_t --+--> destination type v
+ * +-------------------------+ +-------------------------+
+ * | Target Cache |
+ * | overlay_target_t |
+ * | |
+ * cache mode <--+- overlay_target_mode_t |
+ * dest type <--+- overlay_plugin_dest_t |
+ * cache flags <--+- overlay_target_flag_t |
+ * varpd id <--+- uint64_t |
+ * outstanding varpd reqs. <--+- uint_t |
+ * OVERLAY_TARGET_POINT state <--+- overlay_target_point_t |
+ * OVERLAY_TARGET_DYNAMIC state <-+---+- overlay_target_dyn_t |
+ * | +-------------------------+
+ * +-----------------------+
+ * |
+ * v
+ * +-------------------------------+ +------------------------+
+ * | Target Entry |-->| Target Entry |--> ...
+ * | overlay_target_entry_t | | overlay_target_entry_t |
+ * | | +------------------------+
+ * | |
+ * | overlay_target_entry_flags_t -+--> Entry flags
+ * | uint8_t[ETHERADDRL] ---+--> Target MAC address
+ * | overlay_target_point_t ---+--> Target underlay address
+ * | mblk_t * ---+--> outstanding mblk head
+ * | mblk_t * ---+--> outstanding mblk tail
+ * | size_t ---+--> outstanding mblk size
+ * +-------------------------------+
+ *
+ * The primary entries that we care about are the overlay_dev_t, which
+ * correspond to each overlay device that is created with dladm(1M). Globally,
+ * these devices are maintained in a simple list_t which is protected with a
+ * lock. Hence, these include important information such as the mac_handle_t
+ * and a datalink_id_t which is used to interact with the broader MAC and DLS
+ * ecosystem. We also maintain additional information such as the current state,
+ * outstanding operations, the mtu, and importantly, the plugin's private data.
+ * This is the instance of an encapsulation plugin that gets created as part of
+ * creating an overlay device. Another aspect of this is that the overlay_dev_t
+ * also includes information with respect to FMA. For more information, see the
+ * FMA section.
+ *
+ * Each overlay_dev_t has a pointer to a plugin, a mux, and a target. The plugin
+ * is the encapsulation plugin. This allows the device to make downcalls into it
+ * based on doing things like getting and setting properties. Otherwise, the
+ * plugin itself is a fairly straightforward entity. They are maintained in an
+ * (not pictured above) list. The plugins themselves mostly maintain things like
+ * the static list of properties, what kind of destination they require, and the
+ * operations vector. A given module may contain more if necessary.
+ *
+ * The next piece of the puzzle is the mux, or a multiplexor. The mux itself
+ * maintains a ksocket and it is through the mux that we send and receive
+ * message blocks. The mux represents a socket type and address, as well as a
+ * plugin. Multiple overlay_dev_t devices may then share the same mux. For
+ * example, consider the case where you have different instances of vxlan all on
+ * the same underlay network. These would all logically share the same IP
+ * address and port that packets are sent and received on; however, what differs
+ * is the decapuslation ID.
+ *
+ * Each mux maintains a ksocket_t which is similar to a socket(3SOCKET). Unlike
+ * a socket, we enable a direct callback on the ksocket. This means that
+ * whenever a message block chain is received, rather than sitting there and
+ * getting a callback in a context and kicking that back out to a taskq. Instead
+ * data comes into the callback function overlay_mux_recv().
+ *
+ * The mux is given encapsulated packets (via overlay_m_tx, the GLDv3 tx
+ * function) to transmit. It receives encapsulated packets, decapsulates them to
+ * determine the overlay identifier, looks up the given device that matches that
+ * identifier, and then causes the broader MAC world to receive the packet with
+ * a call to mac_rx().
+ *
+ * Today, we don't do too much that's special with the ksocket; however, as
+ * hardware is gaining understanding for these encapuslation protocols, we'll
+ * probably want to think of better ways to get those capabilities passed down
+ * and potentially better ways to program receive filters so they get directly
+ * to us. Though, that's all fantasy future land.
+ *
+ * The next part of the puzzle is the target cache. The purpose of the target
+ * cache is to cache where we should send a packet on the underlay network,
+ * given its mac address. The target cache operates in two modes depending on
+ * whether the lookup module was declared to OVERLAY_TARGET_POINT or
+ * OVERLAY_TARGET_DYANMIC.
+ *
+ * In the case where the target cache has been programmed to be
+ * OVERLAY_TARGET_POINT, then we only maintain a single overlay_target_point_t
+ * which has the destination that we send everything, no matter the destination
+ * mac address.
+ *
+ * On the other hand, when we have an instance of OVERLAY_TARGET_DYNAMIC, things
+ * are much more interesting and as a result, more complicated. We primarily
+ * store lists of overlay_target_entry_t's which are stored in both an avl tree
+ * and a refhash_t. The primary look up path uses the refhash_t and the avl tree
+ * is only used for a few of the target ioctls used to dump data such that we
+ * can get a consistent iteration order for things like dladm show-overlay -t.
+ * The key that we use for the reference hashtable is based on the mac address
+ * in the cache and currently we just do a simple CRC32 to transform it into a
+ * hash.
+ *
+ * Each entry maintains a set of flags to indicate the current status of the
+ * request. The flags may indicate one of three states: that current cache entry
+ * is valid, that the current cache entry has been directed to drop all output,
+ * and that the current cache entry is invalid and may be being looked up. In
+ * the case where it's valid, we just take the destination address and run with
+ * it.
+ *
+ * If it's invalid and a lookup has not been made, then we start the process
+ * that prepares a query that will make its way up to varpd. The cache entry
+ * entry maintains a message block chain of outstanding message blocks and a
+ * size. These lists are populated only when we don't know the answer as to
+ * where should these be sent. The size entry is used to cap the amount of
+ * outstanding data that we don't know the answer to. If we exceed a cap on the
+ * amount of outstanding data (currently 1 Mb), then we'll drop any additional
+ * packets. Once we get an answer indicating a valid destination, we transmit
+ * any outstanding data to that place. For the full story on how we look that up
+ * will be discussed in the section on the Target Cache Lifecycle.
+ *
+ * ------------------------
+ * FMA and Degraded Devices
+ * ------------------------
+ *
+ * Every kernel overlay device keeps track of its FMA state. Today in FMA we
+ * cannot represent partitions between resources nor can we represent that a
+ * given minor node of a psuedo device has failed -- if we degrade the overlay
+ * device, then the entire dev_info_t is degraded. However, we still want to be
+ * able to indicate to administrators that things may go wrong.
+ *
+ * To this end, we've added a notion of a degraded state to every overlay
+ * device. This state is primarily dictated by userland and it can happen for
+ * various reasons. Generally, because a userland lookup plugin has been
+ * partitioned, or something has gone wrong such that there is no longer any
+ * userland lookup module for a device, then we'll mark it degraded.
+ *
+ * As long as any of our minor instances is degraded, then we'll fire off the
+ * FMA event to note that. Once the last degraded instance is no longer
+ * degraded, then we'll end up telling FMA that we're all clean.
+ *
+ * To help administrators get a better sense of which of the various minor
+ * devices is wrong, we store the odd_fmamsg[] character array. This character
+ * array can be fetched with doing a dladm show-overlay -f.
+ *
+ * Note, that it's important that we do not update the link status of the
+ * devices. We want to remain up as much as possible. By changing the link in a
+ * degraded state, this may end up making things worse. We may still actually
+ * have information in the target cache and if we mark the link down, that'll
+ * result in not being able to use it. The reason being that this'll mark all
+ * the downstream VNICs down which will go to IP and from there we end up
+ * dealing with sadness.
+ *
+ * -----------------------
+ * Target Cache Life Cycle
+ * -----------------------
+ *
+ * This section only applies when we have a lookup plugin of
+ * OVERLAY_TARGET_DYNAMIC. None of this applies to those of type
+ * OVERLAY_TARGET_POINT.
+ *
+ * While we got into the target cache in the general architecture section, it's
+ * worth going into more details as to how this actually works and showing some
+ * examples and state machines. Recall that a target cache entry basically has
+ * the following state transition diagram:
+ *
+ * Initial state
+ * . . . . . . first access . . . varpd lookup enqueued
+ * . . .
+ * . . .
+ * +-------+ . +----------+ .
+ * | No |------*---->| Invalid |-------*----+
+ * | Entry | | Entry | |
+ * +-------+ +----------+ |
+ * varpd ^ ^ varpd |
+ * invalidate | | drop |
+ * . . . * * . . v
+ * +-------+ | | +---------+
+ * | Entry |--->-----+ +----<----| Entry |
+ * | Valid |<----------*---------<----| Pending |->-+ varpd
+ * +-------+ . +---------+ * . . drop, but
+ * . varpd ^ | other queued
+ * . success | | entries
+ * +-----+
+ *
+ * When the table is first created, it is empty. As we attempt to lookup entries
+ * and we find there is no entry at all, we'll create a new table entry for it.
+ * At that point the entry is technically in an invalid state, that means that
+ * we have no valid data from varpd. In that case, we'll go ahead and queue the
+ * packet into the entry's pending chain, and queue a varpd lookup, setting the
+ * OVERLAY_ENTRY_F_PENDING flag in the progress.
+ *
+ * If additional mblk_t's come in for this entry, we end up appending them to
+ * the tail of the chain, if and only if, we don't exceed the threshold for the
+ * amount of space they can take up. An entry remains pending until we get a
+ * varpd reply. If varpd replies with a valid results, we move to the valid
+ * entry state, and remove the OVERLAY_ENTRY_F_PENDING flag and set it with one
+ * of OVERLAY_ENTRY_F_VALID or OVERLAY_ENTRY_F_DROP as appropriate.
+ *
+ * Once an entry is valid, it stays valid until user land tells us to invalidate
+ * it with an ioctl or replace it, OVERLAY_TARG_CACHE_REMOE and
+ * OVERLAY_TARG_CACHE_SET respectively.
+ *
+ * If the lookup fails with a call to drop the packet, then the next state is
+ * determined by the state of the queue. If the set of outstanding entries is
+ * empty, then we just transition back to the invalid state. If instead, the
+ * set of outstanding entries is not empty, then we'll queue another entry and
+ * stay in the same state, repeating this until the number of requests is
+ * drained.
+ *
+ * The following images describes the flow of a given lookup and where the
+ * overlay_target_entry_t is at any given time.
+ *
+ * +-------------------+
+ * | Invalid Entry | An entry starts off as an invalid entry
+ * | de:ad:be:ef:00:00 | and only exists in the target cache.
+ * +-------------------+
+ *
+ * ~~~~
+ *
+ * +---------------------+
+ * | Global list_t | A mblk_t comes in for an entry. We
+ * | overlay_target_list | append it to the overlay_target_list.
+ * +---------------------+
+ * |
+ * v
+ * +-------------------+ +-------------------+
+ * | Pending Entry |----->| Pending Entry |--->...
+ * | 42:5e:1a:10:d6:2d | | de:ad:be:ef:00:00 |
+ * +-------------------+ +-------------------+
+ *
+ * ~~~~
+ *
+ * +--------------------------+
+ * | /dev/overlay minor state | User land said that it would look up an
+ * | overlay_target_hdl_t | entry for us. We remove it from the
+ * +--------------------------+ global list and add it to the handle's
+ * | outstanding list.
+ * |
+ * v
+ * +-------------------+ +-------------------+
+ * | Pending Entry |----->| Pending Entry |
+ * | 90:b8:d0:79:02:dd | | de:ad:be:ef:00:00 |
+ * +-------------------+ +-------------------+
+ *
+ * ~~~~
+ *
+ * +-------------------+
+ * | Valid Entry | varpd returned an answer with
+ * | de:ad:be:ef:00:00 | OVERLAY_IOC_RESPOND and the target cache
+ * | 10.169.23.42:4789 | entry is now populated with a
+ * +-------------------+ destination and marked as valid
+ *
+ *
+ * The lookup mechanism is performed via a series of operations on the character
+ * psuedo-device /dev/overlay. The only thing that uses this device is the
+ * userland daemon varpd. /dev/overlay is a cloneable device, each open of it
+ * granting a new minor number which maintains its own state. We maintain this
+ * state so that way if an outstanding lookup was queued to something that
+ * crashed or closed its handle without responding, we can know about this and
+ * thus handle it appropriately.
+ *
+ * When a lookup is first created it's added to our global list of outstanding
+ * lookups. To service requests, userland is required to perform an ioctl to ask
+ * for a request. We will block it in the kernel a set amount of time waiting
+ * for a request. When we give a request to a given minor instance of the
+ * device, we remove it from the global list and append the request to the
+ * device's list of outstanding entries, for the reasons we discussed above.
+ * When a lookup comes in, we give user land a smaller amount of information
+ * specific to that packet, the overlay_targ_lookup_t. It includes a request id
+ * to identify this, and then the overlay id, the varpd id, the header and
+ * packet size, the source and destination mac address, the SAP, and any
+ * potential VLAN header.
+ *
+ * At that point, it stays in that outstanding list until one of two ioctls are
+ * returned: OVERLAY_TARG_RESPOND or OVERLAY_TARG_DROP. During this time,
+ * userland may also perform other operations. For example, it may use
+ * OVERLAY_TARG_PKT to get a copy of this packet so it can perform more in-depth
+ * analysis of what to do beyond what we gave it initially. This is useful for
+ * providing proxy arp and the like. Finally, there are two other ioctls that
+ * varpd can then do. The first is OVERLAY_TARG_INJECT which injects the
+ * non-jumbo frame packet up into that mac device and OVERLAY_TARG_RESEND which
+ * causes us to encapsulate and send out the packet they've given us.
+ *
+ *
+ * Finally, through the target cache, several ioctls are provided to allow for
+ * interrogation and management of the cache. They allow for individual entries
+ * to be retrieved, set, or have the entire table flushed. For the full set of
+ * ioctls here and what they do, take a look at uts/common/sys/overlay_target.h.
+ *
+ * ------------------
+ * Sample Packet Flow
+ * ------------------
+ *
+ * There's a lot of pieces here, hopefully an example of how this all fits
+ * together will help clarify and elucidate what's going on. We're going to
+ * first track an outgoing packet, eg. one that is sent from an IP interface on
+ * a VNIC on top of an overlay device, and then we'll look at what it means to
+ * respond to that.
+ *
+ *
+ * +----------------+ +--------------+ +------------------+
+ * | IP/DLS send |------->| MAC sends it |----------->| mblk_t reaches |
+ * | packet to MAC | | to the GLDv3 | | overlay GLDv3 tx |
+ * +----------------+ | VNIC device | | overlay_m_tx() |
+ * +--------------+ +------------------+
+ * |
+ * . lookup . cache |
+ * . drop . miss v
+ * +---------+ . +--------+ . +------------------+
+ * | freemsg |<-----*-------| varpd |<---*------| Lookup each mblk |
+ * | mblk_t | | lookup | | in the target |
+ * +---------+ | queued | | cache |
+ * ^ +--------+ +------------------+
+ * on send | | | cache
+ * error . . * *. . lookup * . . hit
+ * | | success v
+ * | | +------------------+
+ * +-----------------+ +--------------->| call plugin |
+ * | Send out | | ovpo_encap() to |
+ * | overlay_mux_t's |<----------------------------------| get encap mblk_t |
+ * | ksocket | +------------------+
+ * +-----------------+
+ *
+ * The receive end point looks a little different and looks more like:
+ *
+ * +------------------+ +----------------+ +-----------+
+ * | mblk_t comes off |---->| enter netstack |--->| delivered |---+
+ * | the physical | | IP stack | | to | * . . direct
+ * | device | +----------------+ | ksocket | | callback
+ * +------------------+ +-----------+ |
+ * . overlay id |
+ * . not found v
+ * +-----------+ . +-----------------+ +--------------------+
+ * | freemsg |<--*------| call plugin |<------| overlay_mux_recv() |
+ * | mblk_t | | ovpo_decap() to | +--------------------+
+ * +-----------+ | decap mblk_t |
+ * +-----------------+
+ * |
+ * * . . overlay id
+ * v found
+ * +--------+ +----------------+
+ * | adjust |----->| call mac_rx |
+ * | mblk_t | | on original |
+ * +--------+ | decaped packet |
+ * +----------------+
+ *
+ * ------------------
+ * Netstack Awareness
+ * ------------------
+ *
+ * In the above image we note that this enters a netstack. Today the only
+ * netstack that can be is the global zone as the overlay driver itself is not
+ * exactly netstack aware. What this really means is that varpd cannot run in a
+ * non-global zone and an overlay device cannot belong to a non-global zone.
+ * Non-global zones can still have a VNIC assigned to them that's been created
+ * over the overlay device the same way they would if it had been created over
+ * an etherstub or a physical device.
+ *
+ * The majority of the work to make it netstack aware is straightforward and the
+ * biggest thing is to create a netstack module that allows us to hook into
+ * netstack (and thus zone) creation and destruction. From there, we need to
+ * amend the target cache lookup routines that we discussed earlier to not have
+ * a global outstanding list and a global list of handles, but rather, one per
+ * netstack.
+ *
+ * For the mux, we'll need to open the ksocket in the context of the zone, we
+ * can likely do this with a properly composed credential, but we'll need to do
+ * some more work on that path. Finally, we'll want to make sure the dld ioctls
+ * are aware of the zoneid of the caller and we use that appropriately and store
+ * it in the overlay_dev_t.
+ *
+ * -----------
+ * GLDv3 Notes
+ * -----------
+ *
+ * The overlay driver implements a GLDv3 device. Parts of GLDv3 are more
+ * relevant and other parts are much less relevant for us. For example, the
+ * GLDv3 is used to toggle the device being put into and out of promiscuous
+ * mode, to program MAC addresses for unicast and multicast hardware filters.
+ * Today, an overlay device doesn't have a notion of promiscuous mode nor does
+ * it have a notion of unicast and multicast addresses programmed into the
+ * device. Instead, for the purposes of the hardware filter, we don't do
+ * anything and just always accept new addresses being added and removed.
+ *
+ * If the GLDv3 start function has not been called, then we will not use this
+ * device for I/O purposes. Any calls to transmit or receive should be dropped,
+ * though the GLDv3 guarantees us that transmit will not be called without
+ * calling start. Similarly, once stop is called, then no packets can be dealt
+ * with.
+ *
+ * Today we don't support the stat interfaces, though there's no good reason
+ * that we shouldn't assemble some of the stats based on what we have in the
+ * future.
+ *
+ * When it comes to link properties, many of the traditional link properties do
+ * not apply and many others MAC handles for us. For example, we don't need to
+ * implement anything for overlay_m_getprop() to deal with returning the MTU, as
+ * MAC never calls into us for that. As such, there isn't much of anything to
+ * support in terms of properties.
+ *
+ * Today, we don't support any notion of hardware capabilities. However, if
+ * future NIC hardware or other changes to the system cause it to make sense for
+ * us to emulate logical groups, then we should do that. However, we still do
+ * implement a capab function so that we can identify ourselves as an overlay
+ * device to the broader MAC framework. This is done mostly so that a device
+ * created on top of us can have fanout rings as we don't try to lie about a
+ * speed for our device.
+ *
+ * The other question is what should be done for a device's MTU and margin. We
+ * set our minimum supported MTU to be the minimum value that an IP network may
+ * be set to 576 -- which mimics what an etherstub does. On the flip side, we
+ * have our upper bound set to 8900. This value comes from the fact that a lot
+ * of jumbo networks use their maximum as 9000. As such, we want to reserve 100
+ * bytes, which isn't exactly the most accurate number, but it'll be good enough
+ * for now. Because of that, our default MTU off of these devices is 1400, as
+ * the default MTU for everything is usually 1500 or whatever the underlying
+ * device is at; however, this is a bit simpler than asking the netstack what
+ * are all the IP interfaces at. It also calls into question how PMTU and PMTU
+ * discovery should work here. The challenge, especially for
+ * OVERLAY_TARG_DYNAMIC is that the MTU to any of the places will vary and it's
+ * not clear that if you have a single bad entry that the overall MTU should be
+ * lowered. Instead, we should figure out a better way of determining these
+ * kinds of PMTU errors and appropriately alerting the administrator via FMA.
+ *
+ * Regarding margin, we allow a margin of up to VLAN_TAGSZ depending on whether
+ * or not the underlying encapsulation device supports VLAN tags. If it does,
+ * then we'll set the margin to allow for it, otherwise, we will not.
+ */
+
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/policy.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/strsun.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/ddifm.h>
+
+#include <sys/dls.h>
+#include <sys/dld_ioc.h>
+#include <sys/mac_provider.h>
+#include <sys/mac_client_priv.h>
+#include <sys/mac_ether.h>
+#include <sys/vlan.h>
+
+#include <sys/overlay_impl.h>
+
+dev_info_t *overlay_dip;
+static kmutex_t overlay_dev_lock;
+static list_t overlay_dev_list;
+static uint8_t overlay_macaddr[ETHERADDRL] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+typedef enum overlay_dev_prop {
+ OVERLAY_DEV_P_MTU = 0,
+ OVERLAY_DEV_P_VNETID,
+ OVERLAY_DEV_P_ENCAP,
+ OVERLAY_DEV_P_VARPDID
+} overlay_dev_prop_t;
+
+#define OVERLAY_DEV_NPROPS 4
+static const char *overlay_dev_props[] = {
+ "mtu",
+ "vnetid",
+ "encap",
+ "varpd/id"
+};
+
+#define OVERLAY_MTU_MIN 576
+#define OVERLAY_MTU_DEF 1400
+#define OVERLAY_MTU_MAX 8900
+
+overlay_dev_t *
+overlay_hold_by_dlid(datalink_id_t id)
+{
+ overlay_dev_t *o;
+
+ mutex_enter(&overlay_dev_lock);
+ for (o = list_head(&overlay_dev_list); o != NULL;
+ o = list_next(&overlay_dev_list, o)) {
+ if (id == o->odd_linkid) {
+ mutex_enter(&o->odd_lock);
+ o->odd_ref++;
+ mutex_exit(&o->odd_lock);
+ mutex_exit(&overlay_dev_lock);
+ return (o);
+ }
+ }
+
+ mutex_exit(&overlay_dev_lock);
+ return (NULL);
+}
+
+void
+overlay_hold_rele(overlay_dev_t *odd)
+{
+ mutex_enter(&odd->odd_lock);
+ ASSERT(odd->odd_ref > 0);
+ odd->odd_ref--;
+ mutex_exit(&odd->odd_lock);
+}
+
+void
+overlay_io_start(overlay_dev_t *odd, overlay_dev_flag_t flag)
+{
+ ASSERT(flag == OVERLAY_F_IN_RX || flag == OVERLAY_F_IN_TX);
+ ASSERT(MUTEX_HELD(&odd->odd_lock));
+
+ if (flag & OVERLAY_F_IN_RX)
+ odd->odd_rxcount++;
+ if (flag & OVERLAY_F_IN_TX)
+ odd->odd_txcount++;
+ odd->odd_flags |= flag;
+}
+
+void
+overlay_io_done(overlay_dev_t *odd, overlay_dev_flag_t flag)
+{
+ boolean_t signal = B_FALSE;
+
+ ASSERT(flag == OVERLAY_F_IN_RX || flag == OVERLAY_F_IN_TX);
+ ASSERT(MUTEX_HELD(&odd->odd_lock));
+
+ if (flag & OVERLAY_F_IN_RX) {
+ ASSERT(odd->odd_rxcount > 0);
+ odd->odd_rxcount--;
+ if (odd->odd_rxcount == 0) {
+ signal = B_TRUE;
+ odd->odd_flags &= ~OVERLAY_F_IN_RX;
+ }
+ }
+ if (flag & OVERLAY_F_IN_TX) {
+ ASSERT(odd->odd_txcount > 0);
+ odd->odd_txcount--;
+ if (odd->odd_txcount == 0) {
+ signal = B_TRUE;
+ odd->odd_flags &= ~OVERLAY_F_IN_TX;
+ }
+ }
+
+ if (signal == B_TRUE)
+ cv_broadcast(&odd->odd_iowait);
+}
+
+static void
+overlay_io_wait(overlay_dev_t *odd, overlay_dev_flag_t flag)
+{
+ ASSERT((flag & ~OVERLAY_F_IOMASK) == 0);
+ ASSERT(MUTEX_HELD(&odd->odd_lock));
+
+ while (odd->odd_flags & flag) {
+ cv_wait(&odd->odd_iowait, &odd->odd_lock);
+ }
+}
+
+void
+overlay_dev_iter(overlay_dev_iter_f func, void *arg)
+{
+ overlay_dev_t *odd;
+
+ mutex_enter(&overlay_dev_lock);
+ for (odd = list_head(&overlay_dev_list); odd != NULL;
+ odd = list_next(&overlay_dev_list, odd)) {
+ if (func(odd, arg) != 0) {
+ mutex_exit(&overlay_dev_lock);
+ return;
+ }
+ }
+ mutex_exit(&overlay_dev_lock);
+}
+
+/* ARGSUSED */
+static int
+overlay_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+ return (ENOTSUP);
+}
+
+static int
+overlay_m_start(void *arg)
+{
+ overlay_dev_t *odd = arg;
+ overlay_mux_t *mux;
+ int ret, domain, family, prot;
+ struct sockaddr_storage storage;
+ socklen_t slen;
+
+ mutex_enter(&odd->odd_lock);
+ if ((odd->odd_flags & OVERLAY_F_ACTIVATED) == 0) {
+ mutex_exit(&odd->odd_lock);
+ return (EAGAIN);
+ }
+ mutex_exit(&odd->odd_lock);
+
+ ret = odd->odd_plugin->ovp_ops->ovpo_socket(odd->odd_pvoid, &domain,
+ &family, &prot, (struct sockaddr *)&storage, &slen);
+ if (ret != 0)
+ return (ret);
+
+ mux = overlay_mux_open(odd->odd_plugin, domain, family, prot,
+ (struct sockaddr *)&storage, slen, &ret);
+ if (mux == NULL)
+ return (ret);
+
+ overlay_mux_add_dev(mux, odd);
+ odd->odd_mux = mux;
+ mutex_enter(&odd->odd_lock);
+ ASSERT(!(odd->odd_flags & OVERLAY_F_IN_MUX));
+ odd->odd_flags |= OVERLAY_F_IN_MUX;
+ mutex_exit(&odd->odd_lock);
+
+ return (0);
+}
+
+static void
+overlay_m_stop(void *arg)
+{
+ overlay_dev_t *odd = arg;
+
+ /*
+ * The MAC Perimeter is held here, so we don't have to worry about
+ * synchornizing this with respect to metadata operations.
+ */
+ mutex_enter(&odd->odd_lock);
+ VERIFY(odd->odd_flags & OVERLAY_F_IN_MUX);
+ VERIFY(!(odd->odd_flags & OVERLAY_F_MDDROP));
+ odd->odd_flags |= OVERLAY_F_MDDROP;
+ overlay_io_wait(odd, OVERLAY_F_IOMASK);
+ mutex_exit(&odd->odd_lock);
+
+ overlay_mux_remove_dev(odd->odd_mux, odd);
+ overlay_mux_close(odd->odd_mux);
+ odd->odd_mux = NULL;
+
+ mutex_enter(&odd->odd_lock);
+ odd->odd_flags &= ~OVERLAY_F_IN_MUX;
+ odd->odd_flags &= ~OVERLAY_F_MDDROP;
+ VERIFY((odd->odd_flags & OVERLAY_F_STOPMASK) == 0);
+ mutex_exit(&odd->odd_lock);
+}
+
+/*
+ * For more info on this, see the big theory statement.
+ */
+/* ARGSUSED */
+static int
+overlay_m_promisc(void *arg, boolean_t on)
+{
+ return (0);
+}
+
+/*
+ * For more info on this, see the big theory statement.
+ */
+/* ARGSUSED */
+static int
+overlay_m_multicast(void *arg, boolean_t add, const uint8_t *addrp)
+{
+ return (0);
+}
+
+/*
+ * For more info on this, see the big theory statement.
+ */
+/* ARGSUSED */
+static int
+overlay_m_unicast(void *arg, const uint8_t *macaddr)
+{
+ return (0);
+}
+
+mblk_t *
+overlay_m_tx(void *arg, mblk_t *mp_chain)
+{
+ overlay_dev_t *odd = arg;
+ mblk_t *mp, *ep;
+ int ret;
+ ovep_encap_info_t einfo;
+ struct msghdr hdr;
+
+ mutex_enter(&odd->odd_lock);
+ if ((odd->odd_flags & OVERLAY_F_MDDROP) ||
+ !(odd->odd_flags & OVERLAY_F_IN_MUX)) {
+ mutex_exit(&odd->odd_lock);
+ freemsgchain(mp_chain);
+ return (NULL);
+ }
+ overlay_io_start(odd, OVERLAY_F_IN_TX);
+ mutex_exit(&odd->odd_lock);
+
+ bzero(&hdr, sizeof (struct msghdr));
+
+ bzero(&einfo, sizeof (ovep_encap_info_t));
+ einfo.ovdi_id = odd->odd_vid;
+ mp = mp_chain;
+ while (mp != NULL) {
+ socklen_t slen;
+ struct sockaddr_storage storage;
+
+ mp_chain = mp->b_next;
+ mp->b_next = NULL;
+ ep = NULL;
+
+ ret = overlay_target_lookup(odd, mp,
+ (struct sockaddr *)&storage, &slen);
+ if (ret != OVERLAY_TARGET_OK) {
+ if (ret == OVERLAY_TARGET_DROP)
+ freemsg(mp);
+ mp = mp_chain;
+ continue;
+ }
+
+ hdr.msg_name = &storage;
+ hdr.msg_namelen = slen;
+
+ ret = odd->odd_plugin->ovp_ops->ovpo_encap(odd->odd_mh, mp,
+ &einfo, &ep);
+ if (ret != 0 || ep == NULL) {
+ freemsg(mp);
+ goto out;
+ }
+
+ ep->b_cont = mp;
+ ret = overlay_mux_tx(odd->odd_mux, &hdr, ep);
+ if (ret != 0)
+ goto out;
+
+ mp = mp_chain;
+ }
+
+out:
+ mutex_enter(&odd->odd_lock);
+ overlay_io_done(odd, OVERLAY_F_IN_TX);
+ mutex_exit(&odd->odd_lock);
+ return (mp_chain);
+}
+
+/* ARGSUSED */
+static void
+overlay_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
+{
+ miocnak(q, mp, 0, ENOTSUP);
+}
+
+/* ARGSUSED */
+static boolean_t
+overlay_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
+{
+ /*
+ * Tell MAC we're an overlay.
+ */
+ if (cap == MAC_CAPAB_OVERLAY)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+/* ARGSUSED */
+static int
+overlay_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_valsize, const void *pr_val)
+{
+ uint32_t mtu, old;
+ int err;
+ overlay_dev_t *odd = arg;
+
+ if (pr_num != MAC_PROP_MTU)
+ return (ENOTSUP);
+
+ bcopy(pr_val, &mtu, sizeof (mtu));
+ if (mtu < OVERLAY_MTU_MIN || mtu > OVERLAY_MTU_MAX)
+ return (EINVAL);
+
+ mutex_enter(&odd->odd_lock);
+ old = odd->odd_mtu;
+ odd->odd_mtu = mtu;
+ err = mac_maxsdu_update(odd->odd_mh, mtu);
+ if (err != 0)
+ odd->odd_mtu = old;
+ mutex_exit(&odd->odd_lock);
+
+ return (err);
+}
+
+/* ARGSUSED */
+static int
+overlay_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_valsize, void *pr_val)
+{
+ return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static void
+overlay_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ mac_prop_info_handle_t prh)
+{
+ if (pr_num != MAC_PROP_MTU)
+ return;
+
+ mac_prop_info_set_default_uint32(prh, OVERLAY_MTU_DEF);
+ mac_prop_info_set_range_uint32(prh, OVERLAY_MTU_MIN, OVERLAY_MTU_MAX);
+}
+
+static mac_callbacks_t overlay_m_callbacks = {
+ .mc_callbacks = (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP |
+ MC_PROPINFO),
+ .mc_getstat = overlay_m_stat,
+ .mc_start = overlay_m_start,
+ .mc_stop = overlay_m_stop,
+ .mc_setpromisc = overlay_m_promisc,
+ .mc_multicst = overlay_m_multicast,
+ .mc_unicst = overlay_m_unicast,
+ .mc_tx = overlay_m_tx,
+ .mc_ioctl = overlay_m_ioctl,
+ .mc_getcapab = overlay_m_getcapab,
+ .mc_getprop = overlay_m_getprop,
+ .mc_setprop = overlay_m_setprop,
+ .mc_propinfo = overlay_m_propinfo
+};
+
+static boolean_t
+overlay_valid_name(const char *name, size_t buflen)
+{
+ size_t actlen;
+ int err, i;
+
+ for (i = 0; i < buflen; i++) {
+ if (name[i] == '\0')
+ break;
+ }
+
+ if (i == 0 || i == buflen)
+ return (B_FALSE);
+ actlen = i;
+ if (strchr(name, '/') != NULL)
+ return (B_FALSE);
+ if (u8_validate((char *)name, actlen, NULL,
+ U8_VALIDATE_ENTIRE, &err) < 0)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_create(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
+{
+ int err;
+ uint64_t maxid;
+ overlay_dev_t *odd, *o;
+ mac_register_t *mac;
+ overlay_ioc_create_t *oicp = karg;
+
+ if (overlay_valid_name(oicp->oic_encap, MAXLINKNAMELEN) == B_FALSE)
+ return (EINVAL);
+
+ odd = kmem_zalloc(sizeof (overlay_dev_t), KM_SLEEP);
+ odd->odd_linkid = oicp->oic_linkid;
+ odd->odd_plugin = overlay_plugin_lookup(oicp->oic_encap);
+ if (odd->odd_plugin == NULL) {
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (ENOENT);
+ }
+ err = odd->odd_plugin->ovp_ops->ovpo_init((overlay_handle_t)odd,
+ &odd->odd_pvoid);
+ if (err != 0) {
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (EINVAL);
+ }
+
+ /*
+ * Make sure that our virtual network id is valid for the given plugin
+ * that we're working with.
+ */
+ ASSERT(odd->odd_plugin->ovp_id_size <= 8);
+ maxid = UINT64_MAX;
+ if (odd->odd_plugin->ovp_id_size != 8)
+ maxid = (1ULL << (odd->odd_plugin->ovp_id_size * 8)) - 1ULL;
+ if (oicp->oic_vnetid > maxid) {
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (EINVAL);
+ }
+ odd->odd_vid = oicp->oic_vnetid;
+
+ mac = mac_alloc(MAC_VERSION);
+ if (mac == NULL) {
+ mutex_exit(&overlay_dev_lock);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (EINVAL);
+ }
+
+ mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ mac->m_driver = odd;
+ mac->m_dip = overlay_dip;
+ mac->m_dst_addr = NULL;
+ mac->m_callbacks = &overlay_m_callbacks;
+ mac->m_pdata = NULL;
+ mac->m_pdata_size = 0;
+
+ mac->m_priv_props = NULL;
+
+ /* Let mac handle this itself. */
+ mac->m_instance = (uint_t)-1;
+
+ /*
+ * There is no real source address that should be used here, but saying
+ * that we're not ethernet is going to cause its own problems. At the
+ * end of the say, this is fine.
+ */
+ mac->m_src_addr = overlay_macaddr;
+
+ /*
+ * Start with the default MTU as the max SDU. If the MTU is changed, the
+ * SDU will be changed to reflect that.
+ */
+ mac->m_min_sdu = 1;
+ mac->m_max_sdu = OVERLAY_MTU_DEF;
+ mac->m_multicast_sdu = 0;
+
+ /*
+ * The underlying device doesn't matter, instead this comes from the
+ * encapsulation protocol and whether or not they allow VLAN tags.
+ */
+ if (odd->odd_plugin->ovp_flags & OVEP_F_VLAN_TAG) {
+ mac->m_margin = VLAN_TAGSZ;
+ } else {
+ mac->m_margin = 0;
+ }
+
+ /*
+ * Today, we have no MAC virtualization, it may make sense in the future
+ * to go ahead and emulate some subset of this, but it doesn't today.
+ */
+ mac->m_v12n = MAC_VIRT_NONE;
+
+ mutex_enter(&overlay_dev_lock);
+ for (o = list_head(&overlay_dev_list); o != NULL;
+ o = list_next(&overlay_dev_list, o)) {
+ if (o->odd_linkid == oicp->oic_linkid) {
+ mutex_exit(&overlay_dev_lock);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (EEXIST);
+ }
+
+ if (o->odd_vid == oicp->oic_vnetid &&
+ o->odd_plugin == odd->odd_plugin) {
+ mutex_exit(&overlay_dev_lock);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (EEXIST);
+ }
+ }
+
+ err = mac_register(mac, &odd->odd_mh);
+ mac_free(mac);
+ if (err != 0) {
+ mutex_exit(&overlay_dev_lock);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (err);
+ }
+
+ err = dls_devnet_create(odd->odd_mh, odd->odd_linkid,
+ crgetzoneid(cred));
+ if (err != 0) {
+ mutex_exit(&overlay_dev_lock);
+ (void) mac_unregister(odd->odd_mh);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+ return (err);
+ }
+
+ mutex_init(&odd->odd_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&odd->odd_iowait, NULL, CV_DRIVER, NULL);
+ odd->odd_ref = 0;
+ odd->odd_flags = 0;
+ list_insert_tail(&overlay_dev_list, odd);
+ mutex_exit(&overlay_dev_lock);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_activate(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
+{
+ int i, ret;
+ overlay_dev_t *odd;
+ mac_perim_handle_t mph;
+ overlay_ioc_activate_t *oiap = karg;
+ overlay_ioc_propinfo_t *infop;
+ overlay_ioc_prop_t *oip;
+ overlay_prop_handle_t phdl;
+
+ odd = overlay_hold_by_dlid(oiap->oia_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ infop = kmem_alloc(sizeof (overlay_ioc_propinfo_t), KM_SLEEP);
+ oip = kmem_alloc(sizeof (overlay_ioc_prop_t), KM_SLEEP);
+ phdl = (overlay_prop_handle_t)infop;
+
+ mac_perim_enter_by_mh(odd->odd_mh, &mph);
+ mutex_enter(&odd->odd_lock);
+ if (odd->odd_flags & OVERLAY_F_ACTIVATED) {
+ mutex_exit(&odd->odd_lock);
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+ return (EEXIST);
+ }
+ mutex_exit(&odd->odd_lock);
+
+ for (i = 0; i < odd->odd_plugin->ovp_nprops; i++) {
+ const char *pname = odd->odd_plugin->ovp_props[i];
+ bzero(infop, sizeof (overlay_ioc_propinfo_t));
+ overlay_prop_init(phdl);
+ ret = odd->odd_plugin->ovp_ops->ovpo_propinfo(pname, phdl);
+ if (ret != 0) {
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+ return (ret);
+ }
+
+ if ((infop->oipi_prot & OVERLAY_PROP_PERM_REQ) == 0)
+ continue;
+ bzero(oip, sizeof (overlay_ioc_prop_t));
+ oip->oip_size = sizeof (oip->oip_value);
+ ret = odd->odd_plugin->ovp_ops->ovpo_getprop(odd->odd_pvoid,
+ pname, oip->oip_value, &oip->oip_size);
+ if (ret != 0) {
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+ return (ret);
+ }
+ if (oip->oip_size == 0) {
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+ return (EINVAL);
+ }
+ }
+
+ mutex_enter(&odd->odd_lock);
+ if ((odd->odd_flags & OVERLAY_F_VARPD) == 0) {
+ mutex_exit(&odd->odd_lock);
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+ return (ENXIO);
+ }
+
+ ASSERT((odd->odd_flags & OVERLAY_F_ACTIVATED) == 0);
+ odd->odd_flags |= OVERLAY_F_ACTIVATED;
+
+ /*
+ * Now that we've activated ourselves, we should indicate to the world
+ * that we're up. Note that we may not be able to perform lookups at
+ * this time, but our notion of being 'up' isn't dependent on that
+ * ability.
+ */
+ mac_link_update(odd->odd_mh, LINK_STATE_UP);
+ mutex_exit(&odd->odd_lock);
+
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ kmem_free(infop, sizeof (overlay_ioc_propinfo_t));
+ kmem_free(oip, sizeof (overlay_ioc_prop_t));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_delete(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
+{
+ overlay_ioc_delete_t *oidp = karg;
+ overlay_dev_t *odd;
+ datalink_id_t tid;
+ int ret;
+
+ odd = overlay_hold_by_dlid(oidp->oid_linkid);
+ if (odd == NULL) {
+ return (ENOENT);
+ }
+
+ mutex_enter(&odd->odd_lock);
+ /* If we're not the only hold, we're busy */
+ if (odd->odd_ref != 1) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (EBUSY);
+ }
+
+ if (odd->odd_flags & OVERLAY_F_IN_MUX) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (EBUSY);
+ }
+
+ /*
+ * To remove this, we need to first remove it from dls and then remove
+ * it from mac. The act of removing it from mac will check if there are
+ * devices on top of this, eg. vnics. If there are, then that will fail
+ * and we'll have to go through and recreate the dls entry. Only after
+ * mac_unregister has succeeded, then we'll go through and actually free
+ * everything and drop the dev lock.
+ */
+ ret = dls_devnet_destroy(odd->odd_mh, &tid, B_TRUE);
+ if (ret != 0) {
+ overlay_hold_rele(odd);
+ return (ret);
+ }
+
+ ASSERT(oidp->oid_linkid == tid);
+ ret = mac_disable(odd->odd_mh);
+ if (ret != 0) {
+ (void) dls_devnet_create(odd->odd_mh, odd->odd_linkid,
+ crgetzoneid(cred));
+ overlay_hold_rele(odd);
+ return (ret);
+ }
+
+ overlay_target_quiesce(odd->odd_target);
+
+ mutex_enter(&overlay_dev_lock);
+ list_remove(&overlay_dev_list, odd);
+ mutex_exit(&overlay_dev_lock);
+
+ cv_destroy(&odd->odd_iowait);
+ mutex_destroy(&odd->odd_lock);
+ overlay_target_free(odd);
+ odd->odd_plugin->ovp_ops->ovpo_fini(odd->odd_pvoid);
+ overlay_plugin_rele(odd->odd_plugin);
+ kmem_free(odd, sizeof (overlay_dev_t));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_nprops(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ overlay_dev_t *odd;
+ overlay_ioc_nprops_t *on = karg;
+
+ odd = overlay_hold_by_dlid(on->oipn_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+ on->oipn_nprops = odd->odd_plugin->ovp_nprops + OVERLAY_DEV_NPROPS;
+ overlay_hold_rele(odd);
+
+ return (0);
+}
+
+static int
+overlay_propinfo_plugin_cb(overlay_plugin_t *opp, void *arg)
+{
+ overlay_prop_handle_t phdl = arg;
+ overlay_prop_set_range_str(phdl, opp->ovp_name);
+ return (0);
+}
+
+static int
+overlay_i_name_to_propid(overlay_dev_t *odd, const char *name, uint_t *id)
+{
+ int i;
+
+ for (i = 0; i < OVERLAY_DEV_NPROPS; i++) {
+ if (strcmp(overlay_dev_props[i], name) == 0) {
+ *id = i;
+ return (0);
+ }
+ }
+
+ for (i = 0; i < odd->odd_plugin->ovp_nprops; i++) {
+ if (strcmp(odd->odd_plugin->ovp_props[i], name) == 0) {
+ *id = i + OVERLAY_DEV_NPROPS;
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+static void
+overlay_i_propinfo_mtu(overlay_dev_t *odd, overlay_prop_handle_t phdl)
+{
+ uint32_t def;
+ mac_propval_range_t range;
+ uint_t perm;
+
+ ASSERT(MAC_PERIM_HELD(odd->odd_mh));
+
+ bzero(&range, sizeof (mac_propval_range_t));
+ range.mpr_count = 1;
+ if (mac_prop_info(odd->odd_mh, MAC_PROP_MTU, "mtu", &def,
+ sizeof (def), &range, &perm) != 0)
+ return;
+
+ if (perm == MAC_PROP_PERM_READ)
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_READ);
+ else if (perm == MAC_PROP_PERM_WRITE)
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_WRITE);
+ else if (perm == MAC_PROP_PERM_RW)
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RW);
+
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_UINT);
+ overlay_prop_set_default(phdl, &def, sizeof (def));
+ overlay_prop_set_range_uint32(phdl, range.mpr_range_uint32[0].mpur_min,
+ range.mpr_range_uint32[0].mpur_max);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_propinfo(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ overlay_dev_t *odd;
+ int ret;
+ mac_perim_handle_t mph;
+ uint_t propid = UINT_MAX;
+ overlay_ioc_propinfo_t *oip = karg;
+ overlay_prop_handle_t phdl = (overlay_prop_handle_t)oip;
+
+ odd = overlay_hold_by_dlid(oip->oipi_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ overlay_prop_init(phdl);
+ mac_perim_enter_by_mh(odd->odd_mh, &mph);
+
+ /*
+ * If the id is -1, then the property that we're looking for is named in
+ * oipi_name and we should fill in its id. Otherwise, we've been given
+ * an id and we need to turn that into a name for our plugin's sake. The
+ * id is our own fabrication for property discovery.
+ */
+ if (oip->oipi_id == -1) {
+ /*
+ * Determine if it's a known generic property or it belongs to a
+ * module by checking against the list of known names.
+ */
+ oip->oipi_name[OVERLAY_PROP_NAMELEN-1] = '\0';
+ if ((ret = overlay_i_name_to_propid(odd, oip->oipi_name,
+ &propid)) != 0) {
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+ }
+ oip->oipi_id = propid;
+ if (propid >= OVERLAY_DEV_NPROPS) {
+ ret = odd->odd_plugin->ovp_ops->ovpo_propinfo(
+ oip->oipi_name, phdl);
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+
+ }
+ } else if (oip->oipi_id >= OVERLAY_DEV_NPROPS) {
+ uint_t id = oip->oipi_id - OVERLAY_DEV_NPROPS;
+
+ if (id >= odd->odd_plugin->ovp_nprops) {
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (EINVAL);
+ }
+ ret = odd->odd_plugin->ovp_ops->ovpo_propinfo(
+ odd->odd_plugin->ovp_props[id], phdl);
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+ } else if (oip->oipi_id < -1) {
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (EINVAL);
+ } else {
+ ASSERT(oip->oipi_id < OVERLAY_DEV_NPROPS);
+ ASSERT(oip->oipi_id >= 0);
+ propid = oip->oipi_id;
+ (void) strlcpy(oip->oipi_name, overlay_dev_props[propid],
+ sizeof (oip->oipi_name));
+ }
+
+ switch (propid) {
+ case OVERLAY_DEV_P_MTU:
+ overlay_i_propinfo_mtu(odd, phdl);
+ break;
+ case OVERLAY_DEV_P_VNETID:
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RW);
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_UINT);
+ overlay_prop_set_nodefault(phdl);
+ break;
+ case OVERLAY_DEV_P_ENCAP:
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_READ);
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_STRING);
+ overlay_prop_set_nodefault(phdl);
+ overlay_plugin_walk(overlay_propinfo_plugin_cb, phdl);
+ break;
+ case OVERLAY_DEV_P_VARPDID:
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_READ);
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_UINT);
+ overlay_prop_set_nodefault(phdl);
+ break;
+ default:
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ENOENT);
+ }
+
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_getprop(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ int ret;
+ overlay_dev_t *odd;
+ mac_perim_handle_t mph;
+ overlay_ioc_prop_t *oip = karg;
+ uint_t propid, mtu;
+
+ odd = overlay_hold_by_dlid(oip->oip_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mac_perim_enter_by_mh(odd->odd_mh, &mph);
+ oip->oip_size = OVERLAY_PROP_SIZEMAX;
+ oip->oip_name[OVERLAY_PROP_NAMELEN-1] = '\0';
+ if (oip->oip_id == -1) {
+ int i;
+
+ for (i = 0; i < OVERLAY_DEV_NPROPS; i++) {
+ if (strcmp(overlay_dev_props[i], oip->oip_name) == 0)
+ break;
+ if (i == OVERLAY_DEV_NPROPS) {
+ ret = odd->odd_plugin->ovp_ops->ovpo_getprop(
+ odd->odd_pvoid, oip->oip_name,
+ oip->oip_value, &oip->oip_size);
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+ }
+ }
+
+ propid = i;
+ } else if (oip->oip_id >= OVERLAY_DEV_NPROPS) {
+ uint_t id = oip->oip_id - OVERLAY_DEV_NPROPS;
+
+ if (id > odd->odd_plugin->ovp_nprops) {
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (EINVAL);
+ }
+ ret = odd->odd_plugin->ovp_ops->ovpo_getprop(odd->odd_pvoid,
+ odd->odd_plugin->ovp_props[id], oip->oip_value,
+ &oip->oip_size);
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+ } else if (oip->oip_id < -1) {
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (EINVAL);
+ } else {
+ ASSERT(oip->oip_id < OVERLAY_DEV_NPROPS);
+ ASSERT(oip->oip_id >= 0);
+ propid = oip->oip_id;
+ }
+
+ ret = 0;
+ switch (propid) {
+ case OVERLAY_DEV_P_MTU:
+ /*
+ * The MTU is always set and retrieved through MAC, to allow for
+ * MAC to do whatever it wants, as really that property belongs
+ * to MAC. This is important for things where vnics have hold on
+ * the MTU.
+ */
+ mac_sdu_get(odd->odd_mh, NULL, &mtu);
+ bcopy(&mtu, oip->oip_value, sizeof (uint_t));
+ oip->oip_size = sizeof (uint_t);
+ break;
+ case OVERLAY_DEV_P_VNETID:
+ /*
+ * While it's read-only while inside of a mux, we're not in a
+ * context that can guarantee that. Therefore we always grab the
+ * overlay_dev_t's odd_lock.
+ */
+ mutex_enter(&odd->odd_lock);
+ bcopy(&odd->odd_vid, oip->oip_value, sizeof (uint64_t));
+ mutex_exit(&odd->odd_lock);
+ oip->oip_size = sizeof (uint64_t);
+ break;
+ case OVERLAY_DEV_P_ENCAP:
+ oip->oip_size = strlcpy((char *)oip->oip_value,
+ odd->odd_plugin->ovp_name, oip->oip_size);
+ break;
+ case OVERLAY_DEV_P_VARPDID:
+ mutex_enter(&odd->odd_lock);
+ if (odd->odd_flags & OVERLAY_F_VARPD) {
+ const uint64_t val = odd->odd_target->ott_id;
+ bcopy(&val, oip->oip_value, sizeof (uint64_t));
+ oip->oip_size = sizeof (uint64_t);
+ } else {
+ oip->oip_size = 0;
+ }
+ mutex_exit(&odd->odd_lock);
+ break;
+ default:
+ ret = ENOENT;
+ }
+
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+}
+
+static void
+overlay_setprop_vnetid(overlay_dev_t *odd, uint64_t vnetid)
+{
+ mutex_enter(&odd->odd_lock);
+
+ /* Simple case, not active */
+ if (!(odd->odd_flags & OVERLAY_F_IN_MUX)) {
+ odd->odd_vid = vnetid;
+ mutex_exit(&odd->odd_lock);
+ return;
+ }
+
+ /*
+ * In the hard case, we need to set the drop flag, quiesce I/O and then
+ * we can go ahead and do everything.
+ */
+ odd->odd_flags |= OVERLAY_F_MDDROP;
+ overlay_io_wait(odd, OVERLAY_F_IOMASK);
+ mutex_exit(&odd->odd_lock);
+
+ overlay_mux_remove_dev(odd->odd_mux, odd);
+ mutex_enter(&odd->odd_lock);
+ odd->odd_vid = vnetid;
+ mutex_exit(&odd->odd_lock);
+ overlay_mux_add_dev(odd->odd_mux, odd);
+
+ mutex_enter(&odd->odd_lock);
+ ASSERT(odd->odd_flags & OVERLAY_F_IN_MUX);
+ odd->odd_flags &= ~OVERLAY_F_IN_MUX;
+ mutex_exit(&odd->odd_lock);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_setprop(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ int ret;
+ overlay_dev_t *odd;
+ overlay_ioc_prop_t *oip = karg;
+ uint_t propid = UINT_MAX;
+ mac_perim_handle_t mph;
+ uint64_t maxid, *vidp;
+
+ if (oip->oip_size > OVERLAY_PROP_SIZEMAX)
+ return (EINVAL);
+
+ odd = overlay_hold_by_dlid(oip->oip_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ oip->oip_name[OVERLAY_PROP_NAMELEN-1] = '\0';
+ mac_perim_enter_by_mh(odd->odd_mh, &mph);
+ mutex_enter(&odd->odd_lock);
+ if (odd->odd_flags & OVERLAY_F_ACTIVATED) {
+ mac_perim_exit(mph);
+ mutex_exit(&odd->odd_lock);
+ return (ENOTSUP);
+ }
+ mutex_exit(&odd->odd_lock);
+ if (oip->oip_id == -1) {
+ int i;
+
+ for (i = 0; i < OVERLAY_DEV_NPROPS; i++) {
+ if (strcmp(overlay_dev_props[i], oip->oip_name) == 0)
+ break;
+ if (i == OVERLAY_DEV_NPROPS) {
+ ret = odd->odd_plugin->ovp_ops->ovpo_setprop(
+ odd->odd_pvoid, oip->oip_name,
+ oip->oip_value, oip->oip_size);
+ overlay_hold_rele(odd);
+ mac_perim_exit(mph);
+ return (ret);
+ }
+ }
+
+ propid = i;
+ } else if (oip->oip_id >= OVERLAY_DEV_NPROPS) {
+ uint_t id = oip->oip_id - OVERLAY_DEV_NPROPS;
+
+ if (id > odd->odd_plugin->ovp_nprops) {
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+ ret = odd->odd_plugin->ovp_ops->ovpo_setprop(odd->odd_pvoid,
+ odd->odd_plugin->ovp_props[id], oip->oip_value,
+ oip->oip_size);
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ return (ret);
+ } else if (oip->oip_id < -1) {
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ } else {
+ ASSERT(oip->oip_id < OVERLAY_DEV_NPROPS);
+ ASSERT(oip->oip_id >= 0);
+ propid = oip->oip_id;
+ }
+
+ ret = 0;
+ switch (propid) {
+ case OVERLAY_DEV_P_MTU:
+ ret = mac_set_prop(odd->odd_mh, MAC_PROP_MTU, "mtu",
+ oip->oip_value, oip->oip_size);
+ break;
+ case OVERLAY_DEV_P_VNETID:
+ if (oip->oip_size != sizeof (uint64_t)) {
+ ret = EINVAL;
+ break;
+ }
+ vidp = (uint64_t *)oip->oip_value;
+ ASSERT(odd->odd_plugin->ovp_id_size <= 8);
+ maxid = UINT64_MAX;
+ if (odd->odd_plugin->ovp_id_size != 8)
+ maxid = (1ULL << (odd->odd_plugin->ovp_id_size * 8)) -
+ 1ULL;
+ if (*vidp >= maxid) {
+ ret = EINVAL;
+ break;
+ }
+ overlay_setprop_vnetid(odd, *vidp);
+ break;
+ case OVERLAY_DEV_P_ENCAP:
+ case OVERLAY_DEV_P_VARPDID:
+ ret = EPERM;
+ break;
+ default:
+ ret = ENOENT;
+ }
+
+ mac_perim_exit(mph);
+ overlay_hold_rele(odd);
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+overlay_i_status(void *karg, intptr_t arg, int mode, cred_t *cred,
+ int *rvalp)
+{
+ overlay_dev_t *odd;
+ overlay_ioc_status_t *os = karg;
+
+ odd = overlay_hold_by_dlid(os->ois_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if ((odd->odd_flags & OVERLAY_F_DEGRADED) != 0) {
+ os->ois_status = OVERLAY_I_DEGRADED;
+ if (odd->odd_fmamsg != NULL) {
+ (void) strlcpy(os->ois_message, odd->odd_fmamsg,
+ OVERLAY_STATUS_BUFLEN);
+ } else {
+ os->ois_message[0] = '\0';
+ }
+
+ } else {
+ os->ois_status = OVERLAY_I_OK;
+ os->ois_message[0] = '\0';
+ }
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+
+ return (0);
+}
+
+static dld_ioc_info_t overlay_ioc_list[] = {
+ { OVERLAY_IOC_CREATE, DLDCOPYIN, sizeof (overlay_ioc_create_t),
+ overlay_i_create, secpolicy_dl_config },
+ { OVERLAY_IOC_ACTIVATE, DLDCOPYIN, sizeof (overlay_ioc_activate_t),
+ overlay_i_activate, secpolicy_dl_config },
+ { OVERLAY_IOC_DELETE, DLDCOPYIN, sizeof (overlay_ioc_delete_t),
+ overlay_i_delete, secpolicy_dl_config },
+ { OVERLAY_IOC_PROPINFO, DLDCOPYIN | DLDCOPYOUT,
+ sizeof (overlay_ioc_propinfo_t), overlay_i_propinfo,
+ secpolicy_dl_config },
+ { OVERLAY_IOC_GETPROP, DLDCOPYIN | DLDCOPYOUT,
+ sizeof (overlay_ioc_prop_t), overlay_i_getprop,
+ secpolicy_dl_config },
+ { OVERLAY_IOC_SETPROP, DLDCOPYIN,
+ sizeof (overlay_ioc_prop_t), overlay_i_setprop,
+ secpolicy_dl_config },
+ { OVERLAY_IOC_NPROPS, DLDCOPYIN | DLDCOPYOUT,
+ sizeof (overlay_ioc_nprops_t), overlay_i_nprops,
+ secpolicy_dl_config },
+ { OVERLAY_IOC_STATUS, DLDCOPYIN | DLDCOPYOUT,
+ sizeof (overlay_ioc_status_t), overlay_i_status,
+ NULL }
+};
+
+static int
+overlay_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int fmcap = DDI_FM_EREPORT_CAPABLE;
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ if (overlay_dip != NULL || ddi_get_instance(dip) != 0)
+ return (DDI_FAILURE);
+
+ ddi_fm_init(dip, &fmcap, NULL);
+
+ if (ddi_create_minor_node(dip, OVERLAY_CTL, S_IFCHR,
+ ddi_get_instance(dip), DDI_PSEUDO, 0) == DDI_FAILURE)
+ return (DDI_FAILURE);
+
+ if (dld_ioc_register(OVERLAY_IOC, overlay_ioc_list,
+ DLDIOCCNT(overlay_ioc_list)) != 0) {
+ ddi_remove_minor_node(dip, OVERLAY_CTL);
+ return (DDI_FAILURE);
+ }
+
+ overlay_dip = dip;
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+overlay_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
+{
+ int error;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resp = (void *)overlay_dip;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *resp = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+overlay_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ mutex_enter(&overlay_dev_lock);
+ if (!list_is_empty(&overlay_dev_list) || overlay_target_busy()) {
+ mutex_exit(&overlay_dev_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&overlay_dev_lock);
+
+
+ dld_ioc_unregister(OVERLAY_IOC);
+ ddi_remove_minor_node(dip, OVERLAY_CTL);
+ ddi_fm_fini(dip);
+ overlay_dip = NULL;
+ return (DDI_SUCCESS);
+}
+
+static struct cb_ops overlay_cbops = {
+ overlay_target_open, /* cb_open */
+ overlay_target_close, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ overlay_target_ioctl, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* 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 overlay_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ overlay_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ overlay_attach, /* devo_attach */
+ overlay_detach, /* devo_detach */
+ nulldev, /* devo_reset */
+ &overlay_cbops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ NULL, /* devo_power */
+ ddi_quiesce_not_supported /* devo_quiesce */
+};
+
+static struct modldrv overlay_modldrv = {
+ &mod_driverops,
+ "Overlay Network Driver",
+ &overlay_dev_ops
+};
+
+static struct modlinkage overlay_linkage = {
+ MODREV_1,
+ &overlay_modldrv
+};
+
+static int
+overlay_init(void)
+{
+ mutex_init(&overlay_dev_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&overlay_dev_list, sizeof (overlay_dev_t),
+ offsetof(overlay_dev_t, odd_link));
+ overlay_mux_init();
+ overlay_plugin_init();
+ overlay_target_init();
+
+ return (DDI_SUCCESS);
+}
+
+static void
+overlay_fini(void)
+{
+ overlay_target_fini();
+ overlay_plugin_fini();
+ overlay_mux_fini();
+ mutex_destroy(&overlay_dev_lock);
+ list_destroy(&overlay_dev_list);
+}
+
+int
+_init(void)
+{
+ int err;
+
+ if ((err = overlay_init()) != DDI_SUCCESS)
+ return (err);
+
+ mac_init_ops(NULL, "overlay");
+ err = mod_install(&overlay_linkage);
+ if (err != DDI_SUCCESS) {
+ overlay_fini();
+ return (err);
+ }
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&overlay_linkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ err = mod_remove(&overlay_linkage);
+ if (err != 0)
+ return (err);
+
+ overlay_fini();
+ return (0);
+}
diff --git a/usr/src/uts/common/io/overlay/overlay.conf b/usr/src/uts/common/io/overlay/overlay.conf
new file mode 100644
index 0000000000..4b62fafd94
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay.conf
@@ -0,0 +1,16 @@
+#
+# 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 2015, Joyent, Inc.
+#
+
+name="overlay" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/overlay/overlay.mapfile b/usr/src/uts/common/io/overlay/overlay.mapfile
new file mode 100644
index 0000000000..800d72dc2b
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay.mapfile
@@ -0,0 +1,46 @@
+#
+# 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 2015 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 ILLUMOSprivate {
+ global:
+ # DDI Interfaces
+ _fini;
+ _init;
+ _info;
+
+ # Encapsualation Plugin interfaces
+ overlay_plugin_alloc;
+ overlay_plugin_free;
+ overlay_plugin_register;
+ overlay_plugin_unregister;
+ local:
+ *;
+};
diff --git a/usr/src/uts/common/io/overlay/overlay_fm.c b/usr/src/uts/common/io/overlay/overlay_fm.c
new file mode 100644
index 0000000000..0701d08e8b
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay_fm.c
@@ -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 2016 Joyent, Inc.
+ */
+
+/*
+ * Overlay device FMA operations.
+ *
+ * For more information, see the big theory statement in
+ * uts/common/io/overlay/overlay.c
+ */
+
+#include <sys/ddifm.h>
+#include <sys/overlay_impl.h>
+
+kmutex_t overlay_fm_lock;
+uint_t overlay_fm_count;
+
+void
+overlay_fm_init(void)
+{
+ overlay_fm_count = 0;
+ mutex_init(&overlay_fm_lock, NULL, MUTEX_DRIVER, NULL);
+}
+
+void
+overlay_fm_fini(void)
+{
+ VERIFY(overlay_fm_count == 0);
+ mutex_destroy(&overlay_fm_lock);
+}
+
+void
+overlay_fm_degrade(overlay_dev_t *odd, const char *msg)
+{
+ mutex_enter(&overlay_fm_lock);
+ mutex_enter(&odd->odd_lock);
+
+ if (msg != NULL)
+ (void) strlcpy(odd->odd_fmamsg, msg, OVERLAY_STATUS_BUFLEN);
+
+ if (odd->odd_flags & OVERLAY_F_DEGRADED)
+ goto out;
+
+ odd->odd_flags |= OVERLAY_F_DEGRADED;
+ overlay_fm_count++;
+ if (overlay_fm_count == 1) {
+ ddi_fm_service_impact(overlay_dip, DDI_SERVICE_DEGRADED);
+ }
+out:
+ mutex_exit(&odd->odd_lock);
+ mutex_exit(&overlay_fm_lock);
+}
+
+void
+overlay_fm_restore(overlay_dev_t *odd)
+{
+ mutex_enter(&overlay_fm_lock);
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_DEGRADED))
+ goto out;
+
+ odd->odd_fmamsg[0] = '\0';
+ odd->odd_flags &= ~OVERLAY_F_DEGRADED;
+ overlay_fm_count--;
+ if (overlay_fm_count == 0) {
+ ddi_fm_service_impact(overlay_dip, DDI_SERVICE_RESTORED);
+ }
+out:
+ mutex_exit(&odd->odd_lock);
+ mutex_exit(&overlay_fm_lock);
+}
diff --git a/usr/src/uts/common/io/overlay/overlay_mux.c b/usr/src/uts/common/io/overlay/overlay_mux.c
new file mode 100644
index 0000000000..9f70e8c83e
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay_mux.c
@@ -0,0 +1,354 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * Overlay device ksocket multiplexer.
+ *
+ * For more information, see the big theory statement in
+ * uts/common/io/overlay/overlay.c
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ksynch.h>
+#include <sys/ksocket.h>
+#include <sys/avl.h>
+#include <sys/list.h>
+#include <sys/sysmacros.h>
+#include <sys/strsubr.h>
+#include <sys/strsun.h>
+#include <sys/tihdr.h>
+
+#include <sys/overlay_impl.h>
+
+#include <sys/sdt.h>
+
+#define OVERLAY_FREEMSG(mp, reason) \
+ DTRACE_PROBE2(overlay__fremsg, mblk_t *, mp, char *, reason)
+
+static list_t overlay_mux_list;
+static kmutex_t overlay_mux_lock;
+
+void
+overlay_mux_init(void)
+{
+ list_create(&overlay_mux_list, sizeof (overlay_mux_t),
+ offsetof(overlay_mux_t, omux_lnode));
+ mutex_init(&overlay_mux_lock, NULL, MUTEX_DRIVER, NULL);
+}
+
+void
+overlay_mux_fini(void)
+{
+ mutex_destroy(&overlay_mux_lock);
+ list_destroy(&overlay_mux_list);
+}
+
+static int
+overlay_mux_comparator(const void *a, const void *b)
+{
+ const overlay_dev_t *odl, *odr;
+ odl = a;
+ odr = b;
+ if (odl->odd_vid > odr->odd_vid)
+ return (1);
+ else if (odl->odd_vid < odr->odd_vid)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * This is the central receive data path. We need to decode the packet, if we
+ * can, and then deliver it to the appropriate overlay.
+ */
+/* ARGSUSED */
+static boolean_t
+overlay_mux_recv(ksocket_t ks, mblk_t *mpchain, size_t msgsize, int oob,
+ void *arg)
+{
+ mblk_t *mp, *nmp, *fmp;
+ overlay_mux_t *mux = arg;
+
+ /*
+ * We may have a received a chain of messages. Each messsage in the
+ * chain will likely have a T_unitdata_ind attached to it as an M_PROTO.
+ * If we aren't getting that, we should probably drop that for the
+ * moment.
+ */
+ for (mp = mpchain; mp != NULL; mp = nmp) {
+ struct T_unitdata_ind *tudi;
+ ovep_encap_info_t infop;
+ overlay_dev_t od, *odd;
+ int ret;
+
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+
+ if (DB_TYPE(mp) != M_PROTO) {
+ OVERLAY_FREEMSG(mp, "first one isn't M_PROTO");
+ freemsg(mp);
+ continue;
+ }
+
+ if (mp->b_cont == NULL) {
+ OVERLAY_FREEMSG(mp, "missing a b_cont");
+ freemsg(mp);
+ continue;
+ }
+
+ tudi = (struct T_unitdata_ind *)mp->b_rptr;
+ if (tudi->PRIM_type != T_UNITDATA_IND) {
+ OVERLAY_FREEMSG(mp, "Not a T_unitdata_ind *");
+ freemsg(mp);
+ continue;
+ }
+
+ /*
+ * In the future, we'll care about the source information
+ * for purposes of telling varpd for oob invalidation. But for
+ * now, just drop that block.
+ */
+ fmp = mp;
+ mp = fmp->b_cont;
+ fmp->b_cont = NULL;
+ freemsg(fmp);
+
+ /*
+ * Decap and deliver.
+ */
+ bzero(&infop, sizeof (ovep_encap_info_t));
+ ret = mux->omux_plugin->ovp_ops->ovpo_decap(NULL, mp, &infop);
+ if (ret != 0) {
+ OVERLAY_FREEMSG(mp, "decap failed");
+ freemsg(mp);
+ continue;
+ }
+ if (MBLKL(mp) > infop.ovdi_hdr_size) {
+ mp->b_rptr += infop.ovdi_hdr_size;
+ } else {
+ while (infop.ovdi_hdr_size != 0) {
+ size_t rem, blkl;
+
+ if (mp == NULL)
+ break;
+
+ blkl = MBLKL(mp);
+ rem = MIN(infop.ovdi_hdr_size, blkl);
+ infop.ovdi_hdr_size -= rem;
+ mp->b_rptr += rem;
+ if (rem == blkl) {
+ fmp = mp;
+ mp = fmp->b_cont;
+ fmp->b_cont = NULL;
+ OVERLAY_FREEMSG(mp,
+ "freed a fmp block");
+ freemsg(fmp);
+ }
+ }
+ if (mp == NULL) {
+ OVERLAY_FREEMSG(mp, "freed it all...");
+ continue;
+ }
+ }
+
+
+ od.odd_vid = infop.ovdi_id;
+ mutex_enter(&mux->omux_lock);
+ odd = avl_find(&mux->omux_devices, &od, NULL);
+ if (odd == NULL) {
+ mutex_exit(&mux->omux_lock);
+ OVERLAY_FREEMSG(mp, "no matching vid");
+ freemsg(mp);
+ continue;
+ }
+ mutex_enter(&odd->odd_lock);
+ if ((odd->odd_flags & OVERLAY_F_MDDROP) ||
+ !(odd->odd_flags & OVERLAY_F_IN_MUX)) {
+ mutex_exit(&odd->odd_lock);
+ mutex_exit(&mux->omux_lock);
+ OVERLAY_FREEMSG(mp, "dev dropped");
+ freemsg(mp);
+ continue;
+ }
+ overlay_io_start(odd, OVERLAY_F_IN_RX);
+ mutex_exit(&odd->odd_lock);
+ mutex_exit(&mux->omux_lock);
+
+ mac_rx(odd->odd_mh, NULL, mp);
+
+ mutex_enter(&odd->odd_lock);
+ overlay_io_done(odd, OVERLAY_F_IN_RX);
+ mutex_exit(&odd->odd_lock);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Register a given device with a socket backend. If no such device socket
+ * exists, create a new one.
+ */
+overlay_mux_t *
+overlay_mux_open(overlay_plugin_t *opp, int domain, int family, int protocol,
+ struct sockaddr *addr, socklen_t len, int *errp)
+{
+ int err;
+ overlay_mux_t *mux;
+ ksocket_t ksock;
+
+ if (errp == NULL)
+ errp = &err;
+
+ mutex_enter(&overlay_mux_lock);
+ for (mux = list_head(&overlay_mux_list); mux != NULL;
+ mux = list_next(&overlay_mux_list, mux)) {
+ if (domain == mux->omux_domain &&
+ family == mux->omux_family &&
+ protocol == mux->omux_protocol &&
+ len == mux->omux_alen &&
+ bcmp(addr, mux->omux_addr, len) == 0) {
+
+ if (opp != mux->omux_plugin) {
+ *errp = EEXIST;
+ return (NULL);
+ }
+
+ mutex_enter(&mux->omux_lock);
+ mux->omux_count++;
+ mutex_exit(&mux->omux_lock);
+ mutex_exit(&overlay_mux_lock);
+ *errp = 0;
+ return (mux);
+ }
+ }
+
+ /*
+ * Today we aren't zone-aware and only exist in the global zone. When we
+ * allow for things to exist in the non-global zone, we'll want to use a
+ * credential that's actually specific to the zone.
+ */
+ *errp = ksocket_socket(&ksock, domain, family, protocol, KSOCKET_SLEEP,
+ kcred);
+ if (*errp != 0) {
+ mutex_exit(&overlay_mux_lock);
+ return (NULL);
+ }
+
+ *errp = ksocket_bind(ksock, addr, len, kcred);
+ if (*errp != 0) {
+ mutex_exit(&overlay_mux_lock);
+ ksocket_close(ksock, kcred);
+ return (NULL);
+ }
+
+ /*
+ * Ask our lower layer to optionally toggle anything they need on this
+ * socket. Because a socket is owned by a single type of plugin, we can
+ * then ask it to perform any additional socket set up it'd like to do.
+ */
+ if (opp->ovp_ops->ovpo_sockopt != NULL &&
+ (*errp = opp->ovp_ops->ovpo_sockopt(ksock)) != 0) {
+ mutex_exit(&overlay_mux_lock);
+ ksocket_close(ksock, kcred);
+ return (NULL);
+ }
+
+ mux = kmem_alloc(sizeof (overlay_mux_t), KM_SLEEP);
+ list_link_init(&mux->omux_lnode);
+ mux->omux_ksock = ksock;
+ mux->omux_plugin = opp;
+ mux->omux_domain = domain;
+ mux->omux_family = family;
+ mux->omux_protocol = protocol;
+ mux->omux_addr = kmem_alloc(len, KM_SLEEP);
+ bcopy(addr, mux->omux_addr, len);
+ mux->omux_alen = len;
+ mux->omux_count = 1;
+ avl_create(&mux->omux_devices, overlay_mux_comparator,
+ sizeof (overlay_dev_t), offsetof(overlay_dev_t, odd_muxnode));
+ mutex_init(&mux->omux_lock, NULL, MUTEX_DRIVER, NULL);
+
+
+ /* Once this is called, we need to expect to rx data */
+ *errp = ksocket_krecv_set(ksock, overlay_mux_recv, mux);
+ if (*errp != 0) {
+ ksocket_close(ksock, kcred);
+ mutex_destroy(&mux->omux_lock);
+ avl_destroy(&mux->omux_devices);
+ kmem_free(mux->omux_addr, len);
+ kmem_free(mux, sizeof (overlay_mux_t));
+ return (NULL);
+ }
+
+ list_insert_tail(&overlay_mux_list, mux);
+ mutex_exit(&overlay_mux_lock);
+
+ *errp = 0;
+ return (mux);
+}
+
+void
+overlay_mux_close(overlay_mux_t *mux)
+{
+ mutex_enter(&overlay_mux_lock);
+ mutex_enter(&mux->omux_lock);
+ mux->omux_count--;
+ if (mux->omux_count != 0) {
+ mutex_exit(&mux->omux_lock);
+ mutex_exit(&overlay_mux_lock);
+ return;
+ }
+ list_remove(&overlay_mux_list, mux);
+ mutex_exit(&mux->omux_lock);
+ mutex_exit(&overlay_mux_lock);
+
+ ksocket_close(mux->omux_ksock, kcred);
+ avl_destroy(&mux->omux_devices);
+ kmem_free(mux->omux_addr, mux->omux_alen);
+ kmem_free(mux, sizeof (overlay_mux_t));
+}
+
+void
+overlay_mux_add_dev(overlay_mux_t *mux, overlay_dev_t *odd)
+{
+ mutex_enter(&mux->omux_lock);
+ avl_add(&mux->omux_devices, odd);
+ mutex_exit(&mux->omux_lock);
+}
+
+void
+overlay_mux_remove_dev(overlay_mux_t *mux, overlay_dev_t *odd)
+{
+ mutex_enter(&mux->omux_lock);
+ avl_remove(&mux->omux_devices, odd);
+ mutex_exit(&mux->omux_lock);
+}
+
+int
+overlay_mux_tx(overlay_mux_t *mux, struct msghdr *hdr, mblk_t *mp)
+{
+ int ret;
+
+ /*
+ * It'd be nice to be able to use MSG_MBLK_QUICKRELE, unfortunately,
+ * that isn't actually supported by UDP at this time.
+ */
+ ret = ksocket_sendmblk(mux->omux_ksock, hdr, 0, &mp, kcred);
+ if (ret != 0)
+ freemsg(mp);
+
+ return (ret);
+}
diff --git a/usr/src/uts/common/io/overlay/overlay_plugin.c b/usr/src/uts/common/io/overlay/overlay_plugin.c
new file mode 100644
index 0000000000..348ddb92a2
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay_plugin.c
@@ -0,0 +1,281 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * Overlay device encapsulation plugin management
+ *
+ * For more information, see the big theory statement in
+ * uts/common/io/overlay/overlay.c
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/modctl.h>
+
+#include <sys/overlay_impl.h>
+
+static kmem_cache_t *overlay_plugin_cache;
+static kmutex_t overlay_plugin_lock;
+static list_t overlay_plugin_list;
+
+#define OVERLAY_MODDIR "overlay"
+
+/* ARGSUSED */
+static int
+overlay_plugin_cache_constructor(void *buf, void *arg, int kmflags)
+{
+ overlay_plugin_t *opp = buf;
+
+ mutex_init(&opp->ovp_mutex, NULL, MUTEX_DRIVER, NULL);
+ list_link_init(&opp->ovp_link);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+overlay_plugin_cache_destructor(void *buf, void *arg)
+{
+ overlay_plugin_t *opp = buf;
+ ASSERT(list_link_active(&opp->ovp_link) == 0);
+ mutex_destroy(&opp->ovp_mutex);
+}
+
+void
+overlay_plugin_init(void)
+{
+ mutex_init(&overlay_plugin_lock, NULL, MUTEX_DRIVER, 0);
+
+ /*
+ * In the future we may want to have a reaper to unload unused modules
+ * to help the kernel be able to reclaim memory.
+ */
+ overlay_plugin_cache = kmem_cache_create("overlay_plugin_cache",
+ sizeof (overlay_plugin_t), 0, overlay_plugin_cache_constructor,
+ overlay_plugin_cache_destructor, NULL, NULL, NULL, 0);
+ list_create(&overlay_plugin_list, sizeof (overlay_plugin_t),
+ offsetof(overlay_plugin_t, ovp_link));
+}
+
+void
+overlay_plugin_fini(void)
+{
+ mutex_enter(&overlay_plugin_lock);
+ VERIFY(list_is_empty(&overlay_plugin_list));
+ mutex_exit(&overlay_plugin_lock);
+
+ list_destroy(&overlay_plugin_list);
+ kmem_cache_destroy(overlay_plugin_cache);
+ mutex_destroy(&overlay_plugin_lock);
+}
+
+overlay_plugin_register_t *
+overlay_plugin_alloc(uint_t version)
+{
+ overlay_plugin_register_t *ovrp;
+ /* Version 1 is the only one that exists */
+ if (version != OVEP_VERSION_ONE)
+ return (NULL);
+
+ ovrp = kmem_zalloc(sizeof (overlay_plugin_register_t), KM_SLEEP);
+ ovrp->ovep_version = version;
+ return (ovrp);
+}
+
+void
+overlay_plugin_free(overlay_plugin_register_t *ovrp)
+{
+ kmem_free(ovrp, sizeof (overlay_plugin_register_t));
+}
+
+int
+overlay_plugin_register(overlay_plugin_register_t *ovrp)
+{
+ overlay_plugin_t *opp, *ipp;
+
+ /* Sanity check parameters of the registration */
+ if (ovrp->ovep_version != OVEP_VERSION_ONE)
+ return (EINVAL);
+
+ if (ovrp->ovep_name == NULL || ovrp->ovep_ops == NULL)
+ return (EINVAL);
+
+ if ((ovrp->ovep_flags & ~(OVEP_F_VLAN_TAG)) != 0)
+ return (EINVAL);
+
+ if (ovrp->ovep_id_size < 1)
+ return (EINVAL);
+
+ /* Don't support anything that has an id size larger than 8 bytes */
+ if (ovrp->ovep_id_size > 8)
+ return (ENOTSUP);
+
+ if (ovrp->ovep_dest == OVERLAY_PLUGIN_D_INVALID)
+ return (EINVAL);
+
+ if ((ovrp->ovep_dest & ~OVERLAY_PLUGIN_D_MASK) != 0)
+ return (EINVAL);
+
+ if (ovrp->ovep_ops->ovpo_callbacks != 0)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_init == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_fini == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_encap == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_decap == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_socket == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_getprop == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_setprop == NULL)
+ return (EINVAL);
+ if (ovrp->ovep_ops->ovpo_propinfo == NULL)
+ return (EINVAL);
+
+
+ opp = kmem_cache_alloc(overlay_plugin_cache, KM_SLEEP);
+ opp->ovp_active = 0;
+ opp->ovp_name = ovrp->ovep_name;
+ opp->ovp_ops = ovrp->ovep_ops;
+ opp->ovp_props = ovrp->ovep_props;
+ opp->ovp_id_size = ovrp->ovep_id_size;
+ opp->ovp_flags = ovrp->ovep_flags;
+ opp->ovp_dest = ovrp->ovep_dest;
+
+ opp->ovp_nprops = 0;
+ if (ovrp->ovep_props != NULL) {
+ while (ovrp->ovep_props[opp->ovp_nprops] != NULL) {
+ if (strlen(ovrp->ovep_props[opp->ovp_nprops]) >=
+ OVERLAY_PROP_NAMELEN) {
+ mutex_exit(&overlay_plugin_lock);
+ kmem_cache_free(overlay_plugin_cache, opp);
+ return (EINVAL);
+ }
+ opp->ovp_nprops++;
+ }
+ }
+
+ mutex_enter(&overlay_plugin_lock);
+ for (ipp = list_head(&overlay_plugin_list); ipp != NULL;
+ ipp = list_next(&overlay_plugin_list, ipp)) {
+ if (strcmp(ipp->ovp_name, opp->ovp_name) == 0) {
+ mutex_exit(&overlay_plugin_lock);
+ kmem_cache_free(overlay_plugin_cache, opp);
+ return (EEXIST);
+ }
+ }
+ list_insert_tail(&overlay_plugin_list, opp);
+ mutex_exit(&overlay_plugin_lock);
+
+ return (0);
+}
+
+int
+overlay_plugin_unregister(const char *name)
+{
+ overlay_plugin_t *opp;
+
+ mutex_enter(&overlay_plugin_lock);
+ for (opp = list_head(&overlay_plugin_list); opp != NULL;
+ opp = list_next(&overlay_plugin_list, opp)) {
+ if (strcmp(opp->ovp_name, name) == 0)
+ break;
+ }
+
+ if (opp == NULL) {
+ mutex_exit(&overlay_plugin_lock);
+ return (ENOENT);
+ }
+
+ mutex_enter(&opp->ovp_mutex);
+ if (opp->ovp_active > 0) {
+ mutex_exit(&opp->ovp_mutex);
+ mutex_exit(&overlay_plugin_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&opp->ovp_mutex);
+
+ list_remove(&overlay_plugin_list, opp);
+ mutex_exit(&overlay_plugin_lock);
+
+ kmem_cache_free(overlay_plugin_cache, opp);
+ return (0);
+}
+
+overlay_plugin_t *
+overlay_plugin_lookup(const char *name)
+{
+ overlay_plugin_t *opp;
+ boolean_t trymodload = B_FALSE;
+
+ for (;;) {
+ mutex_enter(&overlay_plugin_lock);
+ for (opp = list_head(&overlay_plugin_list); opp != NULL;
+ opp = list_next(&overlay_plugin_list, opp)) {
+ if (strcmp(name, opp->ovp_name) == 0) {
+ mutex_enter(&opp->ovp_mutex);
+ opp->ovp_active++;
+ mutex_exit(&opp->ovp_mutex);
+ mutex_exit(&overlay_plugin_lock);
+ return (opp);
+ }
+ }
+ mutex_exit(&overlay_plugin_lock);
+
+ if (trymodload == B_TRUE)
+ return (NULL);
+
+ /*
+ * If we didn't find it, it may still exist, but just not have
+ * been a loaded module. In that case, we'll do one attempt to
+ * load it.
+ */
+ if (modload(OVERLAY_MODDIR, (char *)name) == -1)
+ return (NULL);
+ trymodload = B_TRUE;
+ }
+
+}
+
+void
+overlay_plugin_rele(overlay_plugin_t *opp)
+{
+ mutex_enter(&opp->ovp_mutex);
+ ASSERT(opp->ovp_active > 0);
+ opp->ovp_active--;
+ mutex_exit(&opp->ovp_mutex);
+}
+
+void
+overlay_plugin_walk(overlay_plugin_walk_f func, void *arg)
+{
+ overlay_plugin_t *opp;
+ mutex_enter(&overlay_plugin_lock);
+ for (opp = list_head(&overlay_plugin_list); opp != NULL;
+ opp = list_next(&overlay_plugin_list, opp)) {
+ if (func(opp, arg) != 0) {
+ mutex_exit(&overlay_plugin_lock);
+ return;
+ }
+ }
+ mutex_exit(&overlay_plugin_lock);
+}
diff --git a/usr/src/uts/common/io/overlay/overlay_prop.c b/usr/src/uts/common/io/overlay/overlay_prop.c
new file mode 100644
index 0000000000..ba1ea2a629
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay_prop.c
@@ -0,0 +1,122 @@
+/*
+ * 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 2015, Joyent, Inc.
+ */
+
+/*
+ * Routines for manipulating property information structures.
+ *
+ * For more information, see the big theory statement in
+ * uts/common/io/overlay/overlay.c
+ */
+
+#include <sys/overlay_impl.h>
+
+void
+overlay_prop_init(overlay_prop_handle_t phdl)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ mac_propval_range_t *rangep = (mac_propval_range_t *)infop->oipi_poss;
+
+ infop->oipi_posssize = sizeof (mac_propval_range_t);
+ bzero(rangep, sizeof (mac_propval_range_t));
+}
+
+void
+overlay_prop_set_name(overlay_prop_handle_t phdl, const char *name)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ (void) strlcpy(infop->oipi_name, name, OVERLAY_PROP_NAMELEN);
+}
+
+void
+overlay_prop_set_prot(overlay_prop_handle_t phdl, overlay_prop_prot_t prot)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ infop->oipi_prot = prot;
+}
+
+void
+overlay_prop_set_type(overlay_prop_handle_t phdl, overlay_prop_type_t type)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ infop->oipi_type = type;
+}
+
+int
+overlay_prop_set_default(overlay_prop_handle_t phdl, void *def, ssize_t len)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+
+ if (len > OVERLAY_PROP_SIZEMAX)
+ return (E2BIG);
+
+ if (len < 0)
+ return (EOVERFLOW);
+
+ bcopy(def, infop->oipi_default, len);
+ infop->oipi_defsize = (uint32_t)len;
+
+ return (0);
+}
+
+void
+overlay_prop_set_nodefault(overlay_prop_handle_t phdl)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ infop->oipi_default[0] = '\0';
+ infop->oipi_defsize = 0;
+}
+
+void
+overlay_prop_set_range_uint32(overlay_prop_handle_t phdl, uint32_t min,
+ uint32_t max)
+{
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ mac_propval_range_t *rangep = (mac_propval_range_t *)infop->oipi_poss;
+
+ if (rangep->mpr_count != 0 && rangep->mpr_type != MAC_PROPVAL_UINT32)
+ return;
+
+ if (infop->oipi_posssize + sizeof (mac_propval_uint32_range_t) >
+ sizeof (infop->oipi_poss))
+ return;
+
+ infop->oipi_posssize += sizeof (mac_propval_uint32_range_t);
+ rangep->mpr_count++;
+ rangep->mpr_type = MAC_PROPVAL_UINT32;
+ rangep->u.mpr_uint32[rangep->mpr_count-1].mpur_min = min;
+ rangep->u.mpr_uint32[rangep->mpr_count-1].mpur_max = max;
+}
+
+void
+overlay_prop_set_range_str(overlay_prop_handle_t phdl, const char *str)
+{
+ size_t len = strlen(str) + 1; /* Account for a null terminator */
+ overlay_ioc_propinfo_t *infop = (overlay_ioc_propinfo_t *)phdl;
+ mac_propval_range_t *rangep = (mac_propval_range_t *)infop->oipi_poss;
+ mac_propval_str_range_t *pstr = &rangep->u.mpr_str;
+
+ if (rangep->mpr_count != 0 && rangep->mpr_type != MAC_PROPVAL_STR)
+ return;
+
+ if (infop->oipi_posssize + len > sizeof (infop->oipi_poss))
+ return;
+
+ rangep->mpr_count++;
+ rangep->mpr_type = MAC_PROPVAL_STR;
+ strlcpy((char *)&pstr->mpur_data[pstr->mpur_nextbyte], str,
+ sizeof (infop->oipi_poss) - infop->oipi_posssize);
+ pstr->mpur_nextbyte += len;
+ infop->oipi_posssize += len;
+}
diff --git a/usr/src/uts/common/io/overlay/overlay_target.c b/usr/src/uts/common/io/overlay/overlay_target.c
new file mode 100644
index 0000000000..f4147b56d1
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/overlay_target.c
@@ -0,0 +1,1651 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Overlay device target cache management
+ *
+ * For more information, see the big theory statement in
+ * uts/common/io/overlay/overlay.c
+ */
+
+#include <sys/types.h>
+#include <sys/ethernet.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/sysmacros.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+#include <sys/mac_provider.h>
+#include <sys/mac_client.h>
+#include <sys/mac_client_priv.h>
+#include <sys/vlan.h>
+#include <sys/crc32.h>
+#include <sys/cred.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/overlay_impl.h>
+#include <sys/sdt.h>
+
+/*
+ * This is total straw man, but at least it's a prime number. Here we're
+ * going to have to go through and do a lot of evaluation and understanding as
+ * to how these target caches should grow and shrink, as well as, memory
+ * pressure and evictions. This just gives us a starting point that'll be 'good
+ * enough', until it's not.
+ */
+#define OVERLAY_HSIZE 823
+
+/*
+ * We use this data structure to keep track of what requests have been actively
+ * allocated to a given instance so we know what to put back on the pending
+ * list.
+ */
+typedef struct overlay_target_hdl {
+ minor_t oth_minor; /* RO */
+ zoneid_t oth_zoneid; /* RO */
+ int oth_oflags; /* RO */
+ list_node_t oth_link; /* overlay_target_lock */
+ kmutex_t oth_lock;
+ list_t oth_outstanding; /* oth_lock */
+} overlay_target_hdl_t;
+
+typedef int (*overlay_target_copyin_f)(const void *, void **, size_t *, int);
+typedef int (*overlay_target_ioctl_f)(overlay_target_hdl_t *, void *);
+typedef int (*overlay_target_copyout_f)(void *, void *, size_t, int);
+
+typedef struct overaly_target_ioctl {
+ int oti_cmd; /* ioctl id */
+ boolean_t oti_write; /* ioctl requires FWRITE */
+ boolean_t oti_ncopyout; /* copyout data? */
+ overlay_target_copyin_f oti_copyin; /* copyin func */
+ overlay_target_ioctl_f oti_func; /* function to call */
+ overlay_target_copyout_f oti_copyout; /* copyin func */
+ size_t oti_size; /* size of user level structure */
+} overlay_target_ioctl_t;
+
+static kmem_cache_t *overlay_target_cache;
+static kmem_cache_t *overlay_entry_cache;
+static id_space_t *overlay_thdl_idspace;
+static void *overlay_thdl_state;
+
+/*
+ * When we support overlay devices in the NGZ, then all of these need to become
+ * zone aware, by plugging into the netstack engine and becoming per-netstack
+ * data.
+ */
+static list_t overlay_thdl_list;
+static kmutex_t overlay_target_lock;
+static kcondvar_t overlay_target_condvar;
+static list_t overlay_target_list;
+static boolean_t overlay_target_excl;
+
+/*
+ * Outstanding data per hash table entry.
+ */
+static int overlay_ent_size = 128 * 1024;
+
+/* ARGSUSED */
+static int
+overlay_target_cache_constructor(void *buf, void *arg, int kmflgs)
+{
+ overlay_target_t *ott = buf;
+
+ mutex_init(&ott->ott_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&ott->ott_cond, NULL, CV_DRIVER, NULL);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+overlay_target_cache_destructor(void *buf, void *arg)
+{
+ overlay_target_t *ott = buf;
+
+ cv_destroy(&ott->ott_cond);
+ mutex_destroy(&ott->ott_lock);
+}
+
+/* ARGSUSED */
+static int
+overlay_entry_cache_constructor(void *buf, void *arg, int kmflgs)
+{
+ overlay_target_entry_t *ote = buf;
+
+ bzero(ote, sizeof (overlay_target_entry_t));
+ mutex_init(&ote->ote_lock, NULL, MUTEX_DRIVER, NULL);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+overlay_entry_cache_destructor(void *buf, void *arg)
+{
+ overlay_target_entry_t *ote = buf;
+
+ mutex_destroy(&ote->ote_lock);
+}
+
+static uint64_t
+overlay_mac_hash(const void *v)
+{
+ uint32_t crc;
+ CRC32(crc, v, ETHERADDRL, -1U, crc32_table);
+ return (crc);
+}
+
+static int
+overlay_mac_cmp(const void *a, const void *b)
+{
+ return (bcmp(a, b, ETHERADDRL));
+}
+
+/* ARGSUSED */
+static void
+overlay_target_entry_dtor(void *arg)
+{
+ overlay_target_entry_t *ote = arg;
+
+ ote->ote_flags = 0;
+ bzero(ote->ote_addr, ETHERADDRL);
+ ote->ote_ott = NULL;
+ ote->ote_odd = NULL;
+ freemsgchain(ote->ote_chead);
+ ote->ote_chead = ote->ote_ctail = NULL;
+ ote->ote_mbsize = 0;
+ ote->ote_vtime = 0;
+ kmem_cache_free(overlay_entry_cache, ote);
+}
+
+static int
+overlay_mac_avl(const void *a, const void *b)
+{
+ int i;
+ const overlay_target_entry_t *l, *r;
+ l = a;
+ r = b;
+
+ for (i = 0; i < ETHERADDRL; i++) {
+ if (l->ote_addr[i] > r->ote_addr[i])
+ return (1);
+ else if (l->ote_addr[i] < r->ote_addr[i])
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+overlay_target_init(void)
+{
+ int ret;
+ ret = ddi_soft_state_init(&overlay_thdl_state,
+ sizeof (overlay_target_hdl_t), 1);
+ VERIFY(ret == 0);
+ overlay_target_cache = kmem_cache_create("overlay_target",
+ sizeof (overlay_target_t), 0, overlay_target_cache_constructor,
+ overlay_target_cache_destructor, NULL, NULL, NULL, 0);
+ overlay_entry_cache = kmem_cache_create("overlay_entry",
+ sizeof (overlay_target_entry_t), 0, overlay_entry_cache_constructor,
+ overlay_entry_cache_destructor, NULL, NULL, NULL, 0);
+ mutex_init(&overlay_target_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&overlay_target_condvar, NULL, CV_DRIVER, NULL);
+ list_create(&overlay_target_list, sizeof (overlay_target_entry_t),
+ offsetof(overlay_target_entry_t, ote_qlink));
+ list_create(&overlay_thdl_list, sizeof (overlay_target_hdl_t),
+ offsetof(overlay_target_hdl_t, oth_link));
+ overlay_thdl_idspace = id_space_create("overlay_target_minors",
+ 1, INT32_MAX);
+}
+
+void
+overlay_target_fini(void)
+{
+ id_space_destroy(overlay_thdl_idspace);
+ list_destroy(&overlay_thdl_list);
+ list_destroy(&overlay_target_list);
+ cv_destroy(&overlay_target_condvar);
+ mutex_destroy(&overlay_target_lock);
+ kmem_cache_destroy(overlay_entry_cache);
+ kmem_cache_destroy(overlay_target_cache);
+ ddi_soft_state_fini(&overlay_thdl_state);
+}
+
+void
+overlay_target_free(overlay_dev_t *odd)
+{
+ if (odd->odd_target == NULL)
+ return;
+
+ if (odd->odd_target->ott_mode == OVERLAY_TARGET_DYNAMIC) {
+ refhash_t *rp = odd->odd_target->ott_u.ott_dyn.ott_dhash;
+ avl_tree_t *ap = &odd->odd_target->ott_u.ott_dyn.ott_tree;
+ overlay_target_entry_t *ote;
+
+ /*
+ * Our AVL tree and hashtable contain the same elements,
+ * therefore we should just remove it from the tree, but then
+ * delete the entries when we remove them from the hash table
+ * (which happens through the refhash dtor).
+ */
+ while ((ote = avl_first(ap)) != NULL)
+ avl_remove(ap, ote);
+
+ avl_destroy(ap);
+ for (ote = refhash_first(rp); ote != NULL;
+ ote = refhash_next(rp, ote)) {
+ refhash_remove(rp, ote);
+ }
+ refhash_destroy(rp);
+ }
+
+ ASSERT(odd->odd_target->ott_ocount == 0);
+ kmem_cache_free(overlay_target_cache, odd->odd_target);
+}
+
+int
+overlay_target_busy()
+{
+ int ret;
+
+ mutex_enter(&overlay_target_lock);
+ ret = !list_is_empty(&overlay_thdl_list);
+ mutex_exit(&overlay_target_lock);
+
+ return (ret);
+}
+
+static void
+overlay_target_queue(overlay_target_entry_t *entry)
+{
+ mutex_enter(&overlay_target_lock);
+ mutex_enter(&entry->ote_ott->ott_lock);
+ if (entry->ote_ott->ott_flags & OVERLAY_T_TEARDOWN) {
+ mutex_exit(&entry->ote_ott->ott_lock);
+ mutex_exit(&overlay_target_lock);
+ return;
+ }
+ entry->ote_ott->ott_ocount++;
+ mutex_exit(&entry->ote_ott->ott_lock);
+ list_insert_tail(&overlay_target_list, entry);
+ cv_signal(&overlay_target_condvar);
+ mutex_exit(&overlay_target_lock);
+}
+
+void
+overlay_target_quiesce(overlay_target_t *ott)
+{
+ if (ott == NULL)
+ return;
+ mutex_enter(&ott->ott_lock);
+ ott->ott_flags |= OVERLAY_T_TEARDOWN;
+ while (ott->ott_ocount != 0)
+ cv_wait(&ott->ott_cond, &ott->ott_lock);
+ mutex_exit(&ott->ott_lock);
+}
+
+/*
+ * This functions assumes that the destination mode is OVERLAY_PLUGIN_D_IP |
+ * OVERLAY_PLUGIN_D_PORT. As we don't have an implementation of anything else at
+ * this time, say for NVGRE, we drop all packets that mcuh this.
+ */
+int
+overlay_target_lookup(overlay_dev_t *odd, mblk_t *mp, struct sockaddr *sock,
+ socklen_t *slenp)
+{
+ int ret;
+ struct sockaddr_in6 *v6;
+ overlay_target_t *ott;
+ mac_header_info_t mhi;
+ overlay_target_entry_t *entry;
+
+ ASSERT(odd->odd_target != NULL);
+
+ /*
+ * At this point, the overlay device is in a mux which means that it's
+ * been activated. At this point, parts of the target, such as the mode
+ * and the destination are now read-only and we don't have to worry
+ * about synchronization for them.
+ */
+ ott = odd->odd_target;
+ if (ott->ott_dest != (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))
+ return (OVERLAY_TARGET_DROP);
+
+ v6 = (struct sockaddr_in6 *)sock;
+ bzero(v6, sizeof (struct sockaddr_in6));
+ v6->sin6_family = AF_INET6;
+
+ if (ott->ott_mode == OVERLAY_TARGET_POINT) {
+ mutex_enter(&ott->ott_lock);
+ bcopy(&ott->ott_u.ott_point.otp_ip, &v6->sin6_addr,
+ sizeof (struct in6_addr));
+ v6->sin6_port = htons(ott->ott_u.ott_point.otp_port);
+ mutex_exit(&ott->ott_lock);
+ *slenp = sizeof (struct sockaddr_in6);
+
+ return (OVERLAY_TARGET_OK);
+ }
+
+ ASSERT(ott->ott_mode == OVERLAY_TARGET_DYNAMIC);
+
+ /*
+ * Note we only want the MAC address here, therefore we won't bother
+ * using mac_vlan_header_info(). If any caller needs the vlan info at
+ * this point, this should change to a call to mac_vlan_header_info().
+ */
+ if (mac_header_info(odd->odd_mh, mp, &mhi) != 0)
+ return (OVERLAY_TARGET_DROP);
+ mutex_enter(&ott->ott_lock);
+ entry = refhash_lookup(ott->ott_u.ott_dyn.ott_dhash,
+ mhi.mhi_daddr);
+ if (entry == NULL) {
+ entry = kmem_cache_alloc(overlay_entry_cache,
+ KM_NOSLEEP | KM_NORMALPRI);
+ if (entry == NULL) {
+ mutex_exit(&ott->ott_lock);
+ return (OVERLAY_TARGET_DROP);
+ }
+ bcopy(mhi.mhi_daddr, entry->ote_addr, ETHERADDRL);
+ entry->ote_chead = entry->ote_ctail = mp;
+ entry->ote_mbsize = msgsize(mp);
+ entry->ote_flags |= OVERLAY_ENTRY_F_PENDING;
+ entry->ote_ott = ott;
+ entry->ote_odd = odd;
+ refhash_insert(ott->ott_u.ott_dyn.ott_dhash, entry);
+ avl_add(&ott->ott_u.ott_dyn.ott_tree, entry);
+ mutex_exit(&ott->ott_lock);
+ overlay_target_queue(entry);
+ return (OVERLAY_TARGET_ASYNC);
+ }
+ refhash_hold(ott->ott_u.ott_dyn.ott_dhash, entry);
+ mutex_exit(&ott->ott_lock);
+
+ mutex_enter(&entry->ote_lock);
+ if (entry->ote_flags & OVERLAY_ENTRY_F_DROP) {
+ ret = OVERLAY_TARGET_DROP;
+ } else if (entry->ote_flags & OVERLAY_ENTRY_F_VALID) {
+ bcopy(&entry->ote_dest.otp_ip, &v6->sin6_addr,
+ sizeof (struct in6_addr));
+ v6->sin6_port = htons(entry->ote_dest.otp_port);
+ *slenp = sizeof (struct sockaddr_in6);
+ ret = OVERLAY_TARGET_OK;
+ } else {
+ size_t mlen = msgsize(mp);
+
+ if (mlen + entry->ote_mbsize > overlay_ent_size) {
+ ret = OVERLAY_TARGET_DROP;
+ } else {
+ if (entry->ote_ctail != NULL) {
+ ASSERT(entry->ote_ctail->b_next ==
+ NULL);
+ entry->ote_ctail->b_next = mp;
+ entry->ote_ctail = mp;
+ } else {
+ entry->ote_chead = mp;
+ entry->ote_ctail = mp;
+ }
+ entry->ote_mbsize += mlen;
+ if ((entry->ote_flags &
+ OVERLAY_ENTRY_F_PENDING) == 0) {
+ entry->ote_flags |=
+ OVERLAY_ENTRY_F_PENDING;
+ overlay_target_queue(entry);
+ }
+ ret = OVERLAY_TARGET_ASYNC;
+ }
+ }
+ mutex_exit(&entry->ote_lock);
+
+ mutex_enter(&ott->ott_lock);
+ refhash_rele(ott->ott_u.ott_dyn.ott_dhash, entry);
+ mutex_exit(&ott->ott_lock);
+
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_info(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_targ_info_t *oti = arg;
+
+ odd = overlay_hold_by_dlid(oti->oti_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ oti->oti_flags = 0;
+ oti->oti_needs = odd->odd_plugin->ovp_dest;
+ if (odd->odd_flags & OVERLAY_F_DEGRADED)
+ oti->oti_flags |= OVERLAY_TARG_INFO_F_DEGRADED;
+ if (odd->odd_flags & OVERLAY_F_ACTIVATED)
+ oti->oti_flags |= OVERLAY_TARG_INFO_F_ACTIVE;
+ oti->oti_vnetid = odd->odd_vid;
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_associate(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_targ_associate_t *ota = arg;
+
+ odd = overlay_hold_by_dlid(ota->ota_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ if (ota->ota_id == 0) {
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+
+ if (ota->ota_mode != OVERLAY_TARGET_POINT &&
+ ota->ota_mode != OVERLAY_TARGET_DYNAMIC) {
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+
+ if (ota->ota_provides != odd->odd_plugin->ovp_dest) {
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+
+ if (ota->ota_mode == OVERLAY_TARGET_POINT) {
+ if (ota->ota_provides & OVERLAY_PLUGIN_D_IP) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&ota->ota_point.otp_ip) ||
+ IN6_IS_ADDR_V4COMPAT(&ota->ota_point.otp_ip) ||
+ IN6_IS_ADDR_V4MAPPED_ANY(&ota->ota_point.otp_ip)) {
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+ }
+
+ if (ota->ota_provides & OVERLAY_PLUGIN_D_PORT) {
+ if (ota->ota_point.otp_port == 0) {
+ overlay_hold_rele(odd);
+ return (EINVAL);
+ }
+ }
+ }
+
+ ott = kmem_cache_alloc(overlay_target_cache, KM_SLEEP);
+ ott->ott_flags = 0;
+ ott->ott_ocount = 0;
+ ott->ott_mode = ota->ota_mode;
+ ott->ott_dest = ota->ota_provides;
+ ott->ott_id = ota->ota_id;
+
+ if (ott->ott_mode == OVERLAY_TARGET_POINT) {
+ bcopy(&ota->ota_point, &ott->ott_u.ott_point,
+ sizeof (overlay_target_point_t));
+ } else {
+ ott->ott_u.ott_dyn.ott_dhash = refhash_create(OVERLAY_HSIZE,
+ overlay_mac_hash, overlay_mac_cmp,
+ overlay_target_entry_dtor, sizeof (overlay_target_entry_t),
+ offsetof(overlay_target_entry_t, ote_reflink),
+ offsetof(overlay_target_entry_t, ote_addr), KM_SLEEP);
+ avl_create(&ott->ott_u.ott_dyn.ott_tree, overlay_mac_avl,
+ sizeof (overlay_target_entry_t),
+ offsetof(overlay_target_entry_t, ote_avllink));
+ }
+ mutex_enter(&odd->odd_lock);
+ if (odd->odd_flags & OVERLAY_F_VARPD) {
+ mutex_exit(&odd->odd_lock);
+ kmem_cache_free(overlay_target_cache, ott);
+ overlay_hold_rele(odd);
+ return (EEXIST);
+ }
+
+ odd->odd_flags |= OVERLAY_F_VARPD;
+ odd->odd_target = ott;
+ mutex_exit(&odd->odd_lock);
+
+ overlay_hold_rele(odd);
+
+
+ return (0);
+}
+
+
+/* ARGSUSED */
+static int
+overlay_target_degrade(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_targ_degrade_t *otd = arg;
+
+ odd = overlay_hold_by_dlid(otd->otd_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ overlay_fm_degrade(odd, otd->otd_buf);
+ overlay_hold_rele(odd);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_restore(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_targ_id_t *otid = arg;
+
+ odd = overlay_hold_by_dlid(otid->otid_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ overlay_fm_restore(odd);
+ overlay_hold_rele(odd);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_disassociate(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_targ_id_t *otid = arg;
+
+ odd = overlay_hold_by_dlid(otid->otid_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ odd->odd_flags &= ~OVERLAY_F_VARPD;
+ mutex_exit(&odd->odd_lock);
+
+ overlay_hold_rele(odd);
+ return (0);
+
+}
+
+static int
+overlay_target_lookup_request(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_targ_lookup_t *otl = arg;
+ overlay_target_entry_t *entry;
+ clock_t ret, timeout;
+ mac_header_info_t mhi;
+
+ timeout = ddi_get_lbolt() + drv_usectohz(MICROSEC);
+again:
+ mutex_enter(&overlay_target_lock);
+ while (list_is_empty(&overlay_target_list)) {
+ ret = cv_timedwait(&overlay_target_condvar,
+ &overlay_target_lock, timeout);
+ if (ret == -1) {
+ mutex_exit(&overlay_target_lock);
+ return (ETIME);
+ }
+ }
+ entry = list_remove_head(&overlay_target_list);
+ mutex_exit(&overlay_target_lock);
+ mutex_enter(&entry->ote_lock);
+ if (entry->ote_flags & OVERLAY_ENTRY_F_VALID) {
+ ASSERT(entry->ote_chead == NULL);
+ mutex_exit(&entry->ote_lock);
+ goto again;
+ }
+ ASSERT(entry->ote_chead != NULL);
+
+ /*
+ * If we have a bogon that doesn't have a valid mac header, drop it and
+ * try again.
+ */
+ if (mac_vlan_header_info(entry->ote_odd->odd_mh, entry->ote_chead,
+ &mhi) != 0) {
+ boolean_t queue = B_FALSE;
+ mblk_t *mp = entry->ote_chead;
+ entry->ote_chead = mp->b_next;
+ mp->b_next = NULL;
+ if (entry->ote_ctail == mp)
+ entry->ote_ctail = entry->ote_chead;
+ entry->ote_mbsize -= msgsize(mp);
+ if (entry->ote_chead != NULL)
+ queue = B_TRUE;
+ mutex_exit(&entry->ote_lock);
+ if (queue == B_TRUE)
+ overlay_target_queue(entry);
+ freemsg(mp);
+ goto again;
+ }
+
+ otl->otl_dlid = entry->ote_odd->odd_linkid;
+ otl->otl_reqid = (uintptr_t)entry;
+ otl->otl_varpdid = entry->ote_ott->ott_id;
+ otl->otl_vnetid = entry->ote_odd->odd_vid;
+
+ otl->otl_hdrsize = mhi.mhi_hdrsize;
+ otl->otl_pktsize = msgsize(entry->ote_chead) - otl->otl_hdrsize;
+ bcopy(mhi.mhi_daddr, otl->otl_dstaddr, ETHERADDRL);
+ bcopy(mhi.mhi_saddr, otl->otl_srcaddr, ETHERADDRL);
+ otl->otl_dsttype = mhi.mhi_dsttype;
+ otl->otl_sap = mhi.mhi_bindsap;
+ otl->otl_vlan = VLAN_ID(mhi.mhi_tci);
+ mutex_exit(&entry->ote_lock);
+
+ mutex_enter(&thdl->oth_lock);
+ list_insert_tail(&thdl->oth_outstanding, entry);
+ mutex_exit(&thdl->oth_lock);
+
+ return (0);
+}
+
+static int
+overlay_target_lookup_respond(overlay_target_hdl_t *thdl, void *arg)
+{
+ const overlay_targ_resp_t *otr = arg;
+ overlay_target_entry_t *entry;
+ mblk_t *mp;
+
+ mutex_enter(&thdl->oth_lock);
+ for (entry = list_head(&thdl->oth_outstanding); entry != NULL;
+ entry = list_next(&thdl->oth_outstanding, entry)) {
+ if ((uintptr_t)entry == otr->otr_reqid)
+ break;
+ }
+
+ if (entry == NULL) {
+ mutex_exit(&thdl->oth_lock);
+ return (EINVAL);
+ }
+ list_remove(&thdl->oth_outstanding, entry);
+ mutex_exit(&thdl->oth_lock);
+
+ mutex_enter(&entry->ote_lock);
+ bcopy(&otr->otr_answer, &entry->ote_dest,
+ sizeof (overlay_target_point_t));
+ entry->ote_flags &= ~OVERLAY_ENTRY_F_PENDING;
+ entry->ote_flags |= OVERLAY_ENTRY_F_VALID;
+ mp = entry->ote_chead;
+ entry->ote_chead = NULL;
+ entry->ote_ctail = NULL;
+ entry->ote_mbsize = 0;
+ entry->ote_vtime = gethrtime();
+ mutex_exit(&entry->ote_lock);
+
+ /*
+ * For now do an in-situ drain.
+ */
+ mp = overlay_m_tx(entry->ote_odd, mp);
+ freemsgchain(mp);
+
+ mutex_enter(&entry->ote_ott->ott_lock);
+ entry->ote_ott->ott_ocount--;
+ cv_signal(&entry->ote_ott->ott_cond);
+ mutex_exit(&entry->ote_ott->ott_lock);
+
+ return (0);
+}
+
+static int
+overlay_target_lookup_drop(overlay_target_hdl_t *thdl, void *arg)
+{
+ const overlay_targ_resp_t *otr = arg;
+ overlay_target_entry_t *entry;
+ mblk_t *mp;
+ boolean_t queue = B_FALSE;
+
+ mutex_enter(&thdl->oth_lock);
+ for (entry = list_head(&thdl->oth_outstanding); entry != NULL;
+ entry = list_next(&thdl->oth_outstanding, entry)) {
+ if ((uintptr_t)entry == otr->otr_reqid)
+ break;
+ }
+
+ if (entry == NULL) {
+ mutex_exit(&thdl->oth_lock);
+ return (EINVAL);
+ }
+ list_remove(&thdl->oth_outstanding, entry);
+ mutex_exit(&thdl->oth_lock);
+
+ mutex_enter(&entry->ote_lock);
+
+ /* Safeguard against a confused varpd */
+ if (entry->ote_flags & OVERLAY_ENTRY_F_VALID) {
+ entry->ote_flags &= ~OVERLAY_ENTRY_F_PENDING;
+ DTRACE_PROBE1(overlay__target__valid__drop,
+ overlay_target_entry_t *, entry);
+ mutex_exit(&entry->ote_lock);
+ goto done;
+ }
+
+ mp = entry->ote_chead;
+ if (mp != NULL) {
+ entry->ote_chead = mp->b_next;
+ mp->b_next = NULL;
+ if (entry->ote_ctail == mp)
+ entry->ote_ctail = entry->ote_chead;
+ entry->ote_mbsize -= msgsize(mp);
+ }
+ if (entry->ote_chead != NULL) {
+ queue = B_TRUE;
+ entry->ote_flags |= OVERLAY_ENTRY_F_PENDING;
+ } else {
+ entry->ote_flags &= ~OVERLAY_ENTRY_F_PENDING;
+ }
+ mutex_exit(&entry->ote_lock);
+
+ if (queue == B_TRUE)
+ overlay_target_queue(entry);
+ freemsg(mp);
+
+done:
+ mutex_enter(&entry->ote_ott->ott_lock);
+ entry->ote_ott->ott_ocount--;
+ cv_signal(&entry->ote_ott->ott_cond);
+ mutex_exit(&entry->ote_ott->ott_lock);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_pkt_copyin(const void *ubuf, void **outp, size_t *bsize,
+ int flags)
+{
+ overlay_targ_pkt_t *pkt;
+ overlay_targ_pkt32_t *pkt32;
+
+ pkt = kmem_alloc(sizeof (overlay_targ_pkt_t), KM_SLEEP);
+ *outp = pkt;
+ *bsize = sizeof (overlay_targ_pkt_t);
+ if (ddi_model_convert_from(flags & FMODELS) == DDI_MODEL_ILP32) {
+ uintptr_t addr;
+
+ if (ddi_copyin(ubuf, pkt, sizeof (overlay_targ_pkt32_t),
+ flags & FKIOCTL) != 0) {
+ kmem_free(pkt, *bsize);
+ return (EFAULT);
+ }
+ pkt32 = (overlay_targ_pkt32_t *)pkt;
+ addr = pkt32->otp_buf;
+ pkt->otp_buf = (void *)addr;
+ } else {
+ if (ddi_copyin(ubuf, pkt, *bsize, flags & FKIOCTL) != 0) {
+ kmem_free(pkt, *bsize);
+ return (EFAULT);
+ }
+ }
+ return (0);
+}
+
+static int
+overlay_target_pkt_copyout(void *ubuf, void *buf, size_t bufsize,
+ int flags)
+{
+ if (ddi_model_convert_from(flags & FMODELS) == DDI_MODEL_ILP32) {
+ overlay_targ_pkt_t *pkt = buf;
+ overlay_targ_pkt32_t *pkt32 = buf;
+ uintptr_t addr = (uintptr_t)pkt->otp_buf;
+ pkt32->otp_buf = (caddr32_t)addr;
+ if (ddi_copyout(buf, ubuf, sizeof (overlay_targ_pkt32_t),
+ flags & FKIOCTL) != 0)
+ return (EFAULT);
+ } else {
+ if (ddi_copyout(buf, ubuf, bufsize, flags & FKIOCTL) != 0)
+ return (EFAULT);
+ }
+ return (0);
+}
+
+static int
+overlay_target_packet(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_targ_pkt_t *pkt = arg;
+ overlay_target_entry_t *entry;
+ mblk_t *mp;
+ size_t mlen;
+ size_t boff;
+
+ mutex_enter(&thdl->oth_lock);
+ for (entry = list_head(&thdl->oth_outstanding); entry != NULL;
+ entry = list_next(&thdl->oth_outstanding, entry)) {
+ if ((uintptr_t)entry == pkt->otp_reqid)
+ break;
+ }
+
+ if (entry == NULL) {
+ mutex_exit(&thdl->oth_lock);
+ return (EINVAL);
+ }
+ mutex_enter(&entry->ote_lock);
+ mutex_exit(&thdl->oth_lock);
+ mp = entry->ote_chead;
+ /* Protect against a rogue varpd */
+ if (mp == NULL) {
+ mutex_exit(&entry->ote_lock);
+ return (EINVAL);
+ }
+ mlen = MIN(msgsize(mp), pkt->otp_size);
+ pkt->otp_size = mlen;
+ boff = 0;
+ while (mlen > 0) {
+ size_t wlen = MIN(MBLKL(mp), mlen);
+ if (ddi_copyout(mp->b_rptr,
+ (void *)((uintptr_t)pkt->otp_buf + boff),
+ wlen, 0) != 0) {
+ mutex_exit(&entry->ote_lock);
+ return (EFAULT);
+ }
+ mlen -= wlen;
+ boff += wlen;
+ mp = mp->b_cont;
+ }
+ mutex_exit(&entry->ote_lock);
+ return (0);
+}
+
+static int
+overlay_target_inject(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_targ_pkt_t *pkt = arg;
+ overlay_target_entry_t *entry;
+ overlay_dev_t *odd;
+ mblk_t *mp;
+
+ if (pkt->otp_size > ETHERMAX + VLAN_TAGSZ)
+ return (EINVAL);
+
+ mp = allocb(pkt->otp_size, 0);
+ if (mp == NULL)
+ return (ENOMEM);
+
+ if (ddi_copyin(pkt->otp_buf, mp->b_rptr, pkt->otp_size, 0) != 0) {
+ freeb(mp);
+ return (EFAULT);
+ }
+ mp->b_wptr += pkt->otp_size;
+
+ if (pkt->otp_linkid != UINT64_MAX) {
+ odd = overlay_hold_by_dlid(pkt->otp_linkid);
+ if (odd == NULL) {
+ freeb(mp);
+ return (ENOENT);
+ }
+ } else {
+ mutex_enter(&thdl->oth_lock);
+ for (entry = list_head(&thdl->oth_outstanding); entry != NULL;
+ entry = list_next(&thdl->oth_outstanding, entry)) {
+ if ((uintptr_t)entry == pkt->otp_reqid)
+ break;
+ }
+
+ if (entry == NULL) {
+ mutex_exit(&thdl->oth_lock);
+ freeb(mp);
+ return (ENOENT);
+ }
+ odd = entry->ote_odd;
+ mutex_exit(&thdl->oth_lock);
+ }
+
+ mutex_enter(&odd->odd_lock);
+ overlay_io_start(odd, OVERLAY_F_IN_RX);
+ mutex_exit(&odd->odd_lock);
+
+ mac_rx(odd->odd_mh, NULL, mp);
+
+ mutex_enter(&odd->odd_lock);
+ overlay_io_done(odd, OVERLAY_F_IN_RX);
+ mutex_exit(&odd->odd_lock);
+
+ return (0);
+}
+
+static int
+overlay_target_resend(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_targ_pkt_t *pkt = arg;
+ overlay_target_entry_t *entry;
+ overlay_dev_t *odd;
+ mblk_t *mp;
+
+ if (pkt->otp_size > ETHERMAX + VLAN_TAGSZ)
+ return (EINVAL);
+
+ mp = allocb(pkt->otp_size, 0);
+ if (mp == NULL)
+ return (ENOMEM);
+
+ if (ddi_copyin(pkt->otp_buf, mp->b_rptr, pkt->otp_size, 0) != 0) {
+ freeb(mp);
+ return (EFAULT);
+ }
+ mp->b_wptr += pkt->otp_size;
+
+ if (pkt->otp_linkid != UINT64_MAX) {
+ odd = overlay_hold_by_dlid(pkt->otp_linkid);
+ if (odd == NULL) {
+ freeb(mp);
+ return (ENOENT);
+ }
+ } else {
+ mutex_enter(&thdl->oth_lock);
+ for (entry = list_head(&thdl->oth_outstanding); entry != NULL;
+ entry = list_next(&thdl->oth_outstanding, entry)) {
+ if ((uintptr_t)entry == pkt->otp_reqid)
+ break;
+ }
+
+ if (entry == NULL) {
+ mutex_exit(&thdl->oth_lock);
+ freeb(mp);
+ return (ENOENT);
+ }
+ odd = entry->ote_odd;
+ mutex_exit(&thdl->oth_lock);
+ }
+
+ mp = overlay_m_tx(odd, mp);
+ freemsgchain(mp);
+
+ return (0);
+}
+
+typedef struct overlay_targ_list_int {
+ boolean_t otli_count;
+ uint32_t otli_cur;
+ uint32_t otli_nents;
+ uint32_t otli_ents[];
+} overlay_targ_list_int_t;
+
+static int
+overlay_target_list_copyin(const void *ubuf, void **outp, size_t *bsize,
+ int flags)
+{
+ overlay_targ_list_t n;
+ overlay_targ_list_int_t *otl;
+
+ if (ddi_copyin(ubuf, &n, sizeof (overlay_targ_list_t),
+ flags & FKIOCTL) != 0)
+ return (EFAULT);
+
+ /*
+ */
+ if (n.otl_nents >= INT32_MAX / sizeof (uint32_t))
+ return (EINVAL);
+ *bsize = sizeof (overlay_targ_list_int_t) +
+ sizeof (uint32_t) * n.otl_nents;
+ otl = kmem_zalloc(*bsize, KM_SLEEP);
+ otl->otli_cur = 0;
+ otl->otli_nents = n.otl_nents;
+ if (otl->otli_nents != 0) {
+ otl->otli_count = B_FALSE;
+ if (ddi_copyin((void *)((uintptr_t)ubuf +
+ offsetof(overlay_targ_list_t, otl_ents)),
+ otl->otli_ents, n.otl_nents * sizeof (uint32_t),
+ flags & FKIOCTL) != 0) {
+ kmem_free(otl, *bsize);
+ return (EFAULT);
+ }
+ } else {
+ otl->otli_count = B_TRUE;
+ }
+
+ *outp = otl;
+ return (0);
+}
+
+static int
+overlay_target_ioctl_list_cb(overlay_dev_t *odd, void *arg)
+{
+ overlay_targ_list_int_t *otl = arg;
+
+ if (otl->otli_cur < otl->otli_nents)
+ otl->otli_ents[otl->otli_cur] = odd->odd_linkid;
+ otl->otli_cur++;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_ioctl_list(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_iter(overlay_target_ioctl_list_cb, arg);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_list_copyout(void *ubuf, void *buf, size_t bufsize, int flags)
+{
+ overlay_targ_list_int_t *otl = buf;
+
+ if (ddi_copyout(&otl->otli_cur, ubuf, sizeof (uint32_t),
+ flags & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (otl->otli_count == B_FALSE) {
+ if (ddi_copyout(otl->otli_ents,
+ (void *)((uintptr_t)ubuf +
+ offsetof(overlay_targ_list_t, otl_ents)),
+ sizeof (uint32_t) * otl->otli_nents,
+ flags & FKIOCTL) != 0)
+ return (EFAULT);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_cache_get(overlay_target_hdl_t *thdl, void *arg)
+{
+ int ret = 0;
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_targ_cache_t *otc = arg;
+
+ odd = overlay_hold_by_dlid(otc->otc_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_VARPD)) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENXIO);
+ }
+ ott = odd->odd_target;
+ if (ott->ott_mode != OVERLAY_TARGET_POINT &&
+ ott->ott_mode != OVERLAY_TARGET_DYNAMIC) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENOTSUP);
+ }
+ mutex_enter(&ott->ott_lock);
+ mutex_exit(&odd->odd_lock);
+
+ if (ott->ott_mode == OVERLAY_TARGET_POINT) {
+ otc->otc_entry.otce_flags = 0;
+ bcopy(&ott->ott_u.ott_point, &otc->otc_entry.otce_dest,
+ sizeof (overlay_target_point_t));
+ } else {
+ overlay_target_entry_t *ote;
+ ote = refhash_lookup(ott->ott_u.ott_dyn.ott_dhash,
+ otc->otc_entry.otce_mac);
+ if (ote != NULL) {
+ mutex_enter(&ote->ote_lock);
+ if ((ote->ote_flags &
+ OVERLAY_ENTRY_F_VALID_MASK) != 0) {
+ if (ote->ote_flags & OVERLAY_ENTRY_F_DROP) {
+ otc->otc_entry.otce_flags =
+ OVERLAY_TARGET_CACHE_DROP;
+ } else {
+ otc->otc_entry.otce_flags = 0;
+ bcopy(&ote->ote_dest,
+ &otc->otc_entry.otce_dest,
+ sizeof (overlay_target_point_t));
+ }
+ ret = 0;
+ } else {
+ ret = ENOENT;
+ }
+ mutex_exit(&ote->ote_lock);
+ } else {
+ ret = ENOENT;
+ }
+ }
+
+ mutex_exit(&ott->ott_lock);
+ overlay_hold_rele(odd);
+
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_cache_set(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_target_entry_t *ote;
+ overlay_targ_cache_t *otc = arg;
+ mblk_t *mp = NULL;
+
+ if (otc->otc_entry.otce_flags & ~OVERLAY_TARGET_CACHE_DROP)
+ return (EINVAL);
+
+ odd = overlay_hold_by_dlid(otc->otc_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_VARPD)) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENXIO);
+ }
+ ott = odd->odd_target;
+ if (ott->ott_mode != OVERLAY_TARGET_DYNAMIC) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENOTSUP);
+ }
+ mutex_enter(&ott->ott_lock);
+ mutex_exit(&odd->odd_lock);
+
+ ote = refhash_lookup(ott->ott_u.ott_dyn.ott_dhash,
+ otc->otc_entry.otce_mac);
+ if (ote == NULL) {
+ ote = kmem_cache_alloc(overlay_entry_cache, KM_SLEEP);
+ bcopy(otc->otc_entry.otce_mac, ote->ote_addr, ETHERADDRL);
+ ote->ote_chead = ote->ote_ctail = NULL;
+ ote->ote_mbsize = 0;
+ ote->ote_ott = ott;
+ ote->ote_odd = odd;
+ mutex_enter(&ote->ote_lock);
+ refhash_insert(ott->ott_u.ott_dyn.ott_dhash, ote);
+ avl_add(&ott->ott_u.ott_dyn.ott_tree, ote);
+ } else {
+ mutex_enter(&ote->ote_lock);
+ }
+
+ if (otc->otc_entry.otce_flags & OVERLAY_TARGET_CACHE_DROP) {
+ ote->ote_flags |= OVERLAY_ENTRY_F_DROP;
+ } else {
+ ote->ote_flags |= OVERLAY_ENTRY_F_VALID;
+ bcopy(&otc->otc_entry.otce_dest, &ote->ote_dest,
+ sizeof (overlay_target_point_t));
+ mp = ote->ote_chead;
+ ote->ote_chead = NULL;
+ ote->ote_ctail = NULL;
+ ote->ote_mbsize = 0;
+ ote->ote_vtime = gethrtime();
+ }
+
+ mutex_exit(&ote->ote_lock);
+ mutex_exit(&ott->ott_lock);
+
+ if (mp != NULL) {
+ mp = overlay_m_tx(ote->ote_odd, mp);
+ freemsgchain(mp);
+ }
+
+ overlay_hold_rele(odd);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_cache_remove(overlay_target_hdl_t *thdl, void *arg)
+{
+ int ret = 0;
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_target_entry_t *ote;
+ overlay_targ_cache_t *otc = arg;
+
+ odd = overlay_hold_by_dlid(otc->otc_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_VARPD)) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENXIO);
+ }
+ ott = odd->odd_target;
+ if (ott->ott_mode != OVERLAY_TARGET_DYNAMIC) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENOTSUP);
+ }
+ mutex_enter(&ott->ott_lock);
+ mutex_exit(&odd->odd_lock);
+
+ ote = refhash_lookup(ott->ott_u.ott_dyn.ott_dhash,
+ otc->otc_entry.otce_mac);
+ if (ote != NULL) {
+ mutex_enter(&ote->ote_lock);
+ ote->ote_flags &= ~OVERLAY_ENTRY_F_VALID_MASK;
+ mutex_exit(&ote->ote_lock);
+ ret = 0;
+ } else {
+ ret = ENOENT;
+ }
+
+ mutex_exit(&ott->ott_lock);
+ overlay_hold_rele(odd);
+
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_cache_flush(overlay_target_hdl_t *thdl, void *arg)
+{
+ avl_tree_t *avl;
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_target_entry_t *ote;
+ overlay_targ_cache_t *otc = arg;
+
+ odd = overlay_hold_by_dlid(otc->otc_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_VARPD)) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENXIO);
+ }
+ ott = odd->odd_target;
+ if (ott->ott_mode != OVERLAY_TARGET_DYNAMIC) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENOTSUP);
+ }
+ mutex_enter(&ott->ott_lock);
+ mutex_exit(&odd->odd_lock);
+ avl = &ott->ott_u.ott_dyn.ott_tree;
+
+ for (ote = avl_first(avl); ote != NULL; ote = AVL_NEXT(avl, ote)) {
+ mutex_enter(&ote->ote_lock);
+ ote->ote_flags &= ~OVERLAY_ENTRY_F_VALID_MASK;
+ mutex_exit(&ote->ote_lock);
+ }
+ ote = refhash_lookup(ott->ott_u.ott_dyn.ott_dhash,
+ otc->otc_entry.otce_mac);
+
+ mutex_exit(&ott->ott_lock);
+ overlay_hold_rele(odd);
+
+ return (0);
+}
+
+static int
+overlay_target_cache_iter_copyin(const void *ubuf, void **outp, size_t *bsize,
+ int flags)
+{
+ overlay_targ_cache_iter_t base, *iter;
+
+ if (ddi_copyin(ubuf, &base, sizeof (overlay_targ_cache_iter_t),
+ flags & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (base.otci_count > OVERLAY_TARGET_ITER_MAX)
+ return (E2BIG);
+
+ if (base.otci_count == 0)
+ return (EINVAL);
+
+ *bsize = sizeof (overlay_targ_cache_iter_t) +
+ base.otci_count * sizeof (overlay_targ_cache_entry_t);
+ iter = kmem_alloc(*bsize, KM_SLEEP);
+ bcopy(&base, iter, sizeof (overlay_targ_cache_iter_t));
+ *outp = iter;
+
+ return (0);
+}
+
+typedef struct overlay_targ_cache_marker {
+ uint8_t otcm_mac[ETHERADDRL];
+ uint16_t otcm_done;
+} overlay_targ_cache_marker_t;
+
+/* ARGSUSED */
+static int
+overlay_target_cache_iter(overlay_target_hdl_t *thdl, void *arg)
+{
+ overlay_dev_t *odd;
+ overlay_target_t *ott;
+ overlay_target_entry_t lookup, *ent;
+ overlay_targ_cache_marker_t *mark;
+ avl_index_t where;
+ avl_tree_t *avl;
+ uint16_t written = 0;
+
+ overlay_targ_cache_iter_t *iter = arg;
+ mark = (void *)&iter->otci_marker;
+
+ if (mark->otcm_done != 0) {
+ iter->otci_count = 0;
+ return (0);
+ }
+
+ odd = overlay_hold_by_dlid(iter->otci_linkid);
+ if (odd == NULL)
+ return (ENOENT);
+
+ mutex_enter(&odd->odd_lock);
+ if (!(odd->odd_flags & OVERLAY_F_VARPD)) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENXIO);
+ }
+ ott = odd->odd_target;
+ if (ott->ott_mode != OVERLAY_TARGET_DYNAMIC &&
+ ott->ott_mode != OVERLAY_TARGET_POINT) {
+ mutex_exit(&odd->odd_lock);
+ overlay_hold_rele(odd);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Holding this lock across the entire iteration probably isn't very
+ * good. We should perhaps add an r/w lock for the avl tree. But we'll
+ * wait until we now it's necessary before we do more.
+ */
+ mutex_enter(&ott->ott_lock);
+ mutex_exit(&odd->odd_lock);
+
+ if (ott->ott_mode == OVERLAY_TARGET_POINT) {
+ overlay_targ_cache_entry_t *out = &iter->otci_ents[0];
+ bzero(out->otce_mac, ETHERADDRL);
+ out->otce_flags = 0;
+ bcopy(&ott->ott_u.ott_point, &out->otce_dest,
+ sizeof (overlay_target_point_t));
+ written++;
+ mark->otcm_done = 1;
+ }
+
+ avl = &ott->ott_u.ott_dyn.ott_tree;
+ bcopy(mark->otcm_mac, lookup.ote_addr, ETHERADDRL);
+ ent = avl_find(avl, &lookup, &where);
+
+ /*
+ * NULL ent means that the entry does not exist, so we want to start
+ * with the closest node in the tree. This means that we implicitly rely
+ * on the tree's order and the first node will be the mac 00:00:00:00:00
+ * and the last will be ff:ff:ff:ff:ff:ff.
+ */
+ if (ent == NULL) {
+ ent = avl_nearest(avl, where, AVL_AFTER);
+ if (ent == NULL) {
+ mark->otcm_done = 1;
+ goto done;
+ }
+ }
+
+ for (; ent != NULL && written < iter->otci_count;
+ ent = AVL_NEXT(avl, ent)) {
+ overlay_targ_cache_entry_t *out = &iter->otci_ents[written];
+ mutex_enter(&ent->ote_lock);
+ if ((ent->ote_flags & OVERLAY_ENTRY_F_VALID_MASK) == 0) {
+ mutex_exit(&ent->ote_lock);
+ continue;
+ }
+ bcopy(ent->ote_addr, out->otce_mac, ETHERADDRL);
+ out->otce_flags = 0;
+ if (ent->ote_flags & OVERLAY_ENTRY_F_DROP)
+ out->otce_flags |= OVERLAY_TARGET_CACHE_DROP;
+ if (ent->ote_flags & OVERLAY_ENTRY_F_VALID)
+ bcopy(&ent->ote_dest, &out->otce_dest,
+ sizeof (overlay_target_point_t));
+ written++;
+ mutex_exit(&ent->ote_lock);
+ }
+
+ if (ent != NULL) {
+ bcopy(ent->ote_addr, mark->otcm_mac, ETHERADDRL);
+ } else {
+ mark->otcm_done = 1;
+ }
+
+done:
+ iter->otci_count = written;
+ mutex_exit(&ott->ott_lock);
+ overlay_hold_rele(odd);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+overlay_target_cache_iter_copyout(void *ubuf, void *buf, size_t bufsize,
+ int flags)
+{
+ size_t outsize;
+ const overlay_targ_cache_iter_t *iter = buf;
+
+ outsize = sizeof (overlay_targ_cache_iter_t) +
+ iter->otci_count * sizeof (overlay_targ_cache_entry_t);
+
+ if (ddi_copyout(buf, ubuf, outsize, flags & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static overlay_target_ioctl_t overlay_target_ioctab[] = {
+ { OVERLAY_TARG_INFO, B_TRUE, B_TRUE,
+ NULL, overlay_target_info,
+ NULL, sizeof (overlay_targ_info_t) },
+ { OVERLAY_TARG_ASSOCIATE, B_TRUE, B_FALSE,
+ NULL, overlay_target_associate,
+ NULL, sizeof (overlay_targ_associate_t) },
+ { OVERLAY_TARG_DISASSOCIATE, B_TRUE, B_FALSE,
+ NULL, overlay_target_disassociate,
+ NULL, sizeof (overlay_targ_id_t) },
+ { OVERLAY_TARG_DEGRADE, B_TRUE, B_FALSE,
+ NULL, overlay_target_degrade,
+ NULL, sizeof (overlay_targ_degrade_t) },
+ { OVERLAY_TARG_RESTORE, B_TRUE, B_FALSE,
+ NULL, overlay_target_restore,
+ NULL, sizeof (overlay_targ_id_t) },
+ { OVERLAY_TARG_LOOKUP, B_FALSE, B_TRUE,
+ NULL, overlay_target_lookup_request,
+ NULL, sizeof (overlay_targ_lookup_t) },
+ { OVERLAY_TARG_RESPOND, B_TRUE, B_FALSE,
+ NULL, overlay_target_lookup_respond,
+ NULL, sizeof (overlay_targ_resp_t) },
+ { OVERLAY_TARG_DROP, B_TRUE, B_FALSE,
+ NULL, overlay_target_lookup_drop,
+ NULL, sizeof (overlay_targ_resp_t) },
+ { OVERLAY_TARG_PKT, B_TRUE, B_TRUE,
+ overlay_target_pkt_copyin,
+ overlay_target_packet,
+ overlay_target_pkt_copyout,
+ sizeof (overlay_targ_pkt_t) },
+ { OVERLAY_TARG_INJECT, B_TRUE, B_FALSE,
+ overlay_target_pkt_copyin,
+ overlay_target_inject,
+ NULL, sizeof (overlay_targ_pkt_t) },
+ { OVERLAY_TARG_RESEND, B_TRUE, B_FALSE,
+ overlay_target_pkt_copyin,
+ overlay_target_resend,
+ NULL, sizeof (overlay_targ_pkt_t) },
+ { OVERLAY_TARG_LIST, B_FALSE, B_TRUE,
+ overlay_target_list_copyin,
+ overlay_target_ioctl_list,
+ overlay_target_list_copyout,
+ sizeof (overlay_targ_list_t) },
+ { OVERLAY_TARG_CACHE_GET, B_FALSE, B_TRUE,
+ NULL, overlay_target_cache_get,
+ NULL, sizeof (overlay_targ_cache_t) },
+ { OVERLAY_TARG_CACHE_SET, B_TRUE, B_TRUE,
+ NULL, overlay_target_cache_set,
+ NULL, sizeof (overlay_targ_cache_t) },
+ { OVERLAY_TARG_CACHE_REMOVE, B_TRUE, B_TRUE,
+ NULL, overlay_target_cache_remove,
+ NULL, sizeof (overlay_targ_cache_t) },
+ { OVERLAY_TARG_CACHE_FLUSH, B_TRUE, B_TRUE,
+ NULL, overlay_target_cache_flush,
+ NULL, sizeof (overlay_targ_cache_t) },
+ { OVERLAY_TARG_CACHE_ITER, B_FALSE, B_TRUE,
+ overlay_target_cache_iter_copyin,
+ overlay_target_cache_iter,
+ overlay_target_cache_iter_copyout,
+ sizeof (overlay_targ_cache_iter_t) },
+ { 0 }
+};
+
+int
+overlay_target_open(dev_t *devp, int flags, int otype, cred_t *credp)
+{
+ minor_t mid;
+ overlay_target_hdl_t *thdl;
+
+ if (secpolicy_dl_config(credp) != 0)
+ return (EPERM);
+
+ if (getminor(*devp) != 0)
+ return (ENXIO);
+
+ if (otype & OTYP_BLK)
+ return (EINVAL);
+
+ if (flags & ~(FREAD | FWRITE | FEXCL))
+ return (EINVAL);
+
+ if ((flags & FWRITE) &&
+ !(flags & FEXCL))
+ return (EINVAL);
+
+ if (!(flags & FREAD) && !(flags & FWRITE))
+ return (EINVAL);
+
+ if (crgetzoneid(credp) != GLOBAL_ZONEID)
+ return (EPERM);
+
+ mid = id_alloc(overlay_thdl_idspace);
+ if (ddi_soft_state_zalloc(overlay_thdl_state, mid) != 0) {
+ id_free(overlay_thdl_idspace, mid);
+ return (ENXIO);
+ }
+
+ thdl = ddi_get_soft_state(overlay_thdl_state, mid);
+ VERIFY(thdl != NULL);
+ thdl->oth_minor = mid;
+ thdl->oth_zoneid = crgetzoneid(credp);
+ thdl->oth_oflags = flags;
+ mutex_init(&thdl->oth_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&thdl->oth_outstanding, sizeof (overlay_target_entry_t),
+ offsetof(overlay_target_entry_t, ote_qlink));
+ *devp = makedevice(getmajor(*devp), mid);
+
+ mutex_enter(&overlay_target_lock);
+ if ((flags & FEXCL) && overlay_target_excl == B_TRUE) {
+ mutex_exit(&overlay_target_lock);
+ list_destroy(&thdl->oth_outstanding);
+ mutex_destroy(&thdl->oth_lock);
+ ddi_soft_state_free(overlay_thdl_state, mid);
+ id_free(overlay_thdl_idspace, mid);
+ return (EEXIST);
+ } else if ((flags & FEXCL) != 0) {
+ VERIFY(overlay_target_excl == B_FALSE);
+ overlay_target_excl = B_TRUE;
+ }
+ list_insert_tail(&overlay_thdl_list, thdl);
+ mutex_exit(&overlay_target_lock);
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+overlay_target_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ overlay_target_ioctl_t *ioc;
+ overlay_target_hdl_t *thdl;
+
+ if (secpolicy_dl_config(credp) != 0)
+ return (EPERM);
+
+ if ((thdl = ddi_get_soft_state(overlay_thdl_state,
+ getminor(dev))) == NULL)
+ return (ENXIO);
+
+ for (ioc = &overlay_target_ioctab[0]; ioc->oti_cmd != 0; ioc++) {
+ int ret;
+ caddr_t buf;
+ size_t bufsize;
+
+ if (ioc->oti_cmd != cmd)
+ continue;
+
+ if (ioc->oti_write == B_TRUE && !(mode & FWRITE))
+ return (EBADF);
+
+ if (ioc->oti_copyin == NULL) {
+ bufsize = ioc->oti_size;
+ buf = kmem_alloc(bufsize, KM_SLEEP);
+ if (ddi_copyin((void *)(uintptr_t)arg, buf, bufsize,
+ mode & FKIOCTL) != 0) {
+ kmem_free(buf, bufsize);
+ return (EFAULT);
+ }
+ } else {
+ if ((ret = ioc->oti_copyin((void *)(uintptr_t)arg,
+ (void **)&buf, &bufsize, mode)) != 0)
+ return (ret);
+ }
+
+ ret = ioc->oti_func(thdl, buf);
+ if (ret == 0 && ioc->oti_size != 0 &&
+ ioc->oti_ncopyout == B_TRUE) {
+ if (ioc->oti_copyout == NULL) {
+ if (ddi_copyout(buf, (void *)(uintptr_t)arg,
+ bufsize, mode & FKIOCTL) != 0)
+ ret = EFAULT;
+ } else {
+ ret = ioc->oti_copyout((void *)(uintptr_t)arg,
+ buf, bufsize, mode);
+ }
+ }
+
+ kmem_free(buf, bufsize);
+ return (ret);
+ }
+
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+int
+overlay_target_close(dev_t dev, int flags, int otype, cred_t *credp)
+{
+ overlay_target_hdl_t *thdl;
+ overlay_target_entry_t *entry;
+ minor_t mid = getminor(dev);
+
+ if ((thdl = ddi_get_soft_state(overlay_thdl_state, mid)) == NULL)
+ return (ENXIO);
+
+ mutex_enter(&overlay_target_lock);
+ list_remove(&overlay_thdl_list, thdl);
+ mutex_enter(&thdl->oth_lock);
+ while ((entry = list_remove_head(&thdl->oth_outstanding)) != NULL)
+ list_insert_tail(&overlay_target_list, entry);
+ cv_signal(&overlay_target_condvar);
+ mutex_exit(&thdl->oth_lock);
+ if ((thdl->oth_oflags & FEXCL) != 0) {
+ VERIFY(overlay_target_excl == B_TRUE);
+ overlay_target_excl = B_FALSE;
+ }
+ mutex_exit(&overlay_target_lock);
+
+ list_destroy(&thdl->oth_outstanding);
+ mutex_destroy(&thdl->oth_lock);
+ mid = thdl->oth_minor;
+ ddi_soft_state_free(overlay_thdl_state, mid);
+ id_free(overlay_thdl_idspace, mid);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/overlay/plugins/overlay_vxlan.c b/usr/src/uts/common/io/overlay/plugins/overlay_vxlan.c
new file mode 100644
index 0000000000..8b4e4ecb42
--- /dev/null
+++ b/usr/src/uts/common/io/overlay/plugins/overlay_vxlan.c
@@ -0,0 +1,372 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * VXLAN encapsulation module
+ *
+ *
+ * The VXLAN header looks as follows in network byte order:
+ *
+ * |0 3| 4 |5 31|
+ * +----------+---+------------------------+
+ * | Reserved | I | Reserved |
+ * +---------------------------------------+
+ * | Virtual Network ID | Reserved |
+ * +----------------------------+----------+
+ * |0 23|24 31|
+ *
+ * All reserved values must be 0. The I bit must be 1. We call the top
+ * word the VXLAN magic field for the time being. The second word is
+ * definitely not the most friendly way to operate. Specifically, the ID
+ * is a 24-bit big endian value, but we have to make sure not to use the
+ * reserved byte.
+ *
+ * For us, VXLAN encapsulation is a fairly straightforward implementation. It
+ * only has two properties, a listen_ip and a listen_port. These determine on
+ * what address we should be listening on. While we do not have a default
+ * address to listen upon, we do have a default port, which is the IANA assigned
+ * port for VXLAN -- 4789.
+ */
+
+#include <sys/overlay_plugin.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/byteorder.h>
+#include <sys/vxlan.h>
+#include <inet/ip.h>
+#include <netinet/in.h>
+#include <sys/strsun.h>
+#include <netinet/udp.h>
+
+static const char *vxlan_ident = "vxlan";
+static uint16_t vxlan_defport = IPPORT_VXLAN;
+
+/*
+ * Should we enable UDP source port hashing for fanout.
+ */
+boolean_t vxlan_fanout = B_TRUE;
+
+static const char *vxlan_props[] = {
+ "vxlan/listen_ip",
+ "vxlan/listen_port",
+ NULL
+};
+
+typedef struct vxlan {
+ kmutex_t vxl_lock;
+ overlay_handle_t vxl_oh;
+ uint16_t vxl_lport;
+ boolean_t vxl_hladdr;
+ struct in6_addr vxl_laddr;
+} vxlan_t;
+
+static int
+vxlan_o_init(overlay_handle_t oh, void **outp)
+{
+ vxlan_t *vxl;
+
+ vxl = kmem_alloc(sizeof (vxlan_t), KM_SLEEP);
+ *outp = vxl;
+ mutex_init(&vxl->vxl_lock, NULL, MUTEX_DRIVER, NULL);
+ vxl->vxl_oh = oh;
+ vxl->vxl_lport = vxlan_defport;
+ vxl->vxl_hladdr = B_FALSE;
+
+ return (0);
+}
+
+static void
+vxlan_o_fini(void *arg)
+{
+ vxlan_t *vxl = arg;
+
+ mutex_destroy(&vxl->vxl_lock);
+ kmem_free(arg, sizeof (vxlan_t));
+}
+
+static int
+vxlan_o_socket(void *arg, int *dp, int *fp, int *pp, struct sockaddr *addr,
+ socklen_t *slenp)
+{
+ vxlan_t *vxl = arg;
+ struct sockaddr_in6 *in;
+
+ in = (struct sockaddr_in6 *)addr;
+ *dp = AF_INET6;
+ *fp = SOCK_DGRAM;
+ *pp = 0;
+ bzero(in, sizeof (struct sockaddr_in6));
+ in->sin6_family = AF_INET6;
+
+ /*
+ * We should consider a more expressive private errno set that
+ * provider's can use.
+ */
+ mutex_enter(&vxl->vxl_lock);
+ if (vxl->vxl_hladdr == B_FALSE) {
+ mutex_exit(&vxl->vxl_lock);
+ return (EINVAL);
+ }
+ in->sin6_port = htons(vxl->vxl_lport);
+ in->sin6_addr = vxl->vxl_laddr;
+ mutex_exit(&vxl->vxl_lock);
+ *slenp = sizeof (struct sockaddr_in6);
+
+ return (0);
+}
+
+static int
+vxlan_o_sockopt(ksocket_t ksock)
+{
+ int val, err;
+ if (vxlan_fanout == B_FALSE)
+ return (0);
+
+ val = UDP_HASH_VXLAN;
+ err = ksocket_setsockopt(ksock, IPPROTO_UDP, UDP_SRCPORT_HASH, &val,
+ sizeof (val), kcred);
+ return (err);
+}
+
+/* ARGSUSED */
+static int
+vxlan_o_encap(void *arg, mblk_t *mp, ovep_encap_info_t *einfop,
+ mblk_t **outp)
+{
+ mblk_t *ob;
+ vxlan_hdr_t *vxh;
+
+ ASSERT(einfop->ovdi_id < (1 << 24));
+
+ /*
+ * This allocation could get hot. We may want to have a good way to
+ * cache and handle this allocation the same way that IP does with
+ * keeping around a message block per entry, or basically treating this
+ * as an immutable message block in the system. Basically freemsg() will
+ * be a nop, but we'll do the right thing with respect to the rest of
+ * the chain.
+ */
+ ob = allocb(VXLAN_HDR_LEN, 0);
+ if (ob == NULL)
+ return (ENOMEM);
+
+ vxh = (vxlan_hdr_t *)ob->b_rptr;
+ vxh->vxlan_flags = ntohl(VXLAN_F_VDI);
+ vxh->vxlan_id = htonl((uint32_t)einfop->ovdi_id << VXLAN_ID_SHIFT);
+ ob->b_wptr += VXLAN_HDR_LEN;
+ *outp = ob;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+vxlan_o_decap(void *arg, mblk_t *mp, ovep_encap_info_t *dinfop)
+{
+ vxlan_hdr_t *vxh;
+
+ if (MBLKL(mp) < sizeof (vxlan_hdr_t))
+ return (EINVAL);
+ vxh = (vxlan_hdr_t *)mp->b_rptr;
+ if ((ntohl(vxh->vxlan_flags) & VXLAN_F_VDI) == 0)
+ return (EINVAL);
+
+ dinfop->ovdi_id = ntohl(vxh->vxlan_id) >> VXLAN_ID_SHIFT;
+ dinfop->ovdi_hdr_size = VXLAN_HDR_LEN;
+
+ return (0);
+}
+
+static int
+vxlan_o_getprop(void *arg, const char *pr_name, void *buf, uint32_t *bufsize)
+{
+ vxlan_t *vxl = arg;
+
+ /* vxlan/listen_ip */
+ if (strcmp(pr_name, vxlan_props[0]) == 0) {
+ if (*bufsize < sizeof (struct in6_addr))
+ return (EOVERFLOW);
+
+ mutex_enter(&vxl->vxl_lock);
+ if (vxl->vxl_hladdr == B_FALSE) {
+ *bufsize = 0;
+ } else {
+ bcopy(&vxl->vxl_laddr, buf, sizeof (struct in6_addr));
+ *bufsize = sizeof (struct in6_addr);
+ }
+ mutex_exit(&vxl->vxl_lock);
+ return (0);
+ }
+
+ /* vxlan/listen_port */
+ if (strcmp(pr_name, vxlan_props[1]) == 0) {
+ uint64_t val;
+ if (*bufsize < sizeof (uint64_t))
+ return (EOVERFLOW);
+
+ mutex_enter(&vxl->vxl_lock);
+ val = vxl->vxl_lport;
+ bcopy(&val, buf, sizeof (uint64_t));
+ *bufsize = sizeof (uint64_t);
+ mutex_exit(&vxl->vxl_lock);
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+vxlan_o_setprop(void *arg, const char *pr_name, const void *buf,
+ uint32_t bufsize)
+{
+ vxlan_t *vxl = arg;
+
+ /* vxlan/listen_ip */
+ if (strcmp(pr_name, vxlan_props[0]) == 0) {
+ const struct in6_addr *ipv6 = buf;
+ if (bufsize != sizeof (struct in6_addr))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_V4COMPAT(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_MULTICAST(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_6TO4(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_V4MAPPED(ipv6)) {
+ ipaddr_t v4;
+ IN6_V4MAPPED_TO_IPADDR(ipv6, v4);
+ if (IN_MULTICAST(v4))
+ return (EINVAL);
+ }
+
+ mutex_enter(&vxl->vxl_lock);
+ vxl->vxl_hladdr = B_TRUE;
+ bcopy(ipv6, &vxl->vxl_laddr, sizeof (struct in6_addr));
+ mutex_exit(&vxl->vxl_lock);
+
+ return (0);
+ }
+
+ /* vxlan/listen_port */
+ if (strcmp(pr_name, vxlan_props[1]) == 0) {
+ const uint64_t *valp = buf;
+ if (bufsize != 8)
+ return (EINVAL);
+
+ if (*valp == 0 || *valp > UINT16_MAX)
+ return (EINVAL);
+
+ mutex_enter(&vxl->vxl_lock);
+ vxl->vxl_lport = *valp;
+ mutex_exit(&vxl->vxl_lock);
+ return (0);
+ }
+ return (EINVAL);
+}
+
+static int
+vxlan_o_propinfo(const char *pr_name, overlay_prop_handle_t phdl)
+{
+ /* vxlan/listen_ip */
+ if (strcmp(pr_name, vxlan_props[0]) == 0) {
+ overlay_prop_set_name(phdl, vxlan_props[0]);
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RRW);
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_IP);
+ overlay_prop_set_nodefault(phdl);
+ return (0);
+ }
+
+ if (strcmp(pr_name, vxlan_props[1]) == 0) {
+ overlay_prop_set_name(phdl, vxlan_props[1]);
+ overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RRW);
+ overlay_prop_set_type(phdl, OVERLAY_PROP_T_UINT);
+ (void) overlay_prop_set_default(phdl, &vxlan_defport,
+ sizeof (vxlan_defport));
+ overlay_prop_set_range_uint32(phdl, 1, UINT16_MAX);
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static struct overlay_plugin_ops vxlan_o_ops = {
+ 0,
+ vxlan_o_init,
+ vxlan_o_fini,
+ vxlan_o_encap,
+ vxlan_o_decap,
+ vxlan_o_socket,
+ vxlan_o_sockopt,
+ vxlan_o_getprop,
+ vxlan_o_setprop,
+ vxlan_o_propinfo
+};
+
+static struct modlmisc vxlan_modlmisc = {
+ &mod_miscops,
+ "VXLAN encap plugin"
+};
+
+static struct modlinkage vxlan_modlinkage = {
+ MODREV_1,
+ &vxlan_modlmisc
+};
+
+int
+_init(void)
+{
+ int err;
+ overlay_plugin_register_t *ovrp;
+
+ ovrp = overlay_plugin_alloc(OVEP_VERSION);
+ if (ovrp == NULL)
+ return (ENOTSUP);
+ ovrp->ovep_name = vxlan_ident;
+ ovrp->ovep_ops = &vxlan_o_ops;
+ ovrp->ovep_id_size = VXLAN_ID_LEN;
+ ovrp->ovep_flags = OVEP_F_VLAN_TAG;
+ ovrp->ovep_dest = OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT;
+ ovrp->ovep_props = vxlan_props;
+
+ if ((err = overlay_plugin_register(ovrp)) == 0) {
+ if ((err = mod_install(&vxlan_modlinkage)) != 0) {
+ (void) overlay_plugin_unregister(vxlan_ident);
+ }
+ }
+
+ overlay_plugin_free(ovrp);
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&vxlan_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = overlay_plugin_unregister(vxlan_ident)) != 0)
+ return (err);
+
+ return (mod_remove(&vxlan_modlinkage));
+}
diff --git a/usr/src/uts/common/io/physmem.c b/usr/src/uts/common/io/physmem.c
index 39d5003b02..c48fecd133 100644
--- a/usr/src/uts/common/io/physmem.c
+++ b/usr/src/uts/common/io/physmem.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
@@ -807,6 +808,13 @@ physmem_open(dev_t *devp, int flag, int otyp, cred_t *credp)
int ret;
static int msg_printed = 0;
+ /*
+ * This device should never be visible in a zone, but if it somehow
+ * does get created we refuse to allow the zone to use it.
+ */
+ if (crgetzoneid(credp) != GLOBAL_ZONEID)
+ return (EACCES);
+
if ((flag & (FWRITE | FREAD)) != (FWRITE | FREAD)) {
return (EINVAL);
}
diff --git a/usr/src/uts/common/io/pseudo.conf b/usr/src/uts/common/io/pseudo.conf
index 42248e93d6..08affec609 100644
--- a/usr/src/uts/common/io/pseudo.conf
+++ b/usr/src/uts/common/io/pseudo.conf
@@ -22,8 +22,7 @@
#
# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2014 Joyent, Inc. All rights reserved.
#
# This file is private to the pseudonex driver. It should not be edited.
#
@@ -38,3 +37,9 @@ name="pseudo" class="root" instance=0;
# /pseudo; it has as its children the zone console pseudo nodes.
#
name="zconsnex" parent="/pseudo" instance=1 valid-children="zcons";
+
+#
+# zfdnex is an alias for pseudo; this node is instantiated as a child of
+# /pseudo; it has as its children the zone fd pseudo nodes.
+#
+name="zfdnex" parent="/pseudo" instance=2 valid-children="zfd";
diff --git a/usr/src/uts/common/io/pseudonex.c b/usr/src/uts/common/io/pseudonex.c
index f83b0abf39..0ae06f88cc 100644
--- a/usr/src/uts/common/io/pseudonex.c
+++ b/usr/src/uts/common/io/pseudonex.c
@@ -83,6 +83,8 @@ static int pseudonex_detach(dev_info_t *, ddi_detach_cmd_t);
static int pseudonex_open(dev_t *, int, int, cred_t *);
static int pseudonex_close(dev_t, int, int, cred_t *);
static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int pseudonex_fm_init(dev_info_t *, dev_info_t *, int,
+ ddi_iblock_cookie_t *);
static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
void *);
@@ -90,6 +92,8 @@ static void *pseudonex_state;
typedef struct pseudonex_state {
dev_info_t *pnx_devi;
+ int pnx_fmcap;
+ ddi_iblock_cookie_t pnx_fm_ibc;
} pseudonex_state_t;
static struct bus_ops pseudonex_bus_ops = {
@@ -116,7 +120,7 @@ static struct bus_ops pseudonex_bus_ops = {
NULL, /* bus_intr_ctl */
NULL, /* bus_config */
NULL, /* bus_unconfig */
- NULL, /* bus_fm_init */
+ pseudonex_fm_init, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
@@ -228,6 +232,9 @@ pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
pnx_state = ddi_get_soft_state(pseudonex_state, instance);
pnx_state->pnx_devi = devi;
+ pnx_state->pnx_fmcap = DDI_FM_EREPORT_CAPABLE;
+ ddi_fm_init(devi, &pnx_state->pnx_fmcap, &pnx_state->pnx_fm_ibc);
+
if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
ddi_remove_minor_node(devi, NULL);
@@ -247,6 +254,10 @@ pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
if (cmd == DDI_SUSPEND)
return (DDI_SUCCESS);
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ ddi_fm_fini(devi);
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(pseudonex_state, instance);
return (DDI_SUCCESS);
@@ -375,6 +386,19 @@ pseudonex_auto_assign(dev_info_t *child)
}
static int
+pseudonex_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
+ ddi_iblock_cookie_t *ibc)
+{
+ pseudonex_state_t *pnx_state;
+
+ pnx_state = ddi_get_soft_state(pseudonex_state, ddi_get_instance(dip));
+ ASSERT(pnx_state != NULL);
+ ASSERT(ibc != NULL);
+ *ibc = pnx_state->pnx_fm_ibc;
+ return (pnx_state->pnx_fmcap & cap);
+}
+
+static int
pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
void *arg, void *result)
{
diff --git a/usr/src/uts/common/io/ptm.c b/usr/src/uts/common/io/ptm.c
index 400e9ffd10..07ffddc123 100644
--- a/usr/src/uts/common/io/ptm.c
+++ b/usr/src/uts/common/io/ptm.c
@@ -447,6 +447,18 @@ ptmclose(queue_t *rqp, int flag, cred_t *credp)
return (0);
}
+static boolean_t
+ptmptsopencb(ptmptsopencb_arg_t arg)
+{
+ struct pt_ttys *ptmp = (struct pt_ttys *)arg;
+ boolean_t rval;
+
+ PT_ENTER_READ(ptmp);
+ rval = (ptmp->pt_nullmsg != NULL);
+ PT_EXIT_READ(ptmp);
+ return (rval);
+}
+
/*
* The wput procedure will only handle ioctl and flush messages.
*/
@@ -574,6 +586,41 @@ ptmwput(queue_t *qp, mblk_t *mp)
miocack(qp, mp, 0, 0);
break;
}
+ case PTMPTSOPENCB:
+ {
+ mblk_t *dp; /* ioctl reply data */
+ ptmptsopencb_t *ppocb;
+
+ /* only allow the kernel to invoke this ioctl */
+ if (iocp->ioc_cr != kcred) {
+ miocnak(qp, mp, 0, EINVAL);
+ break;
+ }
+
+ /* we don't support transparent ioctls */
+ ASSERT(iocp->ioc_count != TRANSPARENT);
+ if (iocp->ioc_count == TRANSPARENT) {
+ miocnak(qp, mp, 0, EINVAL);
+ break;
+ }
+
+ /* allocate a response message */
+ dp = allocb(sizeof (ptmptsopencb_t), BPRI_MED);
+ if (dp == NULL) {
+ miocnak(qp, mp, 0, EAGAIN);
+ break;
+ }
+
+ /* initialize the ioctl results */
+ ppocb = (ptmptsopencb_t *)dp->b_rptr;
+ ppocb->ppocb_func = ptmptsopencb;
+ ppocb->ppocb_arg = (ptmptsopencb_arg_t)ptmp;
+
+ /* send the reply data */
+ mioc2ack(mp, dp, sizeof (ptmptsopencb_t), 0);
+ qreply(qp, mp);
+ break;
+ }
}
break;
diff --git a/usr/src/uts/common/io/random.c b/usr/src/uts/common/io/random.c
index c4984e956f..b5401b17cd 100644
--- a/usr/src/uts/common/io/random.c
+++ b/usr/src/uts/common/io/random.c
@@ -20,6 +20,8 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
*/
@@ -289,6 +291,9 @@ rnd_write(dev_t dev, struct uio *uiop, cred_t *credp)
if ((error = uiomove(buf, bytes, UIO_WRITE, uiop)) != 0)
return (error);
+ if (crgetzone(credp) != global_zone)
+ continue;
+
switch (devno) {
case DEVRANDOM:
if ((error = random_add_entropy(buf, bytes, 0)) != 0)
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 72c8800f3e..dc5e8eafc9 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
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
* Copyright 2014 OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright (c) 2014, Tegile Systems Inc. All rights reserved.
*/
@@ -72,6 +72,7 @@
#include <sys/file.h>
#include <sys/policy.h>
#include <sys/model.h>
+#include <sys/refhash.h>
#include <sys/sysevent.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/dr.h>
@@ -99,7 +100,6 @@
#include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
#include <sys/scsi/adapters/mpt_sas/mptsas_ioctl.h>
#include <sys/scsi/adapters/mpt_sas/mptsas_smhba.h>
-#include <sys/scsi/adapters/mpt_sas/mptsas_hash.h>
#include <sys/raidioctl.h>
#include <sys/fs/dv_node.h> /* devfs_clean */
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt.c
new file mode 100644
index 0000000000..fa925395b7
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt.c
@@ -0,0 +1,441 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+static int smrt_attach(dev_info_t *, ddi_attach_cmd_t);
+static int smrt_detach(dev_info_t *, ddi_detach_cmd_t);
+static int smrt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static void smrt_cleanup(smrt_t *);
+static int smrt_command_comparator(const void *, const void *);
+
+/*
+ * Controller soft state. Each entry is an object of type "smrt_t".
+ */
+void *smrt_state;
+
+/*
+ * DMA attributes template. Each controller will make a copy of this template
+ * with appropriate customisations; e.g., the Scatter/Gather List Length.
+ */
+static ddi_dma_attr_t smrt_dma_attr_template = {
+ .dma_attr_version = DMA_ATTR_V0,
+ .dma_attr_addr_lo = 0x0000000000000000,
+ .dma_attr_addr_hi = 0xFFFFFFFFFFFFFFFF,
+ .dma_attr_count_max = 0x00FFFFFF,
+ .dma_attr_align = 0x20,
+ .dma_attr_burstsizes = 0x20,
+ .dma_attr_minxfer = DMA_UNIT_8,
+ .dma_attr_maxxfer = 0xFFFFFFFF,
+ /*
+ * There is some suggestion that at least some, possibly older, Smart
+ * Array controllers cannot tolerate a DMA segment that straddles a 4GB
+ * boundary.
+ */
+ .dma_attr_seg = 0xFFFFFFFF,
+ .dma_attr_sgllen = 1,
+ .dma_attr_granular = 512,
+ .dma_attr_flags = 0
+};
+
+/*
+ * Device memory access attributes for device control registers.
+ */
+ddi_device_acc_attr_t smrt_dev_attributes = {
+ .devacc_attr_version = DDI_DEVICE_ATTR_V0,
+ .devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC,
+ .devacc_attr_dataorder = DDI_STRICTORDER_ACC,
+ .devacc_attr_access = 0
+};
+
+/*
+ * Character/Block Operations Structure
+ */
+static struct cb_ops smrt_cb_ops = {
+ .cb_rev = CB_REV,
+ .cb_flag = D_NEW | D_MP,
+
+ .cb_open = scsi_hba_open,
+ .cb_close = scsi_hba_close,
+
+ .cb_ioctl = smrt_ioctl,
+
+ .cb_strategy = nodev,
+ .cb_print = nodev,
+ .cb_dump = nodev,
+ .cb_read = nodev,
+ .cb_write = nodev,
+ .cb_devmap = nodev,
+ .cb_mmap = nodev,
+ .cb_segmap = nodev,
+ .cb_chpoll = nochpoll,
+ .cb_prop_op = ddi_prop_op,
+ .cb_str = NULL,
+ .cb_aread = nodev,
+ .cb_awrite = nodev
+};
+
+/*
+ * Device Operations Structure
+ */
+static struct dev_ops smrt_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+
+ .devo_attach = smrt_attach,
+ .devo_detach = smrt_detach,
+
+ .devo_cb_ops = &smrt_cb_ops,
+
+ .devo_getinfo = nodev,
+ .devo_identify = nulldev,
+ .devo_probe = nulldev,
+ .devo_reset = nodev,
+ .devo_bus_ops = NULL,
+ .devo_power = nodev,
+ .devo_quiesce = nodev
+};
+
+/*
+ * Linkage structures
+ */
+static struct modldrv smrt_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "HP Smart Array",
+ .drv_dev_ops = &smrt_dev_ops
+};
+
+static struct modlinkage smrt_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &smrt_modldrv, NULL }
+};
+
+
+int
+_init()
+{
+ int r;
+
+ VERIFY0(ddi_soft_state_init(&smrt_state, sizeof (smrt_t), 0));
+
+ if ((r = scsi_hba_init(&smrt_modlinkage)) != 0) {
+ goto fail;
+ }
+
+ if ((r = mod_install(&smrt_modlinkage)) != 0) {
+ scsi_hba_fini(&smrt_modlinkage);
+ goto fail;
+ }
+
+ return (r);
+
+fail:
+ ddi_soft_state_fini(&smrt_state);
+ return (r);
+}
+
+int
+_fini()
+{
+ int r;
+
+ if ((r = mod_remove(&smrt_modlinkage)) == 0) {
+ scsi_hba_fini(&smrt_modlinkage);
+ ddi_soft_state_fini(&smrt_state);
+ }
+
+ return (r);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&smrt_modlinkage, modinfop));
+}
+
+static int
+smrt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ uint32_t instance;
+ smrt_t *smrt;
+
+ if (cmd != DDI_ATTACH) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Allocate the per-controller soft state object and get
+ * a pointer to it.
+ */
+ instance = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(smrt_state, instance) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not allocate soft state");
+ return (DDI_FAILURE);
+ }
+ if ((smrt = ddi_get_soft_state(smrt_state, instance)) == NULL) {
+ dev_err(dip, CE_WARN, "could not get soft state");
+ ddi_soft_state_free(smrt_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Initialise per-controller state object.
+ */
+ smrt->smrt_dip = dip;
+ smrt->smrt_instance = instance;
+ smrt->smrt_next_tag = SMRT_MIN_TAG_NUMBER;
+ list_create(&smrt->smrt_commands, sizeof (smrt_command_t),
+ offsetof(smrt_command_t, smcm_link));
+ list_create(&smrt->smrt_finishq, sizeof (smrt_command_t),
+ offsetof(smrt_command_t, smcm_link_finish));
+ list_create(&smrt->smrt_abortq, sizeof (smrt_command_t),
+ offsetof(smrt_command_t, smcm_link_abort));
+ list_create(&smrt->smrt_volumes, sizeof (smrt_volume_t),
+ offsetof(smrt_volume_t, smlv_link));
+ list_create(&smrt->smrt_targets, sizeof (smrt_target_t),
+ offsetof(smrt_target_t, smtg_link_ctlr));
+ avl_create(&smrt->smrt_inflight, smrt_command_comparator,
+ sizeof (smrt_command_t), offsetof(smrt_command_t,
+ smcm_node));
+ cv_init(&smrt->smrt_cv_finishq, NULL, CV_DRIVER, NULL);
+
+ smrt->smrt_init_level |= SMRT_INITLEVEL_BASIC;
+
+ /*
+ * Perform basic device setup, including identifying the board, mapping
+ * the I2O registers and the Configuration Table.
+ */
+ if (smrt_device_setup(smrt) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "device setup failed");
+ goto fail;
+ }
+
+ /*
+ * Select a Transport Method (e.g. Simple or Performant) and update
+ * the Configuration Table. This function also waits for the
+ * controller to become ready.
+ */
+ if (smrt_ctlr_init(smrt) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "controller initialisation failed");
+ goto fail;
+ }
+
+ /*
+ * Each controller may have a different Scatter/Gather Element count.
+ * Configure a per-controller set of DMA attributes with the
+ * appropriate S/G size.
+ */
+ VERIFY(smrt->smrt_sg_cnt > 0);
+ smrt->smrt_dma_attr = smrt_dma_attr_template;
+ smrt->smrt_dma_attr.dma_attr_sgllen = smrt->smrt_sg_cnt;
+
+ /*
+ * Now that we have selected a Transport Method, we can configure
+ * the appropriate interrupt handlers.
+ */
+ if (smrt_interrupts_setup(smrt) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "interrupt handler setup failed");
+ goto fail;
+ }
+
+ /*
+ * Now that we have the correct interrupt priority, we can initialise
+ * the mutex. This must be done before the interrupt handler is
+ * enabled.
+ */
+ mutex_init(&smrt->smrt_mutex, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(smrt->smrt_interrupt_pri));
+ smrt->smrt_init_level |= SMRT_INITLEVEL_MUTEX;
+
+ /*
+ * From this point forward, the controller is able to accept commands
+ * and (at least by polling) return command submissions. Setting this
+ * flag allows the rest of the driver to interact with the device.
+ */
+ smrt->smrt_status |= SMRT_CTLR_STATUS_RUNNING;
+
+ if (smrt_interrupts_enable(smrt) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "interrupt handler could not be enabled");
+ goto fail;
+ }
+
+ if (smrt_hba_setup(smrt) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "SCSI framework setup failed");
+ goto fail;
+ }
+
+ /*
+ * Set the appropriate Interrupt Mask Register bits to start
+ * command completion interrupts from the controller.
+ */
+ smrt_intr_set(smrt, B_TRUE);
+
+ /*
+ * Register the maintenance routine for periodic execution:
+ */
+ smrt->smrt_periodic = ddi_periodic_add(smrt_periodic, smrt,
+ SMRT_PERIODIC_RATE * NANOSEC, DDI_IPL_0);
+ smrt->smrt_init_level |= SMRT_INITLEVEL_PERIODIC;
+
+ /*
+ * Discover the set of logical volumes attached to this controller:
+ */
+ if (smrt_logvol_discover(smrt, SMRT_LOGVOL_DISCOVER_TIMEOUT) != 0) {
+ dev_err(dip, CE_WARN, "could not discover logical volumes");
+ goto fail;
+ }
+
+ /*
+ * Announce the attachment of this controller.
+ */
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+
+fail:
+ smrt_cleanup(smrt);
+ return (DDI_FAILURE);
+}
+
+static int
+smrt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ scsi_hba_tran_t *tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+
+ if (cmd != DDI_DETACH) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * First, check to make sure that all SCSI framework targets have
+ * detached.
+ */
+ mutex_enter(&smrt->smrt_mutex);
+ if (!list_is_empty(&smrt->smrt_targets)) {
+ mutex_exit(&smrt->smrt_mutex);
+ dev_err(smrt->smrt_dip, CE_WARN, "cannot detach; targets still "
+ "using HBA");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Prevent new targets from attaching now:
+ */
+ smrt->smrt_status |= SMRT_CTLR_STATUS_DETACHING;
+ mutex_exit(&smrt->smrt_mutex);
+
+ /*
+ * Clean up all remaining resources.
+ */
+ smrt_cleanup(smrt);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+smrt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rval)
+{
+ int inst = MINOR2INST(getminor(dev));
+ int status;
+
+ if (secpolicy_sys_config(credp, B_FALSE) != 0) {
+ return (EPERM);
+ }
+
+ /*
+ * Ensure that we have a soft state object for this instance.
+ */
+ if (ddi_get_soft_state(smrt_state, inst) == NULL) {
+ return (ENXIO);
+ }
+
+ switch (cmd) {
+ default:
+ status = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rval);
+ break;
+ }
+
+ return (status);
+}
+
+static void
+smrt_cleanup(smrt_t *smrt)
+{
+ smrt_interrupts_teardown(smrt);
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_PERIODIC) {
+ ddi_periodic_delete(smrt->smrt_periodic);
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_PERIODIC;
+ }
+
+ smrt_hba_teardown(smrt);
+
+ smrt_ctlr_teardown(smrt);
+
+ smrt_device_teardown(smrt);
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_BASIC) {
+ smrt_logvol_teardown(smrt);
+
+ cv_destroy(&smrt->smrt_cv_finishq);
+
+ VERIFY(list_is_empty(&smrt->smrt_commands));
+ list_destroy(&smrt->smrt_commands);
+ list_destroy(&smrt->smrt_finishq);
+ list_destroy(&smrt->smrt_abortq);
+
+ VERIFY(list_is_empty(&smrt->smrt_volumes));
+ list_destroy(&smrt->smrt_volumes);
+
+ VERIFY(list_is_empty(&smrt->smrt_targets));
+ list_destroy(&smrt->smrt_targets);
+
+ VERIFY(avl_is_empty(&smrt->smrt_inflight));
+ avl_destroy(&smrt->smrt_inflight);
+
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_BASIC;
+ }
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_MUTEX) {
+ mutex_destroy(&smrt->smrt_mutex);
+
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_MUTEX;
+ }
+
+ VERIFY0(smrt->smrt_init_level);
+
+ ddi_soft_state_free(smrt_state, ddi_get_instance(smrt->smrt_dip));
+}
+
+/*
+ * Comparator for the "smrt_inflight" AVL tree in a "smrt_t". This AVL tree
+ * allows a tag ID to be mapped back to the relevant "smrt_command_t".
+ */
+static int
+smrt_command_comparator(const void *lp, const void *rp)
+{
+ const smrt_command_t *l = lp;
+ const smrt_command_t *r = rp;
+
+ if (l->smcm_tag > r->smcm_tag) {
+ return (1);
+ } else if (l->smcm_tag < r->smcm_tag) {
+ return (-1);
+ } else {
+ return (0);
+ }
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt.conf b/usr/src/uts/common/io/scsi/adapters/smrt/smrt.conf
new file mode 100644
index 0000000000..758ecd0779
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt.conf
@@ -0,0 +1,16 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+scsi-no-quiesce=1;
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss.c
new file mode 100644
index 0000000000..99b396c0e4
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss.c
@@ -0,0 +1,1119 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+/*
+ * The maximum number of seconds to wait for the controller to come online.
+ */
+unsigned smrt_ciss_init_time = 90;
+
+
+void
+smrt_write_lun_addr_phys(LUNAddr_t *lun, boolean_t masked, unsigned bus,
+ unsigned target)
+{
+ lun->PhysDev.Mode = masked ? MASK_PERIPHERIAL_DEV_ADDR :
+ PERIPHERIAL_DEV_ADDR;
+
+ lun->PhysDev.TargetId = target;
+ lun->PhysDev.Bus = bus;
+
+ bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target));
+}
+
+void
+smrt_write_message_common(smrt_command_t *smcm, uint8_t type, int timeout_secs)
+{
+ switch (type) {
+ case CISS_MSG_ABORT:
+ case CISS_MSG_RESET:
+ case CISS_MSG_NOP:
+ break;
+
+ default:
+ panic("unknown message type");
+ }
+
+ smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_MSG;
+ smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
+ smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_NONE;
+ smcm->smcm_va_cmd->Request.Timeout = LE_16(timeout_secs);
+ smcm->smcm_va_cmd->Request.CDBLen = CISS_CDBLEN;
+ smcm->smcm_va_cmd->Request.CDB[0] = type;
+}
+
+void
+smrt_write_message_abort_one(smrt_command_t *smcm, uint32_t tag)
+{
+ smrt_tag_t cisstag;
+
+ /*
+ * When aborting a particular command, the request is addressed
+ * to the controller.
+ */
+ smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
+ B_TRUE, 0, 0);
+
+ smrt_write_message_common(smcm, CISS_MSG_ABORT, 0);
+
+ /*
+ * Abort a single command.
+ */
+ smcm->smcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASK;
+
+ /*
+ * The CISS Specification says that the tag value for a task-level
+ * abort should be in the CDB in bytes 4-11.
+ */
+ bzero(&cisstag, sizeof (cisstag));
+ cisstag.tag_value = tag;
+ bcopy(&cisstag, &smcm->smcm_va_cmd->Request.CDB[4],
+ sizeof (cisstag));
+}
+
+void
+smrt_write_message_abort_all(smrt_command_t *smcm, LogDevAddr_t *addr)
+{
+ /*
+ * When aborting all tasks for a particular Logical Volume,
+ * the command is addressed not to the controller but to
+ * the Volume itself.
+ */
+ smcm->smcm_va_cmd->Header.LUN.LogDev = *addr;
+
+ smrt_write_message_common(smcm, CISS_MSG_ABORT, 0);
+
+ /*
+ * Abort all commands for a particular Logical Volume.
+ */
+ smcm->smcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASKSET;
+}
+
+void
+smrt_write_message_reset_ctlr(smrt_command_t *smcm)
+{
+ smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
+ B_TRUE, 0, 0);
+
+ smrt_write_message_common(smcm, CISS_MSG_RESET, 0);
+
+ smcm->smcm_va_cmd->Request.CDB[1] = CISS_RESET_CTLR;
+}
+
+void
+smrt_write_message_nop(smrt_command_t *smcm, int timeout_secs)
+{
+ /*
+ * No-op messages are always sent to the controller.
+ */
+ smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
+ B_TRUE, 0, 0);
+
+ smrt_write_message_common(smcm, CISS_MSG_NOP, timeout_secs);
+}
+
+/*
+ * This routine is executed regularly by ddi_periodic_add(9F). It checks the
+ * health of the controller and looks for submitted commands that have timed
+ * out.
+ */
+void
+smrt_periodic(void *arg)
+{
+ smrt_t *smrt = arg;
+
+ mutex_enter(&smrt->smrt_mutex);
+ if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) {
+ /*
+ * The device is currently not active, e.g. due to an
+ * in-progress controller reset.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ return;
+ }
+
+ /*
+ * Check on the health of the controller firmware. Note that if the
+ * controller has locked up, this routine will panic the system.
+ */
+ smrt_lockup_check(smrt);
+
+ /*
+ * Check inflight commands to see if they have timed out.
+ */
+ for (smrt_command_t *smcm = avl_first(&smrt->smrt_inflight);
+ smcm != NULL; smcm = AVL_NEXT(&smrt->smrt_inflight, smcm)) {
+ if (smcm->smcm_status & SMRT_CMD_STATUS_POLLED) {
+ /*
+ * Polled commands are timed out by the polling
+ * routine.
+ */
+ continue;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_ABORT_SENT) {
+ /*
+ * This command has been aborted; either it will
+ * complete or the controller will be reset.
+ */
+ continue;
+ }
+
+ if (list_link_active(&smcm->smcm_link_abort)) {
+ /*
+ * Already on the abort queue.
+ */
+ continue;
+ }
+
+ if (smcm->smcm_expiry == 0) {
+ /*
+ * This command has no expiry time.
+ */
+ continue;
+ }
+
+ if (gethrtime() > smcm->smcm_expiry) {
+ list_insert_tail(&smrt->smrt_abortq, smcm);
+ smcm->smcm_status |= SMRT_CMD_STATUS_TIMEOUT;
+ }
+ }
+
+ /*
+ * Process the abort queue.
+ */
+ (void) smrt_process_abortq(smrt);
+
+ mutex_exit(&smrt->smrt_mutex);
+}
+
+int
+smrt_retrieve(smrt_t *smrt)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ switch (smrt->smrt_ctlr_mode) {
+ case SMRT_CTLR_MODE_SIMPLE:
+ smrt_retrieve_simple(smrt);
+ return (DDI_SUCCESS);
+
+ case SMRT_CTLR_MODE_UNKNOWN:
+ break;
+ }
+
+ panic("unknown controller mode");
+ /* LINTED: E_FUNC_NO_RET_VAL */
+}
+
+/*
+ * Grab a new tag number for this command. We aim to avoid reusing tag numbers
+ * as much as possible, so as to avoid spurious double completion from the
+ * controller.
+ */
+static void
+smrt_set_new_tag(smrt_t *smrt, smrt_command_t *smcm)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ /*
+ * Loop until we find a tag that is not in use. The tag space is
+ * very large (~30 bits) and the maximum number of inflight commands
+ * is comparatively small (~1024 in current controllers).
+ */
+ for (;;) {
+ uint32_t new_tag = smrt->smrt_next_tag;
+
+ if (++smrt->smrt_next_tag > SMRT_MAX_TAG_NUMBER) {
+ smrt->smrt_next_tag = SMRT_MIN_TAG_NUMBER;
+ }
+
+ if (smrt_lookup_inflight(smrt, new_tag) != NULL) {
+ /*
+ * This tag is already used on an inflight command.
+ * Choose another.
+ */
+ continue;
+ }
+
+ /*
+ * Set the tag for the command and also write it into the
+ * appropriate part of the request block.
+ */
+ smcm->smcm_tag = new_tag;
+ smcm->smcm_va_cmd->Header.Tag.tag_value = new_tag;
+ return;
+ }
+}
+
+/*
+ * Submit a command to the controller.
+ */
+int
+smrt_submit(smrt_t *smrt, smrt_command_t *smcm)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ /*
+ * If the controller is currently being reset, do not allow command
+ * submission.
+ */
+ if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) {
+ return (EIO);
+ }
+
+ /*
+ * Do not allow submission of more concurrent commands than the
+ * controller supports.
+ */
+ if (avl_numnodes(&smrt->smrt_inflight) >= smrt->smrt_maxcmds) {
+ return (EAGAIN);
+ }
+
+ /*
+ * Synchronise the Command Block DMA resources to ensure that the
+ * device has a consistent view before we pass it the command.
+ */
+ if (ddi_dma_sync(smcm->smcm_contig.smdma_dma_handle, 0, 0,
+ DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "DMA sync failure");
+ return (EIO);
+ }
+
+ /*
+ * Ensure that this command is not re-used without issuing a new
+ * tag number and performing any appropriate cleanup.
+ */
+ VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_USED));
+ smcm->smcm_status |= SMRT_CMD_STATUS_USED;
+
+ /*
+ * Assign a tag that is not currently in use
+ */
+ smrt_set_new_tag(smrt, smcm);
+
+ /*
+ * Insert this command into the inflight AVL.
+ */
+ avl_index_t where;
+ if (avl_find(&smrt->smrt_inflight, smcm, &where) != NULL) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "duplicate submit tag %x",
+ smcm->smcm_tag);
+ }
+ avl_insert(&smrt->smrt_inflight, smcm, where);
+ if (smrt->smrt_stats.smrts_max_inflight <
+ avl_numnodes(&smrt->smrt_inflight)) {
+ smrt->smrt_stats.smrts_max_inflight =
+ avl_numnodes(&smrt->smrt_inflight);
+ }
+
+ VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
+ smcm->smcm_status |= SMRT_CMD_STATUS_INFLIGHT;
+
+ smcm->smcm_time_submit = gethrtime();
+
+ switch (smrt->smrt_ctlr_mode) {
+ case SMRT_CTLR_MODE_SIMPLE:
+ smrt_submit_simple(smrt, smcm);
+ return (0);
+
+ case SMRT_CTLR_MODE_UNKNOWN:
+ break;
+ }
+ panic("unknown controller mode");
+ /* LINTED: E_FUNC_NO_RET_VAL */
+}
+
+static void
+smrt_process_finishq_sync(smrt_command_t *smcm)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+
+ if (ddi_dma_sync(smcm->smcm_contig.smdma_dma_handle, 0, 0,
+ DDI_DMA_SYNC_FORCPU) != DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "finishq DMA sync failure");
+ }
+}
+
+static void
+smrt_process_finishq_one(smrt_command_t *smcm)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+
+ VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_COMPLETE));
+ smcm->smcm_status |= SMRT_CMD_STATUS_COMPLETE;
+
+ switch (smcm->smcm_type) {
+ case SMRT_CMDTYPE_INTERNAL:
+ cv_broadcast(&smcm->smcm_ctlr->smrt_cv_finishq);
+ return;
+
+ case SMRT_CMDTYPE_SCSA:
+ smrt_hba_complete(smcm);
+ return;
+
+ case SMRT_CMDTYPE_ABORTQ:
+ /*
+ * Abort messages sent as part of abort queue processing
+ * do not require any completion activity.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm);
+ mutex_enter(&smrt->smrt_mutex);
+ return;
+ }
+
+ panic("unknown command type");
+}
+
+/*
+ * Process commands in the completion queue.
+ */
+void
+smrt_process_finishq(smrt_t *smrt)
+{
+ smrt_command_t *smcm;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ while ((smcm = list_remove_head(&smrt->smrt_finishq)) != NULL) {
+ /*
+ * Synchronise the Command Block before we read from it or
+ * free it, to ensure that any writes from the controller are
+ * visible.
+ */
+ smrt_process_finishq_sync(smcm);
+
+ /*
+ * Check if this command was in line to be aborted.
+ */
+ if (list_link_active(&smcm->smcm_link_abort)) {
+ /*
+ * This command was in line, but the controller
+ * subsequently completed the command before we
+ * were able to do so.
+ */
+ list_remove(&smrt->smrt_abortq, smcm);
+ smcm->smcm_status &= ~SMRT_CMD_STATUS_TIMEOUT;
+ }
+
+ /*
+ * Check if this command has been abandoned by the original
+ * submitter. If it has, free it now to avoid a leak.
+ */
+ if (smcm->smcm_status & SMRT_CMD_STATUS_ABANDONED) {
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm);
+ mutex_enter(&smrt->smrt_mutex);
+ continue;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_POLLED) {
+ /*
+ * This command will be picked up and processed
+ * by "smrt_poll_for()" once the CV is triggered
+ * at the end of processing.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE;
+ continue;
+ }
+
+ smrt_process_finishq_one(smcm);
+ }
+
+ cv_broadcast(&smrt->smrt_cv_finishq);
+}
+
+/*
+ * Process commands in the abort queue.
+ */
+void
+smrt_process_abortq(smrt_t *smrt)
+{
+ smrt_command_t *smcm;
+ smrt_command_t *abort_smcm = NULL;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ if (list_is_empty(&smrt->smrt_abortq)) {
+ goto out;
+ }
+
+another:
+ mutex_exit(&smrt->smrt_mutex);
+ if ((abort_smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_ABORTQ,
+ KM_NOSLEEP)) == NULL) {
+ /*
+ * No resources available to send abort messages. We will
+ * try again the next time around.
+ */
+ mutex_enter(&smrt->smrt_mutex);
+ goto out;
+ }
+ mutex_enter(&smrt->smrt_mutex);
+
+ while ((smcm = list_remove_head(&smrt->smrt_abortq)) != NULL) {
+ if (!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT)) {
+ /*
+ * This message is not currently inflight, so
+ * no abort is needed.
+ */
+ continue;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_ABORT_SENT) {
+ /*
+ * An abort message has already been sent for
+ * this command.
+ */
+ continue;
+ }
+
+ /*
+ * Send an abort message for the command.
+ */
+ smrt_write_message_abort_one(abort_smcm, smcm->smcm_tag);
+ if (smrt_submit(smrt, abort_smcm) != 0) {
+ /*
+ * The command could not be submitted to the
+ * controller. Put it back in the abort queue
+ * and give up for now.
+ */
+ list_insert_head(&smrt->smrt_abortq, smcm);
+ goto out;
+ }
+ smcm->smcm_status |= SMRT_CMD_STATUS_ABORT_SENT;
+
+ /*
+ * Record some debugging information about the abort we
+ * sent:
+ */
+ smcm->smcm_abort_time = gethrtime();
+ smcm->smcm_abort_tag = abort_smcm->smcm_tag;
+
+ /*
+ * The abort message was sent. Release it and
+ * allocate another command.
+ */
+ abort_smcm = NULL;
+ goto another;
+ }
+
+out:
+ cv_broadcast(&smrt->smrt_cv_finishq);
+ if (abort_smcm != NULL) {
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(abort_smcm);
+ mutex_enter(&smrt->smrt_mutex);
+ }
+}
+
+int
+smrt_poll_for(smrt_t *smrt, smrt_command_t *smcm)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+ VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_POLLED);
+
+ while (!(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE)) {
+ if (smcm->smcm_expiry != 0) {
+ /*
+ * This command has an expiry time. Check to see
+ * if it has already passed:
+ */
+ if (smcm->smcm_expiry < gethrtime()) {
+ return (ETIMEDOUT);
+ }
+ }
+
+ if (ddi_in_panic()) {
+ /*
+ * When the system is panicking, there are no
+ * interrupts or other threads. Drive the polling loop
+ * on our own, but with a small delay to avoid
+ * aggrevating the controller while we're trying to
+ * dump.
+ */
+ (void) smrt_retrieve(smrt);
+ smrt_process_finishq(smrt);
+ drv_usecwait(100);
+ continue;
+ }
+
+ /*
+ * Wait for command completion to return through the regular
+ * interrupt handling path.
+ */
+ if (smcm->smcm_expiry == 0) {
+ cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
+ } else {
+ /*
+ * Wait only until the expiry time for this command.
+ */
+ (void) cv_timedwait_sig_hrtime(&smrt->smrt_cv_finishq,
+ &smrt->smrt_mutex, smcm->smcm_expiry);
+ }
+ }
+
+ /*
+ * Fire the completion callback for this command. The callback
+ * is responsible for freeing the command, so it may not be
+ * referenced again once this call returns.
+ */
+ smrt_process_finishq_one(smcm);
+
+ return (0);
+}
+
+void
+smrt_intr_set(smrt_t *smrt, boolean_t enabled)
+{
+ /*
+ * Read the Interrupt Mask Register.
+ */
+ uint32_t imr = smrt_get32(smrt, CISS_I2O_INTERRUPT_MASK);
+
+ switch (smrt->smrt_ctlr_mode) {
+ case SMRT_CTLR_MODE_SIMPLE:
+ if (enabled) {
+ imr &= ~CISS_IMR_BIT_SIMPLE_INTR_DISABLE;
+ } else {
+ imr |= CISS_IMR_BIT_SIMPLE_INTR_DISABLE;
+ }
+ smrt_put32(smrt, CISS_I2O_INTERRUPT_MASK, imr);
+ return;
+
+ case SMRT_CTLR_MODE_UNKNOWN:
+ break;
+ }
+ panic("unknown controller mode");
+}
+
+/*
+ * Signal to the controller that we have updated the Configuration Table by
+ * writing to the Inbound Doorbell Register. The controller will, after some
+ * number of seconds, acknowledge this by clearing the bit.
+ *
+ * If successful, return DDI_SUCCESS. If the controller takes too long to
+ * acknowledge, return DDI_FAILURE.
+ */
+int
+smrt_cfgtbl_flush(smrt_t *smrt)
+{
+ /*
+ * Read the current value of the Inbound Doorbell Register.
+ */
+ uint32_t idr = smrt_get32(smrt, CISS_I2O_INBOUND_DOORBELL);
+
+ /*
+ * Signal the Configuration Table change to the controller.
+ */
+ idr |= CISS_IDR_BIT_CFGTBL_CHANGE;
+ smrt_put32(smrt, CISS_I2O_INBOUND_DOORBELL, idr);
+
+ /*
+ * Wait for the controller to acknowledge the change.
+ */
+ for (unsigned i = 0; i < smrt_ciss_init_time; i++) {
+ idr = smrt_get32(smrt, CISS_I2O_INBOUND_DOORBELL);
+
+ if ((idr & CISS_IDR_BIT_CFGTBL_CHANGE) == 0) {
+ return (DDI_SUCCESS);
+ }
+
+ /*
+ * Wait for one second before trying again.
+ */
+ delay(drv_usectohz(1000000));
+ }
+
+ dev_err(smrt->smrt_dip, CE_WARN, "time out expired before controller "
+ "configuration completed");
+ return (DDI_FAILURE);
+}
+
+int
+smrt_cfgtbl_transport_has_support(smrt_t *smrt, int xport)
+{
+ VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
+
+ /*
+ * Read the current value of the "Supported Transport Methods" field in
+ * the Configuration Table.
+ */
+ uint32_t xport_active = ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->TransportSupport);
+
+ /*
+ * Check that the desired transport method is supported by the
+ * controller:
+ */
+ if ((xport_active & xport) == 0) {
+ dev_err(smrt->smrt_dip, CE_WARN, "controller does not support "
+ "method \"%s\"", xport == CISS_CFGTBL_XPORT_SIMPLE ?
+ "simple" : "performant");
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+void
+smrt_cfgtbl_transport_set(smrt_t *smrt, int xport)
+{
+ VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
+
+ ddi_put32(smrt->smrt_ct_handle, &smrt->smrt_ct->TransportRequest,
+ xport);
+}
+
+int
+smrt_cfgtbl_transport_confirm(smrt_t *smrt, int xport)
+{
+ VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
+
+ /*
+ * Read the current value of the TransportActive field in the
+ * Configuration Table.
+ */
+ uint32_t xport_active = ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->TransportActive);
+
+ /*
+ * Check that the desired transport method is now active:
+ */
+ if ((xport_active & xport) == 0) {
+ dev_err(smrt->smrt_dip, CE_WARN, "failed to enable transport "
+ "method \"%s\"", xport == CISS_CFGTBL_XPORT_SIMPLE ?
+ "simple" : "performant");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Ensure that the controller is now ready to accept commands.
+ */
+ if ((xport_active & CISS_CFGTBL_READY_FOR_COMMANDS) == 0) {
+ dev_err(smrt->smrt_dip, CE_WARN, "controller not ready to "
+ "accept commands");
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+uint32_t
+smrt_ctlr_get_maxsgelements(smrt_t *smrt)
+{
+ return (ddi_get32(smrt->smrt_ct_handle, &smrt->smrt_ct->MaxSGElements));
+}
+
+uint32_t
+smrt_ctlr_get_cmdsoutmax(smrt_t *smrt)
+{
+ return (ddi_get32(smrt->smrt_ct_handle, &smrt->smrt_ct->CmdsOutMax));
+}
+
+static uint32_t
+smrt_ctlr_get_hostdrvsup(smrt_t *smrt)
+{
+ return (ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->HostDrvrSupport));
+}
+
+int
+smrt_ctlr_init(smrt_t *smrt)
+{
+ uint8_t signature[4] = { 'C', 'I', 'S', 'S' };
+ int e;
+
+ if ((e = smrt_ctlr_wait_for_state(smrt,
+ SMRT_WAIT_STATE_READY)) != DDI_SUCCESS) {
+ return (e);
+ }
+
+ /*
+ * The configuration table contains an ASCII signature ("CISS") which
+ * should be checked as we initialise the controller.
+ * See: "9.1 Configuration Table" in CISS Specification.
+ */
+ for (unsigned i = 0; i < 4; i++) {
+ if (ddi_get8(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->Signature[i]) != signature[i]) {
+ dev_err(smrt->smrt_dip, CE_WARN, "invalid signature "
+ "detected");
+ return (DDI_FAILURE);
+ }
+ }
+
+ /*
+ * Initialise an appropriate Transport Method. For now, this driver
+ * only supports the "Simple" method.
+ */
+ if ((e = smrt_ctlr_init_simple(smrt)) != 0) {
+ return (e);
+ }
+
+ /*
+ * Save some common feature support bitfields.
+ */
+ smrt->smrt_host_support = smrt_ctlr_get_hostdrvsup(smrt);
+ smrt->smrt_bus_support = ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->BusTypes);
+
+ /*
+ * Read initial controller heartbeat value and mark the current
+ * reading time.
+ */
+ smrt->smrt_last_heartbeat = ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->HeartBeat);
+ smrt->smrt_last_heartbeat_time = gethrtime();
+
+ return (DDI_SUCCESS);
+}
+
+void
+smrt_ctlr_teardown(smrt_t *smrt)
+{
+ smrt->smrt_status &= ~SMRT_CTLR_STATUS_RUNNING;
+
+ switch (smrt->smrt_ctlr_mode) {
+ case SMRT_CTLR_MODE_SIMPLE:
+ smrt_ctlr_teardown_simple(smrt);
+ return;
+
+ case SMRT_CTLR_MODE_UNKNOWN:
+ return;
+ }
+
+ panic("unknown controller mode");
+}
+
+int
+smrt_ctlr_wait_for_state(smrt_t *smrt, smrt_wait_state_t state)
+{
+ unsigned wait_usec = 100 * 1000;
+ unsigned wait_count = SMRT_WAIT_DELAY_SECONDS * 1000000 / wait_usec;
+
+ VERIFY(state == SMRT_WAIT_STATE_READY ||
+ state == SMRT_WAIT_STATE_UNREADY);
+
+ /*
+ * Read from the Scratchpad Register until the expected ready signature
+ * is detected. This behaviour is not described in the CISS
+ * specification.
+ *
+ * If the device is not in the desired state immediately, sleep for a
+ * second and try again. If the device has not become ready in 300
+ * seconds, give up.
+ */
+ for (unsigned i = 0; i < wait_count; i++) {
+ uint32_t spr = smrt_get32(smrt, CISS_I2O_SCRATCHPAD);
+
+ switch (state) {
+ case SMRT_WAIT_STATE_READY:
+ if (spr == CISS_SCRATCHPAD_INITIALISED) {
+ return (DDI_SUCCESS);
+ }
+ break;
+
+ case SMRT_WAIT_STATE_UNREADY:
+ if (spr != CISS_SCRATCHPAD_INITIALISED) {
+ return (DDI_SUCCESS);
+ }
+ break;
+ }
+
+ if (ddi_in_panic()) {
+ /*
+ * There is no sleep for the panicking, so we
+ * must spin wait:
+ */
+ drv_usecwait(wait_usec);
+ } else {
+ /*
+ * Wait for a quarter second and try again.
+ */
+ delay(drv_usectohz(wait_usec));
+ }
+ }
+
+ dev_err(smrt->smrt_dip, CE_WARN, "time out waiting for controller "
+ "to enter state \"%s\"", state == SMRT_WAIT_STATE_READY ?
+ "ready": "unready");
+ return (DDI_FAILURE);
+}
+
+void
+smrt_lockup_check(smrt_t *smrt)
+{
+ /*
+ * Read the current controller heartbeat value.
+ */
+ uint32_t heartbeat = ddi_get32(smrt->smrt_ct_handle,
+ &smrt->smrt_ct->HeartBeat);
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ /*
+ * Check to see if the value is the same as last time we looked:
+ */
+ if (heartbeat != smrt->smrt_last_heartbeat) {
+ /*
+ * The heartbeat value has changed, which suggests that the
+ * firmware in the controller has not yet come to a complete
+ * stop. Record the new value, as well as the current time.
+ */
+ smrt->smrt_last_heartbeat = heartbeat;
+ smrt->smrt_last_heartbeat_time = gethrtime();
+ return;
+ }
+
+ /*
+ * The controller _might_ have been able to signal to us that is
+ * has locked up. This is a truly unfathomable state of affairs:
+ * If the firmware can tell it has flown off the rails, why not
+ * simply reset the controller?
+ */
+ uint32_t odr = smrt_get32(smrt, CISS_I2O_OUTBOUND_DOORBELL_STATUS);
+ uint32_t spr = smrt_get32(smrt, CISS_I2O_SCRATCHPAD);
+ if ((odr & CISS_ODR_BIT_LOCKUP) != 0) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "HP SmartArray firmware has "
+ "reported a critical fault (odr %08x spr %08x)",
+ odr, spr);
+ }
+
+ if (gethrtime() > smrt->smrt_last_heartbeat_time + 60 * NANOSEC) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "HP SmartArray firmware has "
+ "stopped responding (odr %08x spr %08x)",
+ odr, spr);
+ }
+}
+
+int
+smrt_ctlr_reset(smrt_t *smrt)
+{
+ smrt_command_t *smcm, *smcm_nop;
+ int r;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ if (ddi_in_panic()) {
+ goto skip_check;
+ }
+
+ if (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
+ /*
+ * Don't pile on. One reset is enough. Wait until
+ * it's complete, and then return success.
+ */
+ while (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
+ cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
+ }
+ return (0);
+ }
+ smrt->smrt_status |= SMRT_CTLR_STATUS_RESETTING;
+ smrt->smrt_last_reset_start = gethrtime();
+ smrt->smrt_stats.smrts_ctlr_resets++;
+
+skip_check:
+ /*
+ * Allocate two commands: one for the soft reset message, which we
+ * cannot free until the controller has reset; and one for the ping we
+ * will use to determine when it is once again functional.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
+ KM_NOSLEEP)) == NULL) {
+ mutex_enter(&smrt->smrt_mutex);
+ return (ENOMEM);
+ }
+ if ((smcm_nop = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
+ KM_NOSLEEP)) == NULL) {
+ smrt_command_free(smcm);
+ mutex_enter(&smrt->smrt_mutex);
+ return (ENOMEM);
+ }
+ mutex_enter(&smrt->smrt_mutex);
+
+ /*
+ * Send a soft reset command to the controller. If this command
+ * succeeds, there will likely be no completion notification. Instead,
+ * the device should become unavailable for some period of time and
+ * then become available again. Once available again, we know the soft
+ * reset has completed and should abort all in-flight commands.
+ */
+ smrt_write_message_reset_ctlr(smcm);
+
+ /*
+ * Disable interrupts now.
+ */
+ smrt_intr_set(smrt, B_FALSE);
+
+ dev_err(smrt->smrt_dip, CE_WARN, "attempting controller soft reset");
+ smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ if ((r = smrt_submit(smrt, smcm)) != 0) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "submit failed (%d)", r);
+ }
+
+ /*
+ * Mark every currently inflight command as being reset, including the
+ * soft reset command we just sent. Once we confirm the reset works,
+ * we can safely report that these commands have failed.
+ */
+ for (smrt_command_t *t = avl_first(&smrt->smrt_inflight);
+ t != NULL; t = AVL_NEXT(&smrt->smrt_inflight, t)) {
+ t->smcm_status |= SMRT_CMD_STATUS_RESET_SENT;
+ }
+
+ /*
+ * Now that we have submitted our soft reset command, prevent
+ * the rest of the driver from interacting with the controller.
+ */
+ smrt->smrt_status &= ~SMRT_CTLR_STATUS_RUNNING;
+
+ /*
+ * We do not expect a completion from the controller for our soft
+ * reset command, but we also cannot remove it from the inflight
+ * list until we know the controller has actually reset. To do
+ * otherwise would potentially allow the controller to scribble
+ * on the memory we were using.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_ABANDONED;
+
+ if (smrt_ctlr_wait_for_state(smrt, SMRT_WAIT_STATE_UNREADY) !=
+ DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "controller did not become unready");
+ }
+ dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller unready");
+
+ if (smrt_ctlr_wait_for_state(smrt, SMRT_WAIT_STATE_READY) !=
+ DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "controller did not come become ready");
+ }
+ dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller ready");
+
+ /*
+ * In at least the Smart Array P420i, the controller can take 30-45
+ * seconds after the scratchpad register shows it as being available
+ * before it is ready to receive commands. In order to avoid hitting
+ * it too early with our post-reset ping, we will sleep for 10 seconds
+ * here.
+ */
+ if (ddi_in_panic()) {
+ drv_usecwait(10 * MICROSEC);
+ } else {
+ delay(drv_usectohz(10 * MICROSEC));
+ }
+
+ smrt_ctlr_teardown(smrt);
+ if (smrt_ctlr_init(smrt) != DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "controller transport could not be configured");
+ }
+ dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller configured");
+
+ smrt_write_message_nop(smcm_nop, 0);
+ smcm_nop->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ smrt->smrt_status |= SMRT_CTLR_STATUS_RUNNING;
+ if ((r = smrt_submit(smrt, smcm_nop)) != 0) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "ping could not be submitted (%d)", r);
+ }
+ smrt->smrt_status &= ~SMRT_CTLR_STATUS_RUNNING;
+
+ /*
+ * Interrupts are still masked at this stage. Poll manually in
+ * a way that will not trigger regular finish queue processing:
+ */
+ VERIFY(smcm_nop->smcm_status & SMRT_CMD_STATUS_INFLIGHT);
+ for (unsigned i = 0; i < 600; i++) {
+ smrt_retrieve_simple(smrt);
+
+ if (!(smcm_nop->smcm_status & SMRT_CMD_STATUS_INFLIGHT)) {
+ /*
+ * Remove the ping command from the finish queue and
+ * process it manually. This processing must mirror
+ * what would have been done in smrt_process_finishq().
+ */
+ VERIFY(list_link_active(&smcm_nop->smcm_link_finish));
+ list_remove(&smrt->smrt_finishq, smcm_nop);
+ smrt_process_finishq_sync(smcm_nop);
+ smcm_nop->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE;
+ smrt_process_finishq_one(smcm_nop);
+ break;
+ }
+
+ if (ddi_in_panic()) {
+ drv_usecwait(100 * 1000);
+ } else {
+ delay(drv_usectohz(100 * 1000));
+ }
+ }
+
+ if (!(smcm_nop->smcm_status & SMRT_CMD_STATUS_COMPLETE)) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
+ "ping did not complete");
+ } else if (smcm_nop->smcm_status & SMRT_CMD_STATUS_ERROR) {
+ dev_err(smrt->smrt_dip, CE_WARN, "soft reset: ping completed "
+ "in error (status %u)",
+ (unsigned)smcm_nop->smcm_va_err->CommandStatus);
+ } else {
+ dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: ping completed");
+ }
+
+ /*
+ * Now that the controller is working again, we can abort any
+ * commands that were inflight during the reset.
+ */
+ smrt_command_t *nt;
+ for (smrt_command_t *t = avl_first(&smrt->smrt_inflight);
+ t != NULL; t = nt) {
+ nt = AVL_NEXT(&smrt->smrt_inflight, t);
+
+ if (t->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
+ avl_remove(&smrt->smrt_inflight, t);
+ t->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT;
+
+ list_insert_tail(&smrt->smrt_finishq, t);
+ }
+ }
+
+ /*
+ * Re-enable interrupts, mark the controller running and
+ * the reset as complete....
+ */
+ smrt_intr_set(smrt, B_TRUE);
+ smrt->smrt_status |= SMRT_CTLR_STATUS_RUNNING;
+ smrt->smrt_status &= ~SMRT_CTLR_STATUS_RESETTING;
+ smrt->smrt_last_reset_finish = gethrtime();
+
+ /*
+ * Wake anybody that was waiting for the reset to complete.
+ */
+ cv_broadcast(&smrt->smrt_cv_finishq);
+
+ /*
+ * Process the completion queue one last time before we let go
+ * of the mutex.
+ */
+ smrt_process_finishq(smrt);
+
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm_nop);
+ mutex_enter(&smrt->smrt_mutex);
+ return (0);
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss_simple.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss_simple.c
new file mode 100644
index 0000000000..a84c7597cc
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_ciss_simple.c
@@ -0,0 +1,212 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+uint_t
+smrt_isr_hw_simple(caddr_t arg1, caddr_t arg2)
+{
+ _NOTE(ARGUNUSED(arg2))
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ smrt_t *smrt = (smrt_t *)arg1;
+ uint32_t isr = smrt_get32(smrt, CISS_I2O_INTERRUPT_STATUS);
+ hrtime_t now = gethrtime();
+
+ mutex_enter(&smrt->smrt_mutex);
+ if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) {
+ smrt->smrt_stats.smrts_unclaimed_interrupts++;
+ smrt->smrt_last_interrupt_unclaimed = now;
+
+ /*
+ * We should not be receiving interrupts from the controller
+ * while the driver is not running.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /*
+ * Check to see if this interrupt came from the device:
+ */
+ if ((isr & CISS_ISR_BIT_SIMPLE_INTR) == 0) {
+ smrt->smrt_stats.smrts_unclaimed_interrupts++;
+ smrt->smrt_last_interrupt_unclaimed = now;
+
+ /*
+ * Check to see if the firmware has come to rest. If it has,
+ * this routine will panic the system.
+ */
+ smrt_lockup_check(smrt);
+
+ mutex_exit(&smrt->smrt_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ smrt->smrt_stats.smrts_claimed_interrupts++;
+ smrt->smrt_last_interrupt_claimed = now;
+
+ /*
+ * The interrupt was from our controller, so collect any pending
+ * command completions.
+ */
+ smrt_retrieve_simple(smrt);
+
+ /*
+ * Process any commands in the completion queue.
+ */
+ smrt_process_finishq(smrt);
+
+ mutex_exit(&smrt->smrt_mutex);
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Read tags and process completion of the associated command until the supply
+ * of tags is exhausted.
+ */
+void
+smrt_retrieve_simple(smrt_t *smrt)
+{
+ uint32_t opq;
+ uint32_t none = 0xffffffff;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ while ((opq = smrt_get32(smrt, CISS_I2O_OUTBOUND_POST_Q)) != none) {
+ uint32_t tag = CISS_OPQ_READ_TAG(opq);
+ smrt_command_t *smcm;
+
+ if ((smcm = smrt_lookup_inflight(smrt, tag)) == NULL) {
+ dev_err(smrt->smrt_dip, CE_WARN, "spurious tag %x",
+ tag);
+ continue;
+ }
+
+ avl_remove(&smrt->smrt_inflight, smcm);
+ smcm->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT;
+ if (CISS_OPQ_READ_ERROR(opq) != 0) {
+ smcm->smcm_status |= SMRT_CMD_STATUS_ERROR;
+ }
+ smcm->smcm_time_complete = gethrtime();
+
+ /*
+ * Push this command onto the completion queue.
+ */
+ list_insert_tail(&smrt->smrt_finishq, smcm);
+ }
+}
+
+/*
+ * Submit a command to the controller by posting it to the Inbound Post Queue
+ * Register.
+ */
+void
+smrt_submit_simple(smrt_t *smrt, smrt_command_t *smcm)
+{
+ smrt_put32(smrt, CISS_I2O_INBOUND_POST_Q, smcm->smcm_pa_cmd);
+}
+
+int
+smrt_ctlr_init_simple(smrt_t *smrt)
+{
+ VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_UNKNOWN);
+
+ if (smrt_cfgtbl_transport_has_support(smrt,
+ CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_SIMPLE;
+
+ /*
+ * Disable device interrupts while we are setting up.
+ */
+ smrt_intr_set(smrt, B_FALSE);
+
+ if ((smrt->smrt_maxcmds = smrt_ctlr_get_cmdsoutmax(smrt)) == 0) {
+ dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding "
+ "commands set to zero");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Determine the number of Scatter/Gather List entries this controller
+ * supports. The maximum number we allow is CISS_MAXSGENTRIES: the
+ * number of elements in the static struct we use for command
+ * submission.
+ */
+ if ((smrt->smrt_sg_cnt = smrt_ctlr_get_maxsgelements(smrt)) == 0) {
+ /*
+ * The CISS specification states that if this value is
+ * zero, we should assume a value of 31 for compatibility
+ * with older firmware.
+ */
+ smrt->smrt_sg_cnt = CISS_SGCNT_FALLBACK;
+
+ } else if (smrt->smrt_sg_cnt > CISS_MAXSGENTRIES) {
+ /*
+ * If the controller supports more than we have allocated,
+ * just cap the count at the allocation size.
+ */
+ smrt->smrt_sg_cnt = CISS_MAXSGENTRIES;
+ }
+
+ /*
+ * Zero the upper 32 bits of the address in the Controller.
+ */
+ ddi_put32(smrt->smrt_ct_handle, &smrt->smrt_ct->Upper32Addr, 0);
+
+ /*
+ * Set the Transport Method and flush the changes to the
+ * Configuration Table.
+ */
+ smrt_cfgtbl_transport_set(smrt, CISS_CFGTBL_XPORT_SIMPLE);
+ if (smrt_cfgtbl_flush(smrt) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if (smrt_cfgtbl_transport_confirm(smrt,
+ CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Check the outstanding command cap a second time now that we have
+ * flushed out the new Transport Method. This is entirely defensive;
+ * we do not expect this value to change.
+ */
+ uint32_t check_again = smrt_ctlr_get_cmdsoutmax(smrt);
+ if (check_again != smrt->smrt_maxcmds) {
+ dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding commands "
+ "changed during initialisation (was %u, now %u)",
+ smrt->smrt_maxcmds, check_again);
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+void
+smrt_ctlr_teardown_simple(smrt_t *smrt)
+{
+ VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_SIMPLE);
+
+ /*
+ * Due to the nominal simplicity of the simple mode, we have no
+ * particular teardown to perform as we do not allocate anything
+ * on the way up.
+ */
+ smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_UNKNOWN;
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_commands.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_commands.c
new file mode 100644
index 0000000000..914e7b926a
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_commands.c
@@ -0,0 +1,325 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+
+static ddi_dma_attr_t smrt_command_dma_attr = {
+ .dma_attr_version = DMA_ATTR_V0,
+ .dma_attr_addr_lo = 0x00000000,
+ .dma_attr_addr_hi = 0xFFFFFFFF,
+ .dma_attr_count_max = 0x00FFFFFF,
+ .dma_attr_align = 0x20,
+ .dma_attr_burstsizes = 0x20,
+ .dma_attr_minxfer = DMA_UNIT_8,
+ .dma_attr_maxxfer = 0xFFFFFFFF,
+ .dma_attr_seg = 0x0000FFFF,
+ .dma_attr_sgllen = 1,
+ .dma_attr_granular = 512,
+ .dma_attr_flags = 0
+};
+
+/*
+ * These device access attributes are for command block allocation, where we do
+ * not use any of the structured byte swapping facilities.
+ */
+static ddi_device_acc_attr_t smrt_command_dev_attr = {
+ .devacc_attr_version = DDI_DEVICE_ATTR_V0,
+ .devacc_attr_endian_flags = DDI_NEVERSWAP_ACC,
+ .devacc_attr_dataorder = DDI_STRICTORDER_ACC,
+ .devacc_attr_access = 0
+};
+
+
+static void smrt_contig_free(smrt_dma_t *);
+
+
+extern __GNU_INLINE size_t
+smrt_round_up(size_t offset)
+{
+ size_t gran = 0x20;
+
+ return ((offset + (gran - 1)) & ~(gran - 1));
+}
+
+static int
+smrt_check_command_type(smrt_command_type_t type)
+{
+ /*
+ * Note that we leave out the default case in order to utilise
+ * compiler warnings about missed enum values.
+ */
+ switch (type) {
+ case SMRT_CMDTYPE_ABORTQ:
+ case SMRT_CMDTYPE_SCSA:
+ case SMRT_CMDTYPE_INTERNAL:
+ return (type);
+ }
+
+ panic("unexpected command type");
+ /* LINTED: E_FUNC_NO_RET_VAL */
+}
+
+static int
+smrt_contig_alloc(smrt_t *smrt, smrt_dma_t *smdma, size_t sz, int kmflags,
+ void **vap, uint32_t *pap)
+{
+ caddr_t va;
+ int rv;
+ dev_info_t *dip = smrt->smrt_dip;
+ int (*dma_wait)(caddr_t) = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP :
+ DDI_DMA_DONTWAIT;
+
+ VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
+
+ /*
+ * Ensure we don't try to allocate a second time using the same
+ * tracking object.
+ */
+ VERIFY0(smdma->smdma_level);
+
+ if ((rv = ddi_dma_alloc_handle(dip, &smrt_command_dma_attr,
+ dma_wait, NULL, &smdma->smdma_dma_handle)) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "DMA handle allocation failed (%x)",
+ rv);
+ goto fail;
+ }
+ smdma->smdma_level |= SMRT_DMALEVEL_HANDLE_ALLOC;
+
+ if ((rv = ddi_dma_mem_alloc(smdma->smdma_dma_handle, sz,
+ &smrt_command_dev_attr, DDI_DMA_CONSISTENT, dma_wait, NULL,
+ &va, &smdma->smdma_real_size, &smdma->smdma_acc_handle)) !=
+ DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "DMA memory allocation failed (%x)", rv);
+ goto fail;
+ }
+ smdma->smdma_level |= SMRT_DMALEVEL_MEMORY_ALLOC;
+
+ if ((rv = ddi_dma_addr_bind_handle(smdma->smdma_dma_handle,
+ NULL, va, smdma->smdma_real_size,
+ DDI_DMA_CONSISTENT | DDI_DMA_RDWR, dma_wait, NULL,
+ smdma->smdma_dma_cookies, &smdma->smdma_dma_ncookies)) !=
+ DDI_DMA_MAPPED) {
+ dev_err(dip, CE_WARN, "DMA handle bind failed (%x)", rv);
+ goto fail;
+ }
+ smdma->smdma_level |= SMRT_DMALEVEL_HANDLE_BOUND;
+
+ VERIFY3U(smdma->smdma_dma_ncookies, ==, 1);
+ *pap = smdma->smdma_dma_cookies[0].dmac_address;
+ *vap = (void *)va;
+ return (DDI_SUCCESS);
+
+fail:
+ *vap = NULL;
+ *pap = 0;
+ smrt_contig_free(smdma);
+ return (DDI_FAILURE);
+}
+
+static void
+smrt_contig_free(smrt_dma_t *smdma)
+{
+ if (smdma->smdma_level & SMRT_DMALEVEL_HANDLE_BOUND) {
+ VERIFY3U(ddi_dma_unbind_handle(smdma->smdma_dma_handle), ==,
+ DDI_SUCCESS);
+
+ smdma->smdma_level &= ~SMRT_DMALEVEL_HANDLE_BOUND;
+ }
+
+ if (smdma->smdma_level & SMRT_DMALEVEL_MEMORY_ALLOC) {
+ ddi_dma_mem_free(&smdma->smdma_acc_handle);
+
+ smdma->smdma_level &= ~SMRT_DMALEVEL_MEMORY_ALLOC;
+ }
+
+ if (smdma->smdma_level & SMRT_DMALEVEL_HANDLE_ALLOC) {
+ ddi_dma_free_handle(&smdma->smdma_dma_handle);
+
+ smdma->smdma_level &= ~SMRT_DMALEVEL_HANDLE_ALLOC;
+ }
+
+ VERIFY(smdma->smdma_level == 0);
+ bzero(smdma, sizeof (*smdma));
+}
+
+smrt_command_t *
+smrt_command_alloc(smrt_t *smrt, smrt_command_type_t type, int kmflags)
+{
+ smrt_command_t *smcm;
+
+ VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
+
+ if ((smcm = kmem_zalloc(sizeof (*smcm), kmflags)) == NULL) {
+ return (NULL);
+ }
+
+ smcm->smcm_ctlr = smrt;
+ smcm->smcm_type = smrt_check_command_type(type);
+
+ /*
+ * Allocate a single contiguous chunk of memory for the command block
+ * (smcm_va_cmd) and the error information block (smcm_va_err). The
+ * physical address of each block should be 32-byte aligned.
+ */
+ size_t contig_size = 0;
+ contig_size += smrt_round_up(sizeof (CommandList_t));
+
+ size_t errorinfo_offset = contig_size;
+ contig_size += smrt_round_up(sizeof (ErrorInfo_t));
+
+ if (smrt_contig_alloc(smrt, &smcm->smcm_contig, contig_size,
+ kmflags, (void **)&smcm->smcm_va_cmd, &smcm->smcm_pa_cmd) !=
+ DDI_SUCCESS) {
+ kmem_free(smcm, sizeof (*smcm));
+ return (NULL);
+ }
+
+ smcm->smcm_va_err = (void *)((caddr_t)smcm->smcm_va_cmd +
+ errorinfo_offset);
+ smcm->smcm_pa_err = smcm->smcm_pa_cmd + errorinfo_offset;
+
+ /*
+ * Ensure we asked for, and received, the correct physical alignment:
+ */
+ VERIFY0(smcm->smcm_pa_cmd & 0x1f);
+ VERIFY0(smcm->smcm_pa_err & 0x1f);
+
+ /*
+ * Populate Fields.
+ */
+ bzero(smcm->smcm_va_cmd, contig_size);
+ smcm->smcm_va_cmd->ErrDesc.Addr = smcm->smcm_pa_err;
+ smcm->smcm_va_cmd->ErrDesc.Len = sizeof (ErrorInfo_t);
+
+ /*
+ * Insert into the per-controller command list.
+ */
+ mutex_enter(&smrt->smrt_mutex);
+ list_insert_tail(&smrt->smrt_commands, smcm);
+ mutex_exit(&smrt->smrt_mutex);
+
+ return (smcm);
+}
+
+int
+smrt_command_attach_internal(smrt_t *smrt, smrt_command_t *smcm, size_t len,
+ int kmflags)
+{
+ smrt_command_internal_t *smcmi;
+
+ VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP);
+ VERIFY3U(len, <=, UINT32_MAX);
+
+ if ((smcmi = kmem_zalloc(sizeof (*smcmi), kmflags)) == NULL) {
+ return (ENOMEM);
+ }
+
+ if (smrt_contig_alloc(smrt, &smcmi->smcmi_contig, len, kmflags,
+ &smcmi->smcmi_va, &smcmi->smcmi_pa) != DDI_SUCCESS) {
+ kmem_free(smcmi, sizeof (*smcmi));
+ return (ENOMEM);
+ }
+
+ bzero(smcmi->smcmi_va, smcmi->smcmi_len);
+
+ smcm->smcm_internal = smcmi;
+
+ smcm->smcm_va_cmd->SG[0].Addr = smcmi->smcmi_pa;
+ smcm->smcm_va_cmd->SG[0].Len = (uint32_t)len;
+ smcm->smcm_va_cmd->Header.SGList = 1;
+ smcm->smcm_va_cmd->Header.SGTotal = 1;
+
+ return (0);
+}
+
+void
+smrt_command_reuse(smrt_command_t *smcm)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+
+ mutex_enter(&smrt->smrt_mutex);
+
+ /*
+ * Make sure the command is not currently inflight, then
+ * reset the command status.
+ */
+ VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
+ smcm->smcm_status = SMRT_CMD_STATUS_REUSED;
+
+ /*
+ * Ensure we are not trying to reuse a command that is in the finish or
+ * abort queue.
+ */
+ VERIFY(!list_link_active(&smcm->smcm_link_abort));
+ VERIFY(!list_link_active(&smcm->smcm_link_finish));
+
+ /*
+ * Clear the previous tag value.
+ */
+ smcm->smcm_tag = 0;
+ smcm->smcm_va_cmd->Header.Tag.tag_value = 0;
+
+ mutex_exit(&smrt->smrt_mutex);
+}
+
+void
+smrt_command_free(smrt_command_t *smcm)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+
+ /*
+ * Ensure the object we are about to free is not currently in the
+ * inflight AVL.
+ */
+ VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
+
+ if (smcm->smcm_internal != NULL) {
+ smrt_command_internal_t *smcmi = smcm->smcm_internal;
+
+ smrt_contig_free(&smcmi->smcmi_contig);
+ kmem_free(smcmi, sizeof (*smcmi));
+ }
+
+ smrt_contig_free(&smcm->smcm_contig);
+
+ mutex_enter(&smrt->smrt_mutex);
+
+ /*
+ * Ensure we are not trying to free a command that is in the finish or
+ * abort queue.
+ */
+ VERIFY(!list_link_active(&smcm->smcm_link_abort));
+ VERIFY(!list_link_active(&smcm->smcm_link_finish));
+
+ list_remove(&smrt->smrt_commands, smcm);
+
+ mutex_exit(&smrt->smrt_mutex);
+
+ kmem_free(smcm, sizeof (*smcm));
+}
+
+smrt_command_t *
+smrt_lookup_inflight(smrt_t *smrt, uint32_t tag)
+{
+ smrt_command_t srch;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ bzero(&srch, sizeof (srch));
+ srch.smcm_tag = tag;
+
+ return (avl_find(&smrt->smrt_inflight, &srch, NULL));
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_device.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_device.c
new file mode 100644
index 0000000000..b50aff03c1
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_device.c
@@ -0,0 +1,215 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+/*
+ * We must locate what the CISS specification describes as the "I2O
+ * registers". The Intelligent I/O (I2O) Architecture Specification describes
+ * this somewhat more coherently as "the memory region specified by the first
+ * base address configuration register indicating memory space (offset 10h,
+ * 14h, and so forth)".
+ */
+static int
+smrt_locate_bar(pci_regspec_t *regs, unsigned nregs,
+ unsigned *i2o_bar)
+{
+ /*
+ * Locate the first memory-mapped BAR:
+ */
+ for (unsigned i = 0; i < nregs; i++) {
+ unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK;
+
+ if (type == PCI_ADDR_MEM32 || type == PCI_ADDR_MEM64) {
+ *i2o_bar = i;
+ return (DDI_SUCCESS);
+ }
+ }
+
+ return (DDI_FAILURE);
+}
+
+static int
+smrt_locate_cfgtbl(smrt_t *smrt, pci_regspec_t *regs, unsigned nregs,
+ unsigned *ct_bar, uint32_t *baseaddr)
+{
+ uint32_t cfg_offset, mem_offset;
+ unsigned want_type;
+ uint32_t want_bar;
+
+ cfg_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_CFG_OFFSET);
+ mem_offset = smrt_get32(smrt, CISS_I2O_CFGTBL_MEM_OFFSET);
+
+ VERIFY3U(cfg_offset, !=, 0xffffffff);
+ VERIFY3U(mem_offset, !=, 0xffffffff);
+
+ /*
+ * Locate the Configuration Table. Three different values read
+ * from two I2O registers allow us to determine the location:
+ * - the correct PCI BAR offset is in the low 16 bits of
+ * CISS_I2O_CFGTBL_CFG_OFFSET
+ * - bit 16 is 0 for a 32-bit space, and 1 for 64-bit
+ * - the memory offset from the base of this BAR is
+ * in CISS_I2O_CFGTBL_MEM_OFFSET
+ */
+ want_bar = (cfg_offset & 0xffff);
+ want_type = (cfg_offset & (1UL << 16)) ? PCI_ADDR_MEM64 :
+ PCI_ADDR_MEM32;
+
+ DTRACE_PROBE4(locate_cfgtbl, uint32_t, want_bar, unsigned,
+ want_type, uint32_t, cfg_offset, uint32_t, mem_offset);
+
+ for (unsigned i = 0; i < nregs; i++) {
+ unsigned type = regs[i].pci_phys_hi & PCI_ADDR_MASK;
+ unsigned bar = PCI_REG_REG_G(regs[i].pci_phys_hi);
+
+ if (type != PCI_ADDR_MEM32 && type != PCI_ADDR_MEM64) {
+ continue;
+ }
+
+ if (bar == want_bar) {
+ *ct_bar = i;
+ *baseaddr = mem_offset;
+ return (DDI_SUCCESS);
+ }
+ }
+
+ return (DDI_FAILURE);
+}
+
+static int
+smrt_map_device(smrt_t *smrt)
+{
+ pci_regspec_t *regs;
+ uint_t regslen, nregs;
+ dev_info_t *dip = smrt->smrt_dip;
+ int r = DDI_FAILURE;
+
+ /*
+ * Get the list of PCI registers from the DDI property "regs":
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (int **)&regs, &regslen) != DDI_PROP_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not load \"reg\" DDI prop");
+ return (DDI_FAILURE);
+ }
+ nregs = regslen * sizeof (int) / sizeof (pci_regspec_t);
+
+ if (smrt_locate_bar(regs, nregs, &smrt->smrt_i2o_bar) !=
+ DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "did not find any memory BARs");
+ goto out;
+ }
+
+ /*
+ * Map enough of the I2O memory space to enable us to talk to the
+ * device.
+ */
+ if (ddi_regs_map_setup(dip, smrt->smrt_i2o_bar, &smrt->smrt_i2o_space,
+ CISS_I2O_MAP_BASE, CISS_I2O_MAP_LIMIT - CISS_I2O_MAP_BASE,
+ &smrt_dev_attributes, &smrt->smrt_i2o_handle) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "failed to map I2O registers");
+ goto out;
+ }
+ smrt->smrt_init_level |= SMRT_INITLEVEL_I2O_MAPPED;
+
+ if (smrt_locate_cfgtbl(smrt, regs, nregs, &smrt->smrt_ct_bar,
+ &smrt->smrt_ct_baseaddr) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not find config table");
+ goto out;
+ }
+
+ /*
+ * Map the Configuration Table.
+ */
+ if (ddi_regs_map_setup(dip, smrt->smrt_ct_bar,
+ (caddr_t *)&smrt->smrt_ct, smrt->smrt_ct_baseaddr,
+ sizeof (CfgTable_t), &smrt_dev_attributes,
+ &smrt->smrt_ct_handle) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not map config table");
+ goto out;
+ }
+ smrt->smrt_init_level |= SMRT_INITLEVEL_CFGTBL_MAPPED;
+
+ r = DDI_SUCCESS;
+
+out:
+ ddi_prop_free(regs);
+ return (r);
+}
+
+int
+smrt_device_setup(smrt_t *smrt)
+{
+ /*
+ * Ensure that the controller is installed in such a fashion that it
+ * may become a DMA master.
+ */
+ if (ddi_slaveonly(smrt->smrt_dip) == DDI_SUCCESS) {
+ dev_err(smrt->smrt_dip, CE_WARN, "device cannot become DMA "
+ "master");
+ return (DDI_FAILURE);
+ }
+
+ if (smrt_map_device(smrt) != DDI_SUCCESS) {
+ goto fail;
+ }
+
+ return (DDI_SUCCESS);
+
+fail:
+ smrt_device_teardown(smrt);
+ return (DDI_FAILURE);
+}
+
+void
+smrt_device_teardown(smrt_t *smrt)
+{
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_CFGTBL_MAPPED) {
+ ddi_regs_map_free(&smrt->smrt_ct_handle);
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_CFGTBL_MAPPED;
+ }
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_I2O_MAPPED) {
+ ddi_regs_map_free(&smrt->smrt_i2o_handle);
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_I2O_MAPPED;
+ }
+}
+
+uint32_t
+smrt_get32(smrt_t *smrt, offset_t off)
+{
+ VERIFY3S(off, >=, CISS_I2O_MAP_BASE);
+ VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT);
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space +
+ (off - CISS_I2O_MAP_BASE));
+
+ return (ddi_get32(smrt->smrt_i2o_handle, addr));
+}
+
+void
+smrt_put32(smrt_t *smrt, offset_t off, uint32_t val)
+{
+ VERIFY3S(off, >=, CISS_I2O_MAP_BASE);
+ VERIFY3S(off, <, CISS_I2O_MAP_BASE + CISS_I2O_MAP_LIMIT);
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ uint32_t *addr = (uint32_t *)(smrt->smrt_i2o_space +
+ (off - CISS_I2O_MAP_BASE));
+
+ ddi_put32(smrt->smrt_i2o_handle, addr, val);
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_hba.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_hba.c
new file mode 100644
index 0000000000..ed226de088
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_hba.c
@@ -0,0 +1,1073 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+static boolean_t
+smrt_device_is_controller(struct scsi_device *sd)
+{
+ return (sd->sd_address.a_target == SMRT_CONTROLLER_TARGET &&
+ sd->sd_address.a_lun == 0);
+}
+
+static int
+smrt_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+ scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+ _NOTE(ARGUNUSED(hba_dip))
+
+ smrt_t *smrt = (smrt_t *)hba_tran->tran_hba_private;
+ smrt_volume_t *smlv;
+ smrt_target_t *smtg;
+ dev_info_t *dip = smrt->smrt_dip;
+
+ /*
+ * Check to see if new logical volumes are available.
+ */
+ if (smrt_logvol_discover(smrt, SMRT_LOGVOL_DISCOVER_TIMEOUT) != 0) {
+ dev_err(dip, CE_WARN, "discover logical volumes failure");
+ return (DDI_FAILURE);
+ }
+
+ if ((smtg = kmem_zalloc(sizeof (*smtg), KM_NOSLEEP)) == NULL) {
+ dev_err(dip, CE_WARN, "could not allocate target object "
+ "due to memory exhaustion");
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&smrt->smrt_mutex);
+
+ if (smrt->smrt_status & SMRT_CTLR_STATUS_DETACHING) {
+ /*
+ * We are detaching. Do not accept any more requests to
+ * attach targets from the framework.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ kmem_free(smtg, sizeof (*smtg));
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Check to see if this is the SCSI address of the pseudo target
+ * representing the Smart Array controller itself.
+ */
+ if (smrt_device_is_controller(sd)) {
+ smtg->smtg_controller_target = B_TRUE;
+ goto skip_logvol;
+ }
+
+ /*
+ * Look for a logical volume for the SCSI address of this target.
+ */
+ if ((smlv = smrt_logvol_lookup_by_addr(smrt, &sd->sd_address)) ==
+ NULL) {
+ mutex_exit(&smrt->smrt_mutex);
+ kmem_free(smtg, sizeof (*smtg));
+ return (DDI_FAILURE);
+ }
+
+ smtg->smtg_volume = smlv;
+ list_insert_tail(&smlv->smlv_targets, smtg);
+
+skip_logvol:
+ /*
+ * Link this target object to the controller:
+ */
+ smtg->smtg_ctlr = smrt;
+ list_insert_tail(&smrt->smrt_targets, smtg);
+
+ smtg->smtg_scsi_dev = sd;
+ VERIFY(sd->sd_dev == tgt_dip);
+
+ /*
+ * We passed SCSI_HBA_TRAN_CLONE to scsi_hba_attach(9F), so we
+ * can stash our target-specific data structure on the (cloned)
+ * "hba_tran" without affecting the private data pointers of the
+ * HBA or of other targets.
+ */
+ hba_tran->tran_tgt_private = smtg;
+
+ mutex_exit(&smrt->smrt_mutex);
+ return (DDI_SUCCESS);
+}
+
+static void
+smrt_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+ scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+ _NOTE(ARGUNUSED(hba_dip, tgt_dip))
+
+ smrt_t *smrt = (smrt_t *)hba_tran->tran_hba_private;
+ smrt_target_t *smtg = (smrt_target_t *)hba_tran->tran_tgt_private;
+ smrt_volume_t *smlv = smtg->smtg_volume;
+
+ VERIFY(smtg->smtg_scsi_dev == sd);
+
+ mutex_enter(&smrt->smrt_mutex);
+
+ /*
+ * Remove this target from the tracking lists:
+ */
+ if (!smtg->smtg_controller_target) {
+ list_remove(&smlv->smlv_targets, smtg);
+ }
+ list_remove(&smrt->smrt_targets, smtg);
+
+ /*
+ * Clear the target-specific private data pointer; see comments
+ * in smrt_tran_tgt_init() above.
+ */
+ hba_tran->tran_tgt_private = NULL;
+
+ mutex_exit(&smrt->smrt_mutex);
+
+ kmem_free(smtg, sizeof (*smtg));
+}
+
+/*
+ * This function is called when the SCSI framework has allocated a packet and
+ * our private per-packet object.
+ *
+ * We choose not to have the framework pre-allocate memory for the CDB.
+ * Instead, we will make available the CDB area in the controller command block
+ * itself.
+ *
+ * Status block memory is allocated by the framework because we passed
+ * SCSI_HBA_TRAN_SCB to scsi_hba_attach_setup(9F).
+ */
+static int
+smrt_tran_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
+ caddr_t arg)
+{
+ _NOTE(ARGUNUSED(arg))
+
+ scsi_hba_tran_t *tran = pkt->pkt_address.a_hba_tran;
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+ smrt_target_t *smtg = (smrt_target_t *)tran->tran_tgt_private;
+ smrt_command_scsa_t *smcms = (smrt_command_scsa_t *)
+ pkt->pkt_ha_private;
+ smrt_command_t *smcm;
+ int kmflags = callback == SLEEP_FUNC ? KM_SLEEP : KM_NOSLEEP;
+
+ /*
+ * Check that we have enough space in the command object for the
+ * request from the target driver:
+ */
+ if (pkt->pkt_cdblen > CISS_CDBLEN) {
+ /*
+ * The CDB member of the Request Block of a controller
+ * command is fixed at 16 bytes.
+ */
+ dev_err(smrt->smrt_dip, CE_WARN, "oversize CDB: had %u, "
+ "needed %u", CISS_CDBLEN, pkt->pkt_cdblen);
+ return (-1);
+ }
+
+ /*
+ * Allocate our command block:
+ */
+ if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_SCSA,
+ kmflags)) == NULL) {
+ return (-1);
+ }
+ smcm->smcm_scsa = smcms;
+ smcms->smcms_command = smcm;
+ smcms->smcms_pkt = pkt;
+
+ pkt->pkt_cdbp = &smcm->smcm_va_cmd->Request.CDB[0];
+ smcm->smcm_va_cmd->Request.CDBLen = pkt->pkt_cdblen;
+
+ smcm->smcm_target = smtg;
+
+ return (0);
+}
+
+static void
+smrt_tran_teardown_pkt(struct scsi_pkt *pkt)
+{
+ smrt_command_scsa_t *smcms = (smrt_command_scsa_t *)
+ pkt->pkt_ha_private;
+ smrt_command_t *smcm = smcms->smcms_command;
+
+ smrt_command_free(smcm);
+
+ pkt->pkt_cdbp = NULL;
+}
+
+static void
+smrt_set_arq_data(struct scsi_pkt *pkt, uchar_t key)
+{
+ struct scsi_arq_status *sts;
+
+ VERIFY3U(pkt->pkt_scblen, >=, sizeof (struct scsi_arq_status));
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ sts = (struct scsi_arq_status *)(pkt->pkt_scbp);
+ bzero(sts, sizeof (*sts));
+
+ /*
+ * Mock up a CHECK CONDITION SCSI status for the original command:
+ */
+ sts->sts_status.sts_chk = 1;
+
+ /*
+ * Pretend that we successfully performed REQUEST SENSE:
+ */
+ sts->sts_rqpkt_reason = CMD_CMPLT;
+ sts->sts_rqpkt_resid = 0;
+ sts->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+ STATE_SENT_CMD | STATE_XFERRED_DATA;
+ sts->sts_rqpkt_statistics = 0;
+
+ /*
+ * Return the key value we were provided in the fake sense data:
+ */
+ sts->sts_sensedata.es_valid = 1;
+ sts->sts_sensedata.es_class = CLASS_EXTENDED_SENSE;
+ sts->sts_sensedata.es_key = key;
+
+ pkt->pkt_state |= STATE_ARQ_DONE;
+}
+
+static int
+smrt_tran_start(struct scsi_address *sa, struct scsi_pkt *pkt)
+{
+ _NOTE(ARGUNUSED(sa))
+
+ scsi_hba_tran_t *tran = pkt->pkt_address.a_hba_tran;
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+ smrt_command_scsa_t *smcms = (smrt_command_scsa_t *)
+ pkt->pkt_ha_private;
+ smrt_command_t *smcm = smcms->smcms_command;
+ int r;
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_TRAN_START) {
+ /*
+ * This is a retry of a command that has already been
+ * used once. Assign it a new tag number.
+ */
+ smrt_command_reuse(smcm);
+ }
+ smcm->smcm_status |= SMRT_CMD_STATUS_TRAN_START;
+
+ /*
+ * The sophisticated firmware in this controller cannot possibly bear
+ * the following SCSI commands. It appears to return a response with
+ * the status STATUS_ACA_ACTIVE (0x30), which is not something we
+ * expect. Instead, fake up a failure response.
+ */
+ switch (pkt->pkt_cdbp[0]) {
+ case SCMD_FORMAT:
+ case SCMD_LOG_SENSE_G1:
+ case SCMD_MODE_SELECT:
+ case SCMD_PERSISTENT_RESERVE_IN:
+ smrt->smrt_stats.smrts_ignored_scsi_cmds++;
+ smcm->smcm_status |= SMRT_CMD_STATUS_TRAN_IGNORED;
+
+ /*
+ * Mark the command as completed to the point where we
+ * received a SCSI status code:
+ */
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
+ STATE_SENT_CMD | STATE_GOT_STATUS;
+
+ /*
+ * Mock up sense data for an illegal request:
+ */
+ smrt_set_arq_data(pkt, KEY_ILLEGAL_REQUEST);
+
+ scsi_hba_pkt_comp(pkt);
+ return (TRAN_ACCEPT);
+ }
+
+ if (pkt->pkt_flags & FLAG_NOINTR) {
+ /*
+ * We must sleep and wait for the completion of this command.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ }
+
+ /*
+ * Because we provide a tran_setup_pkt(9E) entrypoint, we must now
+ * set up the Scatter/Gather List in the Command to reflect any
+ * DMA resources passed to us by the framework.
+ */
+ if (pkt->pkt_numcookies > smrt->smrt_sg_cnt) {
+ /*
+ * More DMA cookies than we are prepared to handle.
+ */
+ dev_err(smrt->smrt_dip, CE_WARN, "too many DMA cookies (got %u;"
+ " expected %u)", pkt->pkt_numcookies, smrt->smrt_sg_cnt);
+ return (TRAN_BADPKT);
+ }
+ smcm->smcm_va_cmd->Header.SGList = pkt->pkt_numcookies;
+ smcm->smcm_va_cmd->Header.SGTotal = pkt->pkt_numcookies;
+ for (unsigned i = 0; i < pkt->pkt_numcookies; i++) {
+ smcm->smcm_va_cmd->SG[i].Addr =
+ LE_64(pkt->pkt_cookies[i].dmac_laddress);
+ smcm->smcm_va_cmd->SG[i].Len =
+ LE_32(pkt->pkt_cookies[i].dmac_size);
+ }
+
+ if (smcm->smcm_target->smtg_controller_target) {
+ /*
+ * The controller is, according to the CISS Specification,
+ * always LUN 0 in the peripheral device addressing mode.
+ */
+ smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
+ B_TRUE, 0, 0);
+ } else {
+ /*
+ * Copy logical volume address from the target object:
+ */
+ smcm->smcm_va_cmd->Header.LUN.LogDev = smcm->smcm_target->
+ smtg_volume->smlv_addr;
+ }
+
+ /*
+ * Initialise the command block.
+ */
+ smcm->smcm_va_cmd->Request.CDBLen = pkt->pkt_cdblen;
+ smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
+ smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_ORDERED;
+ smcm->smcm_va_cmd->Request.Timeout = LE_16(pkt->pkt_time);
+ if (pkt->pkt_numcookies > 0) {
+ /*
+ * There are DMA resources; set the transfer direction
+ * appropriately:
+ */
+ if (pkt->pkt_dma_flags & DDI_DMA_READ) {
+ smcm->smcm_va_cmd->Request.Type.Direction =
+ CISS_XFER_READ;
+ } else if (pkt->pkt_dma_flags & DDI_DMA_WRITE) {
+ smcm->smcm_va_cmd->Request.Type.Direction =
+ CISS_XFER_WRITE;
+ } else {
+ smcm->smcm_va_cmd->Request.Type.Direction =
+ CISS_XFER_NONE;
+ }
+ } else {
+ /*
+ * No DMA resources means no transfer.
+ */
+ smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_NONE;
+ }
+
+ /*
+ * Initialise the SCSI packet as described in tran_start(9E). We will
+ * progressively update these fields as the command moves through the
+ * submission and completion states.
+ */
+ pkt->pkt_resid = 0;
+ pkt->pkt_reason = CMD_CMPLT;
+ pkt->pkt_statistics = 0;
+ pkt->pkt_state = 0;
+
+ /*
+ * If this SCSI packet has a timeout, configure an appropriate
+ * expiry time:
+ */
+ if (pkt->pkt_time != 0) {
+ smcm->smcm_expiry = gethrtime() + pkt->pkt_time * NANOSEC;
+ }
+
+ /*
+ * Submit the command to the controller.
+ */
+ mutex_enter(&smrt->smrt_mutex);
+ smrt->smrt_stats.smrts_tran_starts++;
+ if ((r = smrt_submit(smrt, smcm)) != 0) {
+ mutex_exit(&smrt->smrt_mutex);
+
+ dev_err(smrt->smrt_dip, CE_WARN, "smrt_submit failed %d", r);
+
+ /*
+ * Inform the SCSI framework that we could not submit
+ * the command.
+ */
+ return (r == EAGAIN ? TRAN_BUSY : TRAN_FATAL_ERROR);
+ }
+
+ /*
+ * Update the SCSI packet to reflect submission of the command.
+ */
+ pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD;
+
+ if (pkt->pkt_flags & FLAG_NOINTR) {
+ /*
+ * Poll the controller for completion of the command we
+ * submitted. Once this routine has returned, the completion
+ * callback will have been fired with either an active response
+ * (success or error) or a timeout. The command is freed by
+ * the completion callback, so it may not be referenced again
+ * after this call returns.
+ */
+ smrt_poll_for(smrt, smcm);
+ }
+
+ mutex_exit(&smrt->smrt_mutex);
+ return (TRAN_ACCEPT);
+}
+
+static int
+smrt_tran_reset(struct scsi_address *sa, int level)
+{
+ _NOTE(ARGUNUSED(level))
+
+ scsi_hba_tran_t *tran = sa->a_hba_tran;
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+ int r;
+ smrt_command_t *smcm;
+
+ /*
+ * The framework has requested some kind of SCSI reset. A
+ * controller-level soft reset can take a very long time -- often on
+ * the order of 30-60 seconds -- but might well be our only option if
+ * the controller is non-responsive.
+ *
+ * First, check if the controller is responding to pings.
+ */
+again:
+ if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
+ KM_NOSLEEP)) == NULL) {
+ return (0);
+ }
+
+ smrt_write_message_nop(smcm, SMRT_PING_CHECK_TIMEOUT);
+
+ mutex_enter(&smrt->smrt_mutex);
+ smrt->smrt_stats.smrts_tran_resets++;
+ if (ddi_in_panic()) {
+ goto skip_check;
+ }
+
+ if (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
+ /*
+ * The controller is already resetting. Wait for that
+ * to finish.
+ */
+ while (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
+ cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
+ }
+ }
+
+skip_check:
+ /*
+ * Submit our ping to the controller.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ smcm->smcm_expiry = gethrtime() + SMRT_PING_CHECK_TIMEOUT * NANOSEC;
+ if (smrt_submit(smrt, smcm) != 0) {
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm);
+ return (0);
+ }
+
+ if ((r = smrt_poll_for(smrt, smcm)) != 0) {
+ VERIFY3S(r, ==, ETIMEDOUT);
+ VERIFY0(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE);
+
+ /*
+ * The ping command timed out. Abandon it now.
+ */
+ dev_err(smrt->smrt_dip, CE_WARN, "controller ping timed out");
+ smcm->smcm_status |= SMRT_CMD_STATUS_ABANDONED;
+ smcm->smcm_status &= ~SMRT_CMD_STATUS_POLLED;
+
+ } else if ((smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) ||
+ (smcm->smcm_status & SMRT_CMD_STATUS_ERROR)) {
+ /*
+ * The command completed in error, or a controller reset
+ * was sent while we were trying to ping.
+ */
+ dev_err(smrt->smrt_dip, CE_WARN, "controller ping error");
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm);
+ mutex_enter(&smrt->smrt_mutex);
+
+ } else {
+ VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_COMPLETE);
+
+ /*
+ * The controller is responsive, and a full soft reset would be
+ * extremely disruptive to the system. Given our spotty
+ * support for some SCSI commands (which can upset the target
+ * drivers) and the historically lax behaviour of the "smrt"
+ * driver, we grit our teeth and pretend we were able to
+ * perform a reset.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(smcm);
+ return (1);
+ }
+
+ /*
+ * If a reset has been initiated in the last 90 seconds, try
+ * another ping.
+ */
+ if (gethrtime() < smrt->smrt_last_reset_start + 90 * NANOSEC) {
+ dev_err(smrt->smrt_dip, CE_WARN, "controller ping failed, but "
+ "was recently reset; retrying ping");
+ mutex_exit(&smrt->smrt_mutex);
+
+ /*
+ * Sleep for a second first.
+ */
+ if (ddi_in_panic()) {
+ drv_usecwait(1 * MICROSEC);
+ } else {
+ delay(drv_usectohz(1 * MICROSEC));
+ }
+ goto again;
+ }
+
+ dev_err(smrt->smrt_dip, CE_WARN, "controller ping failed; resetting "
+ "controller");
+ if (smrt_ctlr_reset(smrt) != 0) {
+ dev_err(smrt->smrt_dip, CE_WARN, "controller reset failure");
+ mutex_exit(&smrt->smrt_mutex);
+ return (0);
+ }
+
+ mutex_exit(&smrt->smrt_mutex);
+ return (1);
+}
+
+static int
+smrt_tran_abort(struct scsi_address *sa, struct scsi_pkt *pkt)
+{
+ scsi_hba_tran_t *tran = sa->a_hba_tran;
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+ smrt_command_t *smcm = NULL;
+ smrt_command_t *abort_smcm;
+
+ if ((abort_smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
+ KM_NOSLEEP)) == NULL) {
+ /*
+ * No resources available to send an abort message.
+ */
+ return (0);
+ }
+
+ mutex_enter(&smrt->smrt_mutex);
+ smrt->smrt_stats.smrts_tran_aborts++;
+ if (pkt != NULL) {
+ /*
+ * The framework wants us to abort a specific SCSI packet.
+ */
+ smrt_command_scsa_t *smcms = (smrt_command_scsa_t *)
+ pkt->pkt_ha_private;
+ smcm = smcms->smcms_command;
+
+ if (!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT)) {
+ /*
+ * This message is not currently in flight, so we
+ * cannot abort it.
+ */
+ goto fail;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_ABORT_SENT) {
+ /*
+ * An abort message for this command has already been
+ * sent to the controller. Return failure.
+ */
+ goto fail;
+ }
+
+ smrt_write_message_abort_one(abort_smcm, smcm->smcm_tag);
+ } else {
+ /*
+ * The framework wants us to abort every in flight command
+ * for the target with this address.
+ */
+ smrt_target_t *smtg = (smrt_target_t *)tran->
+ tran_tgt_private;
+
+ if (smtg->smtg_volume == NULL) {
+ /*
+ * We currently do not support sending an abort
+ * to anything but a Logical Volume.
+ */
+ goto fail;
+ }
+
+ smrt_write_message_abort_all(abort_smcm,
+ &smtg->smtg_volume->smlv_addr);
+ }
+
+ /*
+ * Submit the abort message to the controller.
+ */
+ abort_smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ if (smrt_submit(smrt, abort_smcm) != 0) {
+ goto fail;
+ }
+
+ if (pkt != NULL) {
+ /*
+ * Record some debugging information about the abort we
+ * sent:
+ */
+ smcm->smcm_abort_time = gethrtime();
+ smcm->smcm_abort_tag = abort_smcm->smcm_tag;
+
+ /*
+ * Mark the command as aborted so that we do not send
+ * a second abort message:
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_ABORT_SENT;
+ }
+
+ /*
+ * Poll for completion of the abort message. Note that this function
+ * only fails if we set a timeout on the command, which we have not
+ * done.
+ */
+ VERIFY0(smrt_poll_for(smrt, abort_smcm));
+
+ if ((abort_smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) ||
+ (abort_smcm->smcm_status & SMRT_CMD_STATUS_ERROR)) {
+ /*
+ * Either the controller was reset or the abort command
+ * failed.
+ */
+ goto fail;
+ }
+
+ /*
+ * The command was successfully aborted.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(abort_smcm);
+ return (1);
+
+fail:
+ mutex_exit(&smrt->smrt_mutex);
+ smrt_command_free(abort_smcm);
+ return (0);
+}
+
+static void
+smrt_hba_complete_status(smrt_command_t *smcm)
+{
+ ErrorInfo_t *ei = smcm->smcm_va_err;
+ struct scsi_pkt *pkt = smcm->smcm_scsa->smcms_pkt;
+
+ bzero(pkt->pkt_scbp, pkt->pkt_scblen);
+
+ if (ei->ScsiStatus != STATUS_CHECK) {
+ /*
+ * If the SCSI status is not CHECK CONDITION, we don't want
+ * to try and read the sense data buffer.
+ */
+ goto simple_status;
+ }
+
+ if (pkt->pkt_scblen < sizeof (struct scsi_arq_status)) {
+ /*
+ * There is not enough room for a request sense structure.
+ * Fall back to reporting just the SCSI status code.
+ */
+ goto simple_status;
+ }
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ struct scsi_arq_status *sts = (struct scsi_arq_status *)pkt->pkt_scbp;
+
+ /*
+ * Copy in the SCSI status from the original command.
+ */
+ bcopy(&ei->ScsiStatus, &sts->sts_status, sizeof (sts->sts_status));
+
+ /*
+ * Mock up a successful REQUEST SENSE:
+ */
+ sts->sts_rqpkt_reason = CMD_CMPLT;
+ sts->sts_rqpkt_resid = 0;
+ sts->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+ STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ sts->sts_rqpkt_statistics = 0;
+
+ /*
+ * The sense data from the controller should be copied into place
+ * starting at the "sts_sensedata" member of the auto request
+ * sense object.
+ */
+ size_t sense_len = pkt->pkt_scblen - offsetof(struct scsi_arq_status,
+ sts_sensedata);
+ if (ei->SenseLen < sense_len) {
+ /*
+ * Only copy sense data bytes that are within the region
+ * the controller marked as valid.
+ */
+ sense_len = ei->SenseLen;
+ }
+ bcopy(ei->SenseInfo, &sts->sts_sensedata, sense_len);
+
+ pkt->pkt_state |= STATE_ARQ_DONE;
+ return;
+
+simple_status:
+ if (pkt->pkt_scblen < sizeof (struct scsi_status)) {
+ /*
+ * There is not even enough room for the SCSI status byte.
+ */
+ return;
+ }
+
+ bcopy(&ei->ScsiStatus, pkt->pkt_scbp, sizeof (struct scsi_status));
+}
+
+static void
+smrt_hba_complete_log_error(smrt_command_t *smcm, const char *name)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+ ErrorInfo_t *ei = smcm->smcm_va_err;
+
+ dev_err(smrt->smrt_dip, CE_WARN, "!SCSI command failed: %s: "
+ "SCSI op %x, CISS status %x, SCSI status %x", name,
+ (unsigned)smcm->smcm_va_cmd->Request.CDB[0],
+ (unsigned)ei->CommandStatus, (unsigned)ei->ScsiStatus);
+}
+
+/*
+ * Completion routine for commands submitted to the controller via the SCSI
+ * framework.
+ */
+void
+smrt_hba_complete(smrt_command_t *smcm)
+{
+ smrt_t *smrt = smcm->smcm_ctlr;
+ ErrorInfo_t *ei = smcm->smcm_va_err;
+ struct scsi_pkt *pkt = smcm->smcm_scsa->smcms_pkt;
+
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ pkt->pkt_resid = ei->ResidualCnt;
+
+ /*
+ * Check if the controller was reset while this packet was in flight.
+ */
+ if (smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
+ if (pkt->pkt_reason != CMD_CMPLT) {
+ /*
+ * If another error status has already been written,
+ * do not overwrite it.
+ */
+ pkt->pkt_reason = CMD_RESET;
+ }
+ pkt->pkt_statistics |= STAT_BUS_RESET | STAT_DEV_RESET;
+ goto finish;
+ }
+
+ if (!(smcm->smcm_status & SMRT_CMD_STATUS_ERROR)) {
+ /*
+ * The command was completed without error by the controller.
+ *
+ * As per the specification, if an error was not signalled
+ * by the controller through the CISS transport method,
+ * the error information (including CommandStatus) has not
+ * been written and should not be checked.
+ */
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ goto finish;
+ }
+
+ /*
+ * Check the completion status to determine what befell this request.
+ */
+ switch (ei->CommandStatus) {
+ case CISS_CMD_SUCCESS:
+ /*
+ * In a certain sense, the specification contradicts itself.
+ * On the one hand, it suggests that a successful command
+ * will not result in a controller write to the error
+ * information block; on the other hand, it makes room
+ * for a status code (0) which denotes a successful
+ * execution.
+ *
+ * To be on the safe side, we check for that condition here.
+ */
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_DATA_UNDERRUN:
+ /*
+ * A data underrun occurred. Ideally this will result in
+ * an appropriate SCSI status and sense data.
+ */
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_TARGET_STATUS:
+ /*
+ * The command completed, but an error occurred. We need
+ * to provide the sense data to the SCSI framework.
+ */
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_DATA_OVERRUN:
+ /*
+ * Data overrun has occurred.
+ */
+ smrt_hba_complete_log_error(smcm, "data overrun");
+ pkt->pkt_reason = CMD_DATA_OVR;
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_INVALID:
+ /*
+ * One or more fields in the command has invalid data.
+ */
+ smrt_hba_complete_log_error(smcm, "invalid command");
+ pkt->pkt_reason = CMD_BADMSG;
+ pkt->pkt_state |= STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_PROTOCOL_ERR:
+ /*
+ * An error occurred in communication with the end device.
+ */
+ smrt_hba_complete_log_error(smcm, "protocol error");
+ pkt->pkt_reason = CMD_BADMSG;
+ pkt->pkt_state |= STATE_GOT_STATUS;
+ break;
+
+ case CISS_CMD_HARDWARE_ERR:
+ /*
+ * A hardware error occurred.
+ */
+ smrt_hba_complete_log_error(smcm, "hardware error");
+ pkt->pkt_reason = CMD_INCOMPLETE;
+ break;
+
+ case CISS_CMD_CONNECTION_LOST:
+ /*
+ * The connection with the end device cannot be
+ * re-established.
+ */
+ smrt_hba_complete_log_error(smcm, "connection lost");
+ pkt->pkt_reason = CMD_INCOMPLETE;
+ break;
+
+ case CISS_CMD_ABORTED:
+ case CISS_CMD_UNSOLICITED_ABORT:
+ if (smcm->smcm_status & SMRT_CMD_STATUS_TIMEOUT) {
+ /*
+ * This abort was arranged by the periodic routine
+ * in response to an elapsed timeout.
+ */
+ pkt->pkt_reason = CMD_TIMEOUT;
+ pkt->pkt_statistics |= STAT_TIMEOUT;
+ } else {
+ pkt->pkt_reason = CMD_ABORTED;
+ }
+ pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS;
+ pkt->pkt_statistics |= STAT_ABORTED;
+ break;
+
+ case CISS_CMD_TIMEOUT:
+ smrt_hba_complete_log_error(smcm, "timeout");
+ pkt->pkt_reason = CMD_TIMEOUT;
+ pkt->pkt_statistics |= STAT_TIMEOUT;
+ break;
+
+ default:
+ /*
+ * This is an error that we were not prepared to handle.
+ * Signal a generic transport-level error to the framework.
+ */
+ smrt_hba_complete_log_error(smcm, "unexpected error");
+ pkt->pkt_reason = CMD_TRAN_ERR;
+ }
+
+ /*
+ * Attempt to read a SCSI status code and any automatic
+ * request sense data that may exist:
+ */
+ smrt_hba_complete_status(smcm);
+
+finish:
+ mutex_exit(&smrt->smrt_mutex);
+ scsi_hba_pkt_comp(pkt);
+ mutex_enter(&smrt->smrt_mutex);
+}
+
+static int
+smrt_getcap(struct scsi_address *sa, char *cap, int whom)
+{
+ _NOTE(ARGUNUSED(whom))
+
+ scsi_hba_tran_t *tran = sa->a_hba_tran;
+ smrt_t *smrt = (smrt_t *)tran->tran_hba_private;
+ int index;
+
+ if ((index = scsi_hba_lookup_capstr(cap)) == DDI_FAILURE) {
+ /*
+ * This capability string could not be translated to an
+ * ID number, so it must not exist.
+ */
+ return (-1);
+ }
+
+ switch (index) {
+ case SCSI_CAP_CDB_LEN:
+ /*
+ * The CDB field in the CISS request block is fixed at 16
+ * bytes.
+ */
+ return (CISS_CDBLEN);
+
+ case SCSI_CAP_DMA_MAX:
+ if (smrt->smrt_dma_attr.dma_attr_maxxfer > INT_MAX) {
+ return (INT_MAX);
+ }
+ return ((int)smrt->smrt_dma_attr.dma_attr_maxxfer);
+
+ case SCSI_CAP_SECTOR_SIZE:
+ if (smrt->smrt_dma_attr.dma_attr_granular > INT_MAX) {
+ return (-1);
+ }
+ return ((int)smrt->smrt_dma_attr.dma_attr_granular);
+
+ case SCSI_CAP_INITIATOR_ID:
+ return (SMRT_CONTROLLER_TARGET);
+
+ case SCSI_CAP_DISCONNECT:
+ case SCSI_CAP_SYNCHRONOUS:
+ case SCSI_CAP_WIDE_XFER:
+ case SCSI_CAP_ARQ:
+ case SCSI_CAP_UNTAGGED_QING:
+ case SCSI_CAP_TAGGED_QING:
+ /*
+ * These capabilities are supported by the driver and the
+ * controller. See scsi_ifgetcap(9F) for more information.
+ */
+ return (1);
+
+ case SCSI_CAP_RESET_NOTIFICATION:
+ /*
+ * This capability is not supported.
+ */
+ return (0);
+
+ default:
+ /*
+ * The property in question is not known to this driver.
+ */
+ return (-1);
+ }
+}
+
+/* ARGSUSED */
+static int
+smrt_setcap(struct scsi_address *sa, char *cap, int value, int whom)
+{
+ int index;
+
+ if ((index = scsi_hba_lookup_capstr(cap)) == DDI_FAILURE) {
+ /*
+ * This capability string could not be translated to an
+ * ID number, so it must not exist.
+ */
+ return (-1);
+ }
+
+ if (whom == 0) {
+ /*
+ * When whom is 0, this is a request to set a capability for
+ * all targets. As per the recommendation in tran_setcap(9E),
+ * we do not support this mode of operation.
+ */
+ return (-1);
+ }
+
+ switch (index) {
+ case SCSI_CAP_CDB_LEN:
+ case SCSI_CAP_DMA_MAX:
+ case SCSI_CAP_SECTOR_SIZE:
+ case SCSI_CAP_INITIATOR_ID:
+ case SCSI_CAP_DISCONNECT:
+ case SCSI_CAP_SYNCHRONOUS:
+ case SCSI_CAP_WIDE_XFER:
+ case SCSI_CAP_ARQ:
+ case SCSI_CAP_UNTAGGED_QING:
+ case SCSI_CAP_TAGGED_QING:
+ case SCSI_CAP_RESET_NOTIFICATION:
+ /*
+ * We do not support changing any capabilities at this time.
+ */
+ return (0);
+
+ default:
+ /*
+ * The capability in question is not known to this driver.
+ */
+ return (-1);
+ }
+}
+
+int
+smrt_hba_setup(smrt_t *smrt)
+{
+ dev_info_t *dip = smrt->smrt_dip;
+ scsi_hba_tran_t *tran;
+
+ if ((tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP)) == NULL) {
+ dev_err(dip, CE_WARN, "could not allocate SCSA resources");
+ return (DDI_FAILURE);
+ }
+
+ smrt->smrt_hba_tran = tran;
+ tran->tran_hba_private = smrt;
+
+ tran->tran_tgt_init = smrt_tran_tgt_init;
+ tran->tran_tgt_probe = scsi_hba_probe;
+ tran->tran_tgt_free = smrt_tran_tgt_free;
+
+ tran->tran_start = smrt_tran_start;
+ tran->tran_reset = smrt_tran_reset;
+ tran->tran_abort = smrt_tran_abort;
+
+ tran->tran_getcap = smrt_getcap;
+ tran->tran_setcap = smrt_setcap;
+
+ tran->tran_setup_pkt = smrt_tran_setup_pkt;
+ tran->tran_teardown_pkt = smrt_tran_teardown_pkt;
+ tran->tran_hba_len = sizeof (smrt_command_scsa_t);
+
+ if (scsi_hba_attach_setup(dip, &smrt->smrt_dma_attr, tran,
+ SCSI_HBA_TRAN_CLONE | SCSI_HBA_TRAN_SCB) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not attach to SCSA framework");
+ scsi_hba_tran_free(tran);
+ return (DDI_FAILURE);
+ }
+
+ smrt->smrt_init_level |= SMRT_INITLEVEL_SCSA;
+ return (DDI_SUCCESS);
+}
+
+void
+smrt_hba_teardown(smrt_t *smrt)
+{
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_SCSA) {
+ VERIFY(scsi_hba_detach(smrt->smrt_dip) != DDI_FAILURE);
+ scsi_hba_tran_free(smrt->smrt_hba_tran);
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_SCSA;
+ }
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_interrupts.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_interrupts.c
new file mode 100644
index 0000000000..84d38184aa
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_interrupts.c
@@ -0,0 +1,233 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+static char *
+smrt_interrupt_type_name(int type)
+{
+ switch (type) {
+ case DDI_INTR_TYPE_MSIX:
+ return ("MSI-X");
+ case DDI_INTR_TYPE_MSI:
+ return ("MSI");
+ case DDI_INTR_TYPE_FIXED:
+ return ("fixed");
+ default:
+ return ("?");
+ }
+}
+
+static int
+smrt_interrupts_disable(smrt_t *smrt)
+{
+ if (smrt->smrt_interrupt_cap & DDI_INTR_FLAG_BLOCK) {
+ return (ddi_intr_block_disable(smrt->smrt_interrupts,
+ smrt->smrt_ninterrupts));
+ } else {
+ VERIFY3S(smrt->smrt_ninterrupts, ==, 1);
+
+ return (ddi_intr_disable(smrt->smrt_interrupts[0]));
+ }
+}
+
+int
+smrt_interrupts_enable(smrt_t *smrt)
+{
+ int ret;
+
+ VERIFY(!(smrt->smrt_init_level & SMRT_INITLEVEL_INT_ENABLED));
+
+ if (smrt->smrt_interrupt_cap & DDI_INTR_FLAG_BLOCK) {
+ ret = ddi_intr_block_enable(smrt->smrt_interrupts,
+ smrt->smrt_ninterrupts);
+ } else {
+ VERIFY3S(smrt->smrt_ninterrupts, ==, 1);
+
+ ret = ddi_intr_enable(smrt->smrt_interrupts[0]);
+ }
+
+ if (ret == DDI_SUCCESS) {
+ smrt->smrt_init_level |= SMRT_INITLEVEL_INT_ENABLED;
+ }
+
+ return (ret);
+}
+
+static void
+smrt_interrupts_free(smrt_t *smrt)
+{
+ for (int i = 0; i < smrt->smrt_ninterrupts; i++) {
+ (void) ddi_intr_free(smrt->smrt_interrupts[i]);
+ }
+ smrt->smrt_ninterrupts = 0;
+ smrt->smrt_interrupt_type = 0;
+ smrt->smrt_interrupt_cap = 0;
+ smrt->smrt_interrupt_pri = 0;
+}
+
+static int
+smrt_interrupts_alloc(smrt_t *smrt, int type)
+{
+ dev_info_t *dip = smrt->smrt_dip;
+ int nintrs = 0;
+ int navail = 0;
+
+ if (ddi_intr_get_nintrs(dip, type, &nintrs) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not count %s interrupts",
+ smrt_interrupt_type_name(type));
+ return (DDI_FAILURE);
+ }
+ if (nintrs < 1) {
+ dev_err(dip, CE_WARN, "no %s interrupts supported",
+ smrt_interrupt_type_name(type));
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_intr_get_navail(dip, type, &navail) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not count available %s "
+ "interrupts", smrt_interrupt_type_name(type));
+ return (DDI_FAILURE);
+ }
+ if (navail < 1) {
+ dev_err(dip, CE_WARN, "no %s interrupts available",
+ smrt_interrupt_type_name(type));
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_intr_alloc(dip, smrt->smrt_interrupts, type, 0, 1,
+ &smrt->smrt_ninterrupts, DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "%s interrupt allocation failed",
+ smrt_interrupt_type_name(type));
+ smrt_interrupts_free(smrt);
+ return (DDI_FAILURE);
+ }
+
+ smrt->smrt_init_level |= SMRT_INITLEVEL_INT_ALLOC;
+ smrt->smrt_interrupt_type = type;
+ return (DDI_SUCCESS);
+}
+
+int
+smrt_interrupts_setup(smrt_t *smrt)
+{
+ int types;
+ unsigned ipri;
+ uint_t (*hw_isr)(caddr_t, caddr_t);
+ dev_info_t *dip = smrt->smrt_dip;
+
+ /*
+ * Select the correct hardware interrupt service routine for the
+ * Transport Method we have configured:
+ */
+ switch (smrt->smrt_ctlr_mode) {
+ case SMRT_CTLR_MODE_SIMPLE:
+ hw_isr = smrt_isr_hw_simple;
+ break;
+ default:
+ panic("unknown controller mode");
+ }
+
+ if (ddi_intr_get_supported_types(dip, &types) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not get support interrupts");
+ goto fail;
+ }
+
+ /*
+ * The specification is somewhat unclear of the precise nature of MSI-X
+ * support with Smart Array controllers, particularly with respect to
+ * the Simple Transport Method, so we'll just try for classical MSI.
+ */
+ if (types & DDI_INTR_TYPE_MSI) {
+ if (smrt_interrupts_alloc(smrt, DDI_INTR_TYPE_MSI) ==
+ DDI_SUCCESS) {
+ goto add_handler;
+ }
+ }
+
+ /*
+ * If MSI is not available, fall back to fixed interrupts.
+ */
+ if (types & DDI_INTR_TYPE_FIXED) {
+ if (smrt_interrupts_alloc(smrt, DDI_INTR_TYPE_FIXED) ==
+ DDI_SUCCESS) {
+ goto add_handler;
+ }
+ }
+
+ /*
+ * We were unable to allocate any interrupts.
+ */
+ dev_err(dip, CE_WARN, "interrupt allocation failed");
+ goto fail;
+
+add_handler:
+ /*
+ * Ensure that we have not been given a high-level interrupt, as our
+ * interrupt handlers do not support them.
+ */
+ if (ddi_intr_get_pri(smrt->smrt_interrupts[0], &ipri) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not determine interrupt priority");
+ goto fail;
+ }
+ if (ipri >= ddi_intr_get_hilevel_pri()) {
+ dev_err(dip, CE_WARN, "high level interrupts not supported");
+ goto fail;
+ }
+ smrt->smrt_interrupt_pri = ipri;
+
+ if (ddi_intr_get_cap(smrt->smrt_interrupts[0],
+ &smrt->smrt_interrupt_cap) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "could not get %s interrupt cap",
+ smrt_interrupt_type_name(smrt->smrt_interrupt_type));
+ goto fail;
+ }
+
+ if (ddi_intr_add_handler(smrt->smrt_interrupts[0], hw_isr,
+ (caddr_t)smrt, NULL) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "adding %s interrupt failed",
+ smrt_interrupt_type_name(smrt->smrt_interrupt_type));
+ goto fail;
+ }
+ smrt->smrt_init_level |= SMRT_INITLEVEL_INT_ADDED;
+
+ return (DDI_SUCCESS);
+
+fail:
+ smrt_interrupts_teardown(smrt);
+ return (DDI_FAILURE);
+}
+
+void
+smrt_interrupts_teardown(smrt_t *smrt)
+{
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_INT_ENABLED) {
+ (void) smrt_interrupts_disable(smrt);
+
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_INT_ENABLED;
+ }
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_INT_ADDED) {
+ (void) ddi_intr_remove_handler(smrt->smrt_interrupts[0]);
+
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_INT_ADDED;
+ }
+
+ if (smrt->smrt_init_level & SMRT_INITLEVEL_INT_ALLOC) {
+ smrt_interrupts_free(smrt);
+
+ smrt->smrt_init_level &= ~SMRT_INITLEVEL_INT_ALLOC;
+ }
+}
diff --git a/usr/src/uts/common/io/scsi/adapters/smrt/smrt_logvol.c b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_logvol.c
new file mode 100644
index 0000000000..67267ecb60
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/adapters/smrt/smrt_logvol.c
@@ -0,0 +1,312 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/scsi/adapters/smrt/smrt.h>
+
+smrt_volume_t *
+smrt_logvol_lookup_by_id(smrt_t *smrt, unsigned id)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ for (smrt_volume_t *smlv = list_head(&smrt->smrt_volumes);
+ smlv != NULL; smlv = list_next(&smrt->smrt_volumes, smlv)) {
+ if (smlv->smlv_addr.VolId == id) {
+ return (smlv);
+ }
+ }
+
+ return (NULL);
+}
+
+smrt_volume_t *
+smrt_logvol_lookup_by_addr(smrt_t *smrt, struct scsi_address *sa)
+{
+ VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
+
+ /*
+ * As outlined in scsi_address(9S), a SCSI target device is described
+ * by an address in two parts: the target ID, and a logical unit
+ * number. Logical volumes are essentially a simple, single-unit SCSI
+ * "device", which for our purposes is only available on logical unit
+ * number 0.
+ */
+ if (sa->a_lun != 0) {
+ return (NULL);
+ }
+
+ return (smrt_logvol_lookup_by_id(smrt, sa->a_target));
+}
+
+static int
+smrt_read_logvols(smrt_t *smrt, smrt_report_logical_lun_t *smrll)
+{
+ smrt_report_logical_lun_ent_t *ents = smrll->smrll_data.ents;
+ uint32_t count = BE_32(smrll->smrll_datasize) /
+ sizeof (smrt_report_logical_lun_ent_t);
+
+ if (count > SMRT_MAX_LOGDRV) {
+ count = SMRT_MAX_LOGDRV;
+ }
+
+ for (unsigned i = 0; i < count; i++) {
+ smrt_volume_t *smlv;
+
+ DTRACE_PROBE2(read_logvol, unsigned, i,
+ smrt_report_logical_lun_ent_t *, &ents[i]);
+
+ if ((smlv = smrt_logvol_lookup_by_id(smrt,
+ ents[i].smrle_addr.VolId)) != NULL) {
+ continue;
+ }
+
+ /*
+ * This is a new Logical Volume, so add it the the list.
+ */
+ if ((smlv = kmem_zalloc(sizeof (*smlv), KM_NOSLEEP)) ==
+ NULL) {
+ return (ENOMEM);
+ }
+
+ smlv->smlv_addr = ents[i].smrle_addr;
+
+ list_create(&smlv->smlv_targets,
+ sizeof (smrt_target_t),
+ offsetof(smrt_target_t, smtg_link_volume));
+
+ smlv->smlv_ctlr = smrt;
+ list_insert_tail(&smrt->smrt_volumes, smlv);
+ }
+
+ return (0);
+}
+
+static int
+smrt_read_logvols_ext(smrt_t *smrt, smrt_report_logical_lun_t *smrll)
+{
+ smrt_report_logical_lun_extent_t *extents =
+ smrll->smrll_data.extents;
+ uint32_t count = BE_32(smrll->smrll_datasize) /
+ sizeof (smrt_report_logical_lun_extent_t);
+
+ if (count > SMRT_MAX_LOGDRV) {
+ count = SMRT_MAX_LOGDRV;
+ }
+
+ for (unsigned i = 0; i < count; i++) {
+ smrt_volume_t *smlv;
+
+ DTRACE_PROBE2(read_logvol_ext, unsigned, i,
+ smrt_report_logical_lun_extent_t *, &extents[i]);
+
+ if ((smlv = smrt_logvol_lookup_by_id(smrt,
+ extents[i].smrle_addr.VolId)) != NULL) {
+ if ((smlv->smlv_flags & SMRT_VOL_FLAG_WWN) &&
+ bcmp(extents[i].smrle_wwn, smlv->smlv_wwn,
+ 16) != 0) {
+ dev_err(smrt->smrt_dip, CE_PANIC, "logical "
+ "volume %u WWN changed unexpectedly", i);
+ }
+ continue;
+ }
+
+ /*
+ * This is a new Logical Volume, so add it the the list.
+ */
+ if ((smlv = kmem_zalloc(sizeof (*smlv), KM_NOSLEEP)) ==
+ NULL) {
+ return (ENOMEM);
+ }
+
+ smlv->smlv_addr = extents[i].smrle_addr;
+
+ bcopy(extents[i].smrle_wwn, smlv->smlv_wwn, 16);
+ smlv->smlv_flags |= SMRT_VOL_FLAG_WWN;
+
+ list_create(&smlv->smlv_targets,
+ sizeof (smrt_target_t),
+ offsetof(smrt_target_t, smtg_link_volume));
+
+ smlv->smlv_ctlr = smrt;
+ list_insert_tail(&smrt->smrt_volumes, smlv);
+ }
+
+ return (0);
+}
+
+/*
+ * Discover the currently visible set of Logical Volumes exposed by the
+ * controller.
+ */
+int
+smrt_logvol_discover(smrt_t *smrt, uint16_t timeout)
+{
+ smrt_command_t *smcm;
+ smrt_report_logical_lun_t *smrll;
+ smrt_report_logical_lun_req_t smrllr = { 0 };
+ int r;
+
+ if (!ddi_in_panic()) {
+ mutex_enter(&smrt->smrt_mutex);
+ while (smrt->smrt_status & SMRT_CTLR_STATUS_DISCOVERY) {
+ /*
+ * A discovery is already occuring. Wait for
+ * completion.
+ */
+ cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
+ }
+
+ if (gethrtime() < smrt->smrt_last_discovery + 5 * NANOSEC) {
+ /*
+ * A discovery completed successfully within the
+ * last five seconds. Just use the existing data.
+ */
+ mutex_exit(&smrt->smrt_mutex);
+ return (0);
+ }
+
+ smrt->smrt_status |= SMRT_CTLR_STATUS_DISCOVERY;
+ mutex_exit(&smrt->smrt_mutex);
+ }
+
+ /*
+ * Allocate the command to send to the device, including buffer space
+ * for the returned list of Logical Volumes.
+ */
+ if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
+ KM_NOSLEEP)) == NULL || smrt_command_attach_internal(smrt, smcm,
+ sizeof (smrt_report_logical_lun_t), KM_NOSLEEP) != 0) {
+ r = ENOMEM;
+ mutex_enter(&smrt->smrt_mutex);
+ goto out;
+ }
+
+ smrll = smcm->smcm_internal->smcmi_va;
+
+ /*
+ * According to the CISS Specification, the Report Logical LUNs
+ * command is sent to the controller itself. The Masked Peripheral
+ * Device addressing mode is used, with LUN of 0.
+ */
+ smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN, B_TRUE,
+ 0, 0);
+
+ smcm->smcm_va_cmd->Request.CDBLen = 12;
+ smcm->smcm_va_cmd->Request.Timeout = timeout;
+ smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
+ smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_ORDERED;
+ smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_READ;
+
+ /*
+ * The Report Logical LUNs command is essentially a vendor-specific
+ * SCSI command, which we assemble into the CDB region of the command
+ * block.
+ */
+ smrllr.smrllr_opcode = CISS_SCMD_REPORT_LOGICAL_LUNS;
+ smrllr.smrllr_extflag = 1;
+ smrllr.smrllr_datasize = htonl(sizeof (smrt_report_logical_lun_t));
+ bcopy(&smrllr, &smcm->smcm_va_cmd->Request.CDB[0],
+ MIN(CISS_CDBLEN, sizeof (smrllr)));
+
+ mutex_enter(&smrt->smrt_mutex);
+
+ /*
+ * Send the command to the device.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
+ if (smrt_submit(smrt, smcm) != 0) {
+ r = EIO;
+ goto out;
+ }
+
+ /*
+ * Poll for completion.
+ */
+ smcm->smcm_expiry = gethrtime() + timeout * NANOSEC;
+ if ((r = smrt_poll_for(smrt, smcm)) != 0) {
+ VERIFY(r == ETIMEDOUT);
+ VERIFY0(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE);
+
+ /*
+ * The command timed out; abandon it now. Remove the POLLED
+ * flag so that the periodic routine will send an abort to
+ * clean it up next time around.
+ */
+ smcm->smcm_status |= SMRT_CMD_STATUS_ABANDONED;
+ smcm->smcm_status &= ~SMRT_CMD_STATUS_POLLED;
+ smcm = NULL;
+ goto out;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
+ /*
+ * The controller was reset while we were trying to discover
+ * logical volumes. Report failure.
+ */
+ r = EIO;
+ goto out;
+ }
+
+ if (smcm->smcm_status & SMRT_CMD_STATUS_ERROR) {
+ ErrorInfo_t *ei = smcm->smcm_va_err;
+
+ if (ei->CommandStatus != CISS_CMD_DATA_UNDERRUN) {
+ dev_err(smrt->smrt_dip, CE_WARN, "logical volume "
+ "discovery error: status 0x%x", ei->CommandStatus);
+ r = EIO;
+ goto out;
+ }
+ }
+
+ if ((smrll->smrll_extflag & 0x1) != 0) {
+ r = smrt_read_logvols_ext(smrt, smrll);
+ } else {
+ r = smrt_read_logvols(smrt, smrll);
+ }
+
+ if (r == 0) {
+ /*
+ * Update the time of the last successful Logical Volume
+ * discovery:
+ */
+ smrt->smrt_last_discovery = gethrtime();
+ }
+
+out:
+ smrt->smrt_status &= ~SMRT_CTLR_STATUS_DISCOVERY;
+ cv_broadcast(&smrt->smrt_cv_finishq);
+ mutex_exit(&smrt->smrt_mutex);
+
+ if (smcm != NULL) {
+ smrt_command_free(smcm);
+ }
+ return (r);
+}
+
+void
+smrt_logvol_teardown(smrt_t *smrt)
+{
+ smrt_volume_t *smlv;
+
+ while ((smlv = list_remove_head(&smrt->smrt_volumes)) != NULL) {
+ /*
+ * By this stage of teardown, all of the SCSI target drivers
+ * must have been detached from this logical volume.
+ */
+ VERIFY(list_is_empty(&smlv->smlv_targets));
+ list_destroy(&smlv->smlv_targets);
+
+ kmem_free(smlv, sizeof (*smlv));
+ }
+}
diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c
index ae1e7e0fc3..dc5dc22e37 100644
--- a/usr/src/uts/common/io/scsi/targets/sd.c
+++ b/usr/src/uts/common/io/scsi/targets/sd.c
@@ -3503,9 +3503,13 @@ sd_set_mmc_caps(sd_ssc_t *ssc)
* according to the successful response to the page
* 0x2A mode sense request.
*/
- scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
- "sd_set_mmc_caps: Mode Sense returned "
- "invalid block descriptor length\n");
+ /*
+ * The following warning occurs due to the KVM CD-ROM
+ * mishandling the multi-media commands. Ignore it.
+ * scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
+ * "sd_set_mmc_caps: Mode Sense returned "
+ * "invalid block descriptor length\n");
+ */
kmem_free(buf, BUFLEN_MODE_CDROM_CAP);
return;
}
@@ -4450,18 +4454,77 @@ sd_sdconf_id_match(struct sd_lun *un, char *id, int idlen)
{
struct scsi_inquiry *sd_inq;
int rval = SD_SUCCESS;
+ char *p;
+ int chk_vidlen = 0, chk_pidlen = 0;
+ int has_tail = 0;
+ static const int VSZ = sizeof (sd_inq->inq_vid);
+ static const int PSZ = sizeof (sd_inq->inq_pid);
ASSERT(un != NULL);
sd_inq = un->un_sd->sd_inq;
ASSERT(id != NULL);
/*
- * We use the inq_vid as a pointer to a buffer containing the
- * vid and pid and use the entire vid/pid length of the table
- * entry for the comparison. This works because the inq_pid
- * data member follows inq_vid in the scsi_inquiry structure.
+ * We would like to use the inq_vid as a pointer to a buffer
+ * containing the vid and pid and use the entire vid/pid length of
+ * the table entry for the comparison. However, this does not work
+ * because, while the inq_pid data member follows inq_vid in the
+ * scsi_inquiry structure, we do not control the contents of this
+ * buffer, and some broken devices violate SPC 4.3.1 and return
+ * fields with null bytes in them.
+ */
+ chk_vidlen = MIN(VSZ, idlen);
+ p = id + chk_vidlen - 1;
+ while (*p == ' ' && chk_vidlen > 0) {
+ --p;
+ --chk_vidlen;
+ }
+
+ /*
+ * If it's all spaces, check the whole thing.
*/
- if (strncasecmp(sd_inq->inq_vid, id, idlen) != 0) {
+ if (chk_vidlen == 0)
+ chk_vidlen = MIN(VSZ, idlen);
+
+ if (idlen > VSZ) {
+ chk_pidlen = idlen - VSZ;
+ p = id + idlen - 1;
+ while (*p == ' ' && chk_pidlen > 0) {
+ --p;
+ --chk_pidlen;
+ }
+ if (chk_pidlen == 0)
+ chk_pidlen = MIN(PSZ, idlen - VSZ);
+ }
+
+ /*
+ * There's one more thing we need to do here. If the user specified
+ * an ID with trailing spaces, we need to make sure the inquiry
+ * vid/pid has only spaces or NULs after the check length; otherwise, it
+ * can't match.
+ */
+ if (idlen > chk_vidlen && chk_vidlen < VSZ) {
+ for (p = sd_inq->inq_vid + chk_vidlen;
+ p < sd_inq->inq_vid + VSZ; ++p) {
+ if (*p != ' ' && *p != '\0') {
+ ++has_tail;
+ break;
+ }
+ }
+ }
+ if (idlen > chk_pidlen + VSZ && chk_pidlen < PSZ) {
+ for (p = sd_inq->inq_pid + chk_pidlen;
+ p < sd_inq->inq_pid + PSZ; ++p) {
+ if (*p != ' ' && *p != '\0') {
+ ++has_tail;
+ break;
+ }
+ }
+ }
+
+ if (has_tail || strncasecmp(sd_inq->inq_vid, id, chk_vidlen) != 0 ||
+ (idlen > VSZ &&
+ strncasecmp(sd_inq->inq_pid, id + VSZ, chk_pidlen) != 0)) {
/*
* The user id string is compared to the inquiry vid/pid
* using a case insensitive comparison and ignoring
diff --git a/usr/src/uts/common/io/signalfd.c b/usr/src/uts/common/io/signalfd.c
index 32f8f85f7a..a78c935968 100644
--- a/usr/src/uts/common/io/signalfd.c
+++ b/usr/src/uts/common/io/signalfd.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -19,97 +19,81 @@
*
* As described on the signalfd(3C) man page, the general idea behind these
* file descriptors is that they can be used to synchronously consume signals
- * via the read(2) syscall. That capability already exists with the
- * sigwaitinfo(3C) function but the key advantage of signalfd is that, because
- * it is file descriptor based, poll(2) can be used to determine when signals
- * are available to be consumed.
+ * via the read(2) syscall. While that capability already exists with the
+ * sigwaitinfo(3C) function, signalfd holds an advantage since it is file
+ * descriptor based: It is able use the event facilities (poll(2), /dev/poll,
+ * event ports) to notify interested parties when consumable signals arrive.
*
- * The general implementation uses signalfd_state to hold both the signal set
- * and poll head for an open file descriptor. Because a process can be using
- * different sigfds with different signal sets, each signalfd_state poll head
- * can be thought of as an independent signal stream and the thread(s) waiting
- * on that stream will get poll notification when any signal in the
- * corresponding set is received.
+ * The signalfd lifecycle begins When a process opens /dev/signalfd. A minor
+ * will be allocated for them along with an associated signalfd_state_t struct.
+ * It is there where the mask of desired signals resides.
*
- * The sigfd_proc_state_t struct lives on the proc_t and maintains per-proc
- * state for function callbacks and data when the proc needs to do work during
- * signal delivery for pollwakeup.
+ * Reading from the signalfd is straightforward and mimics the kernel behavior
+ * for sigtimedwait(). Signals continue to live on either the proc's p_sig, or
+ * thread's t_sig, member. During a read operation, those which match the mask
+ * are consumed so they are no longer pending.
*
- * The read side of the implementation is straightforward and mimics the
- * kernel behavior for sigtimedwait(). Signals continue to live on either
- * the proc's p_sig, or thread's t_sig, member. Read consumes the signal so
- * that it is no longer pending.
+ * The poll side is more complex. Every time a signal is delivered, all of the
+ * signalfds on the process need to be examined in order to pollwake threads
+ * waiting for signal arrival.
*
- * The poll side is more complex since all of the sigfds on the process need
- * to be examined every time a signal is delivered to the process in order to
- * pollwake any thread waiting in poll for that signal.
+ * When a thread polling on a signalfd requires a pollhead, several steps must
+ * be taken to safely ensure the proper result. A sigfd_proc_state_t is
+ * created for the calling process if it does not yet exist. It is there where
+ * a list of sigfd_poll_waiter_t structures reside which associate pollheads to
+ * signalfd_state_t entries. The sigfd_proc_state_t list is walked to find a
+ * sigfd_poll_waiter_t matching the signalfd_state_t which corresponds to the
+ * polled resource. If one is found, it is reused. Otherwise a new one is
+ * created, incrementing the refcount on the signalfd_state_t, and it is added
+ * to the sigfd_poll_waiter_t list.
*
- * Because it is likely that a process will only be using one, or a few, sigfds,
- * but many total file descriptors, we maintain a list of sigfds which need
- * pollwakeup. The list lives on the proc's p_sigfd struct. In this way only
- * zero, or a few, of the state structs will need to be examined every time a
- * signal is delivered to the process, instead of having to examine all of the
- * file descriptors to find the state structs. When a state struct with a
- * matching signal set is found then pollwakeup is called.
+ * The complications imposed by fork(2) are why the pollhead is stored in the
+ * associated sigfd_poll_waiter_t instead of directly in the signalfd_state_t.
+ * More than one process can hold a reference to the signalfd at a time but
+ * arriving signals should wake only process-local pollers. Additionally,
+ * signalfd_close is called only when the last referencing fd is closed, hiding
+ * occurrences of preceeding threads which released their references. This
+ * necessitates reference counting on the signalfd_state_t so it is able to
+ * persist after close until all poll references have been cleansed. Doing so
+ * ensures that blocked pollers which hold references to the signalfd_state_t
+ * will be able to do clean-up after the descriptor itself has been closed.
*
- * The sigfd_list is self-cleaning; as signalfd_pollwake_cb is called, the list
- * will clear out on its own. There is an exit helper (signalfd_exit_helper)
- * which cleans up any remaining per-proc state when the process exits.
+ * When a signal arrives in a process polling on signalfd, signalfd_pollwake_cb
+ * is called via the pointer in sigfd_proc_state_t. It will walk over the
+ * sigfd_poll_waiter_t entries present in the list, searching for any
+ * associated with a signalfd_state_t with a matching signal mask. The
+ * approach of keeping the poller list in p_sigfd was chosen because a process
+ * is likely to use few signalfds relative to its total file descriptors. It
+ * reduces the work required for each received signal.
+ *
+ * When matching sigfd_poll_waiter_t entries are encountered in the poller list
+ * during signalfd_pollwake_cb, they are dispatched into signalfd_wakeq to
+ * perform the pollwake. This is due to a lock ordering conflict between
+ * signalfd_poll and signalfd_pollwake_cb. The former acquires
+ * pollcache_t`pc_lock before proc_t`p_lock. The latter (via sigtoproc)
+ * reverses the order. Defering the pollwake into a taskq means it can be
+ * performed without proc_t`p_lock held, avoiding the deadlock.
*
- * The main complexity with signalfd is the interaction of forking and polling.
- * This interaction is complex because now two processes have a fd that
- * references the same dev_t (and its associated signalfd_state), but signals
- * go to only one of those processes. Also, we don't know when one of the
- * processes closes its fd because our 'close' entry point is only called when
- * the last fd is closed (which could be by either process).
+ * The sigfd_list is self-cleaning; as signalfd_pollwake_cb is called, the list
+ * will clear out on its own. Any remaining per-process state which remains
+ * will be cleaned up by the exit helper (signalfd_exit_helper).
*
- * Because the state struct is referenced by both file descriptors, and the
- * state struct represents a signal stream needing a pollwakeup, if both
- * processes were polling then both processes would get a pollwakeup when a
- * signal arrives for either process (that is, the pollhead is associated with
- * our dev_t so when a signal arrives the pollwakeup wakes up all waiters).
+ * The structures associated with signalfd state are designed to operate
+ * correctly across fork, but there is one caveat that applies. Using
+ * fork-shared signalfd descriptors in conjuction with fork-shared caching poll
+ * descriptors (such as /dev/poll or event ports) will result in missed poll
+ * wake-ups. This is caused by the pollhead identity of signalfd descriptors
+ * being dependent on the process they are polled from. Because it has a
+ * thread-local cache, poll(2) is unaffected by this limitation.
*
- * Fortunately this is not a common problem in practice, but the implementation
- * attempts to mitigate unexpected behavior. The typical behavior is that the
- * parent has been polling the signalfd (which is why it was open in the first
- * place) and the parent might have a pending signalfd_state (with the
- * pollhead) on its per-process sigfd_list. After the fork the child will
- * simply close that fd (among others) as part of the typical fork/close/exec
- * pattern. Because the child will never poll that fd, it will never get any
- * state onto its own sigfd_list (the child starts with a null list). The
- * intention is that the child sees no pollwakeup activity for signals unless
- * it explicitly reinvokes poll on the sigfd.
+ * Lock ordering:
*
- * As background, there are two primary polling cases to consider when the
- * parent process forks:
- * 1) If any thread is blocked in poll(2) then both the parent and child will
- * return from the poll syscall with EINTR. This means that if either
- * process wants to re-poll on a sigfd then it needs to re-run poll and
- * would come back in to the signalfd_poll entry point. The parent would
- * already have the dev_t's state on its sigfd_list and the child would not
- * have anything there unless it called poll again on its fd.
- * 2) If the process is using /dev/poll(7D) then the polling info is being
- * cached by the poll device and the process might not currently be blocked
- * on anything polling related. A subsequent DP_POLL ioctl will not invoke
- * our signalfd_poll entry point again. Because the parent still has its
- * sigfd_list setup, an incoming signal will hit our signalfd_pollwake_cb
- * entry point, which in turn calls pollwake, and /dev/poll will do the
- * right thing on DP_POLL. The child will not have a sigfd_list yet so the
- * signal will not cause a pollwakeup. The dp code does its own handling for
- * cleaning up its cache.
+ * 1. signalfd_lock
+ * 2. signalfd_state_t`sfd_lock
*
- * This leaves only one odd corner case. If the parent and child both use
- * the dup-ed sigfd to poll then when a signal is delivered to either process
- * there is no way to determine which one should get the pollwakeup (since
- * both processes will be queued on the same signal stream poll head). What
- * happens in this case is that both processes will return from poll, but only
- * one of them will actually have a signal to read. The other will return
- * from read with EAGAIN, or block. This case is actually similar to the
- * situation within a single process which got two different sigfd's with the
- * same mask (or poll on two fd's that are dup-ed). Both would return from poll
- * when a signal arrives but only one read would consume the signal and the
- * other read would fail or block. Applications which poll on shared fd's
- * cannot assume that a subsequent read will actually obtain data.
+ * 1. proc_t`p_lock (to walk p_sigfd)
+ * 2. signalfd_state_t`sfd_lock
+ * 2a. signalfd_lock (after sfd_lock is dropped, when sfd_count falls to 0)
*/
#include <sys/ddi.h>
@@ -123,122 +107,184 @@
#include <sys/schedctl.h>
#include <sys/id_space.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
+#include <sys/disp.h>
+#include <sys/taskq_impl.h>
typedef struct signalfd_state signalfd_state_t;
struct signalfd_state {
- kmutex_t sfd_lock; /* lock protecting state */
- pollhead_t sfd_pollhd; /* poll head */
- k_sigset_t sfd_set; /* signals for this fd */
- signalfd_state_t *sfd_next; /* next state on global list */
+ list_node_t sfd_list; /* node in global list */
+ kmutex_t sfd_lock; /* protects fields below */
+ uint_t sfd_count; /* ref count */
+ boolean_t sfd_valid; /* valid while open */
+ k_sigset_t sfd_set; /* signals for this fd */
};
+typedef struct sigfd_poll_waiter {
+ list_node_t spw_list;
+ signalfd_state_t *spw_state;
+ pollhead_t spw_pollhd;
+ taskq_ent_t spw_taskent;
+ short spw_pollev;
+} sigfd_poll_waiter_t;
+
/*
- * Internal global variables.
+ * Protects global state in signalfd_devi, signalfd_minor, signalfd_softstate,
+ * and signalfd_state (including sfd_list field of members)
*/
-static kmutex_t signalfd_lock; /* lock protecting state */
+static kmutex_t signalfd_lock;
static dev_info_t *signalfd_devi; /* device info */
static id_space_t *signalfd_minor; /* minor number arena */
static void *signalfd_softstate; /* softstate pointer */
-static signalfd_state_t *signalfd_state; /* global list of state */
+static list_t signalfd_state; /* global list of state */
+static taskq_t *signalfd_wakeq; /* pollwake event taskq */
+
-/*
- * If we don't already have an entry in the proc's list for this state, add one.
- */
static void
-signalfd_wake_list_add(signalfd_state_t *state)
+signalfd_state_enter_locked(signalfd_state_t *state)
{
- proc_t *p = curproc;
- list_t *lst;
- sigfd_wake_list_t *wlp;
+ ASSERT(MUTEX_HELD(&state->sfd_lock));
+ ASSERT(state->sfd_count > 0);
+ VERIFY(state->sfd_valid == B_TRUE);
- ASSERT(MUTEX_HELD(&p->p_lock));
- ASSERT(p->p_sigfd != NULL);
+ state->sfd_count++;
+}
- lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
- for (wlp = list_head(lst); wlp != NULL; wlp = list_next(lst, wlp)) {
- if (wlp->sigfd_wl_state == state)
- break;
+static void
+signalfd_state_release(signalfd_state_t *state, boolean_t force_invalidate)
+{
+ mutex_enter(&state->sfd_lock);
+
+ if (force_invalidate) {
+ state->sfd_valid = B_FALSE;
}
- if (wlp == NULL) {
- wlp = kmem_zalloc(sizeof (sigfd_wake_list_t), KM_SLEEP);
- wlp->sigfd_wl_state = state;
- list_insert_head(lst, wlp);
+ ASSERT(state->sfd_count > 0);
+ if (state->sfd_count == 1) {
+ VERIFY(state->sfd_valid == B_FALSE);
+ mutex_exit(&state->sfd_lock);
+ if (force_invalidate) {
+ /*
+ * The invalidation performed in signalfd_close is done
+ * while signalfd_lock is held.
+ */
+ ASSERT(MUTEX_HELD(&signalfd_lock));
+ list_remove(&signalfd_state, state);
+ } else {
+ ASSERT(MUTEX_NOT_HELD(&signalfd_lock));
+ mutex_enter(&signalfd_lock);
+ list_remove(&signalfd_state, state);
+ mutex_exit(&signalfd_lock);
+ }
+ kmem_free(state, sizeof (*state));
+ return;
}
+ state->sfd_count--;
+ mutex_exit(&state->sfd_lock);
}
-static void
-signalfd_wake_rm(list_t *lst, sigfd_wake_list_t *wlp)
+static sigfd_poll_waiter_t *
+signalfd_wake_list_add(sigfd_proc_state_t *pstate, signalfd_state_t *state)
{
- list_remove(lst, wlp);
- kmem_free(wlp, sizeof (sigfd_wake_list_t));
-}
+ list_t *lst = &pstate->sigfd_list;
+ sigfd_poll_waiter_t *pw;
-static void
-signalfd_wake_list_rm(proc_t *p, signalfd_state_t *state)
-{
- sigfd_wake_list_t *wlp;
- list_t *lst;
+ for (pw = list_head(lst); pw != NULL; pw = list_next(lst, pw)) {
+ if (pw->spw_state == state)
+ break;
+ }
- ASSERT(MUTEX_HELD(&p->p_lock));
+ if (pw == NULL) {
+ pw = kmem_zalloc(sizeof (*pw), KM_SLEEP);
- if (p->p_sigfd == NULL)
- return;
+ mutex_enter(&state->sfd_lock);
+ signalfd_state_enter_locked(state);
+ pw->spw_state = state;
+ mutex_exit(&state->sfd_lock);
+ list_insert_head(lst, pw);
+ }
+ return (pw);
+}
+
+static sigfd_poll_waiter_t *
+signalfd_wake_list_rm(sigfd_proc_state_t *pstate, signalfd_state_t *state)
+{
+ list_t *lst = &pstate->sigfd_list;
+ sigfd_poll_waiter_t *pw;
- lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
- for (wlp = list_head(lst); wlp != NULL; wlp = list_next(lst, wlp)) {
- if (wlp->sigfd_wl_state == state) {
- signalfd_wake_rm(lst, wlp);
+ for (pw = list_head(lst); pw != NULL; pw = list_next(lst, pw)) {
+ if (pw->spw_state == state) {
break;
}
}
- if (list_is_empty(lst)) {
- ((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb = NULL;
- list_destroy(lst);
- kmem_free(p->p_sigfd, sizeof (sigfd_proc_state_t));
- p->p_sigfd = NULL;
+ if (pw != NULL) {
+ list_remove(lst, pw);
+ pw->spw_state = NULL;
+ signalfd_state_release(state, B_FALSE);
}
+
+ return (pw);
}
static void
signalfd_wake_list_cleanup(proc_t *p)
{
- sigfd_wake_list_t *wlp;
+ sigfd_proc_state_t *pstate = p->p_sigfd;
+ sigfd_poll_waiter_t *pw;
list_t *lst;
ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(pstate != NULL);
+
+ lst = &pstate->sigfd_list;
+ while ((pw = list_remove_head(lst)) != NULL) {
+ signalfd_state_t *state = pw->spw_state;
- ((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb = NULL;
+ pw->spw_state = NULL;
+ signalfd_state_release(state, B_FALSE);
- lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
- while (!list_is_empty(lst)) {
- wlp = (sigfd_wake_list_t *)list_remove_head(lst);
- kmem_free(wlp, sizeof (sigfd_wake_list_t));
+ pollwakeup(&pw->spw_pollhd, POLLERR);
+ pollhead_clean(&pw->spw_pollhd);
+ kmem_free(pw, sizeof (*pw));
}
+ list_destroy(lst);
+
+ p->p_sigfd = NULL;
+ kmem_free(pstate, sizeof (*pstate));
}
static void
signalfd_exit_helper(void)
{
proc_t *p = curproc;
- list_t *lst;
-
- /* This being non-null is the only way we can get here */
- ASSERT(p->p_sigfd != NULL);
mutex_enter(&p->p_lock);
- lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-
signalfd_wake_list_cleanup(p);
- list_destroy(lst);
- kmem_free(p->p_sigfd, sizeof (sigfd_proc_state_t));
- p->p_sigfd = NULL;
mutex_exit(&p->p_lock);
}
/*
+ * Perform pollwake for a sigfd_poll_waiter_t entry.
+ * Thanks to the strict and conflicting lock orders required for signalfd_poll
+ * (pc_lock before p_lock) and signalfd_pollwake_cb (p_lock before pc_lock),
+ * this is relegated to a taskq to avoid deadlock.
+ */
+static void
+signalfd_wake_task(void *arg)
+{
+ sigfd_poll_waiter_t *pw = arg;
+ signalfd_state_t *state = pw->spw_state;
+
+ pw->spw_state = NULL;
+ signalfd_state_release(state, B_FALSE);
+ pollwakeup(&pw->spw_pollhd, pw->spw_pollev);
+ pollhead_clean(&pw->spw_pollhd);
+ kmem_free(pw, sizeof (*pw));
+}
+
+/*
* Called every time a signal is delivered to the process so that we can
* see if any signal stream needs a pollwakeup. We maintain a list of
* signal state elements so that we don't have to look at every file descriptor
@@ -254,35 +300,42 @@ static void
signalfd_pollwake_cb(void *arg0, int sig)
{
proc_t *p = (proc_t *)arg0;
+ sigfd_proc_state_t *pstate = (sigfd_proc_state_t *)p->p_sigfd;
list_t *lst;
- sigfd_wake_list_t *wlp;
+ sigfd_poll_waiter_t *pw;
ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(pstate != NULL);
- if (p->p_sigfd == NULL)
- return;
-
- lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
- wlp = list_head(lst);
- while (wlp != NULL) {
- signalfd_state_t *state = wlp->sigfd_wl_state;
+ lst = &pstate->sigfd_list;
+ pw = list_head(lst);
+ while (pw != NULL) {
+ signalfd_state_t *state = pw->spw_state;
+ sigfd_poll_waiter_t *next;
mutex_enter(&state->sfd_lock);
-
- if (sigismember(&state->sfd_set, sig) &&
- state->sfd_pollhd.ph_list != NULL) {
- sigfd_wake_list_t *tmp = wlp;
-
- /* remove it from the list */
- wlp = list_next(lst, wlp);
- signalfd_wake_rm(lst, tmp);
-
- mutex_exit(&state->sfd_lock);
- pollwakeup(&state->sfd_pollhd, POLLRDNORM | POLLIN);
+ if (!state->sfd_valid) {
+ pw->spw_pollev = POLLERR;
+ } else if (sigismember(&state->sfd_set, sig)) {
+ pw->spw_pollev = POLLRDNORM | POLLIN;
} else {
mutex_exit(&state->sfd_lock);
- wlp = list_next(lst, wlp);
+ pw = list_next(lst, pw);
+ continue;
}
+ mutex_exit(&state->sfd_lock);
+
+ /*
+ * Pull the sigfd_poll_waiter_t out of the list and dispatch it
+ * to perform a pollwake. This cannot be done synchronously
+ * since signalfd_poll and signalfd_pollwake_cb have
+ * conflicting lock orders which can deadlock.
+ */
+ next = list_next(lst, pw);
+ list_remove(lst, pw);
+ taskq_dispatch_ent(signalfd_wakeq, signalfd_wake_task, pw, 0,
+ &pw->spw_taskent);
+ pw = next;
}
}
@@ -290,7 +343,7 @@ _NOTE(ARGSUSED(1))
static int
signalfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
- signalfd_state_t *state;
+ signalfd_state_t *state, **sstate;
major_t major = getemajor(*devp);
minor_t minor = getminor(*devp);
@@ -300,18 +353,20 @@ signalfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
mutex_enter(&signalfd_lock);
minor = (minor_t)id_allocff(signalfd_minor);
-
if (ddi_soft_state_zalloc(signalfd_softstate, minor) != DDI_SUCCESS) {
id_free(signalfd_minor, minor);
mutex_exit(&signalfd_lock);
return (ENODEV);
}
- state = ddi_get_soft_state(signalfd_softstate, minor);
- *devp = makedevice(major, minor);
+ state = kmem_zalloc(sizeof (*state), KM_SLEEP);
+ state->sfd_valid = B_TRUE;
+ state->sfd_count = 1;
+ list_insert_head(&signalfd_state, (void *)state);
- state->sfd_next = signalfd_state;
- signalfd_state = state;
+ sstate = ddi_get_soft_state(signalfd_softstate, minor);
+ *sstate = state;
+ *devp = makedevice(major, minor);
mutex_exit(&signalfd_lock);
@@ -405,6 +460,9 @@ consume_signal(k_sigset_t set, uio_t *uio, boolean_t block)
lwp->lwp_extsig = 0;
mutex_exit(&p->p_lock);
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sigfd_translate)
+ BROP(p)->b_sigfd_translate(infop);
+
/* Convert k_siginfo into external, datamodel independent, struct. */
bzero(ssp, sizeof (*ssp));
ssp->ssi_signo = infop->si_signo;
@@ -439,7 +497,7 @@ _NOTE(ARGSUSED(2))
static int
signalfd_read(dev_t dev, uio_t *uio, cred_t *cr)
{
- signalfd_state_t *state;
+ signalfd_state_t *state, **sstate;
minor_t minor = getminor(dev);
boolean_t block = B_TRUE;
k_sigset_t set;
@@ -449,7 +507,8 @@ signalfd_read(dev_t dev, uio_t *uio, cred_t *cr)
if (uio->uio_resid < sizeof (signalfd_siginfo_t))
return (EINVAL);
- state = ddi_get_soft_state(signalfd_softstate, minor);
+ sstate = ddi_get_soft_state(signalfd_softstate, minor);
+ state = *sstate;
if (uio->uio_fmode & (FNDELAY|FNONBLOCK))
block = B_FALSE;
@@ -462,15 +521,26 @@ signalfd_read(dev_t dev, uio_t *uio, cred_t *cr)
return (set_errno(EINVAL));
do {
- res = consume_signal(state->sfd_set, uio, block);
- if (res == 0)
- got_one = B_TRUE;
+ res = consume_signal(set, uio, block);
- /*
- * After consuming one signal we won't block trying to consume
- * further signals.
- */
- block = B_FALSE;
+ if (res == 0) {
+ /*
+ * After consuming one signal, do not block while
+ * trying to consume more.
+ */
+ got_one = B_TRUE;
+ block = B_FALSE;
+
+ /*
+ * Refresh the matching signal set in case it was
+ * updated during the wait.
+ */
+ mutex_enter(&state->sfd_lock);
+ set = state->sfd_set;
+ mutex_exit(&state->sfd_lock);
+ if (sigisempty(&set))
+ break;
+ }
} while (res == 0 && uio->uio_resid >= sizeof (signalfd_siginfo_t));
if (got_one)
@@ -499,13 +569,14 @@ static int
signalfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **phpp)
{
- signalfd_state_t *state;
+ signalfd_state_t *state, **sstate;
minor_t minor = getminor(dev);
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
short revents = 0;
- state = ddi_get_soft_state(signalfd_softstate, minor);
+ sstate = ddi_get_soft_state(signalfd_softstate, minor);
+ state = *sstate;
mutex_enter(&state->sfd_lock);
@@ -515,39 +586,36 @@ signalfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
mutex_exit(&state->sfd_lock);
if (!(*reventsp = revents & events) && !anyyet) {
- *phpp = &state->sfd_pollhd;
+ sigfd_proc_state_t *pstate;
+ sigfd_poll_waiter_t *pw;
/*
* Enable pollwakeup handling.
*/
- if (p->p_sigfd == NULL) {
- sigfd_proc_state_t *pstate;
+ mutex_enter(&p->p_lock);
+ if ((pstate = (sigfd_proc_state_t *)p->p_sigfd) == NULL) {
- pstate = kmem_zalloc(sizeof (sigfd_proc_state_t),
- KM_SLEEP);
+ mutex_exit(&p->p_lock);
+ pstate = kmem_zalloc(sizeof (*pstate), KM_SLEEP);
list_create(&pstate->sigfd_list,
- sizeof (sigfd_wake_list_t),
- offsetof(sigfd_wake_list_t, sigfd_wl_lst));
+ sizeof (sigfd_poll_waiter_t),
+ offsetof(sigfd_poll_waiter_t, spw_list));
+ pstate->sigfd_pollwake_cb = signalfd_pollwake_cb;
+ /* Check again, after blocking for the alloc. */
mutex_enter(&p->p_lock);
- /* check again now that we're locked */
if (p->p_sigfd == NULL) {
p->p_sigfd = pstate;
} else {
/* someone beat us to it */
list_destroy(&pstate->sigfd_list);
- kmem_free(pstate, sizeof (sigfd_proc_state_t));
+ kmem_free(pstate, sizeof (*pstate));
+ pstate = p->p_sigfd;
}
- mutex_exit(&p->p_lock);
}
- mutex_enter(&p->p_lock);
- if (((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb ==
- NULL) {
- ((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb =
- signalfd_pollwake_cb;
- }
- signalfd_wake_list_add(state);
+ pw = signalfd_wake_list_add(pstate, state);
+ *phpp = &pw->spw_pollhd;
mutex_exit(&p->p_lock);
}
@@ -558,11 +626,12 @@ _NOTE(ARGSUSED(4))
static int
signalfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
{
- signalfd_state_t *state;
+ signalfd_state_t *state, **sstate;
minor_t minor = getminor(dev);
sigset_t mask;
- state = ddi_get_soft_state(signalfd_softstate, minor);
+ sstate = ddi_get_soft_state(signalfd_softstate, minor);
+ state = *sstate;
switch (cmd) {
case SIGNALFDIOC_MASK:
@@ -587,33 +656,40 @@ _NOTE(ARGSUSED(1))
static int
signalfd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
- signalfd_state_t *state, **sp;
+ signalfd_state_t *state, **sstate;
+ sigfd_poll_waiter_t *pw = NULL;
minor_t minor = getminor(dev);
proc_t *p = curproc;
- state = ddi_get_soft_state(signalfd_softstate, minor);
-
- if (state->sfd_pollhd.ph_list != NULL) {
- pollwakeup(&state->sfd_pollhd, POLLERR);
- pollhead_clean(&state->sfd_pollhd);
- }
+ sstate = ddi_get_soft_state(signalfd_softstate, minor);
+ state = *sstate;
- /* Make sure our state is removed from our proc's pollwake list. */
+ /* Make sure state is removed from this proc's pollwake list. */
mutex_enter(&p->p_lock);
- signalfd_wake_list_rm(p, state);
- mutex_exit(&p->p_lock);
+ if (p->p_sigfd != NULL) {
+ sigfd_proc_state_t *pstate = p->p_sigfd;
- mutex_enter(&signalfd_lock);
+ pw = signalfd_wake_list_rm(pstate, state);
+ if (list_is_empty(&pstate->sigfd_list)) {
+ signalfd_wake_list_cleanup(p);
+ }
+ }
+ mutex_exit(&p->p_lock);
- /* Remove our state from our global list. */
- for (sp = &signalfd_state; *sp != state; sp = &((*sp)->sfd_next))
- VERIFY(*sp != NULL);
+ if (pw != NULL) {
+ pollwakeup(&pw->spw_pollhd, POLLERR);
+ pollhead_clean(&pw->spw_pollhd);
+ kmem_free(pw, sizeof (*pw));
+ }
- *sp = (*sp)->sfd_next;
+ mutex_enter(&signalfd_lock);
+ *sstate = NULL;
ddi_soft_state_free(signalfd_softstate, minor);
id_free(signalfd_minor, minor);
+ signalfd_state_release(state, B_TRUE);
+
mutex_exit(&signalfd_lock);
return (0);
@@ -635,7 +711,7 @@ signalfd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
}
if (ddi_soft_state_init(&signalfd_softstate,
- sizeof (signalfd_state_t), 0) != 0) {
+ sizeof (signalfd_state_t *), 0) != 0) {
cmn_err(CE_WARN, "signalfd failed to create soft state");
id_space_destroy(signalfd_minor);
mutex_exit(&signalfd_lock);
@@ -656,6 +732,12 @@ signalfd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
sigfd_exit_helper = signalfd_exit_helper;
+ list_create(&signalfd_state, sizeof (signalfd_state_t),
+ offsetof(signalfd_state_t, sfd_list));
+
+ signalfd_wakeq = taskq_create("signalfd_wake", 1, minclsyspri,
+ 0, INT_MAX, TASKQ_PREPOPULATE);
+
mutex_exit(&signalfd_lock);
return (DDI_SUCCESS);
@@ -673,10 +755,25 @@ signalfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
return (DDI_FAILURE);
}
- /* list should be empty */
- VERIFY(signalfd_state == NULL);
-
mutex_enter(&signalfd_lock);
+
+ if (!list_is_empty(&signalfd_state)) {
+ /*
+ * There are dangling poll waiters holding signalfd_state_t
+ * entries on the global list. Detach is not possible until
+ * they purge themselves.
+ */
+ mutex_exit(&signalfd_lock);
+ return (DDI_FAILURE);
+ }
+ list_destroy(&signalfd_state);
+
+ /*
+ * With no remaining entries in the signalfd_state list, the wake taskq
+ * should be empty with no possibility for new entries.
+ */
+ taskq_destroy(signalfd_wakeq);
+
id_space_destroy(signalfd_minor);
ddi_remove_minor_node(signalfd_devi, NULL);
diff --git a/usr/src/uts/common/io/stream.c b/usr/src/uts/common/io/stream.c
index e9af19ca18..994ca8baa8 100644
--- a/usr/src/uts/common/io/stream.c
+++ b/usr/src/uts/common/io/stream.c
@@ -1451,6 +1451,16 @@ copyb(mblk_t *bp)
ndp = nbp->b_datap;
/*
+ * Copy the various checksum information that came in
+ * originally.
+ */
+ ndp->db_cksumstart = dp->db_cksumstart;
+ ndp->db_cksumend = dp->db_cksumend;
+ ndp->db_cksumstuff = dp->db_cksumstuff;
+ bcopy(dp->db_struioun.data, ndp->db_struioun.data,
+ sizeof (dp->db_struioun.data));
+
+ /*
* Well, here is a potential issue. If we are trying to
* trace a flow, and we copy the message, we might lose
* information about where this message might have been.
diff --git a/usr/src/uts/common/io/udmf/dm9601reg.h b/usr/src/uts/common/io/udmf/dm9601reg.h
new file mode 100644
index 0000000000..a36f2b0fc8
--- /dev/null
+++ b/usr/src/uts/common/io/udmf/dm9601reg.h
@@ -0,0 +1,348 @@
+/*
+ * %W% %E%
+ * Macro definitions for Davicom DM9601 USB to fast ethernet controler
+ * based on Davicom DM9601E data sheet
+ * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com)
+ */
+
+#ifndef __DM9601_H__
+#define __DM9601_H__
+
+/*
+ * offset of registers
+ */
+#define NCR 0x00U /* network control register */
+#define NSR 0x01U /* network status register */
+#define TCR 0x02U /* tx control register */
+#define TSR1 0x03U /* tx status register 1 */
+#define TSR2 0x04U /* tx status register 2 */
+#define RCR 0x05U /* rx control register */
+#define RSR 0x06U /* rx status register */
+#define ROCR 0x07U /* rx overflow counter register */
+#define BPTR 0x08U /* back pressure threshold regster */
+#define FCTR 0x09U /* flow control threshold regster */
+#define FCR 0x0aU /* flow control threshold regster */
+#define EPCR 0x0bU /* eeprom & phy control register */
+#define EPAR 0x0cU /* eeprom & phy address register */
+#define EPDR 0x0dU /* eeprom & phy data register (2byte) */
+#define WCR 0x0fU /* wake up control register */
+#define PAR 0x10U /* physical address register (6byte) */
+#define MAR 0x16U /* multicast address register (8byte) */
+#define GPCR 0x1eU /* general purpose control register */
+#define GPR 0x1fU /* general purpose register */
+#define VID 0x28U /* vendor ID (2byte) */
+#define PID 0x2aU /* product ID (2byte) */
+#define CHIPR 0x2cU /* chip revision */
+#define USBDA 0xf0U /* usb device address register */
+#define RXC 0xf1U /* received packet counter register */
+#define TUSC 0xf2U /* tx packet counter/usb status register */
+#define USBC 0xf4U /* usb control register */
+
+/*
+ * register definitions
+ */
+/* network control register */
+#define NCR_EXT_PHY 0x80U /* 1: select external phy */
+#define NCR_WAKEEN 0x40U /* 1: wake up event enable */
+#define NCR_FCOL 0x10U /* force collision mode for test */
+#define NCR_FDX 0x08U /* 1: full duplex mode (for external phy) */
+#define NCR_LBK 0x06U
+#define NCR_LBK_SHIFT 1
+#define NCR_LBK_NORMAL (0U << NCR_LBK_SHIFT)
+#define NCR_LBK_MAC (1U << NCR_LBK_SHIFT)
+#define NCR_LBK_PHY_D (2U << NCR_LBK_SHIFT)
+#define NCR_LBK_PHY_A (3U << NCR_LBK_SHIFT)
+#define NCR_RST 0x01U /* 1: reset, auto clear */
+
+#define NCR_BITS \
+ "\020" \
+ "\010EXT_PHY" \
+ "\007WAKEEN" \
+ "\005FCOL" \
+ "\004FDX" \
+ "\001RST"
+
+/* network status register */
+#define NSR_SPEED 0x80U /* 1:10M 0:100M */
+#define NSR_LINKST 0x40U /* 1:ok 0:fail */
+#define NSR_WAKEST 0x20U /* 1:enabled */
+#define NSR_TXFULL 0x10U /* 1:tx fifo full */
+#define NSR_TX2END 0x08U /* tx packet2 complete status */
+#define NSR_TX1END 0x04U /* tx packet1 complete status */
+#define NSR_RXOV 0x02U /* rx fifo overflow */
+#define NSR_RXRDY 0x01U /* rx packet ready */
+
+#define NSR_BITS \
+ "\020" \
+ "\010SPEED_10" \
+ "\007LINKST_UP" \
+ "\006WAKEST" \
+ "\005TXFULL" \
+ "\004TX2END" \
+ "\003TX1END" \
+ "\002RXOV" \
+ "\001RXRDY"
+
+/* tx control register */
+#define TCR_TJDIS 0x40U /* tx jitter control */
+#define TCR_EXCEDM 0x20U /* excessive collision mode */
+#define TCR_PAD_DIS2 0x10U /* PAD appends disable for pkt2 */
+#define TCR_CRC_DIS2 0x08U /* CRC appends disable for pkt2 */
+#define TCR_PAD_DIS1 0x04U /* PAD appends disable for pkt1 */
+#define TCR_CRC_DIS1 0x02U /* CRC appends disable for pkt1 */
+
+#define TCR_BITS \
+ "\020" \
+ "\007TJDIS" \
+ "\006EXCEDM" \
+ "\005PAD_DIS2" \
+ "\004CRC_DIS2" \
+ "\003PAD_DIS1" \
+ "\002CRC_DIS1"
+
+/* tx status register (ro) */
+#define TSR_TJTO 0x80U /* tx jabber time out */
+#define TSR_LC 0x40U /* loss of carrier */
+#define TSR_NC 0x20U /* no carrier */
+#define TSR_LATEC 0x10U /* late collision */
+#define TSR_COL 0x08U /* late collision */
+#define TSR_EL 0x04U /* excessive collision */
+
+#define TSR_BITS \
+ "\020" \
+ "\010TJTO" \
+ "\007LC" \
+ "\006NC" \
+ "\005LATEC" \
+ "\004COL" \
+ "\003EL"
+
+/* rx control register */
+#define RCR_WTDIS 0x40U /* watch dog timer disable */
+#define RCR_DIS_LONG 0x20U /* discard longer packets than 1522 */
+#define RCR_DIS_CRC 0x10U /* discard crc error packets */
+#define RCR_ALL 0x08U /* pass all multicast */
+#define RCR_RUNT 0x04U /* pass runt packets */
+#define RCR_PRMSC 0x02U /* promiscuous mode */
+#define RCR_RXEN 0x01U /* rx enable */
+
+#define RCR_BITS \
+ "\020" \
+ "\007WTDIS" \
+ "\006DIS_LONG" \
+ "\005DIS_CRC" \
+ "\004ALL" \
+ "\003RUNT" \
+ "\002PRMSC" \
+ "\001RXEN"
+
+/* rx status register */
+#define RSR_RF 0x80U /* runt frame */
+#define RSR_MF 0x40U /* multicast frame */
+#define RSR_LCS 0x20U /* late collision seen */
+#define RSR_RWTO 0x10U /* receive watchdog timeout */
+#define RSR_PLE 0x08U /* physical layer error */
+#define RSR_AE 0x04U /* alignment error */
+#define RSR_CE 0x02U /* crc error */
+#define RSR_FOE 0x01U /* fifo overflow error */
+
+#define RSR_BITS \
+ "\020" \
+ "\010RF" \
+ "\007MF" \
+ "\006LCS" \
+ "\005RWTO" \
+ "\004PLE" \
+ "\003AE" \
+ "\002CE" \
+ "\001FOE"
+
+/* receive overflow counter register */
+#define ROCR_RXFU 0x80U /* receive overflow counter overflow */
+#define ROCR_ROC 0x7fU /* receive overflow counter */
+
+#define ROCR_BITS \
+ "\020" \
+ "\010RXFU"
+
+/* back pressure threshold register */
+#define BPTR_BPHW 0xf0U /* high water overflow threshold */
+#define BPTR_BPHW_SHIFT 4
+#define BPTR_BPHW_UNIT 1024U
+#define BPTR_BPHW_DEFAULT (3 << BPTR_BPHW_SHIFT) /* 3k */
+#define BPTR_JPT 0x0fU /* jam pattern time */
+#define BPTR_JPT_SHIFT 0
+#define BPTR_JPT_5us (0U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_10us (1U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_15us (2U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_25us (3U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_50us (4U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_100us (5U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_150us (6U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_200us (7U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_250us (8U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_300us (9U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_350us (10U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_400us (11U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_450us (12U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_500us (13U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_550us (14U << BPTR_JPT_SHIFT)
+#define BPTR_JPT_600us (15U << BPTR_JPT_SHIFT)
+
+/* flow control threshold register */
+#define FCTR_HWOT 0xf0U /* rx fifo high water overflow threshold */
+#define FCTR_HWOT_SHIFT 4
+#define FCTR_HWOT_UNIT 1024U
+#define FCTR_LWOT 0x0fU /* rx fifo low water overflow threshold */
+#define FCTR_LWOT_SHIFT 0
+#define FCTR_LWOT_UNIT 1024U
+
+/* rx/tx flow control register */
+#define FCR_TXPO 0x80U /* tx pause packet */
+#define FCR_TXPF 0x40U /* tx pause packet */
+#define FCR_TXPEN 0x20U /* tx pause packet */
+#define FCR_BKPA 0x10U /* back pressure mode */
+#define FCR_BKPM 0x08U /* back pressure mode */
+#define FCR_BKPS 0x04U /* rx pause packet current status (r/c) */
+#define FCR_RXPCS 0x02U /* rx pause packet current status (ro) */
+#define FCR_FLCE 0x01U /* flow control enbale */
+
+#define FCR_BITS \
+ "\020" \
+ "\000TXPO" \
+ "\000TXPF" \
+ "\000TXPEN" \
+ "\000BKPA" \
+ "\000BKPM" \
+ "\000BKPS" \
+ "\000RXPCS" \
+ "\000FLCE"
+
+/* EEPROM & PHY control register (0x0b) */
+#define EPCR_REEP 0x20U /* reload eeprom */
+#define EPCR_WEP 0x10U /* write eeprom enable */
+#define EPCR_EPOS 0x08U /* select device, 0:eeprom, 1:phy */
+#define EPCR_ERPRR 0x04U /* read command */
+#define EPCR_ERPRW 0x02U /* write command */
+#define EPCR_ERRE 0x01U /* eeprom/phy access in progress (ro) */
+
+#define EPCR_BITS \
+ "\020" \
+ "\005REEP" \
+ "\004WEP" \
+ "\003EPOS" \
+ "\002ERPRR" \
+ "\001ERPRW" \
+ "\000ERRE"
+
+/* EEPROM & PHY access register (0x0c) */
+#define EPAR_PHYADR 0xc0U /* phy address, internal phy(1) or external */
+#define EPAR_PHYADR_SHIFT 6
+#define EPAR_EROA 0x3fU /* eeprom word addr or phy register addr */
+#define EPAR_EROA_SHIFT 0
+
+/* EEPROM & PHY data register (0x0d(low)-0x0e(hi)) */
+
+/* wake up control register (0x0f) */
+#define WCR_LINKEN 0x20U /* enable link status event */
+#define WCR_SAMPLEEN 0x10U /* enable sample frame event */
+#define WCR_MAGICEN 0x08U /* enable magic pkt event */
+#define WCR_LINKST 0x04U /* link status change occur ro */
+#define WCR_SAMPLEST 0x02U /* sample frame rx occur ro */
+#define WCR_MAGICST 0x01U /* magic pkt rx occur ro */
+
+#define WCR_BITS \
+ "\020" \
+ "\000LINKEN" \
+ "\000SAMPLEEN" \
+ "\000MAGICEN" \
+ "\000LINKST" \
+ "\000SAMPLEST" \
+ "\000MAGICST"
+
+/* physical address register (0x10-0x15) */
+/* multicast address register (0x16-0x1c) */
+/* general purpose control register (0x1e) */
+#define GPCR_GEPCTRL 0x7f
+#define GPCR_OUT(n) (1U << (n))
+
+#define GPCR_BITS \
+ "\020" \
+ "\006OUT5" \
+ "\005OUT4" \
+ "\004OUT3" \
+ "\003OUT2" \
+ "\002OUT1" \
+ "\001OUT0"
+
+/* general purpose register (0x1f) */
+#define GPR_GEPIO5 0x20U
+#define GPR_GEPIO4 0x10U
+#define GPR_GEPIO3 0x08U
+#define GPR_GEPIO2 0x04U
+#define GPR_GEPIO1 0x02U
+#define GPR_GEPIO0 0x01U
+
+#define GPR_BITS \
+ "\020" \
+ "\006GEPIO5" \
+ "\005GEPIO4" \
+ "\004GEPIO3" \
+ "\003GEPIO2" \
+ "\002GEPIO1" \
+ "\001GEPIO0"
+
+/* vendor id register (0x28-0x29) */
+/* product id register (0x2a-0x2b) */
+/* chip revision register (0x2c) */
+
+/* usb device address register (0xf0) */
+#define USBDA_USBFA 0x3fU /* usb device address */
+#define USBDA_USBFA_SHIFT 0
+
+/* receive packet counter register (0xf1) */
+
+/* transmitpacket counter/usb status register (0xf2) */
+#define TUSR_RXFAULT 0x80U /* indicate rx has unexpected condition */
+#define TUSR_SUSFLAG 0x40U /* indicate device has suspended condition */
+#define TUSR_EP1RDY 0x20U /* ready for read from ep1 pipe */
+#define TUSR_SRAM 0x18U /* sram size 0:32K, 1:48K, 2:16K, 3:64K */
+#define TUSR_SRAM_SHIFT 3
+#define TUSR_SRAM_32K (0U << TUSR_SRAM_SHIFT)
+#define TUSR_SRAM_48K (1U << TUSR_SRAM_SHIFT)
+#define TUSR_SRAM_16K (2U << TUSR_SRAM_SHIFT)
+#define TUSR_SRAM_64K (3U << TUSR_SRAM_SHIFT)
+#define TUSR_TXC2 0x04U /* two or more packets in tx buffer */
+#define TUSR_TXC1 0x02U /* one packet in tx buffer */
+#define TUSR_TXC0 0x01U /* no packet in tx buffer */
+
+#define TUSR_BITS \
+ "\020" \
+ "\010RXFAULT" \
+ "\007SUSFLAG" \
+ "\006EP1RDY" \
+ "\003TXC2" \
+ "\002TXC1" \
+ "\001TXC0"
+
+/* usb control register (0xf4) */
+#define USBC_EP3ACK 0x20U /* ep3 will alway return 8byte data if NAK=0*/
+#define USBC_EP3NACK 0x10U /* ep3 will alway return NAK */
+#define USBC_MEMTST 0x01U
+
+/* bulk message format */
+#define TX_HEADER_SIZE 2
+#define RX_HEADER_SIZE 3
+
+/* interrupt msg format */
+struct intr_msg {
+ uint8_t im_nsr;
+ uint8_t im_tsr1;
+ uint8_t im_tsr2;
+ uint8_t im_rsr;
+ uint8_t im_rocr;
+ uint8_t im_rxc;
+ uint8_t im_txc;
+ uint8_t im_gpr;
+};
+#endif /* __DM9601_H__ */
diff --git a/usr/src/uts/common/io/udmf/udmf_usbgem.c b/usr/src/uts/common/io/udmf/udmf_usbgem.c
new file mode 100644
index 0000000000..0637de054b
--- /dev/null
+++ b/usr/src/uts/common/io/udmf/udmf_usbgem.c
@@ -0,0 +1,1036 @@
+/*
+ * udmfE_usbgem.c : Davicom DM9601E USB to Fast Ethernet Driver for Solaris
+ *
+ * Copyright (c) 2009-2012 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#pragma ident "%W% %E%"
+
+/*
+ * Changelog:
+ */
+
+/*
+ * TODO
+ */
+/* ======================================================= */
+
+/*
+ * Solaris system header files and macros
+ */
+
+/* minimum kernel headers for drivers */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+
+/* ethernet stuff */
+#include <sys/ethernet.h>
+
+/* interface card depend stuff */
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/usb/usba.h>
+#include "usbgem.h"
+
+/* hardware stuff */
+#include "usbgem_mii.h"
+#include "dm9601reg.h"
+
+char ident[] = "dm9601 usbnic driver v" VERSION;
+
+/*
+ * Useful macros
+ */
+#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label
+#define LE16P(p) ((((uint8_t *)(p))[1] << 8) | ((uint8_t *)(p))[0])
+
+/*
+ * Debugging
+ */
+#ifdef DEBUG_LEVEL
+static int udmf_debug = DEBUG_LEVEL;
+#define DPRINTF(n, args) if (udmf_debug > (n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+/*
+ * Our configration for dm9601
+ */
+/* timeouts */
+#define ONESEC (drv_usectohz(1*1000000))
+
+/*
+ * Local device definitions
+ */
+struct udmf_dev {
+ /*
+ * Misc HW information
+ */
+ uint8_t rcr;
+ uint8_t last_nsr;
+ uint8_t mac_addr[ETHERADDRL];
+};
+
+/*
+ * private functions
+ */
+
+/* mii operations */
+static uint16_t udmf_mii_read(struct usbgem_dev *, uint_t, int *errp);
+static void udmf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp);
+
+/* nic operations */
+static int udmf_reset_chip(struct usbgem_dev *);
+static int udmf_init_chip(struct usbgem_dev *);
+static int udmf_start_chip(struct usbgem_dev *);
+static int udmf_stop_chip(struct usbgem_dev *);
+static int udmf_set_media(struct usbgem_dev *);
+static int udmf_set_rx_filter(struct usbgem_dev *);
+static int udmf_get_stats(struct usbgem_dev *);
+static void udmf_interrupt(struct usbgem_dev *, mblk_t *);
+
+/* packet operations */
+static mblk_t *udmf_tx_make_packet(struct usbgem_dev *, mblk_t *);
+static mblk_t *udmf_rx_make_packet(struct usbgem_dev *, mblk_t *);
+
+/* =============================================================== */
+/*
+ * I/O functions
+ */
+/* =============================================================== */
+#define OUT(dp, ix, len, buf, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ 1, \
+ /* wValue */ 0, \
+ /* wIndex */ (ix), \
+ /* wLength */ (len), \
+ /* value */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+#define OUTB(dp, ix, val, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ 3, \
+ /* wValue */ (val), \
+ /* wIndex */ (ix), \
+ /* wLength */ 0, \
+ /* value */ NULL, \
+ /* size */ 0)) != USB_SUCCESS) goto label
+
+#define IN(dp, ix, len, buf, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ 0, \
+ /* wValue */ 0, \
+ /* wIndex */ (ix), \
+ /* wLength */ (len), \
+ /* valuep */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+/* =============================================================== */
+/*
+ * Hardware manupilation
+ */
+/* =============================================================== */
+static void
+udmf_enable_phy(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+
+ /* de-assert reset signal to phy */
+ OUTB(dp, GPCR, GPCR_OUT(0), &err, usberr);
+ OUTB(dp, GPR, 0, &err, usberr);
+usberr:
+ ;
+}
+
+static int
+udmf_reset_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ OUTB(dp, NCR, NCR_LBK_NORMAL | NCR_RST, &err, usberr);
+ drv_usecwait(100);
+usberr:
+ return (err);
+}
+
+/*
+ * Setup dm9601
+ */
+static int
+udmf_init_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint32_t val;
+ int err = USB_SUCCESS;
+ uint16_t reg;
+ uint8_t buf[2];
+ struct udmf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ OUTB(dp, NCR, NCR_LBK_NORMAL, &err, usberr);
+
+ /* tx control regiser: enable padding and crc generation */
+ OUTB(dp, TCR, 0, &err, usberr);
+
+ /* rx control register: will be set later by udmf_set_rx_filer() */
+ lp->rcr = RCR_RUNT;
+
+ /* back pressure threshold: */
+ OUTB(dp, BPTR, (2 << BPTR_BPHW_SHIFT) | BPTR_JPT_200us,
+ &err, usberr);
+
+ /* flow control threshold: same as default */
+ OUTB(dp, FCTR, (3 << FCTR_HWOT_SHIFT) | (8 << FCTR_LWOT_SHIFT),
+ &err, usberr);
+
+ /* usb control register */
+ OUTB(dp, USBC, USBC_EP3ACK | 0x06, &err, usberr);
+
+ /* flow control: will be set later by udmf_set_media() */
+
+ /* wake up control register: */
+ OUTB(dp, WCR, 0, &err, usberr);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+udmf_start_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ struct udmf_dev *lp = dp->private;
+
+ /* enable Rx */
+ lp->rcr |= RCR_RXEN;
+ OUTB(dp, RCR, lp->rcr, &err, usberr);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+udmf_stop_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ struct udmf_dev *lp = dp->private;
+
+ /* disable rx */
+ lp->rcr &= ~RCR_RXEN;
+ OUTB(dp, RCR, lp->rcr, &err, usberr);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+udmf_get_stats(struct usbgem_dev *dp)
+{
+ /* EMPTY */
+ return (USB_SUCCESS);
+}
+
+static uint_t
+udmf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr)
+{
+ return (usbgem_ether_crc_le(addr) & 0x3f);
+}
+
+static int
+udmf_set_rx_filter(struct usbgem_dev *dp)
+{
+ int i;
+ uint8_t rcr;
+ uint8_t mode;
+ uint8_t mhash[8];
+ uint8_t *mac;
+ uint_t h;
+ int err = USB_SUCCESS;
+ struct udmf_dev *lp = dp->private;
+ static uint8_t invalid_mac[ETHERADDRL] = {0, 0, 0, 0, 0, 0};
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x",
+ dp->name, __func__, dp->rxmode));
+
+ if (lp->rcr & RCR_RXEN) {
+ /* set promiscuous mode before changing rx filter mode */
+ OUTB(dp, RCR, lp->rcr | RCR_PRMSC, &err, usberr);
+ }
+
+ lp->rcr &= ~(RCR_ALL | RCR_PRMSC);
+ mode = 0;
+ bzero(mhash, sizeof (mhash));
+ mac = dp->cur_addr.ether_addr_octet;
+
+ if ((dp->rxmode & RXMODE_ENABLE) == 0) {
+ mac = invalid_mac;
+ } else if (dp->rxmode & RXMODE_PROMISC) {
+ /* promiscious mode implies all multicast and all physical */
+ mode |= RCR_PRMSC;
+ } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 32) {
+ /* accept all multicast packets */
+ mode |= RCR_ALL;
+ } else if (dp->mc_count > 0) {
+ /*
+ * make hash table to select interresting
+ * multicast address only.
+ */
+ for (i = 0; i < dp->mc_count; i++) {
+ /* hash table is 64 = 2^6 bit width */
+ h = dp->mc_list[i].hash;
+ mhash[h / 8] |= 1 << (h % 8);
+ }
+ }
+
+ /* set node address */
+ if (bcmp(mac, lp->mac_addr, ETHERADDRL) != 0) {
+ OUT(dp, PAR, ETHERADDRL, dp->cur_addr.ether_addr_octet,
+ &err, usberr);
+ bcopy(mac, lp->mac_addr, ETHERADDRL);
+ }
+
+ /* set multicast hash table */
+ OUT(dp, MAR, sizeof (mhash), &mhash[0], &err, usberr);
+
+ /* update rcr */
+ lp->rcr |= mode;
+ OUTB(dp, RCR, lp->rcr, &err, usberr);
+
+#if DEBUG_LEVEL > 1
+ /* verify rcr */
+ IN(dp, RCR, 1, &rcr, &err, usberr);
+ cmn_err(CE_CONT, "!%s: %s: rcr:%b returned",
+ dp->name, __func__, rcr, RCR_BITS);
+#endif
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+static int
+udmf_set_media(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ uint8_t fcr;
+ struct udmf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* setup flow control */
+ fcr = 0;
+ if (dp->full_duplex) {
+ /* select flow control */
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_RX_PAUSE:
+ fcr |= FCR_FLCE;
+ break;
+
+ case FLOW_CONTROL_TX_PAUSE:
+ fcr |= FCR_TXPEN;
+ break;
+
+ case FLOW_CONTROL_SYMMETRIC:
+ fcr |= FCR_FLCE | FCR_TXPEN;
+ break;
+ }
+ }
+
+ /* update flow control register */
+ OUTB(dp, FCR, fcr, &err, usberr);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ err, err == USB_SUCCESS ? "success" : "error"));
+ return (err);
+}
+
+/*
+ * send/receive packet check
+ */
+static mblk_t *
+udmf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ int n;
+ size_t pkt_size;
+ mblk_t *new;
+ mblk_t *tp;
+ uint8_t *bp;
+ uint8_t *last_pos;
+ uint_t align_mask;
+
+ pkt_size = msgdsize(mp);
+ align_mask = 63;
+
+ /*
+ * re-allocate the mp
+ */
+
+ /* minimum ethernet packet size of ETHERMIN */
+ pkt_size = max(pkt_size, ETHERMIN);
+
+#if 0 /* CONFIG_ADD_TX_DELIMITOR_ALWAYS */
+ pkt_size += TX_HEADER_SIZE;
+#endif
+ if (((pkt_size + TX_HEADER_SIZE) & align_mask) == 0) {
+ /* padding is required in usb communication */
+ pkt_size += TX_HEADER_SIZE;
+ }
+
+ if ((new = allocb(TX_HEADER_SIZE + pkt_size, 0)) == NULL) {
+ return (NULL);
+ }
+ new->b_wptr = new->b_rptr + TX_HEADER_SIZE + pkt_size;
+
+ /* add a header */
+ bp = new->b_rptr;
+ bp[0] = (uint8_t)pkt_size;
+ bp[1] = (uint8_t)(pkt_size >> 8);
+ bp += TX_HEADER_SIZE;
+
+ /* copy contents of the buffer */
+ for (tp = mp; tp; tp = tp->b_cont) {
+ n = tp->b_wptr - tp->b_rptr;
+ bcopy(tp->b_rptr, bp, n);
+ bp += n;
+ }
+
+ /* clear the rest including the next zero length header */
+ last_pos = new->b_wptr;
+ while (bp < last_pos) {
+ *bp++ = 0;
+ }
+
+ return (new);
+}
+
+static void
+udmf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i += 8, bp += 8) {
+ cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]);
+ }
+}
+
+static mblk_t *
+udmf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ int len;
+ uint8_t rx_stat;
+
+ len = mp->b_wptr - mp->b_rptr;
+
+ if (len <= RX_HEADER_SIZE) {
+ /*
+ * the usb bulk-in frame doesn't include a valid
+ * ethernet packet.
+ */
+ return (NULL);
+ }
+
+ /* remove rx header */
+ rx_stat = mp->b_rptr[0];
+ if (rx_stat & (RSR_RF | RSR_LCS | RSR_RWTO |
+ RSR_PLE | RSR_AE | RSR_CE | RSR_FOE)) {
+ if (rx_stat & RSR_RF) {
+ dp->stats.runt++;
+ }
+ if (rx_stat & RSR_LCS) {
+ /* late collision */
+ dp->stats.rcv_internal_err++;
+ }
+ if (rx_stat & RSR_RWTO) {
+ /* rx timeout */
+ dp->stats.rcv_internal_err++;
+ }
+ if (rx_stat & RSR_PLE) {
+ /* physical layer error */
+ dp->stats.rcv_internal_err++;
+ }
+ if (rx_stat & RSR_AE) {
+ /* alignment error */
+ dp->stats.frame++;
+ }
+ if (rx_stat & RSR_CE) {
+ /* crc error */
+ dp->stats.crc++;
+ }
+ if (rx_stat & RSR_FOE) {
+ /* fifo overflow error */
+ dp->stats.overflow++;
+ }
+ dp->stats.errrcv++;
+ }
+ len = LE16P(&mp->b_rptr[1]);
+ if (len >= ETHERFCSL) {
+ len -= ETHERFCSL;
+ }
+ mp->b_rptr += RX_HEADER_SIZE;
+ mp->b_wptr = mp->b_rptr + len;
+
+ return (mp);
+}
+
+/*
+ * MII Interfaces
+ */
+static uint16_t
+udmf_ep_read(struct usbgem_dev *dp, uint_t which, uint_t addr, int *errp)
+{
+ int i;
+ uint8_t epcr;
+ uint16_t val;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d",
+ dp->name, __func__, addr));
+
+ OUTB(dp, EPAR, addr, errp, usberr);
+ OUTB(dp, EPCR, which | EPCR_ERPRR, errp, usberr);
+
+ for (i = 0; i < 100; i++) {
+ IN(dp, EPCR, sizeof (epcr), &epcr, errp, usberr);
+ if ((epcr & EPCR_ERRE) == 0) {
+ /* done */
+ IN(dp, EPDR, sizeof (val), &val, errp, usberr);
+ val = LE_16(val);
+ goto done;
+ }
+ drv_usecwait(10);
+ }
+ /* timeout */
+ cmn_err(CE_WARN, "!%s: %s: timeout", dp->name, __func__);
+ val = 0;
+done:
+ OUTB(dp, EPCR, 0, errp, usberr);
+ return (val);
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ *errp, *errp == USB_SUCCESS ? "success" : "error"));
+ return (0);
+}
+
+static void
+udmf_ep_write(struct usbgem_dev *dp, uint_t which, uint_t addr,
+ uint16_t val, int *errp)
+{
+ int i;
+ uint8_t epcr;
+
+ DPRINTF(5, (CE_CONT, "!%s: %s called", dp->name, __func__));
+
+ val = LE_16(val);
+ OUT(dp, EPDR, sizeof (val), &val, errp, usberr);
+
+ OUTB(dp, EPAR, addr, errp, usberr);
+
+ OUTB(dp, EPCR, which | EPCR_WEP | EPCR_ERPRW, errp, usberr);
+
+ for (i = 0; i < 100; i++) {
+ IN(dp, EPCR, 1, &epcr, errp, usberr);
+ if ((epcr & EPCR_ERRE) == 0) {
+ /* done */
+ goto done;
+ }
+ drv_usecwait(10);
+ }
+ /* timeout */
+ cmn_err(CE_WARN, "!%s: %s: timeout", dp->name, __func__);
+done:
+ OUTB(dp, EPCR, 0, errp, usberr);
+ return;
+
+usberr:
+ DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)",
+ dp->name, __func__,
+ *errp, *errp == USB_SUCCESS ? "success" : "error"));
+}
+
+static uint16_t
+udmf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp)
+{
+ uint16_t val;
+
+ val = udmf_ep_read(dp, EPCR_EPOS,
+ (dp->mii_phy_addr << EPAR_PHYADR_SHIFT) | index, errp);
+
+ return (val);
+}
+
+static void
+udmf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp)
+{
+ udmf_ep_write(dp, EPCR_EPOS,
+ (dp->mii_phy_addr << EPAR_PHYADR_SHIFT) | index, val, errp);
+}
+
+static void
+udmf_interrupt(struct usbgem_dev *dp, mblk_t *mp)
+{
+ struct intr_msg *imp;
+ struct udmf_dev *lp = dp->private;
+
+ imp = (struct intr_msg *)&mp->b_rptr[0];
+
+ DPRINTF(4, (CE_CONT,
+ "!%s: %s: size:%d, nsr:%b tsr1:%b tsr2:%b"
+ " rsr:%b rocr:%b rxc:%02x txc:%b gpr:%b",
+ dp->name, __func__, mp->b_wptr - mp->b_rptr,
+ imp->im_nsr, NSR_BITS,
+ imp->im_tsr1, TSR_BITS,
+ imp->im_tsr2, TSR_BITS,
+ imp->im_rsr, RSR_BITS,
+ imp->im_rocr, ROCR_BITS,
+ imp->im_rxc,
+ imp->im_txc, TUSR_BITS,
+ imp->im_gpr, GPR_BITS));
+
+ if ((lp->last_nsr ^ imp->im_nsr) & NSR_LINKST) {
+ usbgem_mii_update_link(dp);
+ }
+
+ lp->last_nsr = imp->im_nsr;
+}
+
+/* ======================================================== */
+/*
+ * OS depend (device driver DKI) routine
+ */
+/* ======================================================== */
+static uint16_t
+udmf_eeprom_read(struct usbgem_dev *dp, uint_t index, int *errp)
+{
+ uint16_t val;
+
+ val = udmf_ep_read(dp, 0, index, errp);
+
+ return (val);
+}
+
+#ifdef DEBUG_LEVEL
+static void
+udmf_eeprom_dump(struct usbgem_dev *dp, int size)
+{
+ int i;
+ int err;
+ uint16_t w0, w1, w2, w3;
+
+ cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name);
+
+ err = USB_SUCCESS;
+
+ for (i = 0; i < size; i += 4) {
+ w0 = udmf_eeprom_read(dp, i + 0, &err);
+ w1 = udmf_eeprom_read(dp, i + 1, &err);
+ w2 = udmf_eeprom_read(dp, i + 2, &err);
+ w3 = udmf_eeprom_read(dp, i + 3, &err);
+ cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x",
+ i, w0, w1, w2, w3);
+ }
+usberr:
+ ;
+}
+#endif
+
+static int
+udmf_attach_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint_t val;
+ uint8_t *m;
+ int err;
+ struct udmf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s enter", dp->name, __func__));
+
+ /*
+ * get mac address from EEPROM
+ */
+ m = dp->dev_addr.ether_addr_octet;
+ for (i = 0; i < ETHERADDRL; i += 2) {
+ val = udmf_eeprom_read(dp, i/2, &err);
+ m[i + 0] = (uint8_t)val;
+ m[i + 1] = (uint8_t)(val >> 8);
+ }
+
+ /* invalidate a private cache for mac addr */
+ bzero(lp->mac_addr, sizeof (lp->mac_addr));
+#ifdef CONFIG_VLAN
+ dp->misc_flag = USBGEM_VLAN;
+#endif
+#if DEBUG_LEVEL > 0
+ udmf_eeprom_dump(dp, /* 0x3f + 1 */ 128);
+#endif
+{
+ static uint8_t bcst[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ DPRINTF(0, (CE_CONT, "!%s: %s: hash of bcast:%x",
+ dp->name, __func__, usbgem_ether_crc_be(bcst)));
+}
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "%s: %s: usb error detected (%d)",
+ dp->name, __func__, err);
+ return (USB_FAILURE);
+}
+
+static int
+udmf_mii_probe(struct usbgem_dev *dp)
+{
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ udmf_enable_phy(dp);
+ return (usbgem_mii_probe_default(dp));
+}
+
+static int
+udmf_mii_init(struct usbgem_dev *dp)
+{
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ udmf_enable_phy(dp);
+ return (USB_SUCCESS);
+}
+
+static int
+udmfattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i;
+ ddi_iblock_cookie_t c;
+ int ret;
+ int revid;
+ int unit;
+ int len;
+ const char *drv_name;
+ struct usbgem_dev *dp;
+ void *base;
+ struct usbgem_conf *ugcp;
+ struct udmf_dev *lp;
+
+ unit = ddi_get_instance(dip);
+ drv_name = ddi_driver_name(dip);
+
+ DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d",
+ drv_name, unit, __func__, cmd));
+
+ if (cmd == DDI_ATTACH) {
+ /*
+ * construct usbgem configration
+ */
+ ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP);
+
+ /* name */
+ /*
+ * softmac requires that ppa is the instance number
+ * of the device, otherwise it hangs in seaching the device.
+ */
+ sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit);
+ ugcp->usbgc_ppa = unit;
+
+ ugcp->usbgc_ifnum = 0;
+ ugcp->usbgc_alt = 0;
+
+ ugcp->usbgc_tx_list_max = 64;
+
+ ugcp->usbgc_rx_header_len = RX_HEADER_SIZE;
+ ugcp->usbgc_rx_list_max = 64;
+
+ /* time out parameters */
+ ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT;
+ ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL;
+#if 1
+ /* flow control */
+ ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE;
+#else
+ /*
+ * XXX - flow control caused link down frequently under
+ * heavy traffic
+ */
+ ugcp->usbgc_flow_control = FLOW_CONTROL_NONE;
+#endif
+ /* MII timeout parameters */
+ ugcp->usbgc_mii_link_watch_interval =
+ USBGEM_LINK_WATCH_INTERVAL;
+ ugcp->usbgc_mii_an_watch_interval =
+ USBGEM_LINK_WATCH_INTERVAL/5;
+ ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */
+ ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */
+ ugcp->usbgc_mii_an_wait = (25*ONESEC)/10;
+ ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT;
+
+ ugcp->usbgc_mii_an_delay = ONESEC/10;
+ ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA;
+ ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET;
+ ugcp->usbgc_mii_dont_reset = B_FALSE;
+ ugcp->usbgc_mii_hw_link_detection = B_TRUE;
+
+ /* I/O methods */
+
+ /* mac operation */
+ ugcp->usbgc_attach_chip = &udmf_attach_chip;
+ ugcp->usbgc_reset_chip = &udmf_reset_chip;
+ ugcp->usbgc_init_chip = &udmf_init_chip;
+ ugcp->usbgc_start_chip = &udmf_start_chip;
+ ugcp->usbgc_stop_chip = &udmf_stop_chip;
+ ugcp->usbgc_multicast_hash = &udmf_mcast_hash;
+
+ ugcp->usbgc_set_rx_filter = &udmf_set_rx_filter;
+ ugcp->usbgc_set_media = &udmf_set_media;
+ ugcp->usbgc_get_stats = &udmf_get_stats;
+ ugcp->usbgc_interrupt = &udmf_interrupt;
+
+ /* packet operation */
+ ugcp->usbgc_tx_make_packet = &udmf_tx_make_packet;
+ ugcp->usbgc_rx_make_packet = &udmf_rx_make_packet;
+
+ /* mii operations */
+ ugcp->usbgc_mii_probe = &udmf_mii_probe;
+ ugcp->usbgc_mii_init = &udmf_mii_init;
+ ugcp->usbgc_mii_config = &usbgem_mii_config_default;
+ ugcp->usbgc_mii_read = &udmf_mii_read;
+ ugcp->usbgc_mii_write = &udmf_mii_write;
+ ugcp->usbgc_mii_addr_min = 1;
+
+ /* mtu */
+ ugcp->usbgc_min_mtu = ETHERMTU;
+ ugcp->usbgc_max_mtu = ETHERMTU;
+ ugcp->usbgc_default_mtu = ETHERMTU;
+
+ lp = kmem_zalloc(sizeof (struct udmf_dev), KM_SLEEP);
+ lp->last_nsr;
+
+ ddi_set_driver_private(dip, NULL);
+
+ dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct udmf_dev));
+
+ kmem_free(ugcp, sizeof (*ugcp));
+
+ if (dp != NULL) {
+ return (DDI_SUCCESS);
+ }
+
+err_free_mem:
+ kmem_free(lp, sizeof (struct udmf_dev));
+err_close_pipe:
+err:
+ return (DDI_FAILURE);
+ }
+
+ if (cmd == DDI_RESUME) {
+ return (usbgem_resume(dip));
+ }
+
+ return (DDI_FAILURE);
+}
+
+static int
+udmfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int ret;
+
+ if (cmd == DDI_DETACH) {
+ ret = usbgem_do_detach(dip);
+ if (ret != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+ }
+ if (cmd == DDI_SUSPEND) {
+ return (usbgem_suspend(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+/* ======================================================== */
+/*
+ * OS depend (loadable streams driver) routine
+ */
+/* ======================================================== */
+#ifdef USBGEM_CONFIG_GLDv3
+USBGEM_STREAM_OPS(udmf_ops, udmfattach, udmfdetach);
+#else
+static struct module_info udmfminfo = {
+ 0, /* mi_idnum */
+ "udmf", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ ETHERMTU*128, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit udmfrinit = {
+ (int (*)()) NULL, /* qi_putp */
+ usbgem_rsrv, /* qi_srvp */
+ usbgem_open, /* qi_qopen */
+ usbgem_close, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &udmfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit udmfwinit = {
+ usbgem_wput, /* qi_putp */
+ usbgem_wsrv, /* qi_srvp */
+ (int (*)()) NULL, /* qi_qopen */
+ (int (*)()) NULL, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &udmfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab udmf_info = {
+ &udmfrinit, /* st_rdinit */
+ &udmfwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_udmf_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &udmf_info, /* cb_stream */
+ D_NEW|D_MP /* cb_flag */
+};
+
+static struct dev_ops udmf_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ usbgem_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ udmfattach, /* devo_attach */
+ udmfdetach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_udmf_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ usbgem_power, /* devo_power */
+#if DEVO_REV >= 4
+ usbgem_quiesce, /* devo_quiesce */
+#endif
+};
+#endif
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ ident,
+ &udmf_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+/* ======================================================== */
+/*
+ * _init : done
+ */
+/* ======================================================== */
+int
+_init(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!udmf: _init: called"));
+
+ status = usbgem_mod_init(&udmf_ops, "udmf");
+ if (status != DDI_SUCCESS) {
+ return (status);
+ }
+ status = mod_install(&modlinkage);
+ if (status != DDI_SUCCESS) {
+ usbgem_mod_fini(&udmf_ops);
+ }
+ return (status);
+}
+
+/*
+ * _fini : done
+ */
+int
+_fini(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!udmf: _fini: called"));
+ status = mod_remove(&modlinkage);
+ if (status == DDI_SUCCESS) {
+ usbgem_mod_fini(&udmf_ops);
+ }
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/io/upf/adm8511reg.h b/usr/src/uts/common/io/upf/adm8511reg.h
new file mode 100644
index 0000000000..68a2207bb5
--- /dev/null
+++ b/usr/src/uts/common/io/upf/adm8511reg.h
@@ -0,0 +1,205 @@
+/*
+ * @(#)adm8511reg.h 1.1 09/06/20
+ * Register dehinitsions of ADMtek ADM8511 Fast Ethernet to USB controller.
+ * Codeded by Masayuki Murayama(KHF04453@nifty.ne.jp)
+ * This file is public domain.
+ */
+
+#define EC0 0x00 /* B */
+#define EC1 0x01 /* B */
+#define EC2 0x02 /* B */
+#define MA 0x08 /* 8byte array */
+#define EID 0x10 /* B */
+#define PAUSETIMER 0x18 /* B pause timer */
+#define RPNBFC 0x1a /* B */
+#define ORFBFC 0x1b /* B */
+#define EP1C 0x1c /* B */
+#define RXFC 0x1d /* B */
+#define BIST 0x1e /* B */
+#define EEOFFSET 0x20 /* B */
+#define EEDATA 0x21 /* W */
+#define EECTRL 0x23 /* B */
+#define PHYA 0x25 /* B */
+#define PHYD 0x26 /* W */
+#define PHYAC 0x28 /* B */
+#define USBSTAT 0x2a /* B */
+#define ETHTXSTAT 0x2b /* W */
+#define ETHRXSTAT 0x2d /* B */
+#define LOSTCNT 0x2e /* W */
+#define WF0MASK 0x30 /* 16byte array */
+#define WF0OFFSET 0x40 /* W */
+#define WF0CRC 0x41 /* W */
+#define WF1MASK 0x48 /* 16byte array */
+#define WF1OFFSET 0x58 /* W */
+#define WF1CRC 0x59 /* W */
+#define WF2MASK 0x60 /* 16byte array */
+#define WF2OFFSET 0x70 /* W */
+#define WF2CRC 0x71 /* W */
+#define WCTRL 0x78 /* B */
+#define WSTAT 0x7a /* B */
+#define IPHYC 0x7b /* B */
+#define GPIO54 0x7c /* B */
+#define GPIO10 0x7e /* B */
+#define GPIO32 0x7f /* B */
+#define TEST 0x80 /* B */
+#define TM 0x81 /* B */
+#define RPN 0x82 /* B */
+
+/* Ethernet control register 0: offset 0 */
+#define EC0_TXE 0x80U
+#define EC0_RXE 0x40U
+#define EC0_RXFCE 0x20U
+#define EC0_WOE 0x10U
+#define EC0_RXSA 0x08U
+#define EC0_SBO 0x04U
+#define EC0_RXMA 0x02U
+#define EC0_RXCS 0x01U
+
+#define EC0_BITS \
+ "\020" \
+ "\010TXE" \
+ "\007RXE" \
+ "\006RXFCE" \
+ "\005WOE" \
+ "\004RXSA" \
+ "\003SBO" \
+ "\002RXMA" \
+ "\001RXCS"
+
+/* Ethernet control register 1: offset 1 */
+#define EC1_FD 0x20U
+#define EC1_100M 0x10U /* 0:10Mbps 1:100Mbps */
+#define EC1_RM 0x08U /* reset mac */
+
+#define EC1_BITS \
+ "\020" \
+ "\006FD" \
+ "\005100M" \
+ "\004RM"
+
+/* Ethernet control register 2: offset 2 */
+#define EC2_MEPL 0x80U /* 8515: MTU 0:1528, 1:1638 */
+#define EC2_RPNC 0x40U
+#define EC2_LEEPRS 0x20U
+#define EC2_EEPRW 0x10U
+#define EC2_LB 0x08U
+#define EC2_PROM 0x04U
+#define EC2_RXBP 0x02U
+#define EC2_EP3RC 0x01U
+
+#define EC2_BITS \
+ "\020" \
+ "\010MEPS" \
+ "\007RPNC" \
+ "\006LEEPRS" \
+ "\005EEPRW" \
+ "\004LB" \
+ "\003PROM" \
+ "\002RXBP" \
+ "\001EP3RC"
+
+/* Recieve Packet number based Flow Control register: offset 0x1a */
+#define RPNBFC_PN 0x7eU /* */
+#define RPNBFC_PN_SHIFT 1
+#define RPNBFC_FCP 0x01U /* enable rx flow control */
+
+/* Occupied Recieve FIFO based Flow Control register: offset 0x1b */
+#define ORFBFC_RXS 0x7eU /* */
+#define ORFBFC_RXS_SHIFT 1
+#define ORFBFC_RXS_UNIT 1024U
+#define ORFBFC_FCRXS 0x01U /* enable rx flow control */
+
+/* EP1 control register: offset 0x1c */
+#define EP1C_EP1S0E 0x80U /* send 0 enable */
+#define EP1C_ITMA 0x60U /* internal test mode A */
+#define EP1C_ITMB 0x1fU /* internal test mode B */
+
+#define EP1C_BITS \
+ "\020" \
+ "\010EP1S0E"
+
+/* Rx FIFO Control register: offset 0x1d */
+#define RXFC_EXT_SRAM 0x02 /* enable external 32k sram */
+#define RXFC_RX32PKT 0x01 /* max 32 packet */
+
+/* EEPROM offset register: offset 0x20 */
+#define EEOFFSET_MASK 0x3f /* eeprom offset address in word */
+
+/* EEPROM access control register: offset 0x23 */
+#define EECTRL_DONE 0x04
+#define EECTRL_RD 0x02
+#define EECTRL_WR 0x01
+
+#define EECTRL_BITS \
+ "\020" \
+ "\003DONE" \
+ "\002RD" \
+ "\001WR"
+
+/* PHY control register: offset 28 */
+#define PHYAC_DO 0x80U /* Done */
+#define PHYAC_RDPHY 0x40U /* read phy */
+#define PHYAC_WRPHY 0x20U /* write phy */
+#define PHYAC_PHYRA 0x1fU /* PHY register address */
+
+#define PHYCTRL_BITS \
+ "\020" \
+ "\010DO" \
+ "\007RDPHY" \
+ "\006WRPHY"
+
+/* Internal PHY control register: offset 7b */
+#define IPHYC_EPHY 0x02
+#define IPHYC_PHYR 0x01
+
+#define IPHYC_BITS \
+ "\020" \
+ "\002EPHY" \
+ "\001PHYR"
+
+/* GPIO45 register: offset 7c */
+#define GPIO54_5OE 0x20
+#define GPIO54_5O 0x10
+#define GPIO54_5I 0x08
+#define GPIO54_4OE 0x04
+#define GPIO54_4O 0x02
+#define GPIO54_4I 0x01
+
+/* GPIO01 register: offset 7e */
+#define GPIO10_1OE 0x20
+#define GPIO10_1O 0x10
+#define GPIO10_1I 0x08
+#define GPIO10_0OE 0x04
+#define GPIO10_0O 0x02
+#define GPIO10_0I 0x01
+
+/* GPIO23 register: offset 7f */
+#define GPIO32_3OE 0x20
+#define GPIO32_3O 0x10
+#define GPIO32_3I 0x08
+#define GPIO32_2OE 0x04
+#define GPIO32_2O 0x02
+#define GPIO32_2I 0x01
+
+/* rx status at the end of received packets */
+/* byte 0 and 1 is packet length in little endian */
+/* byte 2 is receive status */
+#define RSR_DRIBBLE 0x10
+#define RSR_CRC 0x08
+#define RSR_RUNT 0x04
+#define RSR_LONG 0x02
+#define RSR_MULTI 0x01
+
+#define RSR_ERRORS \
+ (RSR_DRIBBLE | RSR_CRC | RSR_RUNT | RSR_LONG | RSR_MULTI)
+
+#define RSR_BITS \
+ "\020" \
+ "\005DRIBBLE" \
+ "\004CRC" \
+ "\003RUNT" \
+ "\002LONG" \
+ "\001MULTI"
+/* byte 3 is reserved */
+
+/* TEST register: offset 80 */
diff --git a/usr/src/uts/common/io/upf/upf_usbgem.c b/usr/src/uts/common/io/upf/upf_usbgem.c
new file mode 100644
index 0000000000..5614803158
--- /dev/null
+++ b/usr/src/uts/common/io/upf/upf_usbgem.c
@@ -0,0 +1,1213 @@
+/*
+ * upf_usbgem.c : ADMtek an986/adm8511/adm8513/adm8515 USB to
+ * Fast Ethernet Driver for Solaris
+ */
+
+/*
+ * Copyright (c) 2004-2011 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#pragma ident "%W% %E%"
+
+/*
+ * Changelog:
+ */
+
+/*
+ * TODO
+ */
+/* ======================================================= */
+
+/*
+ * Solaris system header files and macros
+ */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+
+/* ethernet stuff */
+#include <sys/ethernet.h>
+
+/* interface card depend stuff */
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/usb/usba.h>
+#include "usbgem.h"
+
+/* hardware stuff */
+#include "usbgem_mii.h"
+#include "adm8511reg.h"
+
+char ident[] = "pegasus usbnic driver v" VERSION;
+
+/*
+ * Useful macros
+ */
+#define CHECK_AND_JUMP(val, label) \
+ if ((val) != USB_SUCCESS) { goto label; }
+
+/*
+ * Debugging
+ */
+#ifdef DEBUG_LEVEL
+static int upf_debug = DEBUG_LEVEL;
+#define DPRINTF(n, args) if (upf_debug > (n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+/*
+ * Our configration for ADMtek Pegasus/PegasusII
+ */
+/* timeouts */
+#define ONESEC (drv_usectohz(1*1000000))
+
+/*
+ * Local device definitions
+ */
+struct upf_dev {
+ /*
+ * Misc HW information
+ */
+ uint8_t ec[3];
+ uint8_t mac_addr[ETHERADDRL];
+ int chip_type;
+#define CHIP_AN986 1 /* avoid 0 */
+#define CHIP_ADM8511 2 /* including adm8515 */
+#define CHIP_ADM8513 3
+ boolean_t phy_init_done;
+ uint8_t last_link_state;
+
+ uint16_t vid; /* vendor id */
+ uint16_t pid; /* product id */
+};
+
+/*
+ * private functions
+ */
+
+/* mii operations */
+static uint16_t upf_mii_read(struct usbgem_dev *, uint_t, int *errp);
+static void upf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp);
+
+/* nic operations */
+static int upf_attach_chip(struct usbgem_dev *);
+static int upf_reset_chip(struct usbgem_dev *);
+static int upf_init_chip(struct usbgem_dev *);
+static int upf_start_chip(struct usbgem_dev *);
+static int upf_stop_chip(struct usbgem_dev *);
+static int upf_set_media(struct usbgem_dev *);
+static int upf_set_rx_filter(struct usbgem_dev *);
+static int upf_get_stats(struct usbgem_dev *);
+
+/* packet operations */
+static mblk_t *upf_tx_make_packet(struct usbgem_dev *, mblk_t *);
+static mblk_t *upf_rx_make_packet(struct usbgem_dev *, mblk_t *);
+
+/* interrupt handler */
+static void upf_interrupt(struct usbgem_dev *, mblk_t *);
+
+/* =============================================================== */
+/*
+ * I/O functions
+ */
+/* =============================================================== */
+#define UPF_REQ_GET_REGISTER 0xf0
+#define UPF_REQ_SET_REGISTER 0xf1
+#define OUTB(dp, p, v, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_SET_REGISTER, \
+ /* wValue */ (v), \
+ /* wIndex */ (p), \
+ /* wLength */ 1, \
+ /* buf */ NULL, \
+ /* size */ 0)) != USB_SUCCESS) goto label;
+
+#define OUTW(dp, p, v, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_SET_REGISTER, \
+ /* wValue */ 0, \
+ /* wIndex */ (p), \
+ /* wLength */ 2, \
+ /* value */ (v))) != USB_SUCCESS) goto label
+
+#define OUTS(dp, p, buf, len, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_SET_REGISTER, \
+ /* wValue */ 0, \
+ /* wIndex */ (p), \
+ /* wLength */ (len), \
+ /* buf */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+#define INB(dp, p, vp, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_GET_REGISTER, \
+ /* wValue */ 0, \
+ /* wIndex */ (p), \
+ /* wLength */ 1, \
+ /* valuep */ (vp))) != USB_SUCCESS) goto label
+
+#define INW(dp, p, vp, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_GET_REGISTER, \
+ /* wValue */ 0, \
+ /* wIndex */ (p), \
+ /* wLength */ 2, \
+ /* valuep */ (vp))) != USB_SUCCESS) goto label
+
+#define INS(dp, p, buf, len, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ UPF_REQ_GET_REGISTER, \
+ /* wValue */ 0, \
+ /* wIndex */ (p), \
+ /* wLength */ (len), \
+ /* buf */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+/* =============================================================== */
+/*
+ * Hardware manupilation
+ */
+/* =============================================================== */
+static int
+upf_reset_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint8_t val;
+ int err;
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ bzero(lp->mac_addr, sizeof (lp->mac_addr));
+
+ lp->ec[1] = 0;
+ OUTB(dp, EC1, EC1_RM, &err, usberr);
+
+ for (i = 0; i < 1000; i++) {
+ INB(dp, EC1, &val, &err, usberr);
+ if ((val & EC1_RM) == 0) {
+ lp->ec[1] = val;
+ return (USB_SUCCESS);
+ }
+ drv_usecwait(10);
+ }
+
+ /* time out */
+ cmn_err(CE_WARN, "!%s: failed to reset: timeout", dp->name);
+ return (USB_FAILURE);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+/*
+ * Setup an986/adm8511/adm8513/adm8515
+ */
+static int
+upf_init_chip(struct usbgem_dev *dp)
+{
+ uint64_t zero64 = 0;
+ int err = USB_SUCCESS;
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* ethernet control register 0 */
+ lp->ec[0] |= EC0_RXSA | EC0_RXCS;
+ OUTB(dp, EC0, lp->ec[0], &err, usberr);
+
+ /* ethernet control reg1: will be set later in set_rx_filter() */
+
+ /* ethernet control register 2: will be set later in set_rx_filter() */
+ INB(dp, EC2, &lp->ec[2], &err, usberr);
+ lp->ec[2] |= EC2_RXBP | EC2_EP3RC;
+#ifdef CONFIG_VLAN
+ if (dp->misc_flag & USBGEM_VLAN) {
+ lp->ec[2] |= EC2_MEPL;
+ }
+#endif
+ OUTB(dp, EC2, lp->ec[2], &err, usberr);
+
+ /* Multicast address hash: clear */
+ OUTS(dp, MA, &zero64, 8, &err, usberr);
+
+ /* Ethernet ID : will be set later in upf_set_rx_filter() */
+
+ /* PAUSE timer */
+ OUTB(dp, PAUSETIMER, 0x1f, &err, usberr);
+
+ /* receive packet number based pause control:set in upf_set_media() */
+
+ /* occupied receive FIFO based pause control:set in upf_set_media() */
+
+ /* EP1 control: default */
+
+ /* Rx FIFO control */
+ if (lp->chip_type != CHIP_AN986) {
+ /* use 24K internal sram, 16pkts in fifo */
+ OUTB(dp, RXFC, 0, &err, usberr);
+ }
+
+ /* BIST contror: do nothing */
+ err = upf_set_media(dp);
+ CHECK_AND_JUMP(err, usberr);
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (success)", dp->name, __func__));
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr(%d) detected",
+ dp->name, __func__, err);
+ return (err);
+}
+
+static int
+upf_start_chip(struct usbgem_dev *dp)
+{
+ int err = USB_SUCCESS;
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* enable RX and TX */
+ lp->ec[0] |= EC0_TXE | EC0_RXE;
+ OUTB(dp, EC0, lp->ec[0], &err, usberr);
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "!%s: %s: usberr(%d) detected",
+ dp->name, __func__, err);
+ return (err);
+}
+
+static int
+upf_stop_chip(struct usbgem_dev *dp)
+{
+ int err;
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* disable RX and TX */
+ lp->ec[0] &= ~(EC0_TXE | EC0_RXE);
+ OUTB(dp, EC0, lp->ec[0], &err, usberr);
+
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "!%s: %s: usberr(%d) detected",
+ dp->name, __func__, err);
+ return (err);
+}
+
+static int
+upf_get_stats(struct usbgem_dev *dp)
+{
+ /* do nothing */
+ return (USB_SUCCESS);
+}
+
+static uint_t
+upf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr)
+{
+ /* hash table is 64 = 2^6 bit width */
+ return (usbgem_ether_crc_le(addr) & 0x3f);
+}
+
+static int
+upf_set_rx_filter(struct usbgem_dev *dp)
+{
+ int i;
+ int err;
+#ifdef DEBUG_LEVEL
+ uint8_t reg0;
+ uint8_t reg1;
+ uint8_t reg2;
+#endif
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called, rxmode:%b",
+ dp->name, __func__, dp->rxmode, RXMODE_BITS));
+
+ /* reset rx mode */
+ lp->ec[0] &= ~EC0_RXMA;
+ lp->ec[2] &= ~EC2_PROM;
+
+ if (dp->rxmode & RXMODE_PROMISC) {
+ /* promiscious mode implies all multicast and all physical */
+ lp->ec[0] |= EC0_RXMA;
+ lp->ec[2] |= EC2_PROM;
+ } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 0) {
+ /* XXX - multicast hash table didin't work */
+ /* accept all multicast packets */
+ lp->ec[0] |= EC0_RXMA;
+ }
+
+ if (bcmp(dp->cur_addr.ether_addr_octet,
+ lp->mac_addr, ETHERADDRL) != 0) {
+
+ /* need to update mac address */
+ bcopy(dp->cur_addr.ether_addr_octet,
+ lp->mac_addr, ETHERADDRL);
+ OUTS(dp, EID,
+ lp->mac_addr, ETHERADDRL, &err, usberr);
+ }
+
+ /* update rx mode */
+ OUTS(dp, EC0, lp->ec, 3, &err, usberr);
+
+#if DEBUG_LEVEL > 0
+ INB(dp, EC0, &reg0, &err, usberr);
+ INB(dp, EC1, &reg1, &err, usberr);
+ INB(dp, EC2, &reg2, &err, usberr);
+
+ cmn_err(CE_CONT, "!%s: %s: returned, ec:%b %b %b",
+ dp->name, __func__,
+ reg0, EC0_BITS, reg1, EC1_BITS, reg2, EC2_BITS);
+#endif
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (err);
+}
+
+static int
+upf_set_media(struct usbgem_dev *dp)
+{
+ int err;
+ struct upf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ lp->ec[1] &= ~(EC1_FD | EC1_100M);
+
+ /* select duplex */
+ if (dp->full_duplex) {
+ lp->ec[1] |= EC1_FD;
+ }
+
+ /* select speed */
+ if (dp->speed == USBGEM_SPD_100) {
+ lp->ec[1] |= EC1_100M;
+ }
+
+ /* rx flow control */
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_SYMMETRIC:
+ case FLOW_CONTROL_RX_PAUSE:
+ lp->ec[0] |= EC0_RXFCE;
+ break;
+
+ default:
+ lp->ec[0] &= ~EC0_RXFCE;
+ break;
+ }
+
+ /* tx flow control */
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_SYMMETRIC:
+ case FLOW_CONTROL_TX_PAUSE:
+ if (lp->chip_type != CHIP_AN986) {
+ /* pegasus II has internal 24k fifo */
+ OUTB(dp, ORFBFC,
+ (12 << ORFBFC_RXS_SHIFT) | ORFBFC_FCRXS,
+ &err, usberr);
+
+ /* 16 packts can be stored in rx fifo */
+ OUTB(dp, RPNBFC_PN,
+ (8 << RPNBFC_PN_SHIFT) | RPNBFC_FCP,
+ &err, usberr);
+ } else {
+ /* an986 has external 32k fifo */
+ OUTB(dp, ORFBFC,
+ (16 << ORFBFC_RXS_SHIFT) | ORFBFC_FCRXS,
+ &err, usberr);
+
+ /* AN986 fails to link up when RPNBFC is enabled */
+ OUTB(dp, RPNBFC, 0, &err, usberr);
+ }
+ break;
+
+ default:
+ OUTB(dp, ORFBFC, 0, &err, usberr);
+ OUTB(dp, RPNBFC, 0, &err, usberr);
+ break;
+ }
+
+ /* update ether control registers */
+ OUTS(dp, EC0, lp->ec, 2, &err, usberr);
+ DPRINTF(0, (CE_CONT, "!%s: %s: returned, ec0:%b, ec1:%b",
+ dp->name, __func__, lp->ec[0], EC0_BITS, lp->ec[1], EC1_BITS));
+
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "%s: %s: failed to write ec1", dp->name, __func__);
+ return (err);
+}
+
+/*
+ * send/receive packet check
+ */
+static mblk_t *
+upf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ size_t len;
+ mblk_t *new;
+ mblk_t *tp;
+ uint8_t *bp;
+ uint8_t *last_pos;
+ int msglen;
+
+ DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ len = msgdsize(mp);
+ if (len < ETHERMIN) {
+ len = ETHERMIN;
+ }
+
+ /* allocate msg block */
+ msglen = len + sizeof (uint16_t);
+
+ /* avoid usb controller bug */
+ if ((msglen & 0x3f) == 0) {
+ /* add a header for additional 0-length usb message */
+ msglen += sizeof (uint16_t);
+ }
+
+ if ((new = allocb(msglen, 0)) == NULL) {
+ return (NULL);
+ }
+
+ /* copy contents of the buffer */
+ new->b_wptr = new->b_rptr + msglen;
+ bp = new->b_rptr;
+
+ /* the nic requires a two byte header of the packet size */
+ bp[0] = (uint8_t)len;
+ bp[1] = (uint8_t)(len >> 8);
+ bp += sizeof (uint16_t);
+
+ /* copy the payload */
+ for (tp = mp; tp; tp = tp->b_cont) {
+ len = tp->b_wptr - tp->b_rptr;
+ if (len > 0) {
+ bcopy(tp->b_rptr, bp, len);
+ bp += len;
+ }
+ }
+
+ /* clear ethernet pads and additional usb header if we have */
+ last_pos = new->b_wptr;
+ while (bp < last_pos) {
+ *bp++ = 0;
+ }
+
+ return (new);
+}
+
+static void
+upf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i += 8, bp += 8) {
+ cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]);
+ }
+}
+
+static mblk_t *
+upf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ uint8_t *p;
+ uint16_t rxhd;
+ uint_t len;
+ uint8_t rsr;
+ struct upf_dev *lp = dp->private;
+
+ ASSERT(mp != NULL);
+
+#ifdef DEBUG_LEVEL
+ len = msgdsize(mp);
+ DPRINTF(2, (CE_CONT, "!%s: time:%d %s: cont:%p",
+ dp->name, ddi_get_lbolt(), __func__, len, mp->b_cont));
+
+ if (upf_debug > 3) {
+ upf_dump_packet(dp, mp->b_rptr, max(6, len));
+ }
+#endif
+ /* get the length of Rx packet */
+ p = mp->b_wptr - 4;
+ rsr = p[3];
+ if (lp->chip_type == CHIP_ADM8513) {
+ /* As Rx packets from ADM8513 have two byte header, remove it */
+ p = mp->b_rptr;
+ len = ((p[1] << 8) | p[0]) & 0x0fff;
+ mp->b_rptr += 2;
+ } else {
+ len = (((p[1] << 8) | p[0]) & 0x0fff) - ETHERFCSL - 4;
+ }
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: rsr:%b len:%d",
+ dp->name, __func__, rsr, RSR_BITS, len));
+
+ /* check if error happen */
+ if (rsr & RSR_ERRORS) {
+ DPRINTF(0, (CE_CONT, "!%s: rsr:%b", dp->name, rsr, RSR_BITS));
+ if (rsr & (RSR_CRC | RSR_DRIBBLE)) {
+ dp->stats.frame++;
+ }
+ if (rsr & RSR_LONG) {
+ dp->stats.frame_too_long++;
+ }
+ if (rsr & RSR_RUNT) {
+ dp->stats.runt++;
+ }
+
+ dp->stats.errrcv++;
+ return (NULL);
+ }
+#ifndef CONFIG_VLAN
+ /* check packet size */
+ if (len > ETHERMAX) {
+ /* too long */
+ dp->stats.frame_too_long++;
+ dp->stats.errrcv++;
+ return (NULL);
+ } else if (len < ETHERMIN) {
+ dp->stats.runt++;
+ dp->stats.errrcv++;
+ return (NULL);
+ }
+#endif
+ /* remove tailing crc and rx status fields */
+ mp->b_wptr = mp->b_rptr + len;
+ ASSERT(mp->b_next == NULL);
+ return (mp);
+}
+
+/*
+ * Device depend interrupt handler
+ */
+static void
+upf_interrupt(struct usbgem_dev *dp, mblk_t *mp)
+{
+ uint8_t *bp;
+ struct upf_dev *lp = dp->private;
+
+ bp = mp->b_rptr;
+
+ DPRINTF(2, (CE_CONT,
+ "!%s: %s: size:%d, %02x %02x %02x %02x %02x %02x %02x %02x",
+ dp->name, __func__, mp->b_wptr - mp->b_rptr,
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]));
+
+ if ((lp->last_link_state ^ bp[5]) & 1) {
+ DPRINTF(1, (CE_CONT, "!%s:%s link status changed:",
+ dp->name, __func__));
+ usbgem_mii_update_link(dp);
+ }
+
+ lp->last_link_state = bp[5] & 1;
+}
+
+/*
+ * MII Interfaces
+ */
+static uint16_t
+upf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp)
+{
+ uint8_t phyctrl;
+ uint16_t val;
+ int i;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d",
+ dp->name, __func__, index));
+ ASSERT(index >= 0 && index < 32);
+
+ *errp = USB_SUCCESS;
+
+ /* set PHYADDR */
+ OUTB(dp, PHYA, dp->mii_phy_addr, errp, usberr);
+
+ /* Initiate MII read transaction */
+ OUTB(dp, PHYAC, index | PHYAC_RDPHY, errp, usberr);
+
+ for (i = 0; i < 100; i++) {
+ INB(dp, PHYAC, &phyctrl, errp, usberr);
+ if (phyctrl & PHYAC_DO) {
+ /* done */
+ INW(dp, PHYD, &val, errp, usberr);
+ DPRINTF(4, (CE_CONT, "!%s: %s: return %04x",
+ dp->name, __func__, val));
+ return (val);
+ }
+ drv_usecwait(10);
+ }
+ /* timeout */
+ cmn_err(CE_WARN, "!%s: %s: timeout detected", dp->name, __func__);
+ *errp = USB_FAILURE;
+ return (0);
+
+usberr:
+ cmn_err(CE_CONT,
+ "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp);
+ return (0);
+}
+
+static void
+upf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp)
+{
+ int i;
+ uint8_t phyctrl;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s called index:%d val:0x%04x",
+ dp->name, __func__, index, val));
+ ASSERT(index >= 0 && index < 32);
+
+ *errp = USB_SUCCESS;
+
+ OUTW(dp, PHYD, val, errp, usberr);
+ OUTB(dp, PHYA, dp->mii_phy_addr, errp, usberr);
+ OUTB(dp, PHYAC, index | PHYAC_WRPHY, errp, usberr);
+
+ for (i = 0; i < 100; i++) {
+ INB(dp, PHYAC, &phyctrl, errp, usberr);
+ if (phyctrl & PHYAC_DO) {
+ /* done */
+ return;
+ }
+ drv_usecwait(10);
+ }
+
+ /* time out */
+ cmn_err(CE_WARN, "!%s: %s: timeout detected", dp->name, __func__);
+ *errp = USB_FAILURE;
+ return;
+
+usberr:
+ cmn_err(CE_CONT,
+ "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp);
+}
+
+
+static int
+upf_enable_phy(struct usbgem_dev *dp)
+{
+ uint8_t val;
+ int err;
+ struct upf_dev *lp = dp->private;
+
+ /*
+ * first, try to enable internal phy
+ */
+ INB(dp, IPHYC, &val, &err, usberr);
+ val = (val | IPHYC_EPHY) & ~IPHYC_PHYR;
+ OUTB(dp, IPHYC, val, &err, usberr);
+
+ INB(dp, IPHYC, &val, &err, usberr);
+ DPRINTF(0, (CE_CONT, "!%s: %s: IPHYC: %b",
+ dp->name, __func__, val, IPHYC_BITS));
+ if (val) {
+ /* reset internal phy */
+ OUTB(dp, IPHYC, val | IPHYC_PHYR, &err, usberr);
+ OUTB(dp, IPHYC, val, &err, usberr);
+ delay(drv_usectohz(10000));
+
+ /* identify the chip generation */
+ OUTB(dp, 0x83, 0xa5, &err, usberr);
+ INB(dp, 0x83, &val, &err, usberr);
+ if (val == 0xa5) {
+ lp->chip_type = CHIP_ADM8513;
+ } else {
+ /* adm8511 or adm8515 */
+ lp->chip_type = CHIP_ADM8511;
+ }
+ dp->ugc.usbgc_mii_hw_link_detection = B_TRUE;
+ } else {
+ /*
+ * It should be AN986 which doesn't have an internal PHY.
+ * We need to setup gpio ports in AN986, which are
+ * connected to external PHY control pins.
+ */
+ lp->chip_type = CHIP_AN986;
+
+ /* reset external phy */
+ /* output port#0 L, port#1 L */
+ OUTB(dp, GPIO10, GPIO10_0O | GPIO10_0OE, &err, usberr);
+
+ /* output port#0 H, port#1 L */
+ OUTB(dp, GPIO10,
+ GPIO10_0O | GPIO10_0OE | GPIO10_1OE, &err, usberr);
+
+ /* hw link detection doesn't work correctly */
+ dp->ugc.usbgc_mii_hw_link_detection = B_FALSE;
+ }
+
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+static int
+upf_mii_probe(struct usbgem_dev *dp)
+{
+ int err;
+ uint16_t val;
+ struct upf_dev *lp = dp->private;
+
+ if (!lp->phy_init_done) {
+ upf_enable_phy(dp);
+ lp->phy_init_done = B_TRUE;
+ }
+
+ return (usbgem_mii_probe_default(dp));
+}
+
+static int
+upf_mii_init(struct usbgem_dev *dp)
+{
+ uint16_t val;
+ int err = USB_SUCCESS;
+ struct upf_dev *lp = dp->private;
+
+ if (!lp->phy_init_done) {
+ upf_enable_phy(dp);
+ }
+ lp->phy_init_done = B_FALSE;
+
+ if (lp->chip_type == CHIP_AN986 &&
+ (lp->vid == 0x0db7 /* elecom */ ||
+ lp->vid == 0x066b /* linksys */ ||
+ lp->vid == 0x077b /* linksys */ ||
+ lp->vid == 0x2001 /* dlink */)) {
+ /* special treatment for Linksys products */
+ val = upf_mii_read(dp, 0x1b, &err) | 0x4;
+ upf_mii_write(dp, 0x1b, val, &err);
+ }
+ return (err);
+}
+
+/* ======================================================== */
+/*
+ * OS depend (device driver DKI) routine
+ */
+/* ======================================================== */
+static uint16_t
+upf_read_eeprom(struct usbgem_dev *dp, int index, int *errp)
+{
+ int i;
+ uint8_t eectrl;
+ uint16_t data;
+
+ *errp = USB_SUCCESS;
+
+ OUTB(dp, EECTRL, 0, errp, usberr);
+
+ OUTB(dp, EEOFFSET, index, errp, usberr);
+ OUTB(dp, EECTRL, EECTRL_RD, errp, usberr);
+
+ for (i = 0; i < 100; i++) {
+ INB(dp, EECTRL, &eectrl, errp, usberr);
+ if (eectrl & EECTRL_DONE) {
+ INW(dp, EEDATA, &data, errp, usberr);
+ return (data);
+ }
+ drv_usecwait(10);
+ }
+
+ /* time out */
+ *errp = USB_FAILURE;
+ return (0);
+
+usberr:
+ cmn_err(CE_CONT,
+ "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp);
+ return (0);
+}
+
+static void
+upf_eeprom_dump(struct usbgem_dev *dp, int size)
+{
+ int i;
+ int err;
+
+ cmn_err(CE_CONT, "!%s: %s dump:", dp->name, __func__);
+
+ for (i = 0; i < size; i += 4) {
+ cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x",
+ i*2,
+ upf_read_eeprom(dp, i + 0, &err),
+ upf_read_eeprom(dp, i + 1, &err),
+ upf_read_eeprom(dp, i + 2, &err),
+ upf_read_eeprom(dp, i + 3, &err));
+ }
+}
+
+static int
+upf_attach_chip(struct usbgem_dev *dp)
+{
+ int i;
+ int err;
+ uint16_t val;
+ uint8_t *mac;
+ struct upf_dev *lp = dp->private;
+
+ /*
+ * Read mac address from EEPROM
+ */
+ mac = dp->dev_addr.ether_addr_octet;
+ for (i = 0; i < 3; i++) {
+ val = upf_read_eeprom(dp, i, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ mac[i*2+0] = (uint8_t)val;
+ mac[i*2+1] = (uint8_t)(val >> 8);
+ }
+
+ DPRINTF(0, (CE_CONT,
+ "%s: %s: mac: %02x:%02x:%02x:%02x:%02x:%02x",
+ dp->name, __func__,
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
+
+ dp->misc_flag = 0;
+#ifdef CONFIG_VLAN
+ dp->misc_flag |= USBGEM_VLAN;
+#endif
+#if DEBUG_LEVEL > 3
+ upf_eeprom_dump(dp, 0x80);
+#endif
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "!%s: %s: usb error detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+static int
+upfattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i;
+ ddi_iblock_cookie_t c;
+ int ret;
+ int unit;
+ uint32_t tcr;
+ int len;
+ const char *drv_name;
+ struct usbgem_dev *dp;
+ void *base;
+ struct usbgem_conf *ugcp;
+ struct upf_dev *lp;
+
+ unit = ddi_get_instance(dip);
+ drv_name = ddi_driver_name(dip);
+
+ DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d",
+ drv_name, unit, __func__, cmd));
+
+ if (cmd == DDI_ATTACH) {
+ /*
+ * construct usbgem configration
+ */
+ ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP);
+
+ /* name */
+ sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit);
+ ugcp->usbgc_ppa = unit;
+
+ ugcp->usbgc_ifnum = 0;
+ ugcp->usbgc_alt = 0;
+
+ ugcp->usbgc_tx_list_max = 16;
+
+ ugcp->usbgc_rx_header_len = 4;
+ ugcp->usbgc_rx_list_max = 64;
+
+ /* time out parameters */
+ ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT;
+ ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL;
+
+ /* flow control */
+ ugcp->usbgc_flow_control = FLOW_CONTROL_NONE;
+ ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE;
+
+ /* MII timeout parameters */
+ ugcp->usbgc_mii_link_watch_interval = ONESEC;
+ ugcp->usbgc_mii_an_watch_interval = ONESEC/5;
+ ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */
+ ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */
+ ugcp->usbgc_mii_an_wait = MII_AN_TIMEOUT/2;
+ ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT;
+ ugcp->usbgc_mii_an_delay = ONESEC/10;
+
+ ugcp->usbgc_mii_linkdown_action = MII_ACTION_RESET;
+ ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET;
+ ugcp->usbgc_mii_dont_reset = B_FALSE;
+
+ /* I/O methods */
+
+ /* mac operation */
+ ugcp->usbgc_attach_chip = &upf_attach_chip;
+ ugcp->usbgc_reset_chip = &upf_reset_chip;
+ ugcp->usbgc_init_chip = &upf_init_chip;
+ ugcp->usbgc_start_chip = &upf_start_chip;
+ ugcp->usbgc_stop_chip = &upf_stop_chip;
+ ugcp->usbgc_multicast_hash = &upf_mcast_hash;
+
+ ugcp->usbgc_set_rx_filter = &upf_set_rx_filter;
+ ugcp->usbgc_set_media = &upf_set_media;
+ ugcp->usbgc_get_stats = &upf_get_stats;
+ ugcp->usbgc_interrupt = &upf_interrupt;
+
+ /* packet operation */
+ ugcp->usbgc_tx_make_packet = &upf_tx_make_packet;
+ ugcp->usbgc_rx_make_packet = &upf_rx_make_packet;
+
+ /* mii operations */
+ ugcp->usbgc_mii_probe = &upf_mii_probe;
+ ugcp->usbgc_mii_init = &upf_mii_init;
+ ugcp->usbgc_mii_config = &usbgem_mii_config_default;
+ ugcp->usbgc_mii_read = &upf_mii_read;
+ ugcp->usbgc_mii_write = &upf_mii_write;
+
+ /* mtu */
+ ugcp->usbgc_min_mtu = ETHERMTU;
+ ugcp->usbgc_max_mtu = ETHERMTU;
+ ugcp->usbgc_default_mtu = ETHERMTU;
+
+ lp = kmem_zalloc(sizeof (struct upf_dev), KM_SLEEP);
+
+ lp->vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "usb-vendor-id", -1);
+ lp->pid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "usb-product-id", -1);
+
+ dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct upf_dev));
+
+ kmem_free(ugcp, sizeof (*ugcp));
+
+ if (dp != NULL) {
+ return (DDI_SUCCESS);
+ }
+
+err_free_mem:
+ kmem_free(lp, sizeof (struct upf_dev));
+err_close_pipe:
+err:
+ return (DDI_FAILURE);
+ }
+ if (cmd == DDI_RESUME) {
+ dp = USBGEM_GET_DEV(dip);
+ lp = dp->private;
+ lp->phy_init_done = B_FALSE;
+
+ return (usbgem_resume(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+static int
+upfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int ret;
+
+ if (cmd == DDI_DETACH) {
+ ret = usbgem_do_detach(dip);
+ if (ret != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+ }
+ if (cmd == DDI_SUSPEND) {
+ return (usbgem_suspend(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+/* ======================================================== */
+/*
+ * OS depend (loadable streams driver) routine
+ */
+/* ======================================================== */
+#ifdef USBGEM_CONFIG_GLDv3
+USBGEM_STREAM_OPS(upf_ops, upfattach, upfdetach);
+#else
+static struct module_info upfminfo = {
+ 0, /* mi_idnum */
+ "upf", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ 32*1024, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit upfrinit = {
+ (int (*)()) NULL, /* qi_putp */
+ usbgem_rsrv, /* qi_srvp */
+ usbgem_open, /* qi_qopen */
+ usbgem_close, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &upfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit upfwinit = {
+ usbgem_wput, /* qi_putp */
+ usbgem_wsrv, /* qi_srvp */
+ (int (*)()) NULL, /* qi_qopen */
+ (int (*)()) NULL, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &upfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab upf_info = {
+ &upfrinit, /* st_rdinit */
+ &upfwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_upf_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &upf_info, /* cb_stream */
+ D_MP /* cb_flag */
+};
+
+static struct dev_ops upf_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ usbgem_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ upfattach, /* devo_attach */
+ upfdetach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_upf_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ usbgem_power, /* devo_power */
+#if DEVO_REV >= 4
+ usbgem_quiesce, /* devo_quiesce */
+#endif
+
+};
+#endif
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ ident,
+ &upf_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+/* ======================================================== */
+/*
+ * _init : done
+ */
+/* ======================================================== */
+int
+_init(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!upf: _init: called"));
+
+ status = usbgem_mod_init(&upf_ops, "upf");
+ if (status != DDI_SUCCESS) {
+ return (status);
+ }
+ status = mod_install(&modlinkage);
+ if (status != DDI_SUCCESS) {
+ usbgem_mod_fini(&upf_ops);
+ }
+ return (status);
+}
+
+/*
+ * _fini : done
+ */
+int
+_fini(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!upf: _fini: called"));
+ status = mod_remove(&modlinkage);
+ if (status == DDI_SUCCESS) {
+ usbgem_mod_fini(&upf_ops);
+ }
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/io/urf/rtl8150reg.h b/usr/src/uts/common/io/urf/rtl8150reg.h
new file mode 100644
index 0000000000..7cba53356e
--- /dev/null
+++ b/usr/src/uts/common/io/urf/rtl8150reg.h
@@ -0,0 +1,218 @@
+/*
+ * @(#)rtl8150reg.h 1.1 04/09/16
+ * Macro definitions for Realtek 8150 USB to fast ethernet controller
+ * based on Realtek RTL8150 data sheet
+ * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com)
+ */
+
+/*
+ * Register offset
+ */
+#define IDR 0x0120 /* Base of ID registers */
+#define MAR 0x0126 /* Base of multicast registers */
+#define CR 0x012e /* Command register */
+#define TCR 0x012f /* Transmit Configuration register */
+#define RCR 0x0130 /* Receive Configuration register */
+#define TSR 0x0132 /* Transmit Status register */
+#define RSR 0x0133 /* Receive Status register */
+#define CON0 0x0135 /* Configuration register 0 */
+#define CON1 0x0136 /* Configuration register 1 */
+#define MSR 0x0137 /* Media Status register */
+#define PHYADD 0x0138 /* PHY address register */
+#define PHYDAT 0x0139 /* PHY data register */
+#define PHYCNT 0x013b /* PHY control register */
+#define GPPC 0x013d /* General purpose pin control */
+#define WAKECNT 0x013e /* Wake up event control */
+#define BMCR 0x0140 /* Basic Mode Control register */
+#define BMSR 0x0142 /* Basic Mode Status register */
+#define ANAR 0x0144 /* Auto Negotiation Advertisement register */
+#define ANLP 0x0146 /* Auto Negotiation Link Partner register */
+#define ANER 0x0148 /* Auto Negotiation Expansion register */
+#define NWAYT 0x014a /* Nway test register */
+#define CSCR 0x014c /* CS configuration register */
+#define CRC0 0x014e /* Power management register for wakeup frame0 */
+#define CRC1 0x0150 /* Power management register for wakeup frame1 */
+#define CRC2 0x0152 /* Power management register for wakeup frame2 */
+#define CRC3 0x0154 /* Power management register for wakeup frame3 */
+#define CRC4 0x0156 /* Power management register for wakeup frame4 */
+#define BYTEMASK0 0x0158 /* Power management wakeup frame0 bytemask */
+#define BYTEMASK1 0x0160 /* Power management wakeup frame1 bytemask */
+#define BYTEMASK2 0x0168 /* Power management wakeup frame2 bytemask */
+#define BYTEMASK3 0x0170 /* Power management wakeup frame3 bytemask */
+#define BYTEMASK4 0x0178 /* Power management wakeup frame4 bytemask */
+#define PHY1 0x0180 /* PHY parameter 1 */
+#define PHY2 0x0184 /* PHY parameter 2 */
+#define TW1 0x0186 /* Twister parameter 1 */
+
+/*
+ * Bit field definitions
+ */
+/* CR : Command register (uint8_t) */
+#define CR_WEPROM 0x20 /* EEPROM write enable */
+#define CR_SOFT_RST 0x10 /* Reset */
+#define CR_RE 0x08 /* Ethernet receive enable */
+#define CR_TE 0x04 /* Ethernet transmit enable */
+#define CR_EP3CLREN 0x02 /* clear performance counter after EP3 */
+#define CR_AUTOLOAD 0x01 /* autoload contents of 93c46 */
+
+#define CR_BITS "\020\006WEPROM\005SOFT_RST\004RE\003TE\002EP3CLREN\001AUTOLOAD"
+
+/* TCR: Transmit Configuration register */
+#define TCR_TXRR 0xc0 /* Tx retry count */
+#define TCR_TXRR_SHIFT 6
+#define TCR_IFG 0x18 /* Interframe Gap */
+#define TCR_IFG_SHIFT 3
+#define TCR_IFG_802_3 (3 << TCR_IFG_SHIFT) /* 802.3 standard */
+#define TCR_NOCRC 0x01 /* Inhibit Appending CRC */
+
+#define TCR_BITS "\020\001NOCRC"
+
+/* Receive Configuration register */
+#define RCR_TAIL 0x0080 /* Rx header forward to host in CRC field */
+#define RCR_AER 0x0040 /* Accept Error packet */
+#define RCR_AR 0x0020 /* Accept runt */
+#define RCR_AM 0x0010 /* Accept multicast */
+#define RCR_AB 0x0008 /* Accept broadcast */
+#define RCR_AD 0x0004 /* Accept physical match */
+#define RCR_AAM 0x0002 /* Accept all Multicast */
+#define RCR_AAP 0x0001 /* Accept all physical */
+
+#define RCR_ACCEPT_MODE \
+ (RCR_AER | RCR_AR | RCR_AM | RCR_AB | RCR_AD | RCR_AAM | RCR_AAP)
+
+#define RCR_BITS \
+ "\020\010TAIL\007AER\006AR\005AM\004AB\003AD\002AAM\001AAP"
+
+/* Transmit Status register */
+
+#define TSR_ECOL 0x20 /* excessive collision indication */
+#define TSR_LCOL 0x10 /* late collision indication */
+#define TSR_LOSS_CRS 0x08 /* lost of carrier indication */
+#define TSR_JBR 0x04 /* jabber time out indication */
+#define TSR_BUF_EMPTY 0x02 /* Tx buffer is empty */
+#define TSR_BUF_FULL 0x01 /* Tx buffer is full */
+
+#define TSR_BITS \
+ "\020" \
+ "\006ECOL" \
+ "\005LCOL" \
+ "\004LOSS_CRS" \
+ "\003JBR" \
+ "\002BUF_EMPTY" \
+ "\001BUF_FULL"
+
+/* Receive status register in Rx packet field */
+#define RSR_WEVENT 0x80 /* Wakeup event indication */
+#define RSR_RX_BUF_FULL 0x40 /* Receive buffer full indication */
+#define RSR_LKCHG 0x20 /* Link change indication */
+#define RSR_RUNT 0x10 /* short packet indication */
+#define RSR_LONG 0x08 /* Long packet indication*/
+#define RSR_CRC 0x04 /* CRC error indication*/
+#define RSR_FAE 0x02 /* Frame alignment error */
+#define RSR_ROK 0x01 /* Receive OK indication */
+
+#define RSR_ERRS (RSR_RUNT | RSR_LONG | RSR_CRC | RSR_FAE)
+#define RSR_BITS \
+ "\020" \
+ "\010WEVENT" \
+ "\007RX_BUF_FULL" \
+ "\006LKCHG" \
+ "\005RUNT" \
+ "\004LONG" \
+ "\003CRC" \
+ "\002FAE" \
+ "\001ROK"
+
+/* Config 0 */
+
+#define CON0_SUSLED 0x80
+#define CON0_PARM_EN 0x40 /* parameter enable */
+#define CON0_LDPS 0x08
+#define CON0_MSEL 0x04 /* media select 1:MII, 0:auto */
+#define CON0_LEDS 0x03 /* LED pattern */
+
+/* Config 1 */
+#define CON0_BWF 0x40 /* Broadcast wakeup function 1:on 0:off */
+#define CON0_MWF 0x20 /* Multicast wakeup function 1:on 0:off */
+#define CON0_UWF 0x10 /* Unicast wakeup function 1:on 0:off */
+#define CON0_LONGWF1 0x02 /* */
+#define CON0_LONGWF0 0x01 /* */
+
+
+/* MSR : Media Status register */
+#define MSR_TXFCE 0x80 /* Tx Flow control enable */
+#define MSR_RXFCE 0x40 /* Rx Flow control enable */
+#define MSR_DUPLEX 0x10 /* full duplex */
+#define MSR_SPEED_100 0x08 /* 100Mbps mode */
+#define MSR_LINK 0x04 /* link status */
+#define MSR_TXPF 0x02 /* 8150 sends pause packet */
+#define MSR_RXPF 0x01 /* 8150 is in backoff state*/
+
+#define MSR_BITS \
+ "\020" \
+ "\010TXFCE" \
+ "\007RXFCE" \
+ "\005DUPLEX" \
+ "\004SPEED_100" \
+ "\003LINK" \
+ "\002TXPF" \
+ "\001RXPF"
+
+/* MII PHY Address */
+#define PHYADD_MASK 0x1f
+
+/* MII PHY Data */
+#define PHYCNT_OWN 0x40 /* 8150 owns:1 not owns:0 */
+#define PHYCNT_RWCR 0x20 /* write:1 read:0 */
+#define PHYCNT_PHYOFF 0x1f
+
+/* BMCR (almost same with MII_CONTROL register) */
+#define BMCR_RESET 0x8000 /* PHY reset */
+#define BMCR_Spd_Set 0x2000 /* 100Mbps */
+#define BMCR_ANE 0x1000 /* auto negotiation enable */
+#define BMCR_RSA 0x0200 /* restart auto negotiation */
+#define BMCR_duplex 0x0100 /* 100Mbps */
+
+/* Basic mode status register */
+/* Auto-negotiation Advertisement register */
+/* Auto-negotiation Link Partner Ability register */
+/* Auto-negotiation Expansion register */
+
+/* Nway test register */
+#define NWAYT_NWLPBK 0x0080
+#define NWAYT_ENNWLE 0x0008
+#define NWAYT_FLAGABD 0x0004
+#define NWAYT_FLAGPDF 0x0002
+#define NWAYT_FLAGLSC 0x0001
+
+/* CS configuration register */
+#define CS_TESTFUN 0x8000 /* */
+#define CS_LD 0x0200 /* */
+#define CS_HEARTBEAT 0x0100 /* */
+#define CS_JBEN 0x0080 /* */
+#define CS_F_LINK100 0x0040 /* */
+#define CS_F_CONNECT 0x0020 /* */
+#define CS_CON_STATUS 0x0008 /* */
+#define CS_CON_STATUS_EN 0x0004 /* */
+#define CS_PASS_SCR 0x0001 /* bypass scramble function */
+
+/*
+ * header format of rx packet
+ */
+#define RXHD_MULT 0x8000 /* multicast packet */
+#define RXHD_PHYS 0x4000 /* physical match packet */
+#define RXHD_RUNT 0x2000 /* too short */
+#define RXHD_VALID 0x1000 /* packet is ok */
+#define RXHD_BYTECNT 0x0fff /* rx byte count */
+
+#define RXHD_BITS \
+ "\020" \
+ "\020MULT" \
+ "\017PHYS" \
+ "\016RUNT" \
+ "\015VALID"
+/*
+ * Offset to EPROM contents
+ */
+#define URF_EEPROM_BASE 0x1200
+#define EPROM_EthernetID 0x0002
diff --git a/usr/src/uts/common/io/urf/urf_usbgem.c b/usr/src/uts/common/io/urf/urf_usbgem.c
new file mode 100644
index 0000000000..f61c8e3502
--- /dev/null
+++ b/usr/src/uts/common/io/urf/urf_usbgem.c
@@ -0,0 +1,1039 @@
+/*
+ * urf_usbgem.c : Realtek RTL8150 USB to Fast Ethernet Driver for Solaris
+ *
+ * Copyright (c) 2003-2012 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#pragma ident "%W% %E%"
+
+/*
+ * Changelog:
+ */
+
+/*
+ * TODO
+ */
+/* ======================================================= */
+
+/*
+ * Solaris system header files and macros
+ */
+
+/* minimum kernel headers for drivers */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+
+/* ethernet stuff */
+#include <sys/ethernet.h>
+
+/* interface card depend stuff */
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/usb/usba.h>
+#include "usbgem.h"
+#include "usbgem_mii.h"
+#include "rtl8150reg.h"
+
+char ident[] = "rtl8150 usbnic driver v" VERSION;
+
+/*
+ * Useful macros
+ */
+#define ROUNDUP2(x, y) (((x)+(y)-1) & ~((y)-1))
+#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label
+
+/*
+ * Debugging
+ */
+#ifdef DEBUG_LEVEL
+static int urf_debug = DEBUG_LEVEL;
+#define DPRINTF(n, args) if (urf_debug > (n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+/*
+ * Our configration for rtl8150
+ */
+/* timeouts */
+#define ONESEC (drv_usectohz(1*1000000))
+
+/*
+ * Local device definitions
+ */
+struct chip_info {
+ int flags;
+ char *name;
+ int type;
+};
+
+#define CHIPTABLESIZE (sizeof (chiptbl_8150) / sizeof (struct chip_info))
+
+struct urf_dev {
+ /*
+ * Misc HW information
+ */
+ struct chip_info *chip;
+ uint8_t cr;
+ uint8_t tsr;
+ uint16_t rcr;
+ uint8_t txok_cnt;
+};
+
+/*
+ * private functions
+ */
+
+/* mii operations */
+static uint16_t urf_mii_read(struct usbgem_dev *, uint_t, int *errp);
+static void urf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp);
+
+/* nic operations */
+static int urf_attach_chip(struct usbgem_dev *);
+static int urf_reset_chip(struct usbgem_dev *);
+static int urf_init_chip(struct usbgem_dev *);
+static int urf_start_chip(struct usbgem_dev *);
+static int urf_stop_chip(struct usbgem_dev *);
+static int urf_set_media(struct usbgem_dev *);
+static int urf_set_rx_filter(struct usbgem_dev *);
+static int urf_get_stats(struct usbgem_dev *);
+
+/* packet operations */
+static mblk_t *urf_tx_make_packet(struct usbgem_dev *, mblk_t *);
+static mblk_t *urf_rx_make_packet(struct usbgem_dev *, mblk_t *);
+
+/* =============================================================== */
+/*
+ * I/O functions
+ */
+/* =============================================================== */
+#define OUTB(dp, p, v, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ USB_REQ_SET_ADDRESS, \
+ /* wValue */ (p), \
+ /* wIndex */ 0, \
+ /* wLength */ 1, \
+ /* value */ (v))) != USB_SUCCESS) goto label
+
+#define OUTW(dp, p, v, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ USB_REQ_SET_ADDRESS, \
+ /* wValue */ (p), \
+ /* wIndex */ 0, \
+ /* wLength */ 2, \
+ /* value */ (v))) != USB_SUCCESS) goto label
+
+#define OUTS(dp, p, buf, len, errp, label) \
+ if ((*(errp) = usbgem_ctrl_out((dp), \
+ /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ USB_REQ_SET_ADDRESS, \
+ /* wValue */ (p), \
+ /* wIndex */ 0, \
+ /* wLength */ (len), \
+ /* value */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+#define IN(dp, p, vp, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in_val((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ USB_REQ_SET_ADDRESS, \
+ /* wValue */ (p), \
+ /* wIndex */ 0, \
+ /* wLength */ sizeof ((*vp)), \
+ /* valuep */ (vp))) != USB_SUCCESS) goto label
+
+#define INS(dp, p, buf, len, errp, label) \
+ if ((*(errp) = usbgem_ctrl_in((dp), \
+ /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
+ | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \
+ /* bRequest */ USB_REQ_SET_ADDRESS, \
+ /* wValue */ (p), \
+ /* wIndex */ 0, \
+ /* wLength */ (len), \
+ /* valuep */ (buf), \
+ /* size */ (len))) != USB_SUCCESS) goto label
+
+/* =============================================================== */
+/*
+ * variables
+ */
+/* =============================================================== */
+static int urf_ppa = 0;
+
+/* =============================================================== */
+/*
+ * Hardware manupilation
+ */
+/* =============================================================== */
+static int
+urf_reset_chip(struct usbgem_dev *dp)
+{
+ int i;
+ int err;
+ uint8_t reg;
+ struct urf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ lp->cr = 0;
+ OUTB(dp, CR, lp->cr | CR_SOFT_RST, &err, usberr);
+
+ for (i = 0; i < 100; i++) {
+ IN(dp, CR, &reg, &err, usberr);
+ if ((reg & CR_SOFT_RST) == 0) {
+ return (USB_SUCCESS);
+ }
+ }
+ /* time out */
+ cmn_err(CE_WARN, "%s: failed to reset: timeout", dp->name);
+ return (USB_FAILURE);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+/*
+ * Setup rtl8150
+ */
+static int
+urf_init_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint32_t val;
+ int err;
+ struct urf_dev *lp = dp->private;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* ID registers: set later by urf_set_rx_filter */
+
+ /* Multicast registers: set later by urf_set_rx_filter */
+
+ /* Command register : Enable Tx and Rx before writing TCR and RCR */
+ lp->cr |= CR_RE | CR_TE;
+ OUTB(dp, CR, lp->cr, &err, usberr);
+
+ /* Transmit configration register : */
+ OUTB(dp, TCR, TCR_IFG_802_3, &err, usberr);
+
+ /* Receive configuration register : disable rx filter */
+ lp->rcr = RCR_TAIL | RCR_AER | RCR_AR;
+ OUTW(dp, RCR, lp->rcr, &err, usberr);
+#ifdef notdef
+ /* Media status register */
+ err = urf_set_media(dp);
+ CHECK_AND_JUMP(err, usberr);
+#endif
+ /* Configuration register 0: no need to change */
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: end (success)", dp->name, __func__));
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+static int
+urf_start_chip(struct usbgem_dev *dp)
+{
+ struct urf_dev *lp = dp->private;
+
+ /* do nothing */
+ return (USB_SUCCESS);
+}
+
+static int
+urf_stop_chip(struct usbgem_dev *dp)
+{
+ return (urf_reset_chip(dp));
+}
+
+static int
+urf_get_stats(struct usbgem_dev *dp)
+{
+ /* do nothing */
+ return (USB_SUCCESS);
+}
+
+static uint_t
+urf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr)
+{
+ return (usbgem_ether_crc_be(addr));
+}
+
+static int
+urf_set_rx_filter(struct usbgem_dev *dp)
+{
+ int i;
+ uint16_t mode;
+ uint8_t mhash[8];
+ int err;
+ int16_t rcr;
+ struct urf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x",
+ dp->name, __func__, dp->rxmode));
+
+ if (lp->rcr & (RCR_AB | RCR_AD | RCR_AAM | RCR_AAP | RCR_AM)) {
+#ifdef notdef
+ /* disable rx filter before changing it. */
+ lp->rcr &= ~(RCR_AB | RCR_AD | RCR_AAM | RCR_AAP | RCR_AM);
+ OUTW(dp, RCR, lp->rcr, &err, usberr);
+#else
+ /* receive all packets while we change rx filter*/
+ OUTW(dp, RCR, lp->rcr | RCR_AAM | RCR_AAP, &err, usberr);
+#endif
+ }
+
+ mode = RCR_AB /* accept broadcast */
+ | RCR_AD; /* accept physical match */
+ bzero(mhash, sizeof (mhash));
+
+ if (dp->rxmode & RXMODE_PROMISC) {
+ /* promiscious mode implies all multicast and all physical */
+ mode |= RCR_AAM | RCR_AAP;
+ } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 64/2) {
+ /* accept all multicast packets */
+ mode |= RCR_AAM;
+ } else if (dp->mc_count > 0) {
+ /*
+ * make hash table to select interresting
+ * multicast address only.
+ */
+ mode |= RCR_AM;
+ for (i = 0; i < dp->mc_count; i++) {
+ uint_t h;
+ /* hash table is 64 = 2^6 bit width */
+ h = dp->mc_list[i].hash >> (32 - 6);
+ mhash[h / 8] |= 1 << (h % 8);
+ }
+ }
+ lp->rcr |= mode;
+
+ /* set mac address */
+ OUTS(dp, IDR, dp->cur_addr.ether_addr_octet, ETHERADDRL, &err, usberr);
+
+ /* set multicast hash table */
+ if (mode & RCR_AM) {
+ /* need to set up multicast hash table */
+ OUTS(dp, MAR, mhash, sizeof (mhash), &err, usberr);
+ }
+
+ OUTW(dp, RCR, lp->rcr, &err, usberr);
+
+#if DEBUG_LEVEL > 2
+ IN(dp, RCR, &rcr, &err, usberr);
+ cmn_err(CE_CONT, "!%s: %s: rcr:%b returned",
+ dp->name, __func__, rcr, RCR_BITS);
+#endif
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+static int
+urf_set_media(struct usbgem_dev *dp)
+{
+ uint8_t new;
+ uint8_t old;
+ int err;
+ struct urf_dev *lp = dp->private;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* select duplex: do nothing */
+
+ /* select speed: do nothing */
+
+ /* flow control */
+ IN(dp, MSR, &old, &err, usberr);
+
+
+ /* setup flow control */
+ new = old & ~(MSR_TXFCE | MSR_RXFCE);
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_SYMMETRIC:
+ new |= MSR_TXFCE | MSR_RXFCE;
+ break;
+
+ case FLOW_CONTROL_TX_PAUSE:
+ new |= MSR_TXFCE;
+ break;
+
+ case FLOW_CONTROL_RX_PAUSE:
+ new |= MSR_RXFCE;
+ break;
+
+ case FLOW_CONTROL_NONE:
+ default:
+ break;
+ }
+
+ if (new != old) {
+ OUTB(dp, MSR, new, &err, usberr);
+ }
+ DPRINTF(2, (CE_CONT, "!%s: %s: returned", dp->name, __func__));
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__);
+ return (USB_FAILURE);
+}
+
+/*
+ * send/receive packet check
+ */
+static mblk_t *
+urf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ size_t len;
+ mblk_t *new;
+ mblk_t *tp;
+ uint8_t *bp;
+ uint8_t *last_pos;
+
+ len = msgdsize(mp);
+
+ if (len < ETHERMIN || mp->b_cont != NULL || (len & 0x3f) == 0) {
+ /*
+ * re-allocate mp
+ */
+ len = max(len, ETHERMIN);
+
+ if ((len & 0x3f) == 0) {
+ /* workaround for buggy USB hba */
+ len++;
+ }
+
+ if ((new = allocb(len, 0)) == NULL) {
+ return (NULL);
+ }
+
+ /* copy contents of the buffer */
+ new->b_wptr = new->b_rptr + len;
+ bp = new->b_rptr;
+ for (tp = mp; tp; tp = tp->b_cont) {
+ len = tp->b_wptr - tp->b_rptr;
+ bcopy(tp->b_rptr, bp, len);
+ bp += len;
+ }
+
+ last_pos = new->b_wptr;
+ while (bp < last_pos) {
+ *bp++ = 0;
+ }
+
+ mp = new;
+ }
+
+ return (mp);
+}
+
+static void
+urf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i += 8, bp += 8) {
+ cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]);
+ }
+}
+
+static mblk_t *
+urf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp)
+{
+ uint8_t *p;
+ uint16_t rxhd;
+ uint_t len;
+
+ ASSERT(mp != NULL);
+ len = msgdsize(mp);
+#ifdef DEBUG_LEVEL
+ DPRINTF(2, (CE_CONT, "!%s: time:%d %s: len:%d cont:%p",
+ dp->name, ddi_get_lbolt(), __func__, len, mp->b_cont));
+
+ if (urf_debug > 2) {
+ urf_dump_packet(dp, mp->b_rptr, max(6, len));
+ }
+#endif
+ if (len < ETHERMIN + ETHERFCSL) {
+ /* Too short */
+ dp->stats.runt++;
+ dp->stats.errrcv++;
+ return (NULL);
+ }
+
+ /* get Rx header which is placed at tail of the packet. */
+ p = mp->b_wptr - 4;
+ rxhd = (p[1] << 8) | p[0];
+ len = rxhd & RXHD_BYTECNT;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: rsr:%b len:%d",
+ dp->name, __func__, rxhd, RXHD_BITS, len));
+
+ /* check if error happen */
+ if ((rxhd & (RXHD_VALID)) == 0) {
+ DPRINTF(-1, (CE_CONT, "!%s: %s: rxhd:%b",
+ dp->name, __func__, rxhd, RXHD_BITS));
+ if (rxhd & RXHD_RUNT) {
+ dp->stats.runt++;
+ }
+
+ dp->stats.errrcv++;
+ return (NULL);
+ }
+#ifdef notdef
+ /* check packet size */
+ if (len > ETHERMAX + ETHERFCSL) {
+ /* too long */
+ dp->stats.frame_too_long++;
+ dp->stats.errrcv++;
+ return (NULL);
+ } else if (len < ETHERMIN + ETHERFCSL) {
+ dp->stats.runt++;
+ dp->stats.errrcv++;
+ return (NULL);
+ }
+#endif
+ /* remove tailing crc field */
+ mp->b_wptr -= ETHERFCSL;
+ return (mp);
+}
+
+/*
+ * MII Interfaces
+ */
+static uint16_t
+urf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp)
+{
+ int reg;
+ uint16_t val;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d",
+ dp->name, __func__, index));
+
+ *errp = USB_SUCCESS;
+
+ switch (index) {
+ case MII_CONTROL:
+ reg = BMCR;
+ break;
+
+ case MII_STATUS:
+ reg = BMSR;
+ break;
+
+ case MII_AN_ADVERT:
+ reg = ANAR;
+ break;
+
+ case MII_AN_LPABLE:
+ reg = ANLP;
+ break;
+
+ case MII_AN_EXPANSION:
+ reg = ANER;
+ break;
+
+ default:
+ return (0);
+ }
+
+ IN(dp, reg, &val, errp, usberr);
+
+ if (index == MII_STATUS) {
+ uint8_t msr;
+ /*
+ * Fix MII status register as it does't have LINKUP and
+ * MFPRMBLSUPR bits.
+ */
+ IN(dp, MSR, &msr, errp, usberr);
+
+ val |= (MII_STATUS_MFPRMBLSUPR | MII_STATUS_LINKUP);
+ if ((msr & MSR_LINK) == 0) {
+ val &= ~MII_STATUS_LINKUP;
+ }
+ }
+
+ return (val);
+
+usberr:
+ cmn_err(CE_CONT,
+ "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp);
+
+ return (0);
+}
+
+static void
+urf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp)
+{
+ int reg;
+
+ DPRINTF(5, (CE_CONT, "!%s: %s called", dp->name, __func__));
+
+ *errp = USB_SUCCESS;
+
+ switch (index) {
+ case MII_CONTROL:
+ reg = BMCR;
+ break;
+
+ case MII_STATUS:
+ reg = BMSR;
+ break;
+
+ case MII_AN_ADVERT:
+ reg = ANAR;
+ break;
+
+ case MII_AN_LPABLE:
+ reg = ANLP;
+ break;
+
+ case MII_AN_EXPANSION:
+ reg = ANER;
+ break;
+
+ default:
+ return;
+ }
+
+ OUTW(dp, reg, val, errp, usberr);
+usberr:
+ ;
+}
+
+/* ======================================================== */
+/*
+ * OS depend (device driver DKI) routine
+ */
+/* ======================================================== */
+static void
+urf_eeprom_dump(struct usbgem_dev *dp, int size)
+{
+ int i;
+ int err;
+ uint16_t w0, w1, w2, w3;
+
+ cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name);
+ for (i = URF_EEPROM_BASE; i < size + URF_EEPROM_BASE; i += 8) {
+ IN(dp, i + 0, &w0, &err, usberr);
+ IN(dp, i + 2, &w1, &err, usberr);
+ IN(dp, i + 4, &w2, &err, usberr);
+ IN(dp, i + 6, &w3, &err, usberr);
+ cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x",
+ i - URF_EEPROM_BASE, w0, w1, w2, w3);
+ }
+usberr:
+ ;
+}
+
+static int
+urf_attach_chip(struct usbgem_dev *dp)
+{
+ int i;
+ uint8_t old;
+ uint_t new;
+ uint8_t reg;
+ int err;
+ struct urf_dev *lp = dp->private;
+
+ /*
+ * setup flow control bit in eeprom
+ */
+ IN(dp, URF_EEPROM_BASE + 9, &old, &err, usberr);
+
+ DPRINTF(0, (CE_CONT, "!%s: eeprom offset 9: %02x", dp->name, old));
+
+ if (dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE) {
+ /* enable PAUSE bit */
+ new = old | 0x04;
+ } else {
+ /* clear PAUSE bit */
+ new = old & ~0x04;
+ }
+ if (new != old) {
+ /* make eeprom writable */
+ OUTB(dp, CR, lp->cr | CR_WEPROM, &err, usberr);
+
+ /* eerom allows only word access for writing */
+ IN(dp, URF_EEPROM_BASE + 8, &reg, &err, usberr);
+ new = (new << 8) | reg;
+
+ OUTW(dp, URF_EEPROM_BASE + 8, new, &err, usberr);
+
+ /* make eeprom non-writable */
+ OUTB(dp, CR, lp->cr, &err, usberr);
+ }
+
+ /*
+ * load EEPROM contents into nic
+ */
+ OUTB(dp, CR, lp->cr | CR_AUTOLOAD, &err, usberr);
+ CHECK_AND_JUMP(err, usberr);
+
+ for (i = 0; i < 100; i++) {
+ IN(dp, CR, &reg, &err, usberr);
+ if ((reg & CR_AUTOLOAD) == 0) {
+ goto autoload_done;
+ }
+ }
+ /* timeout */
+ cmn_err(CE_WARN, "%s: %s: failed to autoload: timeout",
+ dp->name, __func__);
+ goto usberr;
+
+autoload_done:
+ /*
+ * mac address in EEPROM has loaded to ID registers.
+ */
+ INS(dp, IDR, dp->dev_addr.ether_addr_octet, ETHERADDRL, &err, usberr);
+
+ /* no need to scan phy */
+ dp->mii_phy_addr = -1;
+
+#if DEBUG_LEVEL > 2
+ urf_eeprom_dump(dp, 0x80);
+#endif
+
+#ifdef CONFIG_VLAN
+ dp->misc_flag = USBGEM_VLAN;
+#endif
+ return (USB_SUCCESS);
+
+usberr:
+ cmn_err(CE_WARN, "%s: urf_attach_chip: usb error detected", dp->name);
+ return (USB_FAILURE);
+}
+
+static int
+urfattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i;
+ ddi_iblock_cookie_t c;
+ int ret;
+ int unit;
+ struct chip_info *p;
+ const char *drv_name;
+ struct usbgem_dev *dp;
+ void *base;
+ struct usbgem_conf *ugcp;
+ struct urf_dev *lp;
+
+ unit = ddi_get_instance(dip);
+ drv_name = ddi_driver_name(dip);
+
+ DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d",
+ drv_name, __func__, unit, cmd));
+
+ if (cmd == DDI_ATTACH) {
+ /*
+ * Check if the chip is supported.
+ */
+
+ /*
+ * Check the chip if it is really realtek rtl8150
+ */
+
+ /*
+ * construct usbgem configration
+ */
+ ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP);
+
+ /* name */
+ sprintf(ugcp->usbgc_name,
+ "%s%d(ppa=%d)", drv_name, unit, urf_ppa);
+#ifdef USBGEM_CONFIG_GLDv3
+ ugcp->usbgc_ppa = urf_ppa;
+#else
+ ugcp->usbgc_ppa = unit;
+#endif
+ ugcp->usbgc_ifnum = 0;
+ ugcp->usbgc_alt = 0;
+
+ ugcp->usbgc_tx_list_max = 16;
+
+ /* the rx status partially replaces FCS */
+ ugcp->usbgc_rx_header_len = 0;
+ ugcp->usbgc_rx_list_max = 64;
+
+ /* time out parameters */
+ ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT;
+ ugcp->usbgc_tx_timeout_interval = ONESEC;
+
+ /* flow control */
+ ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE;
+
+ /* MII timeout parameters */
+ ugcp->usbgc_mii_link_watch_interval = ONESEC;
+ ugcp->usbgc_mii_an_watch_interval = ONESEC/5;
+ ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */
+ ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */
+ ugcp->usbgc_mii_an_wait = (25*ONESEC)/10;
+ ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT;
+
+ ugcp->usbgc_mii_an_delay = ONESEC/10;
+ ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA;
+ ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET;
+ ugcp->usbgc_mii_dont_reset = B_FALSE;
+
+ /* I/O methods */
+
+ /* mac operation */
+ ugcp->usbgc_attach_chip = &urf_attach_chip;
+ ugcp->usbgc_reset_chip = &urf_reset_chip;
+ ugcp->usbgc_init_chip = &urf_init_chip;
+ ugcp->usbgc_start_chip = &urf_start_chip;
+ ugcp->usbgc_stop_chip = &urf_stop_chip;
+ ugcp->usbgc_multicast_hash = &urf_mcast_hash;
+
+ ugcp->usbgc_set_rx_filter = &urf_set_rx_filter;
+ ugcp->usbgc_set_media = &urf_set_media;
+ ugcp->usbgc_get_stats = &urf_get_stats;
+#ifdef notdef
+ ugcp->usbgc_interrupt = &urf_interrupt;
+#else
+ ugcp->usbgc_interrupt = NULL;
+#endif
+ /* packet operation */
+ ugcp->usbgc_tx_make_packet = &urf_tx_make_packet;
+ ugcp->usbgc_rx_make_packet = &urf_rx_make_packet;
+
+ /* mii operations */
+ ugcp->usbgc_mii_probe = &usbgem_mii_probe_default;
+ ugcp->usbgc_mii_init = &usbgem_mii_init_default;
+ ugcp->usbgc_mii_config = &usbgem_mii_config_default;
+ ugcp->usbgc_mii_read = &urf_mii_read;
+ ugcp->usbgc_mii_write = &urf_mii_write;
+
+ /* mtu */
+ ugcp->usbgc_min_mtu = ETHERMTU;
+ ugcp->usbgc_max_mtu = ETHERMTU;
+ ugcp->usbgc_default_mtu = ETHERMTU;
+
+ lp = kmem_zalloc(sizeof (struct urf_dev), KM_SLEEP);
+ lp->chip = p;
+
+ ddi_set_driver_private(dip, NULL);
+
+ dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct urf_dev));
+
+ kmem_free(ugcp, sizeof (*ugcp));
+
+ if (dp != NULL) {
+ urf_ppa++;
+ return (DDI_SUCCESS);
+ }
+
+err_free_mem:
+ kmem_free(lp, sizeof (struct urf_dev));
+err_close_pipe:
+err:
+ return (DDI_FAILURE);
+ }
+ if (cmd == DDI_RESUME) {
+ return (usbgem_resume(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+static int
+urfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int ret;
+
+ if (cmd == DDI_DETACH) {
+ ret = usbgem_do_detach(dip);
+ if (ret != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ urf_ppa--;
+ return (DDI_SUCCESS);
+ }
+ if (cmd == DDI_SUSPEND) {
+ return (usbgem_suspend(dip));
+ }
+ return (DDI_FAILURE);
+}
+
+/* ======================================================== */
+/*
+ * OS depend (loadable streams driver) routine
+ */
+/* ======================================================== */
+#ifdef USBGEM_CONFIG_GLDv3
+USBGEM_STREAM_OPS(urf_ops, urfattach, urfdetach);
+#else
+static struct module_info urfminfo = {
+ 0, /* mi_idnum */
+ "urf", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ ETHERMTU*128, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit urfrinit = {
+ (int (*)()) NULL, /* qi_putp */
+ usbgem_rsrv, /* qi_srvp */
+ usbgem_open, /* qi_qopen */
+ usbgem_close, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &urfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit urfwinit = {
+ usbgem_wput, /* qi_putp */
+ usbgem_wsrv, /* qi_srvp */
+ (int (*)()) NULL, /* qi_qopen */
+ (int (*)()) NULL, /* qi_qclose */
+ (int (*)()) NULL, /* qi_qadmin */
+ &urfminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab urf_info = {
+ &urfrinit, /* st_rdinit */
+ &urfwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_urf_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &urf_info, /* cb_stream */
+ D_NEW|D_MP /* cb_flag */
+};
+
+static struct dev_ops urf_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ usbgem_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ urfattach, /* devo_attach */
+ urfdetach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_urf_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ usbgem_power, /* devo_power */
+#if DEVO_REV >= 4
+ usbgem_quiesce, /* devo_quiesce */
+#endif
+
+};
+#endif
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ ident,
+ &urf_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+/* ======================================================== */
+/*
+ * _init : done
+ */
+/* ======================================================== */
+int
+_init(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!urf: _init: called"));
+
+ status = usbgem_mod_init(&urf_ops, "urf");
+ if (status != DDI_SUCCESS) {
+ return (status);
+ }
+ status = mod_install(&modlinkage);
+ if (status != DDI_SUCCESS) {
+ usbgem_mod_fini(&urf_ops);
+ }
+ return (status);
+}
+
+/*
+ * _fini : done
+ */
+int
+_fini(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!urf: _fini: called"));
+ status = mod_remove(&modlinkage);
+ if (status == DDI_SUCCESS) {
+ usbgem_mod_fini(&urf_ops);
+ }
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/io/usbgem/usbgem.c b/usr/src/uts/common/io/usbgem/usbgem.c
new file mode 100644
index 0000000000..a42f7119ef
--- /dev/null
+++ b/usr/src/uts/common/io/usbgem/usbgem.c
@@ -0,0 +1,6389 @@
+/*
+ * usbgem.c: General USB to Fast Ethernet mac driver framework
+ *
+ * Copyright (c) 2002-2012 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#pragma ident "@(#)usbgem.c 1.6 12/02/09"
+
+/*
+ * Change log
+ */
+
+/*
+ * TODO:
+ * implement DELAYED_START
+ */
+
+/*
+ * System Header files.
+ */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/vtrace.h>
+#include <sys/ethernet.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#ifndef USBGEM_CONFIG_GLDv3
+#include <sys/dlpi.h>
+#include <sys/strsubr.h>
+#endif
+#include <sys/stream.h> /* required for MBLK* */
+#include <sys/strsun.h> /* required for mionack() */
+#include <sys/byteorder.h>
+
+#include <sys/usb/usba.h>
+#ifdef USBGEM_CONFIG_GLDv3
+#include <inet/common.h>
+#include <inet/led.h>
+#include <inet/mi.h>
+#include <inet/nd.h>
+#endif
+
+/* supplement definitions */
+extern const char *usb_str_cr(usb_cr_t);
+
+#ifndef USBGEM_CONFIG_GLDv3
+#pragma weak gld_linkstate
+#endif
+#include <sys/note.h>
+
+#include "usbgem_mii.h"
+#include "usbgem.h"
+
+#ifdef MODULE
+char ident[] = "usb general ethernet mac driver v" VERSION;
+#else
+extern char ident[];
+#endif
+
+/* Debugging support */
+#ifdef USBGEM_DEBUG_LEVEL
+static int usbgem_debug = USBGEM_DEBUG_LEVEL;
+#define DPRINTF(n, args) if (usbgem_debug > (n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+/*
+ * Useful macros and typedefs
+ */
+#define ROUNDUP(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define DEFAULT_PIPE(dp) ((dp)->reg_data->dev_default_ph)
+#define VTAG_SIZE 4
+#define BOOLEAN(x) ((x) != 0)
+/*
+ * configuration parameters
+ */
+#define USBDRV_MAJOR_VER 2
+#define USBDRV_MINOR_VER 0
+
+#define ETHERHEADERL (sizeof (struct ether_header))
+#define MAXPKTLEN(dp) ((dp)->mtu + ETHERHEADERL)
+#define MAXPKTBUF(dp) ((dp)->mtu + ETHERHEADERL + ETHERFCSL)
+
+#define WATCH_INTERVAL_FAST drv_usectohz(100*1000)
+
+#define STOP_GRACEFUL B_TRUE
+
+/*
+ * Private functions
+ */
+static int usbgem_open_pipes(struct usbgem_dev *dp);
+static int usbgem_close_pipes(struct usbgem_dev *dp);
+static void usbgem_intr_cb(usb_pipe_handle_t, usb_intr_req_t *);
+static void usbgem_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+static void usbgem_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+
+static int usbgem_mii_start(struct usbgem_dev *);
+static void usbgem_mii_stop(struct usbgem_dev *);
+
+/* local buffer management */
+static int usbgem_init_rx_buf(struct usbgem_dev *);
+
+/* internal mac interfaces */
+static void usbgem_tx_timeout(struct usbgem_dev *);
+static void usbgem_mii_link_watcher(struct usbgem_dev *);
+static int usbgem_mac_init(struct usbgem_dev *);
+static int usbgem_mac_start(struct usbgem_dev *);
+static int usbgem_mac_stop(struct usbgem_dev *, int, boolean_t);
+static void usbgem_mac_ioctl(struct usbgem_dev *, queue_t *, mblk_t *);
+
+int usbgem_speed_value[] = {10, 100, 1000};
+
+static int usbgem_ctrl_retry = 5;
+
+/* usb event support */
+static int usbgem_disconnect_cb(dev_info_t *dip);
+static int usbgem_reconnect_cb(dev_info_t *dip);
+int usbgem_suspend(dev_info_t *dip);
+int usbgem_resume(dev_info_t *dip);
+
+static uint8_t usbgem_bcastaddr[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+#ifdef MODULE
+extern struct mod_ops mod_miscops;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops,
+ "usbgem v" VERSION,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlmisc, NULL
+};
+
+/*
+ * _init : done
+ */
+int
+_init(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!usbgem: _init: called"));
+ status = mod_install(&modlinkage);
+
+ return (status);
+}
+
+/*
+ * _fini : done
+ */
+int
+_fini(void)
+{
+ int status;
+
+ DPRINTF(2, (CE_CONT, "!usbgem: _fini: called"));
+ status = mod_remove(&modlinkage);
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+#endif /* MODULE */
+
+/* ============================================================== */
+/*
+ * Ether CRC calculation utilities
+ */
+/* ============================================================== */
+/*
+ * Ether CRC calculation according to 21143 data sheet
+ */
+#define CRC32_POLY_LE 0xedb88320
+uint32_t
+usbgem_ether_crc_le(const uint8_t *addr)
+{
+ int idx;
+ int bit;
+ uint_t data;
+ uint32_t crc = 0xffffffff;
+
+ crc = 0xffffffff;
+ for (idx = 0; idx < ETHERADDRL; idx++) {
+ for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
+ crc = (crc >> 1) ^
+ (((crc ^ data) & 1) ? CRC32_POLY_LE : 0);
+ }
+ }
+ return (crc);
+}
+
+#define CRC32_POLY_BE 0x04c11db7
+uint32_t
+usbgem_ether_crc_be(const uint8_t *addr)
+{
+ int idx;
+ int bit;
+ uint_t data;
+ uint32_t crc;
+
+ crc = 0xffffffff;
+ for (idx = 0; idx < ETHERADDRL; idx++) {
+ for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) {
+ crc = (crc << 1) ^
+ ((((crc >> 31) ^ data) & 1) ? CRC32_POLY_BE : 0);
+ }
+ }
+ return (crc);
+}
+
+int
+usbgem_prop_get_int(struct usbgem_dev *dp, char *prop_template, int def_val)
+{
+ char propname[32];
+
+ (void) sprintf(propname, prop_template, dp->name);
+
+ return (ddi_prop_get_int(DDI_DEV_T_ANY, dp->dip,
+ DDI_PROP_DONTPASS, propname, def_val));
+}
+
+static int
+usbgem_population(uint32_t x)
+{
+ int i;
+ int cnt;
+
+ cnt = 0;
+ for (i = 0; i < 32; i++) {
+ if (x & (1 << i)) {
+ cnt++;
+ }
+ }
+ return (cnt);
+}
+
+static clock_t
+usbgem_timestamp_nz()
+{
+ clock_t now;
+ now = ddi_get_lbolt();
+ return (now ? now : (clock_t)1);
+}
+
+#ifdef USBGEM_DEBUG_LEVEL
+#ifdef USBGEM_DEBUG_VLAN
+#ifdef notdef
+#include <netinet/in.h>
+#endif
+static void
+usbgem_dump_packet(struct usbgem_dev *dp, char *title, mblk_t *mp,
+ boolean_t check_cksum)
+{
+ char msg[180];
+ uint8_t buf[18+20+20];
+ uint8_t *p;
+ size_t offset;
+ uint_t ethertype;
+ uint_t proto;
+ uint_t ipproto = 0;
+ uint_t iplen;
+ uint_t iphlen;
+ uint_t tcplen;
+ uint_t udplen;
+ uint_t cksum;
+ int rest;
+ int len;
+ char *bp;
+ mblk_t *tp;
+ extern uint_t ip_cksum(mblk_t *, int, uint32_t);
+
+ msg[0] = 0;
+ bp = msg;
+
+ rest = sizeof (buf);
+ offset = 0;
+ for (tp = mp; tp; tp = tp->b_cont) {
+ len = tp->b_wptr - tp->b_rptr;
+ len = min(rest, len);
+ bcopy(tp->b_rptr, &buf[offset], len);
+ rest -= len;
+ offset += len;
+ if (rest == 0) {
+ break;
+ }
+ }
+
+ offset = 0;
+ p = &buf[offset];
+
+ /* ethernet address */
+ sprintf(bp,
+ "ether: %02x:%02x:%02x:%02x:%02x:%02x"
+ " -> %02x:%02x:%02x:%02x:%02x:%02x",
+ p[6], p[7], p[8], p[9], p[10], p[11],
+ p[0], p[1], p[2], p[3], p[4], p[5]);
+ bp = &msg[strlen(msg)];
+
+ /* vlag tag and etherrtype */
+ ethertype = GET_ETHERTYPE(p);
+ if (ethertype == VTAG_TPID) {
+ sprintf(bp, " vtag:0x%04x", GET_NET16(&p[14]));
+ bp = &msg[strlen(msg)];
+
+ offset += VTAG_SIZE;
+ p = &buf[offset];
+ ethertype = GET_ETHERTYPE(p);
+ }
+ sprintf(bp, " type:%04x", ethertype);
+ bp = &msg[strlen(msg)];
+
+ /* ethernet packet length */
+ sprintf(bp, " mblklen:%d", msgdsize(mp));
+ bp = &msg[strlen(msg)];
+ if (mp->b_cont) {
+ sprintf(bp, "(");
+ bp = &msg[strlen(msg)];
+ for (tp = mp; tp; tp = tp->b_cont) {
+ if (tp == mp) {
+ sprintf(bp, "%d", tp->b_wptr - tp->b_rptr);
+ } else {
+ sprintf(bp, "+%d", tp->b_wptr - tp->b_rptr);
+ }
+ bp = &msg[strlen(msg)];
+ }
+ sprintf(bp, ")");
+ bp = &msg[strlen(msg)];
+ }
+
+ if (ethertype != ETHERTYPE_IP) {
+ goto x;
+ }
+
+ /* ip address */
+ offset += sizeof (struct ether_header);
+ p = &buf[offset];
+ ipproto = p[9];
+ iplen = GET_NET16(&p[2]);
+ sprintf(bp, ", ip: %d.%d.%d.%d -> %d.%d.%d.%d proto:%d iplen:%d",
+ p[12], p[13], p[14], p[15],
+ p[16], p[17], p[18], p[19],
+ ipproto, iplen);
+ bp = (void *)&msg[strlen(msg)];
+
+ iphlen = (p[0] & 0xf) * 4;
+
+ /* cksum for psuedo header */
+ cksum = *(uint16_t *)&p[12];
+ cksum += *(uint16_t *)&p[14];
+ cksum += *(uint16_t *)&p[16];
+ cksum += *(uint16_t *)&p[18];
+ cksum += BE_16(ipproto);
+
+ /* tcp or udp protocol header */
+ offset += iphlen;
+ p = &buf[offset];
+ if (ipproto == IPPROTO_TCP) {
+ tcplen = iplen - iphlen;
+ sprintf(bp, ", tcp: len:%d cksum:%x",
+ tcplen, GET_NET16(&p[16]));
+ bp = (void *)&msg[strlen(msg)];
+
+ if (check_cksum) {
+ cksum += BE_16(tcplen);
+ cksum = (uint16_t)ip_cksum(mp, offset, cksum);
+ sprintf(bp, " (%s)",
+ (cksum == 0 || cksum == 0xffff) ? "ok" : "ng");
+ bp = (void *)&msg[strlen(msg)];
+ }
+ } else if (ipproto == IPPROTO_UDP) {
+ udplen = GET_NET16(&p[4]);
+ sprintf(bp, ", udp: len:%d cksum:%x",
+ udplen, GET_NET16(&p[6]));
+ bp = (void *)&msg[strlen(msg)];
+
+ if (GET_NET16(&p[6]) && check_cksum) {
+ cksum += *(uint16_t *)&p[4];
+ cksum = (uint16_t)ip_cksum(mp, offset, cksum);
+ sprintf(bp, " (%s)",
+ (cksum == 0 || cksum == 0xffff) ? "ok" : "ng");
+ bp = (void *)&msg[strlen(msg)];
+ }
+ }
+x:
+ cmn_err(CE_CONT, "!%s: %s: %s", dp->name, title, msg);
+}
+#endif /* USBGEM_DEBUG_VLAN */
+#endif /* USBGEM_DEBUG_LEVEL */
+
+#ifdef GEM_GCC_RUNTIME
+/*
+ * gcc3 runtime routines
+ */
+#pragma weak memcmp
+int
+memcmp(const void *s1, const void *s2, size_t n)
+{
+ int i;
+ int ret;
+
+ ret = 0;
+ for (i = 0; i < n; i++) {
+ ret = (int)((uint8_t *)s1)[i] - (int)((uint8_t *)s2)[i];
+ if (ret) {
+ return (ret);
+ }
+ }
+ return (0);
+}
+
+#pragma weak memset
+void *
+memset(void *s, int c, size_t n)
+{
+ if ((c & 0xff) == 0) {
+ bzero(s, n);
+ } else {
+ while (n--) {
+ ((uint8_t *)s)[n] = c;
+ }
+ }
+ return (s);
+}
+
+#pragma weak _memcpy = memcpy
+#pragma weak memcpy
+void *
+memcpy(void *s1, const void *s2, size_t n)
+{
+ bcopy(s2, s1, n);
+ return (s1);
+}
+#endif /* GEM_GCC_RUNTIME */
+/* ============================================================== */
+/*
+ * hardware operations
+ */
+/* ============================================================== */
+static int
+usbgem_hal_reset_chip(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_reset_chip)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_init_chip(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_init_chip)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_attach_chip(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_attach_chip)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_set_rx_filter(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_set_rx_filter)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_set_media(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_set_media)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_start_chip(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_start_chip)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_stop_chip(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_stop_chip)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+static int
+usbgem_hal_get_stats(struct usbgem_dev *dp)
+{
+ int err;
+
+ sema_p(&dp->hal_op_lock);
+ err = (*dp->ugc.usbgc_get_stats)(dp);
+ sema_v(&dp->hal_op_lock);
+ return (err);
+}
+
+
+/* ============================================================== */
+/*
+ * USB pipe management
+ */
+/* ============================================================== */
+static boolean_t
+usbgem_rx_start_unit(struct usbgem_dev *dp, usb_bulk_req_t *req)
+{
+ mblk_t *mp;
+ int err;
+ usb_flags_t flags;
+
+ ASSERT(req);
+
+ mp = allocb(dp->rx_buf_len, BPRI_MED);
+ if (mp == NULL) {
+ cmn_err(CE_WARN, "!%s: %s: failed to allocate mblk",
+ dp->name, __func__);
+ goto err;
+ }
+
+ req->bulk_len = dp->rx_buf_len;
+ req->bulk_data = mp;
+ req->bulk_client_private = (usb_opaque_t)dp;
+ req->bulk_timeout = 0;
+ req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK;
+ req->bulk_cb = usbgem_bulkin_cb;
+ req->bulk_exc_cb = usbgem_bulkin_cb;
+ req->bulk_completion_reason = 0;
+ req->bulk_cb_flags = 0;
+
+ flags = 0;
+ err = usb_pipe_bulk_xfer(dp->bulkin_pipe, req, flags);
+
+ if (err != USB_SUCCESS) {
+ cmn_err(CE_WARN, "%s: failed to bulk_xfer for rx, err:%d",
+ dp->name, err);
+
+ /* free req and mp */
+ usb_free_bulk_req(req);
+ goto err;
+ }
+ return (B_TRUE);
+err:
+ return (B_FALSE);
+}
+
+/* ============================================================== */
+/*
+ * Rx/Tx buffer management
+ */
+/* ============================================================== */
+static int
+usbgem_init_rx_buf(struct usbgem_dev *dp)
+{
+ int i;
+ usb_bulk_req_t *req;
+
+ ASSERT(dp->mac_state == MAC_STATE_ONLINE);
+
+ for (i = 0; i < dp->ugc.usbgc_rx_list_max; i++) {
+ req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP);
+ if (req == NULL) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to allocate bulkreq for rx",
+ dp->name, __func__);
+ return (USB_FAILURE);
+ }
+ if (!usbgem_rx_start_unit(dp, req)) {
+ return (USB_FAILURE);
+ }
+ mutex_enter(&dp->rxlock);
+ dp->rx_busy_cnt++;
+ mutex_exit(&dp->rxlock);
+ }
+ return (USB_SUCCESS);
+}
+
+/* ============================================================== */
+/*
+ * memory resource management
+ */
+/* ============================================================== */
+static int
+usbgem_free_memory(struct usbgem_dev *dp)
+{
+ usb_bulk_req_t *req;
+
+ /* free all tx requst structure */
+ while ((req = dp->tx_free_list) != NULL) {
+ dp->tx_free_list =
+ (usb_bulk_req_t *)req->bulk_client_private;
+ req->bulk_data = NULL;
+ usb_free_bulk_req(req);
+ }
+ return (USB_SUCCESS);
+}
+
+static int
+usbgem_alloc_memory(struct usbgem_dev *dp)
+{
+ int i;
+ usb_bulk_req_t *req;
+
+ /* allocate tx requests */
+ dp->tx_free_list = NULL;
+ for (i = 0; i < dp->ugc.usbgc_tx_list_max; i++) {
+ req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP);
+ if (req == NULL) {
+ cmn_err(CE_WARN,
+ "%s:%s failed to allocate tx requests",
+ dp->name, __func__);
+
+ /* free partially allocated tx requests */
+ (void) usbgem_free_memory(dp);
+ return (USB_FAILURE);
+ }
+
+ /* add the new one allocated into tx free list */
+ req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
+ dp->tx_free_list = req;
+ }
+
+ return (USB_SUCCESS);
+}
+
+/* ========================================================== */
+/*
+ * Start transmission.
+ * Return zero on success,
+ */
+/* ========================================================== */
+
+#ifdef TXTIMEOUT_TEST
+static int usbgem_send_cnt = 0;
+#endif
+
+/*
+ * usbgem_send is used only to send data packet into ethernet line.
+ */
+static mblk_t *
+usbgem_send_common(struct usbgem_dev *dp, mblk_t *mp, uint32_t flags)
+{
+ int err;
+ mblk_t *new;
+ usb_bulk_req_t *req;
+ int mcast;
+ int bcast;
+ int len;
+ boolean_t intr;
+ usb_flags_t usb_flags = 0;
+#ifdef USBGEM_DEBUG_LEVEL
+ usb_pipe_state_t p_state;
+#endif
+ DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ intr = (flags & 1) != 0;
+ len = msgdsize(mp);
+ bcast = 0;
+ mcast = 0;
+ if (mp->b_rptr[0] & 1) {
+ if (bcmp(mp->b_rptr, &usbgem_bcastaddr, ETHERADDRL) == 0) {
+ bcast = 1;
+ } else {
+ mcast = 1;
+ }
+ }
+ new = (*dp->ugc.usbgc_tx_make_packet)(dp, mp);
+ if (new == NULL) {
+ /*
+ * no memory resource. we don't stop downstream,
+ * we just discard the packet.
+ */
+ DPRINTF(0, (CE_CONT, "!%s: %s: no memory",
+ dp->name, __func__));
+ freemsg(mp);
+
+ mutex_enter(&dp->txlock);
+ dp->stats.noxmtbuf++;
+ dp->stats.errxmt++;
+ mutex_exit(&dp->txlock);
+
+ return (NULL);
+ }
+
+ ASSERT(new->b_cont == NULL);
+
+ mutex_enter(&dp->txlock);
+ if (dp->tx_free_list == NULL) {
+ /*
+ * no tx free slot
+ */
+ ASSERT(dp->tx_busy_cnt == dp->ugc.usbgc_tx_list_max);
+ mutex_exit(&dp->txlock);
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: no free slot",
+ dp->name, __func__));
+ if (new && new != mp) {
+ /* free reallocated message */
+ freemsg(new);
+ }
+ return (mp);
+ }
+ req = dp->tx_free_list;
+ dp->tx_free_list = (usb_bulk_req_t *)req->bulk_client_private;
+ dp->tx_busy_cnt++;
+
+ if (dp->tx_free_list == NULL) {
+ intr = B_TRUE;
+ }
+ if (intr) {
+ dp->tx_intr_pended++;
+ }
+ DB_TCI(new) = intr;
+#ifdef USBGEM_DEBUG_LEVEL
+ new->b_datap->db_cksum32 = dp->tx_seq_num;
+ dp->tx_seq_num++;
+#endif
+ dp->stats.obytes += len;
+ dp->stats.opackets++;
+ if (bcast | mcast) {
+ dp->stats.obcast += bcast;
+ dp->stats.omcast += mcast;
+ }
+ mutex_exit(&dp->txlock);
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: sending", dp->name, __func__));
+
+ req->bulk_len = (long)new->b_wptr - (long)new->b_rptr;
+ req->bulk_data = new;
+ req->bulk_client_private = (usb_opaque_t)dp;
+ req->bulk_timeout = dp->bulkout_timeout; /* in second */
+ req->bulk_attributes = 0;
+ req->bulk_cb = usbgem_bulkout_cb;
+ req->bulk_exc_cb = usbgem_bulkout_cb;
+ req->bulk_completion_reason = 0;
+ req->bulk_cb_flags = 0;
+
+ if (intr) {
+ usb_flags = USB_FLAGS_SLEEP;
+ }
+ if ((err = usb_pipe_bulk_xfer(dp->bulkout_pipe, req, usb_flags))
+ != USB_SUCCESS) {
+
+ /* failed to transfer the packet, discard it. */
+ freemsg(new);
+ req->bulk_data = NULL;
+
+ /* recycle the request block */
+ mutex_enter(&dp->txlock);
+ dp->tx_busy_cnt--;
+ req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
+ dp->tx_free_list = req;
+ mutex_exit(&dp->txlock);
+
+ cmn_err(CE_NOTE,
+ "%s: %s: usb_pipe_bulk_xfer: failed: err:%d",
+ dp->name, __func__, err);
+
+ /* we use another flag to indicate error state. */
+ if (dp->fatal_error == (clock_t)0) {
+ dp->fatal_error = usbgem_timestamp_nz();
+ }
+ } else {
+ /* record the start time */
+ dp->tx_start_time = ddi_get_lbolt();
+ }
+
+ if (err == USB_SUCCESS && (usb_flags & USB_FLAGS_SLEEP)) {
+ usbgem_bulkout_cb(dp->bulkout_pipe, req);
+ }
+
+ if (new != mp) {
+ freemsg(mp);
+ }
+ return (NULL);
+}
+
+int
+usbgem_restart_nic(struct usbgem_dev *dp)
+{
+ int ret;
+ int flags = 0;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ ASSERT(dp->mac_state != MAC_STATE_DISCONNECTED);
+
+ /*
+ * ensure to stop the nic
+ */
+ if (dp->mac_state == MAC_STATE_ONLINE) {
+ (void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL);
+ }
+
+ /* now the nic become quiescent, reset the chip */
+ if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN, "%s: %s: failed to reset chip",
+ dp->name, __func__);
+ goto err;
+ }
+
+ /*
+ * restore the nic state step by step
+ */
+ if (dp->nic_state < NIC_STATE_INITIALIZED) {
+ goto done;
+ }
+
+ if (usbgem_mac_init(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN, "%s: %s: failed to initialize chip",
+ dp->name, __func__);
+ goto err;
+ }
+
+ /* setup mac address and enable rx filter */
+ sema_p(&dp->rxfilter_lock);
+ dp->rxmode |= RXMODE_ENABLE;
+ ret = usbgem_hal_set_rx_filter(dp);
+ sema_v(&dp->rxfilter_lock);
+ if (ret != USB_SUCCESS) {
+ goto err;
+ }
+
+ /*
+ * update the link state asynchronously
+ */
+ cv_signal(&dp->link_watcher_wait_cv);
+
+ /*
+ * XXX - a panic happened because of linkdown.
+ * We must check mii_state here, because the link can be down just
+ * before the restart event happen. If the link is down now,
+ * gem_mac_start() will be called from gem_mii_link_check() when
+ * the link become up later.
+ */
+ if (dp->mii_state == MII_STATE_LINKUP) {
+ if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
+ goto err;
+ }
+ if (dp->nic_state < NIC_STATE_ONLINE) {
+ goto done;
+ }
+
+ (void) usbgem_mac_start(dp);
+
+ }
+done:
+ return (USB_SUCCESS);
+err:
+#ifdef GEM_CONFIG_FMA
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+#endif
+ return (USB_FAILURE);
+}
+
+static void
+usbgem_tx_timeout(struct usbgem_dev *dp)
+{
+ int ret;
+ uint_t rwlock;
+ clock_t now;
+
+ for (; ; ) {
+ mutex_enter(&dp->tx_watcher_lock);
+ ret = cv_timedwait(&dp->tx_watcher_cv, &dp->tx_watcher_lock,
+ dp->tx_watcher_interval + ddi_get_lbolt());
+ mutex_exit(&dp->tx_watcher_lock);
+
+ if (dp->tx_watcher_stop) {
+ break;
+ }
+
+ now = ddi_get_lbolt();
+
+ rwlock = RW_READER;
+again:
+ rw_enter(&dp->dev_state_lock, rwlock);
+
+ if ((dp->mac_state != MAC_STATE_DISCONNECTED &&
+ dp->fatal_error &&
+ now - dp->fatal_error >= dp->ugc.usbgc_tx_timeout) ||
+ (dp->mac_state == MAC_STATE_ONLINE &&
+ dp->mii_state == MII_STATE_LINKUP &&
+ dp->tx_busy_cnt != 0 &&
+ now - dp->tx_start_time >= dp->ugc.usbgc_tx_timeout)) {
+ if (rwlock == RW_READER) {
+ /*
+ * Upgrade dev_state_lock from shared mode
+ * to exclusive mode to restart nic
+ */
+ rwlock = RW_WRITER;
+ rw_exit(&dp->dev_state_lock);
+ goto again;
+ }
+ cmn_err(CE_WARN, "%s: %s: restarting the nic:"
+ " fatal_error:%ld nic_state:%d"
+ " mac_state:%d starttime:%ld",
+ dp->name, __func__,
+ dp->fatal_error ? now - dp->fatal_error: 0,
+ dp->nic_state, dp->mac_state,
+ dp->tx_busy_cnt ? now - dp->tx_start_time : 0);
+
+ (void) usbgem_restart_nic(dp);
+ }
+
+ rw_exit(&dp->dev_state_lock);
+ }
+}
+
+static int
+usbgem_tx_watcher_start(struct usbgem_dev *dp)
+{
+ int err;
+ kthread_t *wdth;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* make a first call of uwgem_lw_link_check() */
+ dp->tx_watcher_stop = 0;
+ dp->tx_watcher_interval = drv_usectohz(1000*1000);
+
+ wdth = thread_create(NULL, 0, usbgem_tx_timeout, dp, 0, &p0,
+ TS_RUN, minclsyspri);
+ if (wdth == NULL) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to create a tx_watcher thread",
+ dp->name, __func__);
+ return (USB_FAILURE);
+ }
+ dp->tx_watcher_did = wdth->t_did;
+
+ return (USB_SUCCESS);
+}
+
+static void
+usbgem_tx_watcher_stop(struct usbgem_dev *dp)
+{
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ if (dp->tx_watcher_did) {
+ /* Ensure timer routine stopped */
+ dp->tx_watcher_stop = 1;
+ cv_signal(&dp->tx_watcher_cv);
+ thread_join(dp->tx_watcher_did);
+ dp->tx_watcher_did = NULL;
+ }
+}
+
+/* ================================================================== */
+/*
+ * Callback handlers
+ */
+/* ================================================================== */
+static void
+usbgem_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+ mblk_t *newmp;
+ mblk_t *mp;
+ mblk_t *tp;
+ int len = 0;
+ int pkts = 0;
+ int bcast = 0;
+ int mcast = 0;
+ boolean_t busy;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)req->bulk_client_private;
+ mp = req->bulk_data;
+ req->bulk_data = NULL;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: mp:%p, cr:%s(%d)",
+ dp->name, __func__, mp,
+ usb_str_cr(req->bulk_completion_reason),
+ req->bulk_completion_reason));
+
+ /*
+ * we cannot acquire dev_state_lock because the routine
+ * must be executed during usbgem_mac_stop() to avoid
+ * dead lock.
+ * we use a simle membar operation to get the state correctly.
+ */
+ membar_consumer();
+
+ if (req->bulk_completion_reason == USB_CR_OK &&
+ dp->nic_state == NIC_STATE_ONLINE) {
+ newmp = (*dp->ugc.usbgc_rx_make_packet)(dp, mp);
+
+ if (newmp != mp) {
+ /* the message has been reallocated, free old one */
+ freemsg(mp);
+ }
+
+ /* the message may includes one or more ethernet packets */
+ for (tp = newmp; tp; tp = tp->b_next) {
+ len += tp->b_wptr - tp->b_rptr;
+ pkts++;
+ if (tp->b_rptr[0] & 1) {
+ if (bcmp(tp->b_rptr, &usbgem_bcastaddr,
+ ETHERADDRL) == 0) {
+ bcast++;
+ } else {
+ mcast++;
+ }
+ }
+ }
+
+ /* send up if it is a valid packet */
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_rx(dp->mh, NULL, newmp);
+#else
+ while (newmp) {
+ tp = newmp;
+ newmp = newmp->b_next;
+ tp->b_next = NULL;
+ gld_recv(dp->macinfo, tp);
+ }
+#endif
+ } else {
+ freemsg(mp);
+ len = 0;
+ }
+
+ mutex_enter(&dp->rxlock);
+ /* update rx_active */
+ if (dp->rx_active) {
+ dp->rx_active = dp->mac_state == MAC_STATE_ONLINE;
+ }
+
+ dp->stats.rbytes += len;
+ dp->stats.rpackets += pkts;
+ if (bcast | mcast) {
+ dp->stats.rbcast += bcast;
+ dp->stats.rmcast += mcast;
+ }
+ mutex_exit(&dp->rxlock);
+
+ if (dp->rx_active) {
+ /* prepare to receive the next packets */
+ if (usbgem_rx_start_unit(dp, req)) {
+ /* we successed */
+ goto done;
+ }
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to fill next rx packet",
+ dp->name, __func__);
+ /*
+ * we use another flag to indicate error state.
+ * if we acquire dev_state_lock for RW_WRITER here,
+ * usbgem_mac_stop() may hang.
+ */
+ if (dp->fatal_error == (clock_t)0) {
+ dp->fatal_error = usbgem_timestamp_nz();
+ }
+ } else {
+ /* no need to prepare the next packets */
+ usb_free_bulk_req(req);
+ }
+
+ mutex_enter(&dp->rxlock);
+ dp->rx_active = B_FALSE;
+ dp->rx_busy_cnt--;
+ if (dp->rx_busy_cnt == 0) {
+ /* wake up someone waits for me */
+ cv_broadcast(&dp->rx_drain_cv);
+ }
+ mutex_exit(&dp->rxlock);
+done:
+ ;
+}
+
+static void
+usbgem_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+ boolean_t intr;
+ boolean_t tx_sched;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)req->bulk_client_private;
+ tx_sched = B_FALSE;
+
+ DPRINTF(2, (CE_CONT,
+ "!%s: %s: cr:%s(%d) cb_flags:0x%x head:%d tail:%d",
+ dp->name, __func__,
+ usb_str_cr(req->bulk_completion_reason),
+ req->bulk_completion_reason,
+ req->bulk_cb_flags,
+ dp->tx_busy_cnt));
+
+ /* we have finished to transfer the packet into tx fifo */
+ intr = DB_TCI(req->bulk_data);
+ freemsg(req->bulk_data);
+
+ if (req->bulk_completion_reason != USB_CR_OK &&
+ dp->fatal_error == (clock_t)0) {
+ dp->fatal_error = usbgem_timestamp_nz();
+ }
+
+ mutex_enter(&dp->txlock);
+
+ if (intr) {
+ ASSERT(dp->tx_intr_pended > 0);
+ /* find the last interrupt we have scheduled */
+ if (--(dp->tx_intr_pended) == 0) {
+ tx_sched = B_TRUE;
+ }
+ }
+
+ ASSERT(dp->tx_busy_cnt > 0);
+ req->bulk_client_private = (usb_opaque_t)dp->tx_free_list;
+ dp->tx_free_list = req;
+ dp->tx_busy_cnt--;
+
+#ifdef CONFIG_TX_LIMITER
+ if (tx_sched) {
+ dp->tx_max_packets =
+ min(dp->tx_max_packets + 1, dp->ugc.usbgc_tx_list_max);
+ }
+#endif
+ if (dp->mac_state != MAC_STATE_ONLINE && dp->tx_busy_cnt == 0) {
+ cv_broadcast(&dp->tx_drain_cv);
+ }
+
+ mutex_exit(&dp->txlock);
+
+ if (tx_sched) {
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_tx_update(dp->mh);
+#else
+ gld_sched(dp->macinfo);
+#endif
+ }
+}
+
+static void
+usbgem_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
+{
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)req->intr_client_private;
+ dp->stats.intr++;
+
+ if (req->intr_completion_reason == USB_CR_OK) {
+ (*dp->ugc.usbgc_interrupt)(dp, req->intr_data);
+ }
+
+ /* free the request and data */
+ usb_free_intr_req(req);
+}
+
+/* ======================================================================== */
+/*
+ * MII support routines
+ */
+/* ======================================================================== */
+static void
+usbgem_choose_forcedmode(struct usbgem_dev *dp)
+{
+ /* choose media mode */
+ if (dp->anadv_1000fdx || dp->anadv_1000hdx) {
+ dp->speed = USBGEM_SPD_1000;
+ dp->full_duplex = dp->anadv_1000fdx;
+ } else if (dp->anadv_100fdx || dp->anadv_100t4) {
+ dp->speed = USBGEM_SPD_100;
+ dp->full_duplex = B_TRUE;
+ } else if (dp->anadv_100hdx) {
+ dp->speed = USBGEM_SPD_100;
+ dp->full_duplex = B_FALSE;
+ } else {
+ dp->speed = USBGEM_SPD_10;
+ dp->full_duplex = dp->anadv_10fdx;
+ }
+}
+
+static uint16_t
+usbgem_mii_read(struct usbgem_dev *dp, uint_t reg, int *errp)
+{
+ uint16_t val;
+
+ sema_p(&dp->hal_op_lock);
+ val = (*dp->ugc.usbgc_mii_read)(dp, reg, errp);
+ sema_v(&dp->hal_op_lock);
+
+ return (val);
+}
+
+static void
+usbgem_mii_write(struct usbgem_dev *dp, uint_t reg, uint16_t val, int *errp)
+{
+ sema_p(&dp->hal_op_lock);
+ (*dp->ugc.usbgc_mii_write)(dp, reg, val, errp);
+ sema_v(&dp->hal_op_lock);
+}
+
+static int
+usbgem_mii_probe(struct usbgem_dev *dp)
+{
+ int err;
+
+ err = (*dp->ugc.usbgc_mii_probe)(dp);
+ return (err);
+}
+
+static int
+usbgem_mii_init(struct usbgem_dev *dp)
+{
+ int err;
+
+ err = (*dp->ugc.usbgc_mii_init)(dp);
+ return (err);
+}
+
+#define fc_cap_decode(x) \
+ ((((x) & MII_ABILITY_PAUSE) != 0 ? 1 : 0) | \
+ (((x) & MII_ABILITY_ASM_DIR) != 0 ? 2 : 0))
+
+int
+usbgem_mii_config_default(struct usbgem_dev *dp, int *errp)
+{
+ uint16_t mii_stat;
+ uint16_t val;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /*
+ * Configure bits in advertisement register
+ */
+ mii_stat = dp->mii_status;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: MII_STATUS reg:%b",
+ dp->name, __func__, mii_stat, MII_STATUS_BITS));
+
+ if ((mii_stat & MII_STATUS_ABILITY_TECH) == 0) {
+ /* it's funny */
+ cmn_err(CE_WARN, "!%s: wrong ability bits: mii_status:%b",
+ dp->name, mii_stat, MII_STATUS_BITS);
+ return (USB_FAILURE);
+ }
+
+ /* Do not change the rest of ability bits in advert reg */
+ val = usbgem_mii_read(dp, MII_AN_ADVERT, errp) & ~MII_ABILITY_ALL;
+ if (*errp != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: 100T4:%d 100F:%d 100H:%d 10F:%d 10H:%d",
+ dp->name, __func__,
+ dp->anadv_100t4, dp->anadv_100fdx, dp->anadv_100hdx,
+ dp->anadv_10fdx, dp->anadv_10hdx));
+
+ /* set technology bits */
+ if (dp->anadv_100t4) {
+ val |= MII_ABILITY_100BASE_T4;
+ }
+ if (dp->anadv_100fdx) {
+ val |= MII_ABILITY_100BASE_TX_FD;
+ }
+ if (dp->anadv_100hdx) {
+ val |= MII_ABILITY_100BASE_TX;
+ }
+ if (dp->anadv_10fdx) {
+ val |= MII_ABILITY_10BASE_T_FD;
+ }
+ if (dp->anadv_10hdx) {
+ val |= MII_ABILITY_10BASE_T;
+ }
+
+ /* set flow control capabilities */
+ if (dp->anadv_pause) {
+ val |= MII_ABILITY_PAUSE;
+ }
+ if (dp->anadv_asmpause) {
+ val |= MII_ABILITY_ASM_DIR;
+ }
+
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: setting MII_AN_ADVERT reg:%b, pause:%d, asmpause:%d",
+ dp->name, __func__, val, MII_ABILITY_BITS,
+ dp->anadv_pause, dp->anadv_asmpause));
+
+ usbgem_mii_write(dp, MII_AN_ADVERT, val, errp);
+ if (*errp != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ if (dp->mii_status & MII_STATUS_XSTATUS) {
+ /*
+ * 1000Base-T GMII support
+ */
+ if (!dp->anadv_autoneg) {
+ /* enable manual configuration */
+ val = MII_1000TC_CFG_EN;
+ if (dp->anadv_1000t_ms == 2) {
+ val |= MII_1000TC_CFG_VAL;
+ }
+ } else {
+ val = 0;
+ if (dp->anadv_1000fdx) {
+ val |= MII_1000TC_ADV_FULL;
+ }
+ if (dp->anadv_1000hdx) {
+ val |= MII_1000TC_ADV_HALF;
+ }
+ switch (dp->anadv_1000t_ms) {
+ case 1:
+ /* slave */
+ val |= MII_1000TC_CFG_EN;
+ break;
+
+ case 2:
+ /* master */
+ val |= MII_1000TC_CFG_EN | MII_1000TC_CFG_VAL;
+ break;
+
+ default:
+ /* auto: do nothing */
+ break;
+ }
+ }
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: setting MII_1000TC reg:%b",
+ dp->name, __func__, val, MII_1000TC_BITS));
+
+ usbgem_mii_write(dp, MII_1000TC, val, errp);
+ if (*errp != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ return (USB_SUCCESS);
+
+usberr:
+ return (*errp);
+}
+
+static char *usbgem_fc_type[] = {
+ "without",
+ "with symmetric",
+ "with tx",
+ "with rx",
+};
+
+#ifdef USBGEM_CONFIG_GLDv3
+#define USBGEM_LINKUP(dp) mac_link_update((dp)->mh, LINK_STATE_UP)
+#define USBGEM_LINKDOWN(dp) mac_link_update((dp)->mh, LINK_STATE_DOWN)
+#else
+#define USBGEM_LINKUP(dp) \
+ if (gld_linkstate) { \
+ gld_linkstate((dp)->macinfo, GLD_LINKSTATE_UP); \
+ }
+#define USBGEM_LINKDOWN(dp) \
+ if (gld_linkstate) { \
+ gld_linkstate((dp)->macinfo, GLD_LINKSTATE_DOWN); \
+ }
+#endif
+
+static uint8_t usbgem_fc_result[4 /* my cap */][4 /* lp cap */] = {
+/* none symm tx rx/symm */
+/* none */
+ {FLOW_CONTROL_NONE,
+ FLOW_CONTROL_NONE,
+ FLOW_CONTROL_NONE,
+ FLOW_CONTROL_NONE},
+/* sym */
+ {FLOW_CONTROL_NONE,
+ FLOW_CONTROL_SYMMETRIC,
+ FLOW_CONTROL_NONE,
+ FLOW_CONTROL_SYMMETRIC},
+/* tx */
+ {FLOW_CONTROL_NONE,
+ FLOW_CONTROL_NONE,
+ FLOW_CONTROL_NONE,
+ FLOW_CONTROL_TX_PAUSE},
+/* rx/symm */
+ {FLOW_CONTROL_NONE,
+ FLOW_CONTROL_SYMMETRIC,
+ FLOW_CONTROL_RX_PAUSE,
+ FLOW_CONTROL_SYMMETRIC},
+};
+
+static boolean_t
+usbgem_mii_link_check(struct usbgem_dev *dp, int *oldstatep, int *newstatep)
+{
+ boolean_t tx_sched = B_FALSE;
+ uint16_t status;
+ uint16_t advert;
+ uint16_t lpable;
+ uint16_t exp;
+ uint16_t ctl1000;
+ uint16_t stat1000;
+ uint16_t val;
+ clock_t now;
+ clock_t diff;
+ int linkdown_action;
+ boolean_t fix_phy = B_FALSE;
+ int err;
+ uint_t rwlock;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: time:%d state:%d",
+ dp->name, __func__, ddi_get_lbolt(), dp->mii_state));
+
+ if (dp->mii_state != MII_STATE_LINKUP) {
+ rwlock = RW_WRITER;
+ } else {
+ rwlock = RW_READER;
+ }
+again:
+ rw_enter(&dp->dev_state_lock, rwlock);
+
+ /* save old mii state */
+ *oldstatep = dp->mii_state;
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ /* stop periodic execution of the link watcher */
+ dp->mii_interval = 0;
+ tx_sched = B_FALSE;
+ goto next;
+ }
+
+ now = ddi_get_lbolt();
+ diff = now - dp->mii_last_check;
+ dp->mii_last_check = now;
+
+ /*
+ * For NWAM, don't show linkdown state right
+ * when the device is attached.
+ */
+ if (dp->linkup_delay > 0) {
+ if (dp->linkup_delay > diff) {
+ dp->linkup_delay -= diff;
+ } else {
+ /* link up timeout */
+ dp->linkup_delay = -1;
+ }
+ }
+
+next_nowait:
+ switch (dp->mii_state) {
+ case MII_STATE_UNKNOWN:
+ goto reset_phy;
+
+ case MII_STATE_RESETTING:
+ dp->mii_timer -= diff;
+ if (dp->mii_timer > 0) {
+ /* don't read phy registers in resetting */
+ dp->mii_interval = WATCH_INTERVAL_FAST;
+ goto next;
+ }
+
+ val = usbgem_mii_read(dp, MII_CONTROL, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ if (val & MII_CONTROL_RESET) {
+ cmn_err(CE_NOTE,
+ "!%s: time:%ld resetting phy not complete."
+ " mii_control:0x%b",
+ dp->name, ddi_get_lbolt(),
+ val, MII_CONTROL_BITS);
+ }
+
+ /* ensure neither isolated nor pwrdown nor auto-nego mode */
+ usbgem_mii_write(dp, MII_CONTROL, 0, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+#if USBGEM_DEBUG_LEVEL > 10
+ val = usbgem_mii_read(dp, MII_CONTROL, &err);
+ cmn_err(CE_CONT, "!%s: readback control %b",
+ dp->name, val, MII_CONTROL_BITS);
+#endif
+ /* As resetting PHY has completed, configure PHY registers */
+ if ((*dp->ugc.usbgc_mii_config)(dp, &err) != USB_SUCCESS) {
+ /* we failed to configure PHY */
+ goto usberr;
+ }
+
+ /* prepare for forced mode */
+ usbgem_choose_forcedmode(dp);
+
+ dp->mii_lpable = 0;
+ dp->mii_advert = 0;
+ dp->mii_exp = 0;
+ dp->mii_ctl1000 = 0;
+ dp->mii_stat1000 = 0;
+
+ dp->flow_control = FLOW_CONTROL_NONE;
+
+ if (!dp->anadv_autoneg) {
+ /* skip auto-negotiation phase */
+ dp->mii_state = MII_STATE_MEDIA_SETUP;
+ dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
+ goto next_nowait;
+ }
+
+ /* issue an auto-negotiation command */
+ goto autonego;
+
+ case MII_STATE_AUTONEGOTIATING:
+ /*
+ * Autonegotiation in progress
+ */
+ dp->mii_timer -= diff;
+ if (dp->mii_timer -
+ (dp->ugc.usbgc_mii_an_timeout - dp->ugc.usbgc_mii_an_wait)
+ > 0) {
+ /* wait for minimum time (2.3 - 2.5 sec) */
+ dp->mii_interval = WATCH_INTERVAL_FAST;
+ goto next;
+ }
+
+ /* read PHY status */
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ DPRINTF(4, (CE_CONT,
+ "!%s: %s: called: mii_state:%d MII_STATUS reg:%b",
+ dp->name, __func__, dp->mii_state,
+ status, MII_STATUS_BITS));
+
+ if (status & MII_STATUS_REMFAULT) {
+ /*
+ * The link parnert told me something wrong happend.
+ * What do we do ?
+ */
+ cmn_err(CE_CONT,
+ "!%s: auto-negotiation failed: remote fault",
+ dp->name);
+ goto autonego;
+ }
+
+ if ((status & MII_STATUS_ANDONE) == 0) {
+ if (dp->mii_timer <= 0) {
+ /*
+ * Auto-negotiation has been timed out,
+ * Reset PHY and try again.
+ */
+ if (!dp->mii_supress_msg) {
+ cmn_err(CE_WARN,
+ "!%s: auto-negotiation failed:"
+ " timeout",
+ dp->name);
+ dp->mii_supress_msg = B_TRUE;
+ }
+ goto autonego;
+ }
+ /*
+ * Auto-negotiation is in progress. Wait for a while.
+ */
+ dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
+ goto next;
+ }
+
+ /*
+ * Auto-negotiation has been completed. Let's go to AN_DONE.
+ */
+ dp->mii_state = MII_STATE_AN_DONE;
+ dp->mii_supress_msg = B_FALSE;
+ DPRINTF(0, (CE_CONT,
+ "!%s: auto-negotiation completed, MII_STATUS:%b",
+ dp->name, status, MII_STATUS_BITS));
+
+ if (dp->ugc.usbgc_mii_an_delay > 0) {
+ dp->mii_timer = dp->ugc.usbgc_mii_an_delay;
+ dp->mii_interval = drv_usectohz(20*1000);
+ goto next;
+ }
+
+ dp->mii_timer = 0;
+ diff = 0;
+ goto next_nowait;
+
+ case MII_STATE_AN_DONE:
+ /*
+ * Auto-negotiation has done. Now we can set up media.
+ */
+ dp->mii_timer -= diff;
+ if (dp->mii_timer > 0) {
+ /* wait for a while */
+ dp->mii_interval = WATCH_INTERVAL_FAST;
+ goto next;
+ }
+
+ /*
+ * Setup speed and duplex mode according with
+ * the result of auto negotiation.
+ */
+
+ /*
+ * Read registers required to determin current
+ * duplex mode and media speed.
+ */
+ if (dp->ugc.usbgc_mii_an_delay > 0) {
+ /* the 'status' variable is not initialized yet */
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ advert = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ lpable = usbgem_mii_read(dp, MII_AN_LPABLE, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ exp = usbgem_mii_read(dp, MII_AN_EXPANSION, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ if (exp == 0xffff) {
+ /* some phys don't have exp register */
+ exp = 0;
+ }
+
+ ctl1000 = 0;
+ stat1000 = 0;
+ if (dp->mii_status & MII_STATUS_XSTATUS) {
+ ctl1000 = usbgem_mii_read(dp, MII_1000TC, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ stat1000 = usbgem_mii_read(dp, MII_1000TS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ dp->mii_lpable = lpable;
+ dp->mii_advert = advert;
+ dp->mii_exp = exp;
+ dp->mii_ctl1000 = ctl1000;
+ dp->mii_stat1000 = stat1000;
+
+ cmn_err(CE_CONT,
+ "!%s: auto-negotiation done: "
+ "status:%b, advert:%b, lpable:%b, exp:%b",
+ dp->name,
+ status, MII_STATUS_BITS,
+ advert, MII_ABILITY_BITS,
+ lpable, MII_ABILITY_BITS,
+ exp, MII_AN_EXP_BITS);
+
+ DPRINTF(0, (CE_CONT, "!%s: MII_STATUS:%b",
+ dp->name, status, MII_STATUS_BITS));
+
+ if (dp->mii_status & MII_STATUS_XSTATUS) {
+ cmn_err(CE_CONT,
+ "! MII_1000TC reg:%b, MII_1000TS reg:%b",
+ ctl1000, MII_1000TC_BITS,
+ stat1000, MII_1000TS_BITS);
+ }
+
+ if (usbgem_population(lpable) <= 1 &&
+ (exp & MII_AN_EXP_LPCANAN) == 0) {
+ if ((advert & MII_ABILITY_TECH) != lpable) {
+ cmn_err(CE_WARN,
+ "!%s: but the link partner doesn't seem"
+ " to have auto-negotiation capability."
+ " please check the link configuration.",
+ dp->name);
+ }
+ /*
+ * it should be a result of pararell detection,
+ * which cannot detect duplex mode.
+ */
+ if ((advert & lpable) == 0 &&
+ lpable & MII_ABILITY_10BASE_T) {
+ /* no common technology, try 10M half mode */
+ lpable |= advert & MII_ABILITY_10BASE_T;
+ fix_phy = B_TRUE;
+ }
+ } else if (lpable == 0) {
+ cmn_err(CE_WARN, "!%s: wrong lpable.", dp->name);
+ goto reset_phy;
+ }
+ /*
+ * configure current link mode according to AN priority.
+ */
+ val = advert & lpable;
+ if ((ctl1000 & MII_1000TC_ADV_FULL) &&
+ (stat1000 & MII_1000TS_LP_FULL)) {
+ /* 1000BaseT & full duplex */
+ dp->speed = USBGEM_SPD_1000;
+ dp->full_duplex = B_TRUE;
+ } else if ((ctl1000 & MII_1000TC_ADV_HALF) &&
+ (stat1000 & MII_1000TS_LP_HALF)) {
+ /* 1000BaseT & half duplex */
+ dp->speed = USBGEM_SPD_1000;
+ dp->full_duplex = B_FALSE;
+ } else if ((val & MII_ABILITY_100BASE_TX_FD)) {
+ /* 100BaseTx & fullduplex */
+ dp->speed = USBGEM_SPD_100;
+ dp->full_duplex = B_TRUE;
+ } else if ((val & MII_ABILITY_100BASE_T4)) {
+ /* 100BaseTx & fullduplex */
+ dp->speed = USBGEM_SPD_100;
+ dp->full_duplex = B_TRUE;
+ } else if ((val & MII_ABILITY_100BASE_TX)) {
+ /* 100BaseTx & half duplex */
+ dp->speed = USBGEM_SPD_100;
+ dp->full_duplex = B_FALSE;
+ } else if ((val & MII_ABILITY_10BASE_T_FD)) {
+ /* 10BaseT & full duplex */
+ dp->speed = USBGEM_SPD_10;
+ dp->full_duplex = B_TRUE;
+ } else if ((val & MII_ABILITY_10BASE_T)) {
+ /* 10BaseT & half duplex */
+ dp->speed = USBGEM_SPD_10;
+ dp->full_duplex = B_FALSE;
+ } else {
+ /*
+ * the link partner doesn't seem to have
+ * auto-negotiation capability and our PHY
+ * could not report current mode correctly.
+ * We guess current mode by mii_control register.
+ */
+ val = usbgem_mii_read(dp, MII_CONTROL, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ /* select 100m half or 10m half */
+ dp->speed = (val & MII_CONTROL_100MB) ?
+ USBGEM_SPD_100 : USBGEM_SPD_10;
+ dp->full_duplex = B_FALSE;
+ fix_phy = B_TRUE;
+
+ cmn_err(CE_NOTE,
+ "!%s: auto-negotiation done but "
+ "common ability not found.\n"
+ "PHY state: control:%b advert:%b lpable:%b\n"
+ "guessing %d Mbps %s duplex mode",
+ dp->name,
+ val, MII_CONTROL_BITS,
+ advert, MII_ABILITY_BITS,
+ lpable, MII_ABILITY_BITS,
+ usbgem_speed_value[dp->speed],
+ dp->full_duplex ? "full" : "half");
+ }
+
+ if (dp->full_duplex) {
+ dp->flow_control =
+ usbgem_fc_result[fc_cap_decode(advert)]
+ [fc_cap_decode(lpable)];
+ } else {
+ dp->flow_control = FLOW_CONTROL_NONE;
+ }
+ dp->mii_state = MII_STATE_MEDIA_SETUP;
+ dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
+ goto next_nowait;
+
+ case MII_STATE_MEDIA_SETUP:
+ DPRINTF(2, (CE_CONT, "!%s: setup midia mode", dp->name));
+
+ /* assume the link state is down */
+ dp->mii_state = MII_STATE_LINKDOWN;
+ dp->mii_supress_msg = B_FALSE;
+
+ /* use short interval */
+ dp->mii_interval = WATCH_INTERVAL_FAST;
+
+ if ((!dp->anadv_autoneg) ||
+ dp->ugc.usbgc_mii_an_oneshot || fix_phy) {
+
+ /*
+ * write the result of auto negotiation back.
+ */
+ val = usbgem_mii_read(dp, MII_CONTROL, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ val &= ~(MII_CONTROL_SPEED | MII_CONTROL_FDUPLEX |
+ MII_CONTROL_ANE | MII_CONTROL_RSAN);
+
+ if (dp->full_duplex) {
+ val |= MII_CONTROL_FDUPLEX;
+ }
+
+ switch (dp->speed) {
+ case USBGEM_SPD_1000:
+ val |= MII_CONTROL_1000MB;
+ break;
+
+ case USBGEM_SPD_100:
+ val |= MII_CONTROL_100MB;
+ break;
+
+ default:
+ cmn_err(CE_WARN, "%s: unknown speed:%d",
+ dp->name, dp->speed);
+ /* FALLTHROUGH */
+
+ case USBGEM_SPD_10:
+ /* for USBGEM_SPD_10, do nothing */
+ break;
+ }
+
+ if (dp->mii_status & MII_STATUS_XSTATUS) {
+ usbgem_mii_write(dp,
+ MII_1000TC, MII_1000TC_CFG_EN, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ usbgem_mii_write(dp, MII_CONTROL, val, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ /*
+ * XXX -- nic state should be one of
+ * NIC_STATE_DISCONNECTED
+ * NIC_STATE_STOPPED
+ * NIC_STATE_INITIALIZED
+ * NIC_STATE_ONLINE
+ */
+ if (dp->nic_state >= NIC_STATE_INITIALIZED) {
+ /* notify the result of autonegotiation to mac */
+ if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ goto next_nowait;
+
+ case MII_STATE_LINKDOWN:
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ if (status & MII_STATUS_LINKUP) {
+ /*
+ * Link is going up
+ */
+ dp->mii_state = MII_STATE_LINKUP;
+ dp->mii_supress_msg = B_FALSE;
+
+ DPRINTF(0, (CE_CONT,
+ "!%s: link up detected: status:%b",
+ dp->name, status, MII_STATUS_BITS));
+
+ /*
+ * MII_CONTROL_100MB and MII_CONTROL_FDUPLEX are
+ * ignored when MII_CONTROL_ANE is set.
+ */
+ cmn_err(CE_CONT,
+ "!%s: Link up: %d Mbps %s duplex %s flow control",
+ dp->name,
+ usbgem_speed_value[dp->speed],
+ dp->full_duplex ? "full" : "half",
+ usbgem_fc_type[dp->flow_control]);
+
+ dp->mii_interval =
+ dp->ugc.usbgc_mii_link_watch_interval;
+
+ if (dp->ugc.usbgc_mii_hw_link_detection &&
+ dp->nic_state == NIC_STATE_ONLINE) {
+ dp->mii_interval = 0;
+ }
+
+ if (dp->nic_state == NIC_STATE_ONLINE) {
+ if (dp->mac_state == MAC_STATE_INITIALIZED) {
+ (void) usbgem_mac_start(dp);
+ }
+ tx_sched = B_TRUE;
+ }
+
+ goto next;
+ }
+
+ dp->mii_supress_msg = B_TRUE;
+ if (dp->anadv_autoneg) {
+ dp->mii_timer -= diff;
+ if (dp->mii_timer <= 0) {
+ /*
+ * the link down timer expired.
+ * need to restart auto-negotiation.
+ */
+ linkdown_action =
+ dp->ugc.usbgc_mii_linkdown_timeout_action;
+ goto restart_autonego;
+ }
+ }
+ /* don't change mii_state */
+ goto next;
+
+ case MII_STATE_LINKUP:
+ if (rwlock == RW_READER) {
+ /* first pass, read mii status */
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ if ((status & MII_STATUS_LINKUP) == 0) {
+ /*
+ * Link is going down
+ */
+ cmn_err(CE_NOTE,
+ "!%s: link down detected: status:%b",
+ dp->name, status, MII_STATUS_BITS);
+ /*
+ * Acquire exclusive lock to change mii_state
+ */
+ if (rwlock == RW_READER) {
+ rwlock = RW_WRITER;
+ rw_exit(&dp->dev_state_lock);
+ goto again;
+ }
+
+ dp->mii_state = MII_STATE_LINKDOWN;
+ dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout;
+
+ /*
+ * As we may change the state of the device,
+ * let us acquire exclusive lock for the state.
+ */
+ if (dp->nic_state == NIC_STATE_ONLINE &&
+ dp->mac_state == MAC_STATE_ONLINE &&
+ dp->ugc.usbgc_mii_stop_mac_on_linkdown) {
+ (void) usbgem_restart_nic(dp);
+ /* drain tx */
+ tx_sched = B_TRUE;
+ }
+
+ if (dp->anadv_autoneg) {
+ /* need to restart auto-negotiation */
+ linkdown_action =
+ dp->ugc.usbgc_mii_linkdown_action;
+ goto restart_autonego;
+ }
+ /*
+ * don't use hw link down detection until the link
+ * status become stable for a while.
+ */
+ dp->mii_interval =
+ dp->ugc.usbgc_mii_link_watch_interval;
+
+ goto next;
+ }
+
+ /*
+ * still link up, no need to change mii_state
+ */
+ if (dp->ugc.usbgc_mii_hw_link_detection &&
+ dp->nic_state == NIC_STATE_ONLINE) {
+ /*
+ * no need to check link status periodicly
+ * if nic can generate interrupts when link go down.
+ */
+ dp->mii_interval = 0;
+ }
+ goto next;
+ }
+ /* NOTREACHED */
+ cmn_err(CE_PANIC, "!%s: %s: not reached", dp->name, __func__);
+
+ /*
+ * Actions for new state.
+ */
+restart_autonego:
+ switch (linkdown_action) {
+ case MII_ACTION_RESET:
+ if (!dp->mii_supress_msg) {
+ cmn_err(CE_CONT, "!%s: resetting PHY", dp->name);
+ }
+ dp->mii_supress_msg = B_TRUE;
+ goto reset_phy;
+
+ case MII_ACTION_NONE:
+ dp->mii_supress_msg = B_TRUE;
+ if (dp->ugc.usbgc_mii_an_oneshot) {
+ goto autonego;
+ }
+ /* PHY will restart autonego automatically */
+ dp->mii_state = MII_STATE_AUTONEGOTIATING;
+ dp->mii_timer = dp->ugc.usbgc_mii_an_timeout;
+ dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
+ goto next;
+
+ case MII_ACTION_RSA:
+ if (!dp->mii_supress_msg) {
+ cmn_err(CE_CONT, "!%s: restarting auto-negotiation",
+ dp->name);
+ }
+ dp->mii_supress_msg = B_TRUE;
+ goto autonego;
+
+ default:
+ cmn_err(CE_PANIC, "!%s: unknowm linkdown action: %d",
+ dp->name, dp->ugc.usbgc_mii_linkdown_action);
+ dp->mii_supress_msg = B_TRUE;
+ }
+ /* NOTREACHED */
+
+reset_phy:
+ if (!dp->mii_supress_msg) {
+ cmn_err(CE_CONT, "!%s: resetting PHY", dp->name);
+ }
+ dp->mii_state = MII_STATE_RESETTING;
+ dp->mii_timer = dp->ugc.usbgc_mii_reset_timeout;
+ if (!dp->ugc.usbgc_mii_dont_reset) {
+ usbgem_mii_write(dp, MII_CONTROL, MII_CONTROL_RESET, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ }
+ dp->mii_interval = WATCH_INTERVAL_FAST;
+ goto next;
+
+autonego:
+ if (!dp->mii_supress_msg) {
+ cmn_err(CE_CONT, "!%s: auto-negotiation started", dp->name);
+ }
+ dp->mii_state = MII_STATE_AUTONEGOTIATING;
+ dp->mii_timer = dp->ugc.usbgc_mii_an_timeout;
+
+ /* start/restart autoneg */
+ val = usbgem_mii_read(dp, MII_CONTROL, &err) &
+ ~(MII_CONTROL_ISOLATE | MII_CONTROL_PWRDN | MII_CONTROL_RESET);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ if (val & MII_CONTROL_ANE) {
+ val |= MII_CONTROL_RSAN;
+ }
+ usbgem_mii_write(dp, MII_CONTROL,
+ val | dp->ugc.usbgc_mii_an_cmd | MII_CONTROL_ANE, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval;
+ goto next;
+
+usberr:
+ dp->mii_state = MII_STATE_UNKNOWN;
+ dp->mii_interval = dp->ugc.usbgc_mii_link_watch_interval;
+ tx_sched = B_TRUE;
+
+next:
+ *newstatep = dp->mii_state;
+ rw_exit(&dp->dev_state_lock);
+ return (tx_sched);
+}
+
+static void
+usbgem_mii_link_watcher(struct usbgem_dev *dp)
+{
+ int old_mii_state;
+ int new_mii_state;
+ boolean_t tx_sched;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ for (; ; ) {
+
+ mutex_enter(&dp->link_watcher_lock);
+ if (dp->mii_interval) {
+ (void) cv_timedwait(&dp->link_watcher_wait_cv,
+ &dp->link_watcher_lock,
+ dp->mii_interval + ddi_get_lbolt());
+ } else {
+ cv_wait(&dp->link_watcher_wait_cv,
+ &dp->link_watcher_lock);
+ }
+ mutex_exit(&dp->link_watcher_lock);
+
+ if (dp->link_watcher_stop) {
+ break;
+ }
+
+ /* we block callbacks from disconnect/suspend and restart */
+ tx_sched = usbgem_mii_link_check(dp,
+ &old_mii_state, &new_mii_state);
+
+ /*
+ * gld v2 notifier functions are not able to
+ * be called with any locks in this layer.
+ */
+ if (tx_sched) {
+ /* kick potentially stopped downstream */
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_tx_update(dp->mh);
+#else
+ gld_sched(dp->macinfo);
+#endif
+ }
+
+ if (old_mii_state != new_mii_state) {
+ /* notify new mii link state */
+ if (new_mii_state == MII_STATE_LINKUP) {
+ dp->linkup_delay = 0;
+ USBGEM_LINKUP(dp);
+ } else if (dp->linkup_delay <= 0) {
+ USBGEM_LINKDOWN(dp);
+ }
+ } else if (dp->linkup_delay < 0) {
+ /* first linkup timeout */
+ dp->linkup_delay = 0;
+ USBGEM_LINKDOWN(dp);
+ }
+ }
+
+ thread_exit();
+}
+
+void
+usbgem_mii_update_link(struct usbgem_dev *dp)
+{
+ cv_signal(&dp->link_watcher_wait_cv);
+}
+
+int
+usbgem_mii_probe_default(struct usbgem_dev *dp)
+{
+ int phy;
+ uint16_t status;
+ uint16_t xstatus;
+ int err;
+ uint16_t adv;
+ uint16_t adv_org;
+
+ DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /*
+ * Scan PHY
+ */
+ dp->mii_status = 0;
+
+ /* Try default phy first */
+ if (dp->mii_phy_addr) {
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ if (status != 0xffff && status != 0x0000) {
+ goto PHY_found;
+ }
+
+ if (dp->mii_phy_addr < 0) {
+ cmn_err(CE_NOTE,
+ "!%s: failed to probe default internal and/or non-MII PHY",
+ dp->name);
+ return (USB_FAILURE);
+ }
+
+ cmn_err(CE_NOTE,
+ "!%s: failed to probe default MII PHY at %d",
+ dp->name, dp->mii_phy_addr);
+ }
+
+ /* Try all possible address */
+ for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) {
+ dp->mii_phy_addr = phy;
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_read(status) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+
+ if (status != 0xffff && status != 0x0000) {
+ usbgem_mii_write(dp, MII_CONTROL, 0, &err);
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_write(control) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+ goto PHY_found;
+ }
+ }
+ for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) {
+ dp->mii_phy_addr = phy;
+ usbgem_mii_write(dp, MII_CONTROL, 0, &err);
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_write(control) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+ status = usbgem_mii_read(dp, MII_STATUS, &err);
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_read(status) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+
+ if (status != 0xffff && status != 0) {
+ goto PHY_found;
+ }
+ }
+
+ cmn_err(CE_NOTE, "!%s: no MII PHY found", dp->name);
+ return (USB_FAILURE);
+
+PHY_found:
+ dp->mii_status = status;
+ dp->mii_status_ro = ~status;
+ dp->mii_phy_id = usbgem_mii_read(dp, MII_PHYIDH, &err) << 16;
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_read(PHYIDH) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+ dp->mii_phy_id |= usbgem_mii_read(dp, MII_PHYIDL, &err);
+ if (err != USB_SUCCESS) {
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mii_read(PHYIDL) failed",
+ dp->name, __func__));
+ goto usberr;
+ }
+
+ if (dp->mii_phy_addr < 0) {
+ cmn_err(CE_CONT, "!%s: using internal/non-MII PHY(0x%08x)",
+ dp->name, dp->mii_phy_id);
+ } else {
+ cmn_err(CE_CONT, "!%s: MII PHY (0x%08x) found at %d",
+ dp->name, dp->mii_phy_id, dp->mii_phy_addr);
+ }
+
+ cmn_err(CE_CONT,
+ "!%s: PHY control:%b, status:%b, advert:%b, lpar:%b, exp:%b",
+ dp->name,
+ usbgem_mii_read(dp, MII_CONTROL, &err), MII_CONTROL_BITS,
+ status, MII_STATUS_BITS,
+ usbgem_mii_read(dp, MII_AN_ADVERT, &err), MII_ABILITY_BITS,
+ usbgem_mii_read(dp, MII_AN_LPABLE, &err), MII_ABILITY_BITS,
+ usbgem_mii_read(dp, MII_AN_EXPANSION, &err), MII_AN_EXP_BITS);
+
+ dp->mii_xstatus = 0;
+ if (status & MII_STATUS_XSTATUS) {
+ dp->mii_xstatus = usbgem_mii_read(dp, MII_XSTATUS, &err);
+
+ cmn_err(CE_CONT, "!%s: xstatus:%b",
+ dp->name, dp->mii_xstatus, MII_XSTATUS_BITS);
+ }
+ dp->mii_xstatus_ro = ~dp->mii_xstatus;
+
+ /* check if the phy can advertize pause abilities */
+ adv_org = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ usbgem_mii_write(dp, MII_AN_ADVERT,
+ MII_ABILITY_PAUSE | MII_ABILITY_ASM_DIR, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ adv = usbgem_mii_read(dp, MII_AN_ADVERT, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+
+ if ((adv & MII_ABILITY_PAUSE) == 0) {
+ dp->ugc.usbgc_flow_control &= ~1;
+ }
+
+ if ((adv & MII_ABILITY_ASM_DIR) == 0) {
+ dp->ugc.usbgc_flow_control &= ~2;
+ }
+
+ usbgem_mii_write(dp, MII_AN_ADVERT, adv_org, &err);
+ if (err != USB_SUCCESS) {
+ goto usberr;
+ }
+ return (USB_SUCCESS);
+
+usberr:
+ return (USB_FAILURE);
+}
+
+int
+usbgem_mii_init_default(struct usbgem_dev *dp)
+{
+ /* ENPTY */
+ return (USB_SUCCESS);
+}
+
+static int
+usbgem_mii_start(struct usbgem_dev *dp)
+{
+ int err;
+ kthread_t *lwth;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* make a first call of usbgem_mii_link_check() */
+ dp->link_watcher_stop = 0;
+ dp->mii_state = MII_STATE_UNKNOWN;
+ dp->mii_interval = drv_usectohz(1000*1000); /* 1sec */
+ dp->mii_last_check = ddi_get_lbolt();
+ dp->linkup_delay = 600 * drv_usectohz(1000*1000); /* 10 minutes */
+
+ lwth = thread_create(NULL, 0, usbgem_mii_link_watcher, dp, 0, &p0,
+ TS_RUN, minclsyspri);
+ if (lwth == NULL) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to create a link watcher thread",
+ dp->name, __func__);
+ return (USB_FAILURE);
+ }
+ dp->link_watcher_did = lwth->t_did;
+
+ return (USB_SUCCESS);
+}
+
+static void
+usbgem_mii_stop(struct usbgem_dev *dp)
+{
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* Ensure timer routine stopped */
+ dp->link_watcher_stop = 1;
+ cv_signal(&dp->link_watcher_wait_cv);
+ thread_join(dp->link_watcher_did);
+}
+
+/* ============================================================== */
+/*
+ * internal mac register operation interface
+ */
+/* ============================================================== */
+/*
+ * usbgem_mac_init: cold start
+ */
+static int
+usbgem_mac_init(struct usbgem_dev *dp)
+{
+ int err;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ /* pretend we succeeded */
+ return (USB_SUCCESS);
+ }
+
+ ASSERT(dp->mac_state == MAC_STATE_STOPPED);
+
+ /* reset fatal error timestamp */
+ dp->fatal_error = (clock_t)0;
+
+ /* reset tx side state */
+ mutex_enter(&dp->txlock);
+ dp->tx_busy_cnt = 0;
+ dp->tx_max_packets = dp->ugc.usbgc_tx_list_max;
+ mutex_exit(&dp->txlock);
+
+ /* reset rx side state */
+ mutex_enter(&dp->rxlock);
+ dp->rx_busy_cnt = 0;
+ mutex_exit(&dp->rxlock);
+
+ err = usbgem_hal_init_chip(dp);
+ if (err == USB_SUCCESS) {
+ dp->mac_state = MAC_STATE_INITIALIZED;
+ }
+
+ return (err);
+}
+
+/*
+ * usbgem_mac_start: warm start
+ */
+static int
+usbgem_mac_start(struct usbgem_dev *dp)
+{
+ int err;
+ int i;
+ usb_flags_t flags = 0;
+ usb_intr_req_t *req;
+#ifdef USBGEM_DEBUG_LEVEL
+ usb_pipe_state_t p_state;
+#endif
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ /* do nothing but don't return failure */
+ return (USB_SUCCESS);
+ }
+
+ if (dp->mac_state != MAC_STATE_INITIALIZED) {
+ /* don't return failer */
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: mac_state(%d) is not MAC_STATE_INITIALIZED",
+ dp->name, __func__, dp->mac_state));
+ goto x;
+ }
+
+ dp->mac_state = MAC_STATE_ONLINE;
+
+ if (usbgem_hal_start_chip(dp) != USB_SUCCESS) {
+ cmn_err(CE_NOTE,
+ "!%s: %s: usb error was detected during start_chip",
+ dp->name, __func__);
+ goto x;
+ }
+
+#ifdef USBGEM_DEBUG_LEVEL
+ usb_pipe_get_state(dp->intr_pipe, &p_state, 0);
+ ASSERT(p_state == USB_PIPE_STATE_IDLE);
+#endif /* USBGEM_DEBUG_LEVEL */
+
+ if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
+
+ /* make a request for interrupt */
+
+ req = usb_alloc_intr_req(dp->dip, 0, USB_FLAGS_SLEEP);
+ if (req == NULL) {
+ cmn_err(CE_WARN, "!%s: %s: failed to allocate intreq",
+ dp->name, __func__);
+ goto x;
+ }
+ req->intr_data = NULL;
+ req->intr_client_private = (usb_opaque_t)dp;
+ req->intr_timeout = 0;
+ req->intr_attributes =
+ USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
+ req->intr_len = dp->ep_intr->wMaxPacketSize;
+ req->intr_cb = usbgem_intr_cb;
+ req->intr_exc_cb = usbgem_intr_cb;
+ req->intr_completion_reason = 0;
+ req->intr_cb_flags = 0;
+
+ err = usb_pipe_intr_xfer(dp->intr_pipe, req, flags);
+ if (err != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "%s: err:%d failed to start polling of intr pipe",
+ dp->name, err);
+ goto x;
+ }
+ }
+
+ /* kick to receive the first packet */
+ if (usbgem_init_rx_buf(dp) != USB_SUCCESS) {
+ goto err_stop_intr;
+ }
+ dp->rx_active = B_TRUE;
+
+ return (USB_SUCCESS);
+
+err_stop_intr:
+ /* stop the interrupt pipe */
+ DPRINTF(0, (CE_CONT, "!%s: %s: FAULURE", dp->name, __func__));
+ if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
+ usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP);
+ }
+x:
+ ASSERT(dp->mac_state == MAC_STATE_ONLINE);
+ /* we use another flag to indicate error state. */
+ if (dp->fatal_error == (clock_t)0) {
+ dp->fatal_error = usbgem_timestamp_nz();
+ }
+ return (USB_FAILURE);
+}
+
+static int
+usbgem_mac_stop(struct usbgem_dev *dp, int new_state, boolean_t graceful)
+{
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /*
+ * we must have writer lock for dev_state_lock
+ */
+ ASSERT(new_state == MAC_STATE_STOPPED
+ || new_state == MAC_STATE_DISCONNECTED);
+
+ /* stop polling interrupt pipe */
+ if (dp->ugc.usbgc_interrupt && dp->intr_pipe) {
+ usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP);
+ }
+
+ if (new_state == MAC_STATE_STOPPED || graceful) {
+ /* stop the nic hardware completely */
+ if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) {
+ (void) usbgem_hal_reset_chip(dp);
+ }
+ }
+
+ /* stop preparing new rx packets and sending new packets */
+ dp->mac_state = new_state;
+
+ /* other processors must get mac_state correctly after here */
+ membar_producer();
+
+ /* cancel all requests we have sent */
+ usb_pipe_reset(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ usb_pipe_reset(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
+
+ DPRINTF(0, (CE_CONT,
+ "!%s: %s: rx_busy_cnt:%d tx_busy_cnt:%d",
+ dp->name, __func__, dp->rx_busy_cnt, dp->tx_busy_cnt));
+
+ /*
+ * Here all rx packets has been cancelled and their call back
+ * function has been exeuted, because we called usb_pipe_reset
+ * synchronously.
+ * So actually we just ensure rx_busy_cnt == 0.
+ */
+ mutex_enter(&dp->rxlock);
+ while (dp->rx_busy_cnt > 0) {
+ cv_wait(&dp->rx_drain_cv, &dp->rxlock);
+ }
+ mutex_exit(&dp->rxlock);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: rx_busy_cnt is %d now",
+ dp->name, __func__, dp->rx_busy_cnt));
+
+ mutex_enter(&dp->txlock);
+ while (dp->tx_busy_cnt > 0) {
+ cv_wait(&dp->tx_drain_cv, &dp->txlock);
+ }
+ mutex_exit(&dp->txlock);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: tx_busy_cnt is %d now",
+ dp->name, __func__, dp->tx_busy_cnt));
+
+ return (USB_SUCCESS);
+}
+
+static int
+usbgem_add_multicast(struct usbgem_dev *dp, const uint8_t *ep)
+{
+ int cnt;
+ int err;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ sema_p(&dp->rxfilter_lock);
+ if (dp->mc_count_req++ < USBGEM_MAXMC) {
+ /* append the new address at the end of the mclist */
+ cnt = dp->mc_count;
+ bcopy(ep, dp->mc_list[cnt].addr.ether_addr_octet,
+ ETHERADDRL);
+ if (dp->ugc.usbgc_multicast_hash) {
+ dp->mc_list[cnt].hash =
+ (*dp->ugc.usbgc_multicast_hash)(dp, ep);
+ }
+ dp->mc_count = cnt + 1;
+ }
+
+ if (dp->mc_count_req != dp->mc_count) {
+ /* multicast address list overflow */
+ dp->rxmode |= RXMODE_MULTI_OVF;
+ } else {
+ dp->rxmode &= ~RXMODE_MULTI_OVF;
+ }
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ /* tell new multicast list to the hardware */
+ err = usbgem_hal_set_rx_filter(dp);
+ }
+ sema_v(&dp->rxfilter_lock);
+
+ return (err);
+}
+
+static int
+usbgem_remove_multicast(struct usbgem_dev *dp, const uint8_t *ep)
+{
+ size_t len;
+ int i;
+ int cnt;
+ int err;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ sema_p(&dp->rxfilter_lock);
+ dp->mc_count_req--;
+ cnt = dp->mc_count;
+ for (i = 0; i < cnt; i++) {
+ if (bcmp(ep, &dp->mc_list[i].addr, ETHERADDRL)) {
+ continue;
+ }
+ /* shrink the mclist by copying forward */
+ len = (cnt - (i + 1)) * sizeof (*dp->mc_list);
+ if (len > 0) {
+ bcopy(&dp->mc_list[i+1], &dp->mc_list[i], len);
+ }
+ dp->mc_count--;
+ break;
+ }
+
+ if (dp->mc_count_req != dp->mc_count) {
+ /* multicast address list overflow */
+ dp->rxmode |= RXMODE_MULTI_OVF;
+ } else {
+ dp->rxmode &= ~RXMODE_MULTI_OVF;
+ }
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ err = usbgem_hal_set_rx_filter(dp);
+ }
+ sema_v(&dp->rxfilter_lock);
+
+ return (err);
+}
+
+
+/* ============================================================== */
+/*
+ * ioctl
+ */
+/* ============================================================== */
+enum ioc_reply {
+ IOC_INVAL = -1, /* bad, NAK with EINVAL */
+ IOC_DONE, /* OK, reply sent */
+ IOC_ACK, /* OK, just send ACK */
+ IOC_REPLY, /* OK, just send reply */
+ IOC_RESTART_ACK, /* OK, restart & ACK */
+ IOC_RESTART_REPLY /* OK, restart & reply */
+};
+
+
+#ifdef USBGEM_CONFIG_MAC_PROP
+static int
+usbgem_get_def_val(struct usbgem_dev *dp, mac_prop_id_t pr_num,
+ uint_t pr_valsize, void *pr_val)
+{
+ link_flowctrl_t fl;
+ int err = 0;
+
+ ASSERT(pr_valsize > 0);
+ switch (pr_num) {
+ case MAC_PROP_AUTONEG:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ if (pr_valsize < sizeof (link_flowctrl_t)) {
+ return (EINVAL);
+ }
+ switch (dp->ugc.usbgc_flow_control) {
+ case FLOW_CONTROL_NONE:
+ fl = LINK_FLOWCTRL_NONE;
+ break;
+ case FLOW_CONTROL_SYMMETRIC:
+ fl = LINK_FLOWCTRL_BI;
+ break;
+ case FLOW_CONTROL_TX_PAUSE:
+ fl = LINK_FLOWCTRL_TX;
+ break;
+ case FLOW_CONTROL_RX_PAUSE:
+ fl = LINK_FLOWCTRL_RX;
+ break;
+ }
+ bcopy(&fl, pr_val, sizeof (fl));
+ break;
+
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_EN_1000FDX_CAP:
+ *(uint8_t *)pr_val =
+ (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
+ break;
+
+ case MAC_PROP_ADV_1000HDX_CAP:
+ case MAC_PROP_EN_1000HDX_CAP:
+ *(uint8_t *)pr_val =
+ (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
+ break;
+
+ case MAC_PROP_ADV_100T4_CAP:
+ case MAC_PROP_EN_100T4_CAP:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
+ break;
+
+ case MAC_PROP_ADV_100FDX_CAP:
+ case MAC_PROP_EN_100FDX_CAP:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
+ break;
+
+ case MAC_PROP_ADV_100HDX_CAP:
+ case MAC_PROP_EN_100HDX_CAP:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
+ break;
+
+ case MAC_PROP_ADV_10FDX_CAP:
+ case MAC_PROP_EN_10FDX_CAP:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
+ break;
+
+ case MAC_PROP_ADV_10HDX_CAP:
+ case MAC_PROP_EN_10HDX_CAP:
+ *(uint8_t *)pr_val =
+ BOOLEAN(dp->mii_status & MII_STATUS_10);
+ break;
+
+ default:
+ err = ENOTSUP;
+ break;
+ }
+ return (err);
+}
+
+#ifdef MAC_VERSION_V1
+static void
+usbgem_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ mac_prop_info_handle_t prh)
+{
+ struct usbgem_dev *dp = arg;
+ link_flowctrl_t fl;
+
+ /*
+ * By default permissions are read/write unless specified
+ * otherwise by the driver.
+ */
+
+ switch (pr_num) {
+ case MAC_PROP_DUPLEX:
+ case MAC_PROP_SPEED:
+ case MAC_PROP_STATUS:
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_ADV_1000HDX_CAP:
+ case MAC_PROP_ADV_100FDX_CAP:
+ case MAC_PROP_ADV_100HDX_CAP:
+ case MAC_PROP_ADV_10FDX_CAP:
+ case MAC_PROP_ADV_10HDX_CAP:
+ case MAC_PROP_ADV_100T4_CAP:
+ case MAC_PROP_EN_100T4_CAP:
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ break;
+
+ case MAC_PROP_EN_1000FDX_CAP:
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(
+ dp->mii_xstatus & MII_XSTATUS_1000BASET_FD));
+ } else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD)
+ == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(
+ dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_EN_1000HDX_CAP:
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(
+ dp->mii_xstatus & MII_XSTATUS_1000BASET));
+ } else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(
+ dp->mii_xstatus & MII_XSTATUS_1000BASEX));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_EN_100FDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_EN_100HDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_EN_10FDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(dp->mii_status & MII_STATUS_10_FD));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_EN_10HDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_10) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(dp->mii_status & MII_STATUS_10));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_AUTONEG:
+ if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) {
+ mac_prop_info_set_default_uint8(prh,
+ BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG));
+ } else {
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
+ }
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ switch (dp->ugc.usbgc_flow_control) {
+ case FLOW_CONTROL_NONE:
+ fl = LINK_FLOWCTRL_NONE;
+ break;
+ case FLOW_CONTROL_SYMMETRIC:
+ fl = LINK_FLOWCTRL_BI;
+ break;
+ case FLOW_CONTROL_TX_PAUSE:
+ fl = LINK_FLOWCTRL_TX;
+ break;
+ case FLOW_CONTROL_RX_PAUSE:
+ fl = LINK_FLOWCTRL_RX;
+ break;
+ }
+ mac_prop_info_set_default_link_flowctrl(prh, fl);
+ break;
+
+ case MAC_PROP_MTU:
+ mac_prop_info_set_range_uint32(prh,
+ dp->ugc.usbgc_min_mtu, dp->ugc.usbgc_max_mtu);
+ break;
+
+ case MAC_PROP_PRIVATE:
+ break;
+ }
+}
+#endif
+
+static int
+usbgem_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_valsize, const void *pr_val)
+{
+ struct usbgem_dev *dp = arg;
+ int err = 0;
+ boolean_t update = B_FALSE;
+ link_flowctrl_t flowctrl;
+ uint32_t cur_mtu, new_mtu;
+
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+ switch (pr_num) {
+ case MAC_PROP_EN_1000FDX_CAP:
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0 ||
+ (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD) == 0) {
+ if (dp->anadv_1000fdx != *(uint8_t *)pr_val) {
+ dp->anadv_1000fdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_EN_1000HDX_CAP:
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0 ||
+ (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) {
+ if (dp->anadv_1000hdx != *(uint8_t *)pr_val) {
+ dp->anadv_1000hdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_EN_100FDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) {
+ if (dp->anadv_100fdx != *(uint8_t *)pr_val) {
+ dp->anadv_100fdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_EN_100HDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) {
+ if (dp->anadv_100hdx != *(uint8_t *)pr_val) {
+ dp->anadv_100hdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_EN_10FDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
+ if (dp->anadv_10fdx != *(uint8_t *)pr_val) {
+ dp->anadv_10fdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_EN_10HDX_CAP:
+ if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) {
+ if (dp->anadv_10hdx != *(uint8_t *)pr_val) {
+ dp->anadv_10hdx = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_AUTONEG:
+ if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) {
+ if (dp->anadv_autoneg != *(uint8_t *)pr_val) {
+ dp->anadv_autoneg = *(uint8_t *)pr_val;
+ update = B_TRUE;
+ }
+ } else {
+ err = ENOTSUP;
+ }
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ bcopy(pr_val, &flowctrl, sizeof (flowctrl));
+
+ switch (flowctrl) {
+ default:
+ err = EINVAL;
+ break;
+
+ case LINK_FLOWCTRL_NONE:
+ if (dp->flow_control != FLOW_CONTROL_NONE) {
+ dp->flow_control = FLOW_CONTROL_NONE;
+ update = B_TRUE;
+ }
+ break;
+
+ case LINK_FLOWCTRL_RX:
+ if (dp->flow_control != FLOW_CONTROL_RX_PAUSE) {
+ dp->flow_control = FLOW_CONTROL_RX_PAUSE;
+ update = B_TRUE;
+ }
+ break;
+
+ case LINK_FLOWCTRL_TX:
+ if (dp->flow_control != FLOW_CONTROL_TX_PAUSE) {
+ dp->flow_control = FLOW_CONTROL_TX_PAUSE;
+ update = B_TRUE;
+ }
+ break;
+
+ case LINK_FLOWCTRL_BI:
+ if (dp->flow_control != FLOW_CONTROL_SYMMETRIC) {
+ dp->flow_control = FLOW_CONTROL_SYMMETRIC;
+ update = B_TRUE;
+ }
+ break;
+ }
+ break;
+
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_ADV_1000HDX_CAP:
+ case MAC_PROP_ADV_100FDX_CAP:
+ case MAC_PROP_ADV_100HDX_CAP:
+ case MAC_PROP_ADV_10FDX_CAP:
+ case MAC_PROP_ADV_10HDX_CAP:
+ case MAC_PROP_STATUS:
+ case MAC_PROP_SPEED:
+ case MAC_PROP_DUPLEX:
+ err = ENOTSUP; /* read-only prop. Can't set this. */
+ break;
+
+ case MAC_PROP_MTU:
+ bcopy(pr_val, &new_mtu, sizeof (new_mtu));
+ if (new_mtu != dp->mtu) {
+ err = EINVAL;
+ }
+ break;
+
+ case MAC_PROP_PRIVATE:
+ err = ENOTSUP;
+ break;
+
+ default:
+ err = ENOTSUP;
+ break;
+ }
+
+ if (update) {
+ /* sync with PHY */
+ usbgem_choose_forcedmode(dp);
+ dp->mii_state = MII_STATE_UNKNOWN;
+ cv_signal(&dp->link_watcher_wait_cv);
+ }
+ rw_exit(&dp->dev_state_lock);
+ return (err);
+}
+
+static int
+#ifdef MAC_VERSION_V1
+usbgem_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_valsize, void *pr_val)
+#else
+usbgem_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
+ uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
+#endif
+{
+ struct usbgem_dev *dp = arg;
+ int err = 0;
+ link_flowctrl_t flowctrl;
+ uint64_t tmp = 0;
+
+ if (pr_valsize == 0) {
+ return (EINVAL);
+ }
+#ifndef MAC_VERSION_V1
+ *perm = MAC_PROP_PERM_RW;
+#endif
+ bzero(pr_val, pr_valsize);
+#ifndef MAC_VERSION_V1
+ if ((pr_flags & MAC_PROP_DEFAULT) && (pr_num != MAC_PROP_PRIVATE)) {
+ return (usbgem_get_def_val(dp, pr_num, pr_valsize, pr_val));
+ }
+#endif
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ switch (pr_num) {
+ case MAC_PROP_DUPLEX:
+#ifndef MAC_VERSION_V1
+ *perm = MAC_PROP_PERM_READ;
+#endif
+ if (pr_valsize >= sizeof (link_duplex_t)) {
+ if (dp->mii_state != MII_STATE_LINKUP) {
+ *(link_duplex_t *)pr_val = LINK_DUPLEX_UNKNOWN;
+ } else if (dp->full_duplex) {
+ *(link_duplex_t *)pr_val = LINK_DUPLEX_FULL;
+ } else {
+ *(link_duplex_t *)pr_val = LINK_DUPLEX_HALF;
+ }
+ } else {
+ err = EINVAL;
+ }
+ break;
+ case MAC_PROP_SPEED:
+#ifndef MAC_VERSION_V1
+ *perm = MAC_PROP_PERM_READ;
+#endif
+ if (pr_valsize >= sizeof (uint64_t)) {
+ switch (dp->speed) {
+ case USBGEM_SPD_1000:
+ tmp = 1000000000;
+ break;
+ case USBGEM_SPD_100:
+ tmp = 100000000;
+ break;
+ case USBGEM_SPD_10:
+ tmp = 10000000;
+ break;
+ default:
+ tmp = 0;
+ }
+ bcopy(&tmp, pr_val, sizeof (tmp));
+ } else {
+ err = EINVAL;
+ }
+ break;
+
+ case MAC_PROP_AUTONEG:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_CANAUTONEG) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_autoneg;
+ break;
+
+ case MAC_PROP_FLOWCTRL:
+ if (pr_valsize >= sizeof (link_flowctrl_t)) {
+ switch (dp->flow_control) {
+ case FLOW_CONTROL_NONE:
+ flowctrl = LINK_FLOWCTRL_NONE;
+ break;
+ case FLOW_CONTROL_RX_PAUSE:
+ flowctrl = LINK_FLOWCTRL_RX;
+ break;
+ case FLOW_CONTROL_TX_PAUSE:
+ flowctrl = LINK_FLOWCTRL_TX;
+ break;
+ case FLOW_CONTROL_SYMMETRIC:
+ flowctrl = LINK_FLOWCTRL_BI;
+ break;
+ }
+ bcopy(&flowctrl, pr_val, sizeof (flowctrl));
+ } else {
+ err = EINVAL;
+ }
+ break;
+
+ case MAC_PROP_ADV_1000FDX_CAP:
+ case MAC_PROP_ADV_1000HDX_CAP:
+ case MAC_PROP_ADV_100FDX_CAP:
+ case MAC_PROP_ADV_100HDX_CAP:
+ case MAC_PROP_ADV_10FDX_CAP:
+ case MAC_PROP_ADV_10HDX_CAP:
+ case MAC_PROP_ADV_100T4_CAP:
+ usbgem_get_def_val(dp, pr_num, pr_valsize, pr_val);
+ break;
+
+ case MAC_PROP_EN_1000FDX_CAP:
+#ifndef MAC_VERSION_V1
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) &&
+ (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD)) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_1000fdx;
+ break;
+
+ case MAC_PROP_EN_1000HDX_CAP:
+#ifndef MAC_VERSION_V1
+ if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) &&
+ (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX)) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_1000hdx;
+ break;
+
+ case MAC_PROP_EN_100FDX_CAP:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_100_BASEX_FD) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_100fdx;
+ break;
+
+ case MAC_PROP_EN_100HDX_CAP:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_100_BASEX) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_100hdx;
+ break;
+
+ case MAC_PROP_EN_10FDX_CAP:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_10_FD) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_10fdx;
+ break;
+
+ case MAC_PROP_EN_10HDX_CAP:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_10) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_10hdx;
+ break;
+
+ case MAC_PROP_EN_100T4_CAP:
+#ifndef MAC_VERSION_V1
+ if (dp->mii_status_ro & MII_STATUS_100_BASE_T4) {
+ *perm = MAC_PROP_PERM_READ;
+ }
+#endif
+ *(uint8_t *)pr_val = dp->anadv_100t4;
+ break;
+
+ case MAC_PROP_PRIVATE:
+ err = ENOTSUP;
+ break;
+
+#ifndef MAC_VERSION_V1
+ case MAC_PROP_MTU: {
+ mac_propval_range_t range;
+ if (!(pr_flags & MAC_PROP_POSSIBLE)) {
+ err = ENOTSUP;
+ break;
+ }
+ if (pr_valsize < sizeof (mac_propval_range_t)) {
+ err = EINVAL;
+ break;
+ }
+ range.mpr_count = 1;
+ range.mpr_type = MAC_PROPVAL_UINT32;
+ range.range_uint32[0].mpur_min = ETHERMTU;
+ range.range_uint32[0].mpur_max = dp->mtu;
+ bcopy(&range, pr_val, sizeof (range));
+ break;
+ }
+#endif
+ default:
+ err = ENOTSUP;
+ break;
+ }
+
+ rw_exit(&dp->dev_state_lock);
+ return (err);
+}
+#endif /* USBGEM_CONFIG_MAC_PROP */
+
+#ifdef USBGEM_CONFIG_ND
+/* ============================================================== */
+/*
+ * ND interface
+ */
+/* ============================================================== */
+enum {
+ PARAM_AUTONEG_CAP,
+ PARAM_PAUSE_CAP,
+ PARAM_ASYM_PAUSE_CAP,
+ PARAM_1000FDX_CAP,
+ PARAM_1000HDX_CAP,
+ PARAM_100T4_CAP,
+ PARAM_100FDX_CAP,
+ PARAM_100HDX_CAP,
+ PARAM_10FDX_CAP,
+ PARAM_10HDX_CAP,
+
+ PARAM_ADV_AUTONEG_CAP,
+ PARAM_ADV_PAUSE_CAP,
+ PARAM_ADV_ASYM_PAUSE_CAP,
+ PARAM_ADV_1000FDX_CAP,
+ PARAM_ADV_1000HDX_CAP,
+ PARAM_ADV_100T4_CAP,
+ PARAM_ADV_100FDX_CAP,
+ PARAM_ADV_100HDX_CAP,
+ PARAM_ADV_10FDX_CAP,
+ PARAM_ADV_10HDX_CAP,
+ PARAM_ADV_1000T_MS,
+
+ PARAM_LP_AUTONEG_CAP,
+ PARAM_LP_PAUSE_CAP,
+ PARAM_LP_ASYM_PAUSE_CAP,
+ PARAM_LP_1000FDX_CAP,
+ PARAM_LP_1000HDX_CAP,
+ PARAM_LP_100T4_CAP,
+ PARAM_LP_100FDX_CAP,
+ PARAM_LP_100HDX_CAP,
+ PARAM_LP_10FDX_CAP,
+ PARAM_LP_10HDX_CAP,
+
+ PARAM_LINK_STATUS,
+ PARAM_LINK_SPEED,
+ PARAM_LINK_DUPLEX,
+
+ PARAM_LINK_AUTONEG,
+ PARAM_LINK_RX_PAUSE,
+ PARAM_LINK_TX_PAUSE,
+
+ PARAM_LOOP_MODE,
+ PARAM_MSI_CNT,
+#ifdef DEBUG_RESUME
+ PARAM_RESUME_TEST,
+#endif
+
+ PARAM_COUNT
+};
+
+struct usbgem_nd_arg {
+ struct usbgem_dev *dp;
+ int item;
+};
+
+static int
+usbgem_param_get(queue_t *q, mblk_t *mp, caddr_t arg, cred_t *credp)
+{
+ struct usbgem_dev *dp = ((struct usbgem_nd_arg *)(void *)arg)->dp;
+ int item = ((struct usbgem_nd_arg *)(void *)arg)->item;
+ long val;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called, item:%d",
+ dp->name, __func__, item));
+
+ switch (item) {
+ case PARAM_AUTONEG_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
+ DPRINTF(1, (CE_CONT, "autoneg_cap:%d", val));
+ break;
+
+ case PARAM_PAUSE_CAP:
+ val = dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE;
+ break;
+
+ case PARAM_ASYM_PAUSE_CAP:
+ val = dp->ugc.usbgc_flow_control > FLOW_CONTROL_SYMMETRIC;
+ break;
+
+ case PARAM_1000FDX_CAP:
+ val = (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
+ break;
+
+ case PARAM_1000HDX_CAP:
+ val = (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
+ break;
+
+ case PARAM_100T4_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
+ break;
+
+ case PARAM_100FDX_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
+ break;
+
+ case PARAM_100HDX_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
+ break;
+
+ case PARAM_10FDX_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
+ break;
+
+ case PARAM_10HDX_CAP:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_10);
+ break;
+
+ case PARAM_ADV_AUTONEG_CAP:
+ val = dp->anadv_autoneg;
+ break;
+
+ case PARAM_ADV_PAUSE_CAP:
+ val = dp->anadv_pause;
+ break;
+
+ case PARAM_ADV_ASYM_PAUSE_CAP:
+ val = dp->anadv_asmpause;
+ break;
+
+ case PARAM_ADV_1000FDX_CAP:
+ val = dp->anadv_1000fdx;
+ break;
+
+ case PARAM_ADV_1000HDX_CAP:
+ val = dp->anadv_1000hdx;
+ break;
+
+ case PARAM_ADV_100T4_CAP:
+ val = dp->anadv_100t4;
+ break;
+
+ case PARAM_ADV_100FDX_CAP:
+ val = dp->anadv_100fdx;
+ break;
+
+ case PARAM_ADV_100HDX_CAP:
+ val = dp->anadv_100hdx;
+ break;
+
+ case PARAM_ADV_10FDX_CAP:
+ val = dp->anadv_10fdx;
+ break;
+
+ case PARAM_ADV_10HDX_CAP:
+ val = dp->anadv_10hdx;
+ break;
+
+ case PARAM_ADV_1000T_MS:
+ val = dp->anadv_1000t_ms;
+ break;
+
+ case PARAM_LP_AUTONEG_CAP:
+ val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
+ break;
+
+ case PARAM_LP_PAUSE_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_PAUSE);
+ break;
+
+ case PARAM_LP_ASYM_PAUSE_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_ASM_DIR);
+ break;
+
+ case PARAM_LP_1000FDX_CAP:
+ val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL);
+ break;
+
+ case PARAM_LP_1000HDX_CAP:
+ val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF);
+ break;
+
+ case PARAM_LP_100T4_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_T4);
+ break;
+
+ case PARAM_LP_100FDX_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD);
+ break;
+
+ case PARAM_LP_100HDX_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX);
+ break;
+
+ case PARAM_LP_10FDX_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD);
+ break;
+
+ case PARAM_LP_10HDX_CAP:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T);
+ break;
+
+ case PARAM_LINK_STATUS:
+ val = (dp->mii_state == MII_STATE_LINKUP);
+ break;
+
+ case PARAM_LINK_SPEED:
+ val = usbgem_speed_value[dp->speed];
+ break;
+
+ case PARAM_LINK_DUPLEX:
+ val = 0;
+ if (dp->mii_state == MII_STATE_LINKUP) {
+ val = dp->full_duplex ? 2 : 1;
+ }
+ break;
+
+ case PARAM_LINK_AUTONEG:
+ val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
+ break;
+
+ case PARAM_LINK_RX_PAUSE:
+ val = (dp->flow_control == FLOW_CONTROL_SYMMETRIC) ||
+ (dp->flow_control == FLOW_CONTROL_RX_PAUSE);
+ break;
+
+ case PARAM_LINK_TX_PAUSE:
+ val = (dp->flow_control == FLOW_CONTROL_SYMMETRIC) ||
+ (dp->flow_control == FLOW_CONTROL_TX_PAUSE);
+ break;
+
+#ifdef DEBUG_RESUME
+ case PARAM_RESUME_TEST:
+ val = 0;
+ break;
+#endif
+ default:
+ cmn_err(CE_WARN, "%s: unimplemented ndd control (%d)",
+ dp->name, item);
+ break;
+ }
+
+ (void) mi_mpprintf(mp, "%ld", val);
+
+ return (0);
+}
+
+static int
+usbgem_param_set(queue_t *q,
+ mblk_t *mp, char *value, caddr_t arg, cred_t *credp)
+{
+ struct usbgem_dev *dp = ((struct usbgem_nd_arg *)(void *)arg)->dp;
+ int item = ((struct usbgem_nd_arg *)(void *)arg)->item;
+ long val;
+ char *end;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ if (ddi_strtol(value, &end, 10, &val)) {
+ return (EINVAL);
+ }
+ if (end == value) {
+ return (EINVAL);
+ }
+
+ switch (item) {
+ case PARAM_ADV_AUTONEG_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_CANAUTONEG) == 0) {
+ goto err;
+ }
+ dp->anadv_autoneg = (int)val;
+ break;
+
+ case PARAM_ADV_PAUSE_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && dp->ugc.usbgc_flow_control == FLOW_CONTROL_NONE) {
+ goto err;
+ }
+ dp->anadv_pause = (int)val;
+ break;
+
+ case PARAM_ADV_ASYM_PAUSE_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val &&
+ dp->ugc.usbgc_flow_control <= FLOW_CONTROL_SYMMETRIC) {
+ goto err;
+ }
+ dp->anadv_asmpause = (int)val;
+ break;
+
+ case PARAM_ADV_1000FDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_xstatus &
+ (MII_XSTATUS_1000BASET_FD |
+ MII_XSTATUS_1000BASEX_FD)) == 0) {
+ goto err;
+ }
+ dp->anadv_1000fdx = (int)val;
+ break;
+
+ case PARAM_ADV_1000HDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_xstatus &
+ (MII_XSTATUS_1000BASET | MII_XSTATUS_1000BASEX)) == 0) {
+ goto err;
+ }
+ dp->anadv_1000hdx = (int)val;
+ break;
+
+ case PARAM_ADV_100T4_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_100_BASE_T4) == 0) {
+ goto err;
+ }
+ dp->anadv_100t4 = (int)val;
+ break;
+
+ case PARAM_ADV_100FDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_100_BASEX_FD) == 0) {
+ goto err;
+ }
+ dp->anadv_100fdx = (int)val;
+ break;
+
+ case PARAM_ADV_100HDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_100_BASEX) == 0) {
+ goto err;
+ }
+ dp->anadv_100hdx = (int)val;
+ break;
+
+ case PARAM_ADV_10FDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_10_FD) == 0) {
+ goto err;
+ }
+ dp->anadv_10fdx = (int)val;
+ break;
+
+ case PARAM_ADV_10HDX_CAP:
+ if (val != 0 && val != 1) {
+ goto err;
+ }
+ if (val && (dp->mii_status & MII_STATUS_10) == 0) {
+ goto err;
+ }
+ dp->anadv_10hdx = (int)val;
+ break;
+
+ case PARAM_ADV_1000T_MS:
+ if (val != 0 && val != 1 && val != 2) {
+ goto err;
+ }
+ if (val && (dp->mii_xstatus &
+ (MII_XSTATUS_1000BASET | MII_XSTATUS_1000BASET_FD)) == 0) {
+ goto err;
+ }
+ dp->anadv_1000t_ms = (int)val;
+ break;
+
+#ifdef DEBUG_RESUME
+ case PARAM_RESUME_TEST:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ gem_suspend(dp->dip);
+ gem_resume(dp->dip);
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+ break;
+#endif
+ }
+
+ /* sync with PHY */
+ usbgem_choose_forcedmode(dp);
+
+ dp->mii_state = MII_STATE_UNKNOWN;
+ if (dp->ugc.usbgc_mii_hw_link_detection) {
+ /* wake up link watcher possiblely sleeps */
+ cv_signal(&dp->link_watcher_wait_cv);
+ }
+
+ return (0);
+err:
+ return (EINVAL);
+}
+
+static void
+usbgem_nd_load(struct usbgem_dev *dp,
+ char *name, ndgetf_t gf, ndsetf_t sf, int item)
+{
+ struct usbgem_nd_arg *arg;
+
+ ASSERT(item >= 0);
+ ASSERT(item < PARAM_COUNT);
+
+ arg = &((struct usbgem_nd_arg *)(void *)dp->nd_arg_p)[item];
+ arg->dp = dp;
+ arg->item = item;
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: name:%s, item:%d",
+ dp->name, __func__, name, item));
+ (void) nd_load(&dp->nd_data_p, name, gf, sf, (caddr_t)arg);
+}
+
+static void
+usbgem_nd_setup(struct usbgem_dev *dp)
+{
+ DPRINTF(1, (CE_CONT, "!%s: %s: called, mii_status:0x%b",
+ dp->name, __func__, dp->mii_status, MII_STATUS_BITS));
+
+ ASSERT(dp->nd_arg_p == NULL);
+
+ dp->nd_arg_p =
+ kmem_zalloc(sizeof (struct usbgem_nd_arg) * PARAM_COUNT, KM_SLEEP);
+
+#define SETFUNC(x) ((x) ? usbgem_param_set : NULL)
+
+ usbgem_nd_load(dp, "autoneg_cap",
+ usbgem_param_get, NULL, PARAM_AUTONEG_CAP);
+ usbgem_nd_load(dp, "pause_cap",
+ usbgem_param_get, NULL, PARAM_PAUSE_CAP);
+ usbgem_nd_load(dp, "asym_pause_cap",
+ usbgem_param_get, NULL, PARAM_ASYM_PAUSE_CAP);
+ usbgem_nd_load(dp, "1000fdx_cap",
+ usbgem_param_get, NULL, PARAM_1000FDX_CAP);
+ usbgem_nd_load(dp, "1000hdx_cap",
+ usbgem_param_get, NULL, PARAM_1000HDX_CAP);
+ usbgem_nd_load(dp, "100T4_cap",
+ usbgem_param_get, NULL, PARAM_100T4_CAP);
+ usbgem_nd_load(dp, "100fdx_cap",
+ usbgem_param_get, NULL, PARAM_100FDX_CAP);
+ usbgem_nd_load(dp, "100hdx_cap",
+ usbgem_param_get, NULL, PARAM_100HDX_CAP);
+ usbgem_nd_load(dp, "10fdx_cap",
+ usbgem_param_get, NULL, PARAM_10FDX_CAP);
+ usbgem_nd_load(dp, "10hdx_cap",
+ usbgem_param_get, NULL, PARAM_10HDX_CAP);
+
+ /* Our advertised capabilities */
+ usbgem_nd_load(dp, "adv_autoneg_cap", usbgem_param_get,
+ SETFUNC(dp->mii_status & MII_STATUS_CANAUTONEG),
+ PARAM_ADV_AUTONEG_CAP);
+ usbgem_nd_load(dp, "adv_pause_cap", usbgem_param_get,
+ SETFUNC(dp->ugc.usbgc_flow_control & 1),
+ PARAM_ADV_PAUSE_CAP);
+ usbgem_nd_load(dp, "adv_asym_pause_cap", usbgem_param_get,
+ SETFUNC(dp->ugc.usbgc_flow_control & 2),
+ PARAM_ADV_ASYM_PAUSE_CAP);
+ usbgem_nd_load(dp, "adv_1000fdx_cap", usbgem_param_get,
+ SETFUNC(dp->mii_xstatus &
+ (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASET_FD)),
+ PARAM_ADV_1000FDX_CAP);
+ usbgem_nd_load(dp, "adv_1000hdx_cap", usbgem_param_get,
+ SETFUNC(dp->mii_xstatus &
+ (MII_XSTATUS_1000BASEX | MII_XSTATUS_1000BASET)),
+ PARAM_ADV_1000HDX_CAP);
+ usbgem_nd_load(dp, "adv_100T4_cap", usbgem_param_get,
+ SETFUNC((dp->mii_status & MII_STATUS_100_BASE_T4) &&
+ !dp->mii_advert_ro),
+ PARAM_ADV_100T4_CAP);
+ usbgem_nd_load(dp, "adv_100fdx_cap", usbgem_param_get,
+ SETFUNC((dp->mii_status & MII_STATUS_100_BASEX_FD) &&
+ !dp->mii_advert_ro),
+ PARAM_ADV_100FDX_CAP);
+ usbgem_nd_load(dp, "adv_100hdx_cap", usbgem_param_get,
+ SETFUNC((dp->mii_status & MII_STATUS_100_BASEX) &&
+ !dp->mii_advert_ro),
+ PARAM_ADV_100HDX_CAP);
+ usbgem_nd_load(dp, "adv_10fdx_cap", usbgem_param_get,
+ SETFUNC((dp->mii_status & MII_STATUS_10_FD) &&
+ !dp->mii_advert_ro),
+ PARAM_ADV_10FDX_CAP);
+ usbgem_nd_load(dp, "adv_10hdx_cap", usbgem_param_get,
+ SETFUNC((dp->mii_status & MII_STATUS_10) &&
+ !dp->mii_advert_ro),
+ PARAM_ADV_10HDX_CAP);
+ usbgem_nd_load(dp, "adv_1000t_ms", usbgem_param_get,
+ SETFUNC(dp->mii_xstatus &
+ (MII_XSTATUS_1000BASET_FD | MII_XSTATUS_1000BASET)),
+ PARAM_ADV_1000T_MS);
+
+
+ /* Partner's advertised capabilities */
+ usbgem_nd_load(dp, "lp_autoneg_cap",
+ usbgem_param_get, NULL, PARAM_LP_AUTONEG_CAP);
+ usbgem_nd_load(dp, "lp_pause_cap",
+ usbgem_param_get, NULL, PARAM_LP_PAUSE_CAP);
+ usbgem_nd_load(dp, "lp_asym_pause_cap",
+ usbgem_param_get, NULL, PARAM_LP_ASYM_PAUSE_CAP);
+ usbgem_nd_load(dp, "lp_1000fdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_1000FDX_CAP);
+ usbgem_nd_load(dp, "lp_1000hdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_1000HDX_CAP);
+ usbgem_nd_load(dp, "lp_100T4_cap",
+ usbgem_param_get, NULL, PARAM_LP_100T4_CAP);
+ usbgem_nd_load(dp, "lp_100fdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_100FDX_CAP);
+ usbgem_nd_load(dp, "lp_100hdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_100HDX_CAP);
+ usbgem_nd_load(dp, "lp_10fdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_10FDX_CAP);
+ usbgem_nd_load(dp, "lp_10hdx_cap",
+ usbgem_param_get, NULL, PARAM_LP_10HDX_CAP);
+
+ /* Current operating modes */
+ usbgem_nd_load(dp, "link_status",
+ usbgem_param_get, NULL, PARAM_LINK_STATUS);
+ usbgem_nd_load(dp, "link_speed",
+ usbgem_param_get, NULL, PARAM_LINK_SPEED);
+ usbgem_nd_load(dp, "link_duplex",
+ usbgem_param_get, NULL, PARAM_LINK_DUPLEX);
+ usbgem_nd_load(dp, "link_autoneg",
+ usbgem_param_get, NULL, PARAM_LINK_AUTONEG);
+ usbgem_nd_load(dp, "link_rx_pause",
+ usbgem_param_get, NULL, PARAM_LINK_RX_PAUSE);
+ usbgem_nd_load(dp, "link_tx_pause",
+ usbgem_param_get, NULL, PARAM_LINK_TX_PAUSE);
+#ifdef DEBUG_RESUME
+ usbgem_nd_load(dp, "resume_test",
+ usbgem_param_get, usbgem_param_set, PARAM_RESUME_TEST);
+#endif
+#undef SETFUNC
+}
+
+static
+enum ioc_reply
+usbgem_nd_ioctl(struct usbgem_dev *dp,
+ queue_t *wq, mblk_t *mp, struct iocblk *iocp)
+{
+ boolean_t ok;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ switch (iocp->ioc_cmd) {
+ case ND_GET:
+ ok = nd_getset(wq, dp->nd_data_p, mp);
+ DPRINTF(1, (CE_CONT,
+ "%s: get %s", dp->name, ok ? "OK" : "FAIL"));
+ return (ok ? IOC_REPLY : IOC_INVAL);
+
+ case ND_SET:
+ ok = nd_getset(wq, dp->nd_data_p, mp);
+
+ DPRINTF(1, (CE_CONT, "%s: set %s err %d",
+ dp->name, ok ? "OK" : "FAIL", iocp->ioc_error));
+
+ if (!ok) {
+ return (IOC_INVAL);
+ }
+
+ if (iocp->ioc_error) {
+ return (IOC_REPLY);
+ }
+
+ return (IOC_RESTART_REPLY);
+ }
+
+ cmn_err(CE_WARN, "%s: invalid cmd 0x%x", dp->name, iocp->ioc_cmd);
+
+ return (IOC_INVAL);
+}
+
+static void
+usbgem_nd_cleanup(struct usbgem_dev *dp)
+{
+ ASSERT(dp->nd_data_p != NULL);
+ ASSERT(dp->nd_arg_p != NULL);
+
+ nd_free(&dp->nd_data_p);
+
+ kmem_free(dp->nd_arg_p, sizeof (struct usbgem_nd_arg) * PARAM_COUNT);
+ dp->nd_arg_p = NULL;
+}
+#endif /* USBGEM_CONFIG_ND */
+
+static void
+usbgem_mac_ioctl(struct usbgem_dev *dp, queue_t *wq, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ enum ioc_reply status;
+ int cmd;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /*
+ * Validate the command before bothering with the mutex ...
+ */
+ iocp = (void *)mp->b_rptr;
+ iocp->ioc_error = 0;
+ cmd = iocp->ioc_cmd;
+
+ DPRINTF(1, (CE_CONT, "%s: %s cmd:0x%x", dp->name, __func__, cmd));
+
+#ifdef USBGEM_CONFIG_ND
+ switch (cmd) {
+ default:
+ _NOTE(NOTREACHED)
+ status = IOC_INVAL;
+ break;
+
+ case ND_GET:
+ case ND_SET:
+ status = usbgem_nd_ioctl(dp, wq, mp, iocp);
+ break;
+ }
+
+ /*
+ * Finally, decide how to reply
+ */
+ switch (status) {
+ default:
+ case IOC_INVAL:
+ /*
+ * Error, reply with a NAK and EINVAL or the specified error
+ */
+ miocnak(wq, mp, 0, iocp->ioc_error == 0 ?
+ EINVAL : iocp->ioc_error);
+ break;
+
+ case IOC_DONE:
+ /*
+ * OK, reply already sent
+ */
+ break;
+
+ case IOC_RESTART_ACK:
+ case IOC_ACK:
+ /*
+ * OK, reply with an ACK
+ */
+ miocack(wq, mp, 0, 0);
+ break;
+
+ case IOC_RESTART_REPLY:
+ case IOC_REPLY:
+ /*
+ * OK, send prepared reply as ACK or NAK
+ */
+ mp->b_datap->db_type =
+ iocp->ioc_error == 0 ? M_IOCACK : M_IOCNAK;
+ qreply(wq, mp);
+ break;
+ }
+#else
+ miocnak(wq, mp, 0, EINVAL);
+ return;
+#endif /* USBGEM_CONFIG_GLDv3 */
+}
+
+#ifndef SYS_MAC_H
+#define XCVR_UNDEFINED 0
+#define XCVR_NONE 1
+#define XCVR_10 2
+#define XCVR_100T4 3
+#define XCVR_100X 4
+#define XCVR_100T2 5
+#define XCVR_1000X 6
+#define XCVR_1000T 7
+#endif
+static int
+usbgem_mac_xcvr_inuse(struct usbgem_dev *dp)
+{
+ int val = XCVR_UNDEFINED;
+
+ if ((dp->mii_status & MII_STATUS_XSTATUS) == 0) {
+ if (dp->mii_status & MII_STATUS_100_BASE_T4) {
+ val = XCVR_100T4;
+ } else if (dp->mii_status &
+ (MII_STATUS_100_BASEX_FD |
+ MII_STATUS_100_BASEX)) {
+ val = XCVR_100X;
+ } else if (dp->mii_status &
+ (MII_STATUS_100_BASE_T2_FD |
+ MII_STATUS_100_BASE_T2)) {
+ val = XCVR_100T2;
+ } else if (dp->mii_status &
+ (MII_STATUS_10_FD | MII_STATUS_10)) {
+ val = XCVR_10;
+ }
+ } else if (dp->mii_xstatus &
+ (MII_XSTATUS_1000BASET_FD | MII_XSTATUS_1000BASET)) {
+ val = XCVR_1000T;
+ } else if (dp->mii_xstatus &
+ (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASEX)) {
+ val = XCVR_1000X;
+ }
+
+ return (val);
+}
+
+#ifdef USBGEM_CONFIG_GLDv3
+/* ============================================================== */
+/*
+ * GLDv3 interface
+ */
+/* ============================================================== */
+static int usbgem_m_getstat(void *, uint_t, uint64_t *);
+static int usbgem_m_start(void *);
+static void usbgem_m_stop(void *);
+static int usbgem_m_setpromisc(void *, boolean_t);
+static int usbgem_m_multicst(void *, boolean_t, const uint8_t *);
+static int usbgem_m_unicst(void *, const uint8_t *);
+static mblk_t *usbgem_m_tx(void *, mblk_t *);
+static void usbgem_m_ioctl(void *, queue_t *, mblk_t *);
+#ifdef GEM_CONFIG_MAC_PROP
+static int usbgem_m_setprop(void *, const char *, mac_prop_id_t,
+ uint_t, const void *);
+#ifdef MAC_VERSION_V1
+static int usbgem_m_getprop(void *, const char *, mac_prop_id_t,
+ uint_t, void *);
+#else
+static int usbgem_m_getprop(void *, const char *, mac_prop_id_t,
+ uint_t, uint_t, void *, uint_t *);
+#endif
+#endif
+
+#ifdef _SYS_MAC_PROVIDER_H
+#define GEM_M_CALLBACK_FLAGS (MC_IOCTL)
+#else
+#define GEM_M_CALLBACK_FLAGS (MC_IOCTL)
+#endif
+
+static mac_callbacks_t gem_m_callbacks = {
+#ifdef USBGEM_CONFIG_MAC_PROP
+#ifdef MAC_VERSION_V1
+ GEM_M_CALLBACK_FLAGS | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
+#else
+ GEM_M_CALLBACK_FLAGS | MC_SETPROP | MC_GETPROP,
+#endif
+#else
+ GEM_M_CALLBACK_FLAGS,
+#endif
+ usbgem_m_getstat,
+ usbgem_m_start,
+ usbgem_m_stop,
+ usbgem_m_setpromisc,
+ usbgem_m_multicst,
+ usbgem_m_unicst,
+ usbgem_m_tx,
+#ifdef _SYS_MAC_PROVIDER_H
+#ifdef MAC_VERSION_V1
+ NULL,
+#endif
+#else
+ NULL, /* m_resources */
+#endif
+ usbgem_m_ioctl,
+ NULL, /* m_getcapab */
+#ifdef USBGEM_CONFIG_MAC_PROP
+ NULL,
+ NULL,
+ usbgem_m_setprop,
+ usbgem_m_getprop,
+#endif
+#ifdef MAC_VERSION_V1
+ usbgem_m_propinfo,
+#endif
+};
+
+static int
+usbgem_m_start(void *arg)
+{
+ int ret;
+ int err;
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ err = EIO;
+
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+ dp->nic_state = NIC_STATE_ONLINE;
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ err = 0;
+ goto x;
+ }
+ if (usbgem_mac_init(dp) != USB_SUCCESS) {
+ goto x;
+ }
+
+ /* initialize rx filter state */
+ sema_p(&dp->rxfilter_lock);
+ dp->mc_count = 0;
+ dp->mc_count_req = 0;
+
+ bcopy(dp->dev_addr.ether_addr_octet,
+ dp->cur_addr.ether_addr_octet, ETHERADDRL);
+ dp->rxmode |= RXMODE_ENABLE;
+
+ ret = usbgem_hal_set_rx_filter(dp);
+ sema_v(&dp->rxfilter_lock);
+
+ if (ret != USB_SUCCESS) {
+ goto x;
+ }
+
+ if (dp->mii_state == MII_STATE_LINKUP) {
+ /* setup media mode if the link have been up */
+ if (usbgem_hal_set_media(dp) != USB_SUCCESS) {
+ goto x;
+ }
+ if (usbgem_mac_start(dp) != USB_SUCCESS) {
+ goto x;
+ }
+ }
+
+ err = 0;
+x:
+ rw_exit(&dp->dev_state_lock);
+ return (err);
+}
+
+static void
+usbgem_m_stop(void *arg)
+{
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* stop rx gracefully */
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ sema_p(&dp->rxfilter_lock);
+ dp->rxmode &= ~RXMODE_ENABLE;
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_hal_set_rx_filter(dp);
+ }
+ sema_v(&dp->rxfilter_lock);
+ rw_exit(&dp->dev_state_lock);
+
+ /* make the nic state inactive */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+ dp->nic_state = NIC_STATE_STOPPED;
+
+ /* stop mac completely */
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL);
+ }
+ rw_exit(&dp->dev_state_lock);
+}
+
+static int
+usbgem_m_multicst(void *arg, boolean_t add, const uint8_t *ep)
+{
+ int err;
+ int ret;
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ if (add) {
+ ret = usbgem_add_multicast(dp, ep);
+ } else {
+ ret = usbgem_remove_multicast(dp, ep);
+ }
+ rw_exit(&dp->dev_state_lock);
+
+ err = 0;
+ if (ret != USB_SUCCESS) {
+#ifdef GEM_CONFIG_FMA
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+#endif
+ err = EIO;
+ }
+
+ return (err);
+}
+
+static int
+usbgem_m_setpromisc(void *arg, boolean_t on)
+{
+ int err;
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+
+ sema_p(&dp->rxfilter_lock);
+ if (on) {
+ dp->rxmode |= RXMODE_PROMISC;
+ } else {
+ dp->rxmode &= ~RXMODE_PROMISC;
+ }
+
+ err = 0;
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) {
+ err = EIO;
+ }
+ }
+ sema_v(&dp->rxfilter_lock);
+
+ rw_exit(&dp->dev_state_lock);
+
+#ifdef GEM_CONFIG_FMA
+ if (err != 0) {
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+ }
+#endif
+ return (err);
+}
+
+int
+usbgem_m_getstat(void *arg, uint_t stat, uint64_t *valp)
+{
+ int ret;
+ uint64_t val;
+ struct usbgem_dev *dp = arg;
+ struct usbgem_stats *gstp = &dp->stats;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ rw_exit(&dp->dev_state_lock);
+ return (0);
+ }
+ ret = usbgem_hal_get_stats(dp);
+ rw_exit(&dp->dev_state_lock);
+
+#ifdef GEM_CONFIG_FMA
+ if (ret != USB_SUCCESS) {
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+ return (EIO);
+ }
+#endif
+
+ switch (stat) {
+ case MAC_STAT_IFSPEED:
+ val = usbgem_speed_value[dp->speed] *1000000ull;
+ break;
+
+ case MAC_STAT_MULTIRCV:
+ val = gstp->rmcast;
+ break;
+
+ case MAC_STAT_BRDCSTRCV:
+ val = gstp->rbcast;
+ break;
+
+ case MAC_STAT_MULTIXMT:
+ val = gstp->omcast;
+ break;
+
+ case MAC_STAT_BRDCSTXMT:
+ val = gstp->obcast;
+ break;
+
+ case MAC_STAT_NORCVBUF:
+ val = gstp->norcvbuf + gstp->missed;
+ break;
+
+ case MAC_STAT_IERRORS:
+ val = gstp->errrcv;
+ break;
+
+ case MAC_STAT_NOXMTBUF:
+ val = gstp->noxmtbuf;
+ break;
+
+ case MAC_STAT_OERRORS:
+ val = gstp->errxmt;
+ break;
+
+ case MAC_STAT_COLLISIONS:
+ val = gstp->collisions;
+ break;
+
+ case MAC_STAT_RBYTES:
+ val = gstp->rbytes;
+ break;
+
+ case MAC_STAT_IPACKETS:
+ val = gstp->rpackets;
+ break;
+
+ case MAC_STAT_OBYTES:
+ val = gstp->obytes;
+ break;
+
+ case MAC_STAT_OPACKETS:
+ val = gstp->opackets;
+ break;
+
+ case MAC_STAT_UNDERFLOWS:
+ val = gstp->underflow;
+ break;
+
+ case MAC_STAT_OVERFLOWS:
+ val = gstp->overflow;
+ break;
+
+ case ETHER_STAT_ALIGN_ERRORS:
+ val = gstp->frame;
+ break;
+
+ case ETHER_STAT_FCS_ERRORS:
+ val = gstp->crc;
+ break;
+
+ case ETHER_STAT_FIRST_COLLISIONS:
+ val = gstp->first_coll;
+ break;
+
+ case ETHER_STAT_MULTI_COLLISIONS:
+ val = gstp->multi_coll;
+ break;
+
+ case ETHER_STAT_SQE_ERRORS:
+ val = gstp->sqe;
+ break;
+
+ case ETHER_STAT_DEFER_XMTS:
+ val = gstp->defer;
+ break;
+
+ case ETHER_STAT_TX_LATE_COLLISIONS:
+ val = gstp->xmtlatecoll;
+ break;
+
+ case ETHER_STAT_EX_COLLISIONS:
+ val = gstp->excoll;
+ break;
+
+ case ETHER_STAT_MACXMT_ERRORS:
+ val = gstp->xmit_internal_err;
+ break;
+
+ case ETHER_STAT_CARRIER_ERRORS:
+ val = gstp->nocarrier;
+ break;
+
+ case ETHER_STAT_TOOLONG_ERRORS:
+ val = gstp->frame_too_long;
+ break;
+
+ case ETHER_STAT_MACRCV_ERRORS:
+ val = gstp->rcv_internal_err;
+ break;
+
+ case ETHER_STAT_XCVR_ADDR:
+ val = dp->mii_phy_addr;
+ break;
+
+ case ETHER_STAT_XCVR_ID:
+ val = dp->mii_phy_id;
+ break;
+
+ case ETHER_STAT_XCVR_INUSE:
+ val = usbgem_mac_xcvr_inuse(dp);
+ break;
+
+ case ETHER_STAT_CAP_1000FDX:
+ val = (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
+ break;
+
+ case ETHER_STAT_CAP_1000HDX:
+ val = (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
+ break;
+
+ case ETHER_STAT_CAP_100FDX:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
+ break;
+
+ case ETHER_STAT_CAP_100HDX:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
+ break;
+
+ case ETHER_STAT_CAP_10FDX:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
+ break;
+
+ case ETHER_STAT_CAP_10HDX:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_10);
+ break;
+
+ case ETHER_STAT_CAP_ASMPAUSE:
+ val = dp->ugc.usbgc_flow_control > FLOW_CONTROL_SYMMETRIC;
+ break;
+
+ case ETHER_STAT_CAP_PAUSE:
+ val = dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE;
+ break;
+
+ case ETHER_STAT_CAP_AUTONEG:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
+ break;
+
+ case ETHER_STAT_ADV_CAP_1000FDX:
+ val = dp->anadv_1000fdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_1000HDX:
+ val = dp->anadv_1000hdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_100FDX:
+ val = dp->anadv_100fdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_100HDX:
+ val = dp->anadv_100hdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_10FDX:
+ val = dp->anadv_10fdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_10HDX:
+ val = dp->anadv_10hdx;
+ break;
+
+ case ETHER_STAT_ADV_CAP_ASMPAUSE:
+ val = dp->anadv_asmpause;
+ break;
+
+ case ETHER_STAT_ADV_CAP_PAUSE:
+ val = dp->anadv_pause;
+ break;
+
+ case ETHER_STAT_ADV_CAP_AUTONEG:
+ val = dp->anadv_autoneg;
+ break;
+
+ case ETHER_STAT_LP_CAP_1000FDX:
+ val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL);
+ break;
+
+ case ETHER_STAT_LP_CAP_1000HDX:
+ val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF);
+ break;
+
+ case ETHER_STAT_LP_CAP_100FDX:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD);
+ break;
+
+ case ETHER_STAT_LP_CAP_100HDX:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX);
+ break;
+
+ case ETHER_STAT_LP_CAP_10FDX:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD);
+ break;
+
+ case ETHER_STAT_LP_CAP_10HDX:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T);
+ break;
+
+ case ETHER_STAT_LP_CAP_ASMPAUSE:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_ASM_DIR);
+ break;
+
+ case ETHER_STAT_LP_CAP_PAUSE:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_PAUSE);
+ break;
+
+ case ETHER_STAT_LP_CAP_AUTONEG:
+ val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
+ break;
+
+ case ETHER_STAT_LINK_ASMPAUSE:
+ val = BOOLEAN(dp->flow_control & 2);
+ break;
+
+ case ETHER_STAT_LINK_PAUSE:
+ val = BOOLEAN(dp->flow_control & 1);
+ break;
+
+ case ETHER_STAT_LINK_AUTONEG:
+ val = dp->anadv_autoneg &&
+ BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
+ break;
+
+ case ETHER_STAT_LINK_DUPLEX:
+ val = (dp->mii_state == MII_STATE_LINKUP) ?
+ (dp->full_duplex ? 2 : 1) : 0;
+ break;
+
+ case ETHER_STAT_TOOSHORT_ERRORS:
+ val = gstp->runt;
+ break;
+#ifdef NEVER /* it doesn't make sense */
+ case ETHER_STAT_CAP_REMFAULT:
+ val = B_TRUE;
+ break;
+
+ case ETHER_STAT_ADV_REMFAULT:
+ val = dp->anadv_remfault;
+ break;
+#endif
+ case ETHER_STAT_LP_REMFAULT:
+ val = BOOLEAN(dp->mii_lpable & MII_AN_ADVERT_REMFAULT);
+ break;
+
+ case ETHER_STAT_JABBER_ERRORS:
+ val = gstp->jabber;
+ break;
+
+ case ETHER_STAT_CAP_100T4:
+ val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
+ break;
+
+ case ETHER_STAT_ADV_CAP_100T4:
+ val = dp->anadv_100t4;
+ break;
+
+ case ETHER_STAT_LP_CAP_100T4:
+ val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_T4);
+ break;
+
+ default:
+#if GEM_DEBUG_LEVEL > 2
+ cmn_err(CE_WARN,
+ "%s: unrecognized parameter value = %d",
+ __func__, stat);
+#endif
+ *valp = 0;
+ return (ENOTSUP);
+ }
+
+ *valp = val;
+
+ return (0);
+}
+
+static int
+usbgem_m_unicst(void *arg, const uint8_t *mac)
+{
+ int err;
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+
+ sema_p(&dp->rxfilter_lock);
+ bcopy(mac, dp->cur_addr.ether_addr_octet, ETHERADDRL);
+ dp->rxmode |= RXMODE_ENABLE;
+
+ err = 0;
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) {
+ err = EIO;
+ }
+ }
+ sema_v(&dp->rxfilter_lock);
+ rw_exit(&dp->dev_state_lock);
+
+#ifdef GEM_CONFIG_FMA
+ if (err != 0) {
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+ }
+#endif
+ return (err);
+}
+
+/*
+ * usbgem_m_tx is used only for sending data packets into ethernet wire.
+ */
+static mblk_t *
+usbgem_m_tx(void *arg, mblk_t *mp_head)
+{
+ int limit;
+ mblk_t *mp;
+ mblk_t *nmp;
+ uint32_t flags;
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ mp = mp_head;
+ flags = 0;
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+
+ if (dp->mii_state != MII_STATE_LINKUP ||
+ dp->mac_state != MAC_STATE_ONLINE) {
+ /* some nics hate to send packets during the link is down */
+ for (; mp; mp = nmp) {
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+ freemsg(mp);
+ }
+ goto x;
+ }
+
+ ASSERT(dp->nic_state == NIC_STATE_ONLINE);
+
+ limit = dp->tx_max_packets;
+ for (; limit-- && mp; mp = nmp) {
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+ if (usbgem_send_common(dp, mp,
+ (limit == 0 && nmp) ? 1 : 0)) {
+ mp->b_next = nmp;
+ break;
+ }
+ }
+#ifdef CONFIG_TX_LIMITER
+ if (mp == mp_head) {
+ /* no packets were sent, descrease allocation limit */
+ mutex_enter(&dp->txlock);
+ dp->tx_max_packets = max(dp->tx_max_packets - 1, 1);
+ mutex_exit(&dp->txlock);
+ }
+#endif
+x:
+ rw_exit(&dp->dev_state_lock);
+
+ return (mp);
+}
+
+static void
+usbgem_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+ struct usbgem_dev *dp = arg;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called",
+ ((struct usbgem_dev *)arg)->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ usbgem_mac_ioctl((struct usbgem_dev *)arg, wq, mp);
+ rw_exit(&dp->dev_state_lock);
+}
+
+static void
+usbgem_gld3_init(struct usbgem_dev *dp, mac_register_t *macp)
+{
+ macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ macp->m_driver = dp;
+ macp->m_dip = dp->dip;
+ macp->m_src_addr = dp->dev_addr.ether_addr_octet;
+ macp->m_callbacks = &gem_m_callbacks;
+ macp->m_min_sdu = 0;
+ macp->m_max_sdu = dp->mtu;
+
+ if (dp->misc_flag & USBGEM_VLAN) {
+ macp->m_margin = VTAG_SIZE;
+ }
+}
+#else
+/* ============================================================== */
+/*
+ * GLDv2 interface
+ */
+/* ============================================================== */
+static int usbgem_gld_reset(gld_mac_info_t *);
+static int usbgem_gld_start(gld_mac_info_t *);
+static int usbgem_gld_stop(gld_mac_info_t *);
+static int usbgem_gld_set_mac_address(gld_mac_info_t *, uint8_t *);
+static int usbgem_gld_set_multicast(gld_mac_info_t *, uint8_t *, int);
+static int usbgem_gld_set_promiscuous(gld_mac_info_t *, int);
+static int usbgem_gld_get_stats(gld_mac_info_t *, struct gld_stats *);
+static int usbgem_gld_send(gld_mac_info_t *, mblk_t *);
+static int usbgem_gld_send_tagged(gld_mac_info_t *, mblk_t *, uint32_t);
+
+static int
+usbgem_gld_reset(gld_mac_info_t *macinfo)
+{
+ int err;
+ struct usbgem_dev *dp;
+
+ err = GLD_SUCCESS;
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+ if (usbgem_mac_init(dp) != USB_SUCCESS) {
+ err = GLD_FAILURE;
+ goto x;
+ }
+
+ dp->nic_state = NIC_STATE_INITIALIZED;
+
+ /* setup media mode if the link have been up */
+ if (dp->mii_state == MII_STATE_LINKUP) {
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_hal_set_media(dp);
+ }
+ }
+x:
+ rw_exit(&dp->dev_state_lock);
+ return (err);
+}
+
+static int
+usbgem_gld_start(gld_mac_info_t *macinfo)
+{
+ int err;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+
+ dp->nic_state = NIC_STATE_ONLINE;
+
+ if (dp->mii_state == MII_STATE_LINKUP) {
+ if (usbgem_mac_start(dp) != USB_SUCCESS) {
+ /* sema_v(&dp->mii_lock); */
+ err = GLD_FAILURE;
+ goto x;
+ }
+ }
+
+ /*
+ * XXX - don't call gld_linkstate() here,
+ * otherwise it cause recursive mutex call.
+ */
+ err = GLD_SUCCESS;
+x:
+ rw_exit(&dp->dev_state_lock);
+
+ return (err);
+}
+
+static int
+usbgem_gld_stop(gld_mac_info_t *macinfo)
+{
+ int err = GLD_SUCCESS;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /* try to stop rx gracefully */
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ sema_p(&dp->rxfilter_lock);
+ dp->rxmode &= ~RXMODE_ENABLE;
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_hal_set_rx_filter(dp);
+ }
+ sema_v(&dp->rxfilter_lock);
+ rw_exit(&dp->dev_state_lock);
+
+ /* make the nic state inactive */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+ dp->nic_state = NIC_STATE_STOPPED;
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ if (usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL)
+ != USB_SUCCESS) {
+ err = GLD_FAILURE;
+ }
+ }
+ rw_exit(&dp->dev_state_lock);
+
+ return (err);
+}
+
+static int
+usbgem_gld_set_multicast(gld_mac_info_t *macinfo, uint8_t *ep, int flag)
+{
+ int err;
+ int ret;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ rw_enter(&dp->dev_state_lock, RW_READER);
+ if (flag == GLD_MULTI_ENABLE) {
+ ret = usbgem_add_multicast(dp, ep);
+ } else {
+ ret = usbgem_remove_multicast(dp, ep);
+ }
+ rw_exit(&dp->dev_state_lock);
+
+ err = GLD_SUCCESS;
+ if (ret != USB_SUCCESS) {
+#ifdef GEM_CONFIG_FMA
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+#endif
+ err = GLD_FAILURE;
+ }
+ return (err);
+}
+
+static int
+usbgem_gld_set_promiscuous(gld_mac_info_t *macinfo, int flag)
+{
+ boolean_t need_to_change = B_TRUE;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ sema_p(&dp->rxfilter_lock);
+ if (flag == GLD_MAC_PROMISC_NONE) {
+ dp->rxmode &= ~(RXMODE_PROMISC | RXMODE_ALLMULTI_REQ);
+ } else if (flag == GLD_MAC_PROMISC_MULTI) {
+ dp->rxmode |= RXMODE_ALLMULTI_REQ;
+ } else if (flag == GLD_MAC_PROMISC_PHYS) {
+ dp->rxmode |= RXMODE_PROMISC;
+ } else {
+ /* mode unchanged */
+ need_to_change = B_FALSE;
+ }
+
+ if (need_to_change) {
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_hal_set_rx_filter(dp);
+ }
+ }
+ sema_v(&dp->rxfilter_lock);
+
+ return (GLD_SUCCESS);
+}
+
+static int
+usbgem_gld_set_mac_address(gld_mac_info_t *macinfo, uint8_t *mac)
+{
+ struct usbgem_dev *dp;
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ sema_p(&dp->rxfilter_lock);
+ bcopy(mac, dp->cur_addr.ether_addr_octet, ETHERADDRL);
+ dp->rxmode |= RXMODE_ENABLE;
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED) {
+ (void) usbgem_hal_set_rx_filter(dp);
+ }
+ sema_v(&dp->rxfilter_lock);
+
+ return (GLD_SUCCESS);
+}
+
+static int
+usbgem_gld_get_stats(gld_mac_info_t *macinfo, struct gld_stats *gs)
+{
+ struct usbgem_dev *dp;
+ struct usbgem_stats *vs;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ if ((*dp->ugc.usbgc_get_stats)(dp) != USB_SUCCESS) {
+#ifdef GEM_CONFIG_FMA
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+#endif
+ return (USB_FAILURE);
+ }
+
+ vs = &dp->stats;
+
+ gs->glds_errxmt = vs->errxmt;
+ gs->glds_errrcv = vs->errrcv;
+ gs->glds_collisions = vs->collisions;
+
+ gs->glds_excoll = vs->excoll;
+ gs->glds_defer = vs->defer;
+ gs->glds_frame = vs->frame;
+ gs->glds_crc = vs->crc;
+
+ gs->glds_overflow = vs->overflow; /* fifo err,underrun,rbufovf */
+ gs->glds_underflow = vs->underflow;
+ gs->glds_short = vs->runt;
+ gs->glds_missed = vs->missed; /* missed pkts while rbuf ovf */
+ gs->glds_xmtlatecoll = vs->xmtlatecoll;
+ gs->glds_nocarrier = vs->nocarrier;
+ gs->glds_norcvbuf = vs->norcvbuf; /* OS resource exaust */
+ gs->glds_intr = vs->intr;
+
+ /* all before here must be kept in place for v0 compatibility */
+ gs->glds_speed = usbgem_speed_value[dp->speed] * 1000000;
+ gs->glds_media = GLDM_PHYMII;
+ gs->glds_duplex = dp->full_duplex ? GLD_DUPLEX_FULL : GLD_DUPLEX_HALF;
+
+ /* gs->glds_media_specific */
+ gs->glds_dot3_first_coll = vs->first_coll;
+ gs->glds_dot3_multi_coll = vs->multi_coll;
+ gs->glds_dot3_sqe_error = 0;
+ gs->glds_dot3_mac_xmt_error = 0;
+ gs->glds_dot3_mac_rcv_error = 0;
+ gs->glds_dot3_frame_too_long = vs->frame_too_long;
+
+ return (GLD_SUCCESS);
+}
+
+static int
+usbgem_gld_ioctl(gld_mac_info_t *macinfo, queue_t *wq, mblk_t *mp)
+{
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+ usbgem_mac_ioctl(dp, wq, mp);
+
+ return (GLD_SUCCESS);
+}
+
+/*
+ * gem_gld_send is used only for sending data packets into ethernet wire.
+ */
+static int
+usbgem_gld_send(gld_mac_info_t *macinfo, mblk_t *mp)
+{
+ int ret;
+ uint32_t flags = 0;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ /* nic state must be online of suspended */
+ rw_enter(&dp->dev_state_lock, RW_READER);
+
+ ASSERT(dp->nic_state == NIC_STATE_ONLINE);
+ ASSERT(mp->b_next == NULL);
+
+ if (dp->mii_state != MII_STATE_LINKUP) {
+ /* Some nics hate to send packets while the link is down. */
+ /* we discard the untransmitted packets silently */
+ rw_exit(&dp->dev_state_lock);
+
+ freemsg(mp);
+#ifdef GEM_CONFIG_FMA
+ /* FIXME - should we ignore the error? */
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED);
+#endif
+ return (GLD_SUCCESS);
+ }
+
+ ret = (usbgem_send_common(dp, mp, flags) == NULL)
+ ? GLD_SUCCESS : GLD_NORESOURCES;
+ rw_exit(&dp->dev_state_lock);
+
+ return (ret);
+}
+
+/*
+ * usbgem_gld_send is used only for sending data packets into ethernet wire.
+ */
+static int
+usbgem_gld_send_tagged(gld_mac_info_t *macinfo, mblk_t *mp, uint32_t vtag)
+{
+ uint32_t flags;
+ struct usbgem_dev *dp;
+
+ dp = (struct usbgem_dev *)macinfo->gldm_private;
+
+ /*
+ * Some nics hate to send packets while the link is down.
+ */
+ if (dp->mii_state != MII_STATE_LINKUP) {
+ /* we dicard the untransmitted packets silently */
+ freemsg(mp);
+#ifdef GEM_CONFIG_FMA
+ /* FIXME - should we ignore the error? */
+ ddi_fm_service_impact(dp->dip, DDI_SERVICE_UNAFFECTED);
+#endif
+ return (GLD_SUCCESS);
+ }
+#ifdef notyet
+ flags = GLD_VTAG_TCI(vtag) << GEM_SEND_VTAG_SHIFT;
+#endif
+ return ((usbgem_send_common(dp, mp, 0) == NULL) ?
+ GLD_SUCCESS : GLD_NORESOURCES);
+}
+
+static void
+usbgem_gld_init(struct usbgem_dev *dp, gld_mac_info_t *macinfo, char *ident)
+{
+ /*
+ * configure GLD
+ */
+ macinfo->gldm_devinfo = dp->dip;
+ macinfo->gldm_private = (caddr_t)dp;
+
+ macinfo->gldm_reset = usbgem_gld_reset;
+ macinfo->gldm_start = usbgem_gld_start;
+ macinfo->gldm_stop = usbgem_gld_stop;
+ macinfo->gldm_set_mac_addr = usbgem_gld_set_mac_address;
+ macinfo->gldm_send = usbgem_gld_send;
+ macinfo->gldm_set_promiscuous = usbgem_gld_set_promiscuous;
+ macinfo->gldm_get_stats = usbgem_gld_get_stats;
+ macinfo->gldm_ioctl = usbgem_gld_ioctl;
+ macinfo->gldm_set_multicast = usbgem_gld_set_multicast;
+ macinfo->gldm_intr = NULL;
+ macinfo->gldm_mctl = NULL;
+
+ macinfo->gldm_ident = ident;
+ macinfo->gldm_type = DL_ETHER;
+ macinfo->gldm_minpkt = 0;
+ macinfo->gldm_maxpkt = dp->mtu;
+ macinfo->gldm_addrlen = ETHERADDRL;
+ macinfo->gldm_saplen = -2;
+ macinfo->gldm_ppa = ddi_get_instance(dp->dip);
+#ifdef GLD_CAP_LINKSTATE
+ macinfo->gldm_capabilities = GLD_CAP_LINKSTATE;
+#endif
+ macinfo->gldm_vendor_addr = dp->dev_addr.ether_addr_octet;
+ macinfo->gldm_broadcast_addr = usbgem_bcastaddr;
+}
+#endif /* USBGEM_CONFIG_GLDv3 */
+
+
+/* ======================================================================== */
+/*
+ * .conf interface
+ */
+/* ======================================================================== */
+void
+usbgem_generate_macaddr(struct usbgem_dev *dp, uint8_t *mac)
+{
+ extern char hw_serial[];
+ char *hw_serial_p;
+ int i;
+ uint64_t val;
+ uint64_t key;
+
+ cmn_err(CE_NOTE,
+ "!%s: using temp ether address,"
+ " do not use this for long time",
+ dp->name);
+
+ /* prefer a fixed address for DHCP */
+ hw_serial_p = &hw_serial[0];
+ val = stoi(&hw_serial_p);
+
+ key = 0;
+ for (i = 0; i < USBGEM_NAME_LEN; i++) {
+ if (dp->name[i] == 0) {
+ break;
+ }
+ key ^= dp->name[i];
+ }
+ key ^= ddi_get_instance(dp->dip);
+ val ^= key << 32;
+
+ /* generate a local address */
+ mac[0] = 0x02;
+ mac[1] = (uint8_t)(val >> 32);
+ mac[2] = (uint8_t)(val >> 24);
+ mac[3] = (uint8_t)(val >> 16);
+ mac[4] = (uint8_t)(val >> 8);
+ mac[5] = (uint8_t)val;
+}
+
+boolean_t
+usbgem_get_mac_addr_conf(struct usbgem_dev *dp)
+{
+ char propname[32];
+ char *valstr;
+ uint8_t mac[ETHERADDRL];
+ char *cp;
+ int c;
+ int i;
+ int j;
+ uint8_t v;
+ uint8_t d;
+ uint8_t ored;
+
+ DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+ /*
+ * Get ethernet address from .conf file
+ */
+ (void) sprintf(propname, "mac-addr");
+ if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dp->dip,
+ DDI_PROP_DONTPASS, propname, &valstr)) != DDI_PROP_SUCCESS) {
+ return (B_FALSE);
+ }
+
+ if (strlen(valstr) != ETHERADDRL*3-1) {
+ goto syntax_err;
+ }
+
+ cp = valstr;
+ j = 0;
+ ored = 0;
+ for (;;) {
+ v = 0;
+ for (i = 0; i < 2; i++) {
+ c = *cp++;
+
+ if (c >= 'a' && c <= 'f') {
+ d = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ d = c - 'A' + 10;
+ } else if (c >= '0' && c <= '9') {
+ d = c - '0';
+ } else {
+ goto syntax_err;
+ }
+ v = (v << 4) | d;
+ }
+
+ mac[j++] = v;
+ ored |= v;
+ if (j == ETHERADDRL) {
+ /* done */
+ break;
+ }
+
+ c = *cp++;
+ if (c != ':') {
+ goto syntax_err;
+ }
+ }
+
+ if (ored == 0) {
+ usbgem_generate_macaddr(dp, mac);
+ }
+ for (i = 0; i < ETHERADDRL; i++) {
+ dp->dev_addr.ether_addr_octet[i] = mac[i];
+ }
+ ddi_prop_free(valstr);
+ return (B_TRUE);
+
+syntax_err:
+ cmn_err(CE_CONT,
+ "!%s: read mac addr: trying .conf: syntax err %s",
+ dp->name, valstr);
+ ddi_prop_free(valstr);
+
+ return (B_FALSE);
+}
+
+static void
+usbgem_read_conf(struct usbgem_dev *dp)
+{
+ int val;
+
+ DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ /*
+ * Get media mode infomation from .conf file
+ */
+ dp->anadv_autoneg = usbgem_prop_get_int(dp, "adv_autoneg_cap", 1) != 0;
+ dp->anadv_1000fdx = usbgem_prop_get_int(dp, "adv_1000fdx_cap", 1) != 0;
+ dp->anadv_1000hdx = usbgem_prop_get_int(dp, "adv_1000hdx_cap", 1) != 0;
+ dp->anadv_100t4 = usbgem_prop_get_int(dp, "adv_100T4_cap", 1) != 0;
+ dp->anadv_100fdx = usbgem_prop_get_int(dp, "adv_100fdx_cap", 1) != 0;
+ dp->anadv_100hdx = usbgem_prop_get_int(dp, "adv_100hdx_cap", 1) != 0;
+ dp->anadv_10fdx = usbgem_prop_get_int(dp, "adv_10fdx_cap", 1) != 0;
+ dp->anadv_10hdx = usbgem_prop_get_int(dp, "adv_10hdx_cap", 1) != 0;
+ dp->anadv_1000t_ms = usbgem_prop_get_int(dp, "adv_1000t_ms", 0);
+
+ if ((ddi_prop_exists(DDI_DEV_T_ANY, dp->dip,
+ DDI_PROP_DONTPASS, "full-duplex"))) {
+ dp->full_duplex =
+ usbgem_prop_get_int(dp, "full-duplex", 1) != 0;
+ dp->anadv_autoneg = B_FALSE;
+ if (dp->full_duplex) {
+ dp->anadv_1000hdx = B_FALSE;
+ dp->anadv_100hdx = B_FALSE;
+ dp->anadv_10hdx = B_FALSE;
+ } else {
+ dp->anadv_1000fdx = B_FALSE;
+ dp->anadv_100fdx = B_FALSE;
+ dp->anadv_10fdx = B_FALSE;
+ }
+ }
+
+ if ((val = usbgem_prop_get_int(dp, "speed", 0)) > 0) {
+ dp->anadv_autoneg = B_FALSE;
+ switch (val) {
+ case 1000:
+ dp->speed = USBGEM_SPD_1000;
+ dp->anadv_100t4 = B_FALSE;
+ dp->anadv_100fdx = B_FALSE;
+ dp->anadv_100hdx = B_FALSE;
+ dp->anadv_10fdx = B_FALSE;
+ dp->anadv_10hdx = B_FALSE;
+ break;
+ case 100:
+ dp->speed = USBGEM_SPD_100;
+ dp->anadv_1000fdx = B_FALSE;
+ dp->anadv_1000hdx = B_FALSE;
+ dp->anadv_10fdx = B_FALSE;
+ dp->anadv_10hdx = B_FALSE;
+ break;
+ case 10:
+ dp->speed = USBGEM_SPD_10;
+ dp->anadv_1000fdx = B_FALSE;
+ dp->anadv_1000hdx = B_FALSE;
+ dp->anadv_100t4 = B_FALSE;
+ dp->anadv_100fdx = B_FALSE;
+ dp->anadv_100hdx = B_FALSE;
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "!%s: property %s: illegal value:%d",
+ dp->name, "speed", val);
+ dp->anadv_autoneg = B_TRUE;
+ break;
+ }
+ }
+ val = usbgem_prop_get_int(dp,
+ "adv_pause", dp->ugc.usbgc_flow_control & 1);
+ val |= usbgem_prop_get_int(dp,
+ "adv_asmpause", BOOLEAN(dp->ugc.usbgc_flow_control & 2)) << 1;
+ if (val > FLOW_CONTROL_RX_PAUSE || val < FLOW_CONTROL_NONE) {
+ cmn_err(CE_WARN,
+ "!%s: property %s: illegal value:%d",
+ dp->name, "flow-control", val);
+ } else {
+ val = min(val, dp->ugc.usbgc_flow_control);
+ }
+ dp->anadv_pause = BOOLEAN(val & 1);
+ dp->anadv_asmpause = BOOLEAN(val & 2);
+
+ dp->mtu = usbgem_prop_get_int(dp, "mtu", dp->mtu);
+ dp->txthr = usbgem_prop_get_int(dp, "txthr", dp->txthr);
+ dp->rxthr = usbgem_prop_get_int(dp, "rxthr", dp->rxthr);
+ dp->txmaxdma = usbgem_prop_get_int(dp, "txmaxdma", dp->txmaxdma);
+ dp->rxmaxdma = usbgem_prop_get_int(dp, "rxmaxdma", dp->rxmaxdma);
+#ifdef GEM_CONFIG_POLLING
+ dp->poll_pkt_delay =
+ usbgem_prop_get_int(dp, "pkt_delay", dp->poll_pkt_delay);
+
+ dp->max_poll_interval[GEM_SPD_10] =
+ usbgem_prop_get_int(dp, "max_poll_interval_10",
+ dp->max_poll_interval[GEM_SPD_10]);
+ dp->max_poll_interval[GEM_SPD_100] =
+ usbgem_prop_get_int(dp, "max_poll_interval_100",
+ dp->max_poll_interval[GEM_SPD_100]);
+ dp->max_poll_interval[GEM_SPD_1000] =
+ usbgem_prop_get_int(dp, "max_poll_interval_1000",
+ dp->max_poll_interval[GEM_SPD_1000]);
+
+ dp->min_poll_interval[GEM_SPD_10] =
+ usbgem_prop_get_int(dp, "min_poll_interval_10",
+ dp->min_poll_interval[GEM_SPD_10]);
+ dp->min_poll_interval[GEM_SPD_100] =
+ usbgem_prop_get_int(dp, "min_poll_interval_100",
+ dp->min_poll_interval[GEM_SPD_100]);
+ dp->min_poll_interval[GEM_SPD_1000] =
+ usbgem_prop_get_int(dp, "min_poll_interval_1000",
+ dp->min_poll_interval[GEM_SPD_1000]);
+#endif
+}
+
+/*
+ * usbem kstat support
+ */
+#ifndef GEM_CONFIG_GLDv3
+/* kstat items based from dmfe driver */
+
+struct usbgem_kstat_named {
+ struct kstat_named ks_xcvr_addr;
+ struct kstat_named ks_xcvr_id;
+ struct kstat_named ks_xcvr_inuse;
+ struct kstat_named ks_link_up;
+ struct kstat_named ks_link_duplex; /* 0:unknwon, 1:half, 2:full */
+ struct kstat_named ks_cap_1000fdx;
+ struct kstat_named ks_cap_1000hdx;
+ struct kstat_named ks_cap_100fdx;
+ struct kstat_named ks_cap_100hdx;
+ struct kstat_named ks_cap_10fdx;
+ struct kstat_named ks_cap_10hdx;
+#ifdef NEVER
+ struct kstat_named ks_cap_remfault;
+#endif
+ struct kstat_named ks_cap_autoneg;
+
+ struct kstat_named ks_adv_cap_1000fdx;
+ struct kstat_named ks_adv_cap_1000hdx;
+ struct kstat_named ks_adv_cap_100fdx;
+ struct kstat_named ks_adv_cap_100hdx;
+ struct kstat_named ks_adv_cap_10fdx;
+ struct kstat_named ks_adv_cap_10hdx;
+#ifdef NEVER
+ struct kstat_named ks_adv_cap_remfault;
+#endif
+ struct kstat_named ks_adv_cap_autoneg;
+ struct kstat_named ks_lp_cap_1000fdx;
+ struct kstat_named ks_lp_cap_1000hdx;
+ struct kstat_named ks_lp_cap_100fdx;
+ struct kstat_named ks_lp_cap_100hdx;
+ struct kstat_named ks_lp_cap_10fdx;
+ struct kstat_named ks_lp_cap_10hdx;
+ struct kstat_named ks_lp_cap_remfault;
+ struct kstat_named ks_lp_cap_autoneg;
+};
+
+static int
+usbgem_kstat_update(kstat_t *ksp, int rw)
+{
+ struct usbgem_kstat_named *knp;
+ struct usbgem_dev *dp = (struct usbgem_dev *)ksp->ks_private;
+
+ if (rw != KSTAT_READ) {
+ return (0);
+ }
+
+ knp = (struct usbgem_kstat_named *)ksp->ks_data;
+
+ knp->ks_xcvr_addr.value.ul = dp->mii_phy_addr;
+ knp->ks_xcvr_id.value.ul = dp->mii_phy_id;
+ knp->ks_xcvr_inuse.value.ul = usbgem_mac_xcvr_inuse(dp);
+ knp->ks_link_up.value.ul = dp->mii_state == MII_STATE_LINKUP;
+ knp->ks_link_duplex.value.ul =
+ (dp->mii_state == MII_STATE_LINKUP) ?
+ (dp->full_duplex ? 2 : 1) : 0;
+
+ knp->ks_cap_1000fdx.value.ul =
+ (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD);
+ knp->ks_cap_1000hdx.value.ul =
+ (dp->mii_xstatus & MII_XSTATUS_1000BASET) ||
+ (dp->mii_xstatus & MII_XSTATUS_1000BASEX);
+ knp->ks_cap_100fdx.value.ul =
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
+ knp->ks_cap_100hdx.value.ul =
+ BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
+ knp->ks_cap_10fdx.value.ul =
+ BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
+ knp->ks_cap_10hdx.value.ul =
+ BOOLEAN(dp->mii_status & MII_STATUS_10);
+#ifdef NEVER
+ knp->ks_cap_remfault.value.ul = B_TRUE;
+#endif
+ knp->ks_cap_autoneg.value.ul =
+ BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
+
+ knp->ks_adv_cap_1000fdx.value.ul = dp->anadv_1000fdx;
+ knp->ks_adv_cap_1000hdx.value.ul = dp->anadv_1000hdx;
+ knp->ks_adv_cap_100fdx.value.ul = dp->anadv_100fdx;
+ knp->ks_adv_cap_100hdx.value.ul = dp->anadv_100hdx;
+ knp->ks_adv_cap_10fdx.value.ul = dp->anadv_10fdx;
+ knp->ks_adv_cap_10hdx.value.ul = dp->anadv_10hdx;
+#ifdef NEVER
+ knp->ks_adv_cap_remfault.value.ul = 0;
+#endif
+ knp->ks_adv_cap_autoneg.value.ul = dp->anadv_autoneg;
+
+ knp->ks_lp_cap_1000fdx.value.ul =
+ BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL);
+ knp->ks_lp_cap_1000hdx.value.ul =
+ BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF);
+ knp->ks_lp_cap_100fdx.value.ul =
+ BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD);
+ knp->ks_lp_cap_100hdx.value.ul =
+ BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX);
+ knp->ks_lp_cap_10fdx.value.ul =
+ BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD);
+ knp->ks_lp_cap_10hdx.value.ul =
+ BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T);
+ knp->ks_lp_cap_remfault.value.ul =
+ BOOLEAN(dp->mii_exp & MII_AN_EXP_PARFAULT);
+ knp->ks_lp_cap_autoneg.value.ul =
+ BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN);
+
+ return (0);
+}
+
+
+static int
+usbgem_kstat_init(struct usbgem_dev *dp)
+{
+ int i;
+ kstat_t *ksp;
+ struct usbgem_kstat_named *knp;
+
+ ksp = kstat_create(
+ (char *)ddi_driver_name(dp->dip), ddi_get_instance(dp->dip),
+ "mii", "net", KSTAT_TYPE_NAMED,
+ sizeof (*knp) / sizeof (knp->ks_xcvr_addr), 0);
+
+ if (ksp == NULL) {
+ cmn_err(CE_WARN, "%s: %s() for mii failed",
+ dp->name, __func__);
+ return (USB_FAILURE);
+ }
+
+ knp = (struct usbgem_kstat_named *)ksp->ks_data;
+
+ kstat_named_init(&knp->ks_xcvr_addr, "xcvr_addr",
+ KSTAT_DATA_INT32);
+ kstat_named_init(&knp->ks_xcvr_id, "xcvr_id",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_xcvr_inuse, "xcvr_inuse",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_link_up, "link_up",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_link_duplex, "link_duplex",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_1000fdx, "cap_1000fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_1000hdx, "cap_1000hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_100fdx, "cap_100fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_100hdx, "cap_100hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_10fdx, "cap_10fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_cap_10hdx, "cap_10hdx",
+ KSTAT_DATA_UINT32);
+#ifdef NEVER
+ kstat_named_init(&knp->ks_cap_remfault, "cap_rem_fault",
+ KSTAT_DATA_UINT32);
+#endif
+ kstat_named_init(&knp->ks_cap_autoneg, "cap_autoneg",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_1000fdx, "adv_cap_1000fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_1000hdx, "adv_cap_1000hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_100fdx, "adv_cap_100fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_100hdx, "adv_cap_100hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_10fdx, "adv_cap_10fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_adv_cap_10hdx, "adv_cap_10hdx",
+ KSTAT_DATA_UINT32);
+#ifdef NEVER
+ kstat_named_init(&knp->ks_adv_cap_remfault, "adv_rem_fault",
+ KSTAT_DATA_UINT32);
+#endif
+ kstat_named_init(&knp->ks_adv_cap_autoneg, "adv_cap_autoneg",
+ KSTAT_DATA_UINT32);
+
+ kstat_named_init(&knp->ks_lp_cap_1000fdx, "lp_cap_1000fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_1000hdx, "lp_cap_1000hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_100fdx, "lp_cap_100fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_100hdx, "lp_cap_100hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_10fdx, "lp_cap_10fdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_10hdx, "lp_cap_10hdx",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_remfault, "lp_cap_rem_fault",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&knp->ks_lp_cap_autoneg, "lp_cap_autoneg",
+ KSTAT_DATA_UINT32);
+
+ ksp->ks_private = (void *) dp;
+ ksp->ks_update = usbgem_kstat_update;
+ dp->ksp = ksp;
+
+ kstat_install(ksp);
+
+ return (USB_SUCCESS);
+}
+#endif /* GEM_CONFIG_GLDv3 */
+/* ======================================================================== */
+/*
+ * attach/detatch/usb support
+ */
+/* ======================================================================== */
+int
+usbgem_ctrl_out(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *bp, int size)
+{
+ mblk_t *data;
+ usb_ctrl_setup_t setup;
+ usb_cr_t completion_reason;
+ usb_cb_flags_t cb_flags;
+ usb_flags_t flags;
+ int i;
+ int ret;
+
+ DPRINTF(4, (CE_CONT, "!%s: %s "
+ "reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x "
+ "bp:0x%p nic_state:%d",
+ dp->name, __func__, reqt, req, val, ix, len, bp, dp->nic_state));
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ return (USB_PIPE_ERROR);
+ }
+
+ data = NULL;
+ if (size > 0) {
+ if ((data = allocb(size, 0)) == NULL) {
+ return (USB_FAILURE);
+ }
+
+ bcopy(bp, data->b_rptr, size);
+ data->b_wptr = data->b_rptr + size;
+ }
+
+ setup.bmRequestType = reqt;
+ setup.bRequest = req;
+ setup.wValue = val;
+ setup.wIndex = ix;
+ setup.wLength = len;
+ setup.attrs = 0; /* attributes */
+
+ for (i = usbgem_ctrl_retry; i > 0; i--) {
+ completion_reason = 0;
+ cb_flags = 0;
+
+ ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp),
+ &setup, &data, &completion_reason, &cb_flags, 0);
+
+ if (ret == USB_SUCCESS) {
+ break;
+ }
+ if (i == 1) {
+ cmn_err(CE_WARN,
+ "!%s: %s failed: "
+ "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x "
+ "ret:%d cr:%s(%d), cb_flags:0x%x %s",
+ dp->name, __func__, reqt, req, val, ix, len,
+ ret, usb_str_cr(completion_reason),
+ completion_reason,
+ cb_flags,
+ (i > 1) ? "retrying..." : "fatal");
+ }
+ }
+
+ if (data != NULL) {
+ freemsg(data);
+ }
+
+ return (ret);
+}
+
+int
+usbgem_ctrl_in(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *bp, int size)
+{
+ mblk_t *data;
+ usb_ctrl_setup_t setup;
+ usb_cr_t completion_reason;
+ usb_cb_flags_t cb_flags;
+ int i;
+ int ret;
+ int reclen;
+
+ DPRINTF(4, (CE_CONT,
+ "!%s: %s:"
+ " reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x"
+ " bp:x%p mac_state:%d",
+ dp->name, __func__, reqt, req, val, ix, len, bp, dp->mac_state));
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ return (USB_PIPE_ERROR);
+ }
+
+ data = NULL;
+
+ setup.bmRequestType = reqt;
+ setup.bRequest = req;
+ setup.wValue = val;
+ setup.wIndex = ix;
+ setup.wLength = len;
+ setup.attrs = USB_ATTRS_AUTOCLEARING; /* XXX */
+
+ for (i = usbgem_ctrl_retry; i > 0; i--) {
+ completion_reason = 0;
+ cb_flags = 0;
+ ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp), &setup, &data,
+ &completion_reason, &cb_flags, 0);
+
+ if (ret == USB_SUCCESS) {
+ reclen = msgdsize(data);
+ bcopy(data->b_rptr, bp, min(reclen, size));
+ break;
+ }
+ if (i == 1) {
+ cmn_err(CE_WARN,
+ "!%s: %s failed: "
+ "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x "
+ "ret:%d cr:%s(%d) cb_flags:0x%x %s",
+ dp->name, __func__,
+ reqt, req, val, ix, len,
+ ret, usb_str_cr(completion_reason),
+ completion_reason,
+ cb_flags,
+ (i > 1) ? "retrying..." : "fatal");
+ }
+ }
+
+ if (data) {
+ freemsg(data);
+ }
+
+ return (ret);
+}
+
+int
+usbgem_ctrl_out_val(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ uint32_t v)
+{
+ uint8_t buf[4];
+
+ /* convert to little endian from native byte order */
+ switch (len) {
+ case 4:
+ buf[3] = v >> 24;
+ buf[2] = v >> 16;
+ /* fall thru */
+ case 2:
+ buf[1] = v >> 8;
+ /* fall thru */
+ case 1:
+ buf[0] = v;
+ }
+
+ return (usbgem_ctrl_out(dp, reqt, req, val, ix, len, buf, len));
+}
+
+int
+usbgem_ctrl_in_val(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *valp)
+{
+ uint8_t buf[4];
+ uint_t v;
+ int err;
+
+#ifdef SANITY
+ bzero(buf, sizeof (buf));
+#endif
+ err = usbgem_ctrl_in(dp, reqt, req, val, ix, len, buf, len);
+ if (err == USB_SUCCESS) {
+ v = 0;
+ switch (len) {
+ case 4:
+ v |= buf[3] << 24;
+ v |= buf[2] << 16;
+ /* FALLTHROUGH */
+ case 2:
+ v |= buf[1] << 8;
+ /* FALLTHROUGH */
+ case 1:
+ v |= buf[0];
+ }
+
+ switch (len) {
+ case 4:
+ *(uint32_t *)valp = v;
+ break;
+ case 2:
+ *(uint16_t *)valp = v;
+ break;
+ case 1:
+ *(uint8_t *)valp = v;
+ break;
+ }
+ }
+ return (err);
+}
+
+/*
+ * Attach / detach / disconnect / reconnect management
+ */
+static int
+usbgem_open_pipes(struct usbgem_dev *dp)
+{
+ int i;
+ int ret;
+ int ifnum;
+ int alt;
+ usb_client_dev_data_t *reg_data;
+ usb_ep_data_t *ep_tree_node;
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ ifnum = dp->ugc.usbgc_ifnum;
+ alt = dp->ugc.usbgc_alt;
+
+ ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
+ 0, USB_EP_ATTR_BULK, USB_EP_DIR_IN);
+ if (ep_tree_node == NULL) {
+ cmn_err(CE_WARN, "!%s: %s: ep_bulkin is NULL",
+ dp->name, __func__);
+ goto err;
+ }
+ dp->ep_bulkin = &ep_tree_node->ep_descr;
+
+ ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
+ 0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
+ if (ep_tree_node == NULL) {
+ cmn_err(CE_WARN, "!%s: %s: ep_bulkout is NULL",
+ dp->name, __func__);
+ goto err;
+ }
+ dp->ep_bulkout = &ep_tree_node->ep_descr;
+
+ ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt,
+ 0, USB_EP_ATTR_INTR, USB_EP_DIR_IN);
+ if (ep_tree_node) {
+ dp->ep_intr = &ep_tree_node->ep_descr;
+ } else {
+ /* don't care */
+ DPRINTF(1, (CE_CONT, "!%s: %s: ep_intr is NULL",
+ dp->name, __func__));
+ dp->ep_intr = NULL;
+ }
+
+ /* XXX -- no need to open default pipe */
+
+ /* open bulk out pipe */
+ bzero(&dp->policy_bulkout, sizeof (usb_pipe_policy_t));
+ dp->policy_bulkout.pp_max_async_reqs = 1;
+
+ if ((ret = usb_pipe_open(dp->dip,
+ dp->ep_bulkout, &dp->policy_bulkout, USB_FLAGS_SLEEP,
+ &dp->bulkout_pipe)) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: err:%x: failed to open bulk-out pipe",
+ dp->name, __func__, ret);
+ dp->bulkout_pipe = NULL;
+ goto err;
+ }
+ DPRINTF(1, (CE_CONT, "!%s: %s: bulkout_pipe opened successfully",
+ dp->name, __func__));
+
+ /* open bulk in pipe */
+ bzero(&dp->policy_bulkin, sizeof (usb_pipe_policy_t));
+ dp->policy_bulkin.pp_max_async_reqs = 1;
+ if ((ret = usb_pipe_open(dp->dip,
+ dp->ep_bulkin, &dp->policy_bulkin, USB_FLAGS_SLEEP,
+ &dp->bulkin_pipe)) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: ret:%x failed to open bulk-in pipe",
+ dp->name, __func__, ret);
+ dp->bulkin_pipe = NULL;
+ goto err;
+ }
+ DPRINTF(1, (CE_CONT, "!%s: %s: bulkin_pipe opened successfully",
+ dp->name, __func__));
+
+ if (dp->ep_intr) {
+ /* open interrupt pipe */
+ bzero(&dp->policy_interrupt, sizeof (usb_pipe_policy_t));
+ dp->policy_interrupt.pp_max_async_reqs = 1;
+ if ((ret = usb_pipe_open(dp->dip, dp->ep_intr,
+ &dp->policy_interrupt, USB_FLAGS_SLEEP,
+ &dp->intr_pipe)) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: ret:%x failed to open interrupt pipe",
+ dp->name, __func__, ret);
+ dp->intr_pipe = NULL;
+ goto err;
+ }
+ }
+ DPRINTF(1, (CE_CONT, "!%s: %s: intr_pipe opened successfully",
+ dp->name, __func__));
+
+ return (USB_SUCCESS);
+
+err:
+ if (dp->bulkin_pipe) {
+ usb_pipe_close(dp->dip,
+ dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->bulkin_pipe = NULL;
+ }
+ if (dp->bulkout_pipe) {
+ usb_pipe_close(dp->dip,
+ dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->bulkout_pipe = NULL;
+ }
+ if (dp->intr_pipe) {
+ usb_pipe_close(dp->dip,
+ dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->intr_pipe = NULL;
+ }
+
+ return (USB_FAILURE);
+}
+
+static int
+usbgem_close_pipes(struct usbgem_dev *dp)
+{
+ DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__));
+
+ if (dp->intr_pipe) {
+ usb_pipe_close(dp->dip,
+ dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->intr_pipe = NULL;
+ }
+ DPRINTF(1, (CE_CONT, "!%s: %s: 1", dp->name, __func__));
+
+ ASSERT(dp->bulkin_pipe);
+ usb_pipe_close(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->bulkin_pipe = NULL;
+ DPRINTF(1, (CE_CONT, "!%s: %s: 2", dp->name, __func__));
+
+ ASSERT(dp->bulkout_pipe);
+ usb_pipe_close(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0);
+ dp->bulkout_pipe = NULL;
+ DPRINTF(1, (CE_CONT, "!%s: %s: 3", dp->name, __func__));
+
+ return (USB_SUCCESS);
+}
+
+#define FREEZE_GRACEFUL (B_TRUE)
+#define FREEZE_NO_GRACEFUL (B_FALSE)
+static int
+usbgem_freeze_device(struct usbgem_dev *dp, boolean_t graceful)
+{
+ DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__));
+
+ /* stop nic activity */
+ (void) usbgem_mac_stop(dp, MAC_STATE_DISCONNECTED, graceful);
+
+ /*
+ * Here we free all memory resource allocated, because it will
+ * cause to panic the system that we free usb_bulk_req objects
+ * during the usb device is disconnected.
+ */
+ (void) usbgem_free_memory(dp);
+
+ return (USB_SUCCESS);
+}
+
+static int
+usbgem_disconnect_cb(dev_info_t *dip)
+{
+ int ret;
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+
+ cmn_err(CE_NOTE, "!%s: the usb device was disconnected (dp=%p)",
+ dp->name, dp);
+
+ /* start serialize */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+
+ ret = usbgem_freeze_device(dp, 0);
+
+ /* end of serialize */
+ rw_exit(&dp->dev_state_lock);
+
+ return (ret);
+}
+
+static int
+usbgem_recover_device(struct usbgem_dev *dp)
+{
+ int err;
+
+ DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__));
+
+ err = USB_SUCCESS;
+
+ /* reinitialize the usb connection */
+ usbgem_close_pipes(dp);
+ if ((err = usbgem_open_pipes(dp)) != USB_SUCCESS) {
+ goto x;
+ }
+
+ /* initialize nic state */
+ dp->mac_state = MAC_STATE_STOPPED;
+ dp->mii_state = MII_STATE_UNKNOWN;
+
+ /* allocate memory resources again */
+ if ((err = usbgem_alloc_memory(dp)) != USB_SUCCESS) {
+ goto x;
+ }
+
+ /* restart nic and recover state */
+ (void) usbgem_restart_nic(dp);
+
+ usbgem_mii_init(dp);
+
+ /* kick potentially stopped house keeping thread */
+ cv_signal(&dp->link_watcher_wait_cv);
+x:
+ return (err);
+}
+
+static int
+usbgem_reconnect_cb(dev_info_t *dip)
+{
+ int err = USB_SUCCESS;
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+ DPRINTF(0, (CE_CONT, "!%s: dp=%p", ddi_get_name(dip), dp));
+#ifdef notdef
+ /* check device changes after disconnect */
+ if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1,
+ USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
+ cmn_err(CE_CONT,
+ "!%s: no or different device installed", dp->name);
+ return (DDI_SUCCESS);
+ }
+#endif
+ cmn_err(CE_NOTE, "%s: the usb device was reconnected", dp->name);
+
+ /* start serialize */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ err = usbgem_recover_device(dp);
+ }
+
+ /* end of serialize */
+ rw_exit(&dp->dev_state_lock);
+
+ return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+usbgem_suspend(dev_info_t *dip)
+{
+ int err = USB_SUCCESS;
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__));
+
+ /* start serialize */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ err = usbgem_freeze_device(dp, STOP_GRACEFUL);
+ }
+
+ /* end of serialize */
+ rw_exit(&dp->dev_state_lock);
+
+ return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+usbgem_resume(dev_info_t *dip)
+{
+ int err = USB_SUCCESS;
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+
+ DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__));
+#ifdef notdef
+ /* check device changes after disconnect */
+ if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1,
+ USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
+ cmn_err(CE_CONT,
+ "!%s: no or different device installed", dp->name);
+ return (DDI_SUCCESS);
+ }
+#endif
+ /* start serialize */
+ rw_enter(&dp->dev_state_lock, RW_WRITER);
+
+ if (dp->mac_state == MAC_STATE_DISCONNECTED) {
+ err = usbgem_recover_device(dp);
+ }
+
+ /* end of serialize */
+ rw_exit(&dp->dev_state_lock);
+
+ return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+#define USBGEM_LOCAL_DATA_SIZE(gc) \
+ (sizeof (struct usbgem_dev) + USBGEM_MCALLOC)
+
+struct usbgem_dev *
+usbgem_do_attach(dev_info_t *dip,
+ struct usbgem_conf *gc, void *lp, int lmsize)
+{
+ struct usbgem_dev *dp;
+ int i;
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_register_t *macp = NULL;
+#else
+ gld_mac_info_t *macinfo;
+ void *tmp;
+#endif
+ int ret;
+ int unit;
+ int err;
+
+ unit = ddi_get_instance(dip);
+
+ DPRINTF(2, (CE_CONT, "!usbgem%d: %s: called", unit, __func__));
+
+ /*
+ * Allocate soft data structure
+ */
+ dp = kmem_zalloc(USBGEM_LOCAL_DATA_SIZE(gc), KM_SLEEP);
+ if (dp == NULL) {
+#ifndef USBGEM_CONFIG_GLDv3
+ gld_mac_free(macinfo);
+#endif
+ return (NULL);
+ }
+#ifdef USBGEM_CONFIG_GLDv3
+ if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+ cmn_err(CE_WARN, "!gem%d: %s: mac_alloc failed",
+ unit, __func__);
+ return (NULL);
+ }
+#else
+ macinfo = gld_mac_alloc(dip);
+ dp->macinfo = macinfo;
+#endif
+
+ /* link to private area */
+ dp->private = lp;
+ dp->priv_size = lmsize;
+ dp->mc_list = (struct mcast_addr *)&dp[1];
+
+ dp->dip = dip;
+ bcopy(gc->usbgc_name, dp->name, USBGEM_NAME_LEN);
+
+ /*
+ * register with usb service
+ */
+ if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "%s: %s: usb_client_attach failed",
+ dp->name, __func__);
+ goto err_free_private;
+ }
+
+ if (usb_get_dev_data(dip, &dp->reg_data,
+ USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
+ dp->reg_data = NULL;
+ goto err_unregister_client;
+ }
+#ifdef USBGEM_DEBUG_LEVEL
+ usb_print_descr_tree(dp->dip, dp->reg_data);
+#endif
+
+ if (usbgem_open_pipes(dp) != USB_SUCCESS) {
+ /* failed to open pipes */
+ cmn_err(CE_WARN, "!%s: %s: failed to open pipes",
+ dp->name, __func__);
+ goto err_unregister_client;
+ }
+
+ /*
+ * Initialize mutexs and condition variables
+ */
+ mutex_init(&dp->rxlock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&dp->txlock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&dp->rx_drain_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&dp->tx_drain_cv, NULL, CV_DRIVER, NULL);
+ rw_init(&dp->dev_state_lock, NULL, RW_DRIVER, NULL);
+ mutex_init(&dp->link_watcher_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&dp->link_watcher_wait_cv, NULL, CV_DRIVER, NULL);
+ sema_init(&dp->hal_op_lock, 1, NULL, SEMA_DRIVER, NULL);
+ sema_init(&dp->rxfilter_lock, 1, NULL, SEMA_DRIVER, NULL);
+
+ /*
+ * Initialize configuration
+ */
+ dp->ugc = *gc;
+
+ dp->mtu = ETHERMTU;
+ dp->rxmode = 0;
+ dp->speed = USBGEM_SPD_10; /* default is 10Mbps */
+ dp->full_duplex = B_FALSE; /* default is half */
+ dp->flow_control = FLOW_CONTROL_NONE;
+
+ dp->nic_state = NIC_STATE_STOPPED;
+ dp->mac_state = MAC_STATE_STOPPED;
+ dp->mii_state = MII_STATE_UNKNOWN;
+
+ /* performance tuning parameters */
+ dp->txthr = ETHERMAX; /* tx fifo threshoold */
+ dp->txmaxdma = 16*4; /* tx max dma burst size */
+ dp->rxthr = 128; /* rx fifo threshoold */
+ dp->rxmaxdma = 16*4; /* rx max dma burst size */
+
+ /*
+ * Get media mode infomation from .conf file
+ */
+ usbgem_read_conf(dp);
+
+ /* rx_buf_len depend on MTU */
+ dp->rx_buf_len = MAXPKTBUF(dp) + dp->ugc.usbgc_rx_header_len;
+
+ /*
+ * Reset the chip
+ */
+ if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to reset the usb device",
+ dp->name, __func__);
+ goto err_destroy_locks;
+ }
+
+ /*
+ * HW dependant paremeter initialization
+ */
+ if (usbgem_hal_attach_chip(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to attach the usb device",
+ dp->name, __func__);
+ goto err_destroy_locks;
+ }
+
+ /* allocate resources */
+ if (usbgem_alloc_memory(dp) != USB_SUCCESS) {
+ goto err_destroy_locks;
+ }
+
+ DPRINTF(0, (CE_CONT,
+ "!%s: %02x:%02x:%02x:%02x:%02x:%02x",
+ dp->name,
+ dp->dev_addr.ether_addr_octet[0],
+ dp->dev_addr.ether_addr_octet[1],
+ dp->dev_addr.ether_addr_octet[2],
+ dp->dev_addr.ether_addr_octet[3],
+ dp->dev_addr.ether_addr_octet[4],
+ dp->dev_addr.ether_addr_octet[5]));
+
+ /* copy mac address */
+ dp->cur_addr = dp->dev_addr;
+
+ /* pre-calculated tx timeout in second for performance */
+ dp->bulkout_timeout =
+ dp->ugc.usbgc_tx_timeout / drv_usectohz(1000*1000);
+
+#ifdef USBGEM_CONFIG_GLDv3
+ usbgem_gld3_init(dp, macp);
+#else
+ usbgem_gld_init(dp, macinfo, ident);
+#endif
+
+ /* Probe MII phy (scan phy) */
+ dp->mii_lpable = 0;
+ dp->mii_advert = 0;
+ dp->mii_exp = 0;
+ dp->mii_ctl1000 = 0;
+ dp->mii_stat1000 = 0;
+
+ dp->mii_status_ro = 0;
+ dp->mii_xstatus_ro = 0;
+
+ if (usbgem_mii_probe(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN, "!%s: %s: mii_probe failed",
+ dp->name, __func__);
+ goto err_free_memory;
+ }
+
+ /* mask unsupported abilities */
+ dp->anadv_autoneg &= BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG);
+ dp->anadv_1000fdx &=
+ BOOLEAN(dp->mii_xstatus &
+ (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASET_FD));
+ dp->anadv_1000hdx &=
+ BOOLEAN(dp->mii_xstatus &
+ (MII_XSTATUS_1000BASEX | MII_XSTATUS_1000BASET));
+ dp->anadv_100t4 &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4);
+ dp->anadv_100fdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD);
+ dp->anadv_100hdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX);
+ dp->anadv_10fdx &= BOOLEAN(dp->mii_status & MII_STATUS_10_FD);
+ dp->anadv_10hdx &= BOOLEAN(dp->mii_status & MII_STATUS_10);
+
+ if (usbgem_mii_init(dp) != USB_SUCCESS) {
+ cmn_err(CE_WARN, "!%s: %s: mii_init failed",
+ dp->name, __func__);
+ goto err_free_memory;
+ }
+
+ /*
+ * initialize kstats including mii statistics
+ */
+#ifdef USBGEM_CONFIG_GLDv3
+#ifdef USBGEM_CONFIG_ND
+ usbgem_nd_setup(dp);
+#endif
+#else
+ if (usbgem_kstat_init(dp) != USB_SUCCESS) {
+ goto err_free_memory;
+ }
+#endif
+
+ /*
+ * Add interrupt to system.
+ */
+#ifdef USBGEM_CONFIG_GLDv3
+ if (ret = mac_register(macp, &dp->mh)) {
+ cmn_err(CE_WARN, "!%s: mac_register failed, error:%d",
+ dp->name, ret);
+ goto err_release_stats;
+ }
+ mac_free(macp);
+ macp = NULL;
+#else
+ /* gld_register will corrupts driver_private */
+ tmp = ddi_get_driver_private(dip);
+ if (gld_register(dip,
+ (char *)ddi_driver_name(dip), macinfo) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "!%s: %s: gld_register failed",
+ dp->name, __func__);
+ ddi_set_driver_private(dip, tmp);
+ goto err_release_stats;
+ }
+ /* restore driver private */
+ ddi_set_driver_private(dip, tmp);
+#endif /* USBGEM_CONFIG_GLDv3 */
+ if (usb_register_hotplug_cbs(dip,
+ usbgem_suspend, usbgem_resume) != USB_SUCCESS) {
+ cmn_err(CE_WARN,
+ "!%s: %s: failed to register hotplug cbs",
+ dp->name, __func__);
+ goto err_unregister_gld;
+ }
+
+ /* reset mii and start mii link watcher */
+ if (usbgem_mii_start(dp) != USB_SUCCESS) {
+ goto err_unregister_hotplug;
+ }
+
+ /* start tx watchdow watcher */
+ if (usbgem_tx_watcher_start(dp)) {
+ goto err_usbgem_mii_stop;
+ }
+
+ ddi_set_driver_private(dip, (caddr_t)dp);
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: return: success", dp->name, __func__));
+
+ return (dp);
+
+err_usbgem_mii_stop:
+ usbgem_mii_stop(dp);
+
+err_unregister_hotplug:
+ usb_unregister_hotplug_cbs(dip);
+
+err_unregister_gld:
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_unregister(dp->mh);
+#else
+ gld_unregister(macinfo);
+#endif
+
+err_release_stats:
+#ifdef USBGEM_CONFIG_GLDv3
+#ifdef USBGEM_CONFIG_ND
+ /* release NDD resources */
+ usbgem_nd_cleanup(dp);
+#endif
+#else
+ kstat_delete(dp->ksp);
+#endif
+
+err_free_memory:
+ usbgem_free_memory(dp);
+
+err_destroy_locks:
+ cv_destroy(&dp->tx_drain_cv);
+ cv_destroy(&dp->rx_drain_cv);
+ mutex_destroy(&dp->txlock);
+ mutex_destroy(&dp->rxlock);
+ rw_destroy(&dp->dev_state_lock);
+ mutex_destroy(&dp->link_watcher_lock);
+ cv_destroy(&dp->link_watcher_wait_cv);
+ sema_destroy(&dp->hal_op_lock);
+ sema_destroy(&dp->rxfilter_lock);
+
+err_close_pipes:
+ (void) usbgem_close_pipes(dp);
+
+err_unregister_client:
+ usb_client_detach(dp->dip, dp->reg_data);
+
+err_free_private:
+#ifdef USBGEM_CONFIG_GLDv3
+ if (macp) {
+ mac_free(macp);
+ }
+#else
+ gld_mac_free(macinfo);
+#endif
+ kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(gc));
+
+ return (NULL);
+}
+
+int
+usbgem_do_detach(dev_info_t *dip)
+{
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+
+#ifdef USBGEM_CONFIG_GLDv3
+ /* unregister with gld v3 */
+ if (mac_unregister(dp->mh) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+#else
+ /* unregister with gld v2 */
+ if (gld_unregister(dp->macinfo) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+#endif
+ /* unregister with hotplug service */
+ usb_unregister_hotplug_cbs(dip);
+
+ /* stop tx watchdog watcher*/
+ usbgem_tx_watcher_stop(dp);
+
+ /* stop the link manager */
+ usbgem_mii_stop(dp);
+
+ /* unregister with usb service */
+ (void) usbgem_free_memory(dp);
+ (void) usbgem_close_pipes(dp);
+ usb_client_detach(dp->dip, dp->reg_data);
+ dp->reg_data = NULL;
+
+ /* unregister with kernel statistics */
+#ifdef USBGEM_CONFIG_GLDv3
+#ifdef USBGEM_CONFIG_ND
+ /* release ndd resources */
+ usbgem_nd_cleanup(dp);
+#endif
+#else
+ /* destroy kstat objects */
+ kstat_delete(dp->ksp);
+#endif
+
+ /* release locks and condition variables */
+ mutex_destroy(&dp->txlock);
+ mutex_destroy(&dp->rxlock);
+ cv_destroy(&dp->tx_drain_cv);
+ cv_destroy(&dp->rx_drain_cv);
+ rw_destroy(&dp->dev_state_lock);
+ mutex_destroy(&dp->link_watcher_lock);
+ cv_destroy(&dp->link_watcher_wait_cv);
+ sema_destroy(&dp->hal_op_lock);
+ sema_destroy(&dp->rxfilter_lock);
+
+ /* release basic memory resources */
+#ifndef USBGEM_CONFIG_GLDv3
+ gld_mac_free(dp->macinfo);
+#endif
+ kmem_free((caddr_t)(dp->private), dp->priv_size);
+ kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(&dp->ugc));
+
+ DPRINTF(2, (CE_CONT, "!%s: %s: return: success",
+ ddi_driver_name(dip), __func__));
+
+ return (DDI_SUCCESS);
+}
+
+int
+usbgem_mod_init(struct dev_ops *dop, char *name)
+{
+#ifdef USBGEM_CONFIG_GLDv3
+ major_t major;
+ major = ddi_name_to_major(name);
+ if (major == DDI_MAJOR_T_NONE) {
+ return (DDI_FAILURE);
+ }
+ mac_init_ops(dop, name);
+#endif
+ return (DDI_SUCCESS);
+}
+
+void
+usbgem_mod_fini(struct dev_ops *dop)
+{
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_fini_ops(dop);
+#endif
+}
+
+int
+usbgem_quiesce(dev_info_t *dip)
+{
+ struct usbgem_dev *dp;
+
+ dp = USBGEM_GET_DEV(dip);
+
+ ASSERT(dp != NULL);
+
+ if (dp->mac_state != MAC_STATE_DISCONNECTED &&
+ dp->mac_state != MAC_STATE_STOPPED) {
+ if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) {
+ (void) usbgem_hal_reset_chip(dp);
+ }
+ }
+
+ /* devo_quiesce() must return DDI_SUCCESS always */
+ return (DDI_SUCCESS);
+}
diff --git a/usr/src/uts/common/io/usbgem/usbgem.h b/usr/src/uts/common/io/usbgem/usbgem.h
new file mode 100644
index 0000000000..80b89a260e
--- /dev/null
+++ b/usr/src/uts/common/io/usbgem/usbgem.h
@@ -0,0 +1,428 @@
+/*
+ * usbgem.h: General USB to Ethernet MAC driver framework
+ * @(#)usbgem.h 1.4 12/02/09
+ * (C) Copyright 2003-2009 Masayuki Murayama KHF04453@nifty.ne.jp
+ */
+
+#ifndef __USBGEM_H__
+#define __USBGEM_H__
+
+#pragma ident "@(#)usbgem.h 1.4 12/02/09"
+
+#ifdef USBGEM_CONFIG_GLDv3
+#include <sys/mac.h>
+#ifndef MAC_VERSION
+#include <sys/mac_provider.h>
+#endif
+#include <sys/mac_ether.h>
+#else
+#include <sys/gld.h>
+#endif /* GLDv3 */
+
+/*
+ * Useful macros and typedefs
+ */
+#define USBGEM_NAME_LEN 32
+
+#define USBGEM_TX_TIMEOUT (drv_usectohz(3*1000000))
+#define USBGEM_TX_TIMEOUT_INTERVAL (drv_usectohz(1*1000000))
+#define USBGEM_LINK_WATCH_INTERVAL (drv_usectohz(1*1000000))
+
+/* general return code */
+#define USBGEM_SUCCESS 0
+#define USBGEM_FAILURE 1
+
+/* return code of usbgem_tx_done */
+#define INTR_RESTART_TX 0x80000000U
+
+struct usbgem_stats {
+ uint32_t intr;
+
+ uint32_t crc;
+ uint32_t errrcv;
+ uint32_t overflow;
+ uint32_t frame;
+ uint32_t missed;
+ uint32_t runt;
+ uint32_t frame_too_long;
+ uint32_t norcvbuf;
+ uint32_t sqe;
+
+ uint32_t collisions;
+ uint32_t first_coll;
+ uint32_t multi_coll;
+ uint32_t excoll;
+ uint32_t xmit_internal_err;
+ uint32_t nocarrier;
+ uint32_t defer;
+ uint32_t errxmt;
+ uint32_t underflow;
+ uint32_t xmtlatecoll;
+ uint32_t noxmtbuf;
+ uint32_t jabber;
+
+
+ uint64_t rbytes;
+ uint64_t obytes;
+ uint64_t rpackets;
+ uint64_t opackets;
+ uint32_t rbcast;
+ uint32_t obcast;
+ uint32_t rmcast;
+ uint32_t omcast;
+ uint32_t rcv_internal_err;
+};
+
+struct mcast_addr {
+ struct ether_addr addr;
+ uint32_t hash;
+};
+
+#define USBGEM_MAXMC 64
+#define USBGEM_MCALLOC (sizeof(struct mcast_addr) * USBGEM_MAXMC)
+
+#define SLOT(dp, n) ((n) % (dp)->ugc.usbgc_tx_list_max)
+
+/*
+ * mac soft state
+ */
+struct usbgem_dev {
+ dev_info_t *dip;
+#ifdef USBGEM_CONFIG_GLDv3
+ mac_handle_t mh;
+#else
+ void *macinfo; /* opaque handle for upper layer */
+#endif
+ char name[USBGEM_NAME_LEN];
+
+ /* pointer to usb private data */
+ usb_client_dev_data_t *reg_data;
+
+ /* usb handles */
+ usb_pipe_handle_t default_pipe;
+ usb_pipe_handle_t bulkin_pipe;
+ usb_pipe_handle_t bulkout_pipe;
+ usb_pipe_handle_t intr_pipe;
+
+ /* usb endpoints */
+ usb_ep_descr_t *ep_default;
+ usb_ep_descr_t *ep_bulkin;
+ usb_ep_descr_t *ep_bulkout;
+ usb_ep_descr_t *ep_intr;
+
+ /* usb policies */
+ usb_pipe_policy_t policy_default;
+ usb_pipe_policy_t policy_bulkin;
+ usb_pipe_policy_t policy_bulkout;
+ usb_pipe_policy_t policy_interrupt;
+
+ /* MAC address information */
+ struct ether_addr cur_addr;
+ struct ether_addr dev_addr;
+
+ /* RX state and resource management */
+ kmutex_t rxlock;
+ int rx_busy_cnt;
+ boolean_t rx_active;
+ kcondvar_t rx_drain_cv;
+
+ /* RX buffer management */
+ int rx_buf_len;
+
+ /* TX state and resource management */
+ kmutex_t txlock;
+ int tx_busy_cnt;
+ usb_bulk_req_t *tx_free_list;
+ kcondvar_t tx_drain_cv;
+ clock_t tx_start_time;
+ int bulkout_timeout; /* in second */
+ int tx_max_packets;
+ int tx_seq_num;
+ int tx_intr_pended;
+
+ /* NIC state from OS view */
+ int nic_state;
+#define NIC_STATE_UNKNOWN 0
+#define NIC_STATE_STOPPED 1
+#define NIC_STATE_INITIALIZED 2
+#define NIC_STATE_ONLINE 3
+
+ /* MAC state from hardware view */
+ int mac_state;
+#define MAC_STATE_DISCONNECTED 0 /* it includes suspended state too */
+#define MAC_STATE_STOPPED 1 /* powered up / buf not initialized */
+#define MAC_STATE_INITIALIZED 2 /* initialized */
+#define MAC_STATE_ONLINE 3 /* working correctly */
+#define MAC_STATE_ERROR 4 /* need to restart nic */
+
+ clock_t fatal_error;
+
+ /* robustness: timer and watchdog */
+ uint_t tx_watcher_stop;
+ kt_did_t tx_watcher_did;
+ kcondvar_t tx_watcher_cv;
+ kmutex_t tx_watcher_lock;
+ clock_t tx_watcher_timeout;
+ clock_t tx_watcher_interval;
+
+ /* MII mamagement */
+ boolean_t anadv_autoneg:1;
+ boolean_t anadv_1000fdx:1;
+ boolean_t anadv_1000hdx:1;
+ boolean_t anadv_100t4:1;
+ boolean_t anadv_100fdx:1;
+ boolean_t anadv_100hdx:1;
+ boolean_t anadv_10fdx:1;
+ boolean_t anadv_10hdx:1;
+ boolean_t anadv_1000t_ms:2;
+ boolean_t anadv_pause:1;
+ boolean_t anadv_asmpause:1;
+ boolean_t mii_advert_ro:1;
+
+ boolean_t full_duplex:1;
+ int speed:3;
+#define USBGEM_SPD_10 0
+#define USBGEM_SPD_100 1
+#define USBGEM_SPD_1000 2
+#define USBGEM_SPD_NUM 3
+ unsigned int flow_control:2;
+#define FLOW_CONTROL_NONE 0
+#define FLOW_CONTROL_SYMMETRIC 1
+#define FLOW_CONTROL_TX_PAUSE 2
+#define FLOW_CONTROL_RX_PAUSE 3
+
+ boolean_t mii_supress_msg:1;
+
+ uint32_t mii_phy_id;
+ uint16_t mii_status;
+ uint16_t mii_advert;
+ uint16_t mii_lpable;
+ uint16_t mii_exp;
+ uint16_t mii_ctl1000;
+ uint16_t mii_stat1000;
+ uint16_t mii_xstatus;
+ int8_t mii_phy_addr; /* must be signed */
+
+ uint16_t mii_status_ro;
+ uint16_t mii_xstatus_ro;
+
+ int mii_state;
+#define MII_STATE_UNKNOWN 0
+#define MII_STATE_RESETTING 1
+#define MII_STATE_AUTONEGOTIATING 2
+#define MII_STATE_AN_DONE 3
+#define MII_STATE_MEDIA_SETUP 4
+#define MII_STATE_LINKUP 5
+#define MII_STATE_LINKDOWN 6
+
+ clock_t mii_last_check; /* in tick */
+ clock_t mii_timer; /* in tick */
+#define MII_RESET_TIMEOUT drv_usectohz(1000*1000)
+#define MII_AN_TIMEOUT drv_usectohz(5000*1000)
+#define MII_LINKDOWN_TIMEOUT drv_usectohz(10000*1000)
+
+ clock_t mii_interval; /* in tick */
+ clock_t linkup_delay; /* in tick */
+
+ uint_t link_watcher_stop;
+ kt_did_t link_watcher_did;
+ kcondvar_t link_watcher_wait_cv;
+ kmutex_t link_watcher_lock;
+
+ krwlock_t dev_state_lock; /* mac_state and nic_state */
+ ksema_t hal_op_lock; /* serialize hw operations */
+ ksema_t drv_op_lock; /* hotplug op lock */
+
+ /* multcast list */
+ ksema_t rxfilter_lock;
+ int mc_count;
+ int mc_count_req;
+ struct mcast_addr *mc_list;
+ int rxmode;
+#define RXMODE_PROMISC 0x01
+#define RXMODE_ALLMULTI_REQ 0x02
+#define RXMODE_MULTI_OVF 0x04
+#define RXMODE_ENABLE 0x08
+#define RXMODE_ALLMULTI (RXMODE_ALLMULTI_REQ | RXMODE_MULTI_OVF)
+#define RXMODE_BITS \
+ "\020" \
+ "\004ENABLE" \
+ "\003MULTI_OVF" \
+ "\002ALLMULTI_REQ" \
+ "\001PROMISC"
+
+ /* statistcs */
+ struct usbgem_stats stats;
+
+ /* pointer to local structure */
+ void *private;
+ int priv_size;
+
+ /* configuration */
+ struct usbgem_conf {
+ /* name */
+ char usbgc_name[USBGEM_NAME_LEN];
+ int usbgc_ppa;
+
+ /* specification on usb */
+ int usbgc_ifnum; /* interface number */
+ int usbgc_alt; /* alternate */
+
+ /* specification on tx engine */
+ int usbgc_tx_list_max;
+
+ /* specification on rx engine */
+ int usbgc_rx_header_len;
+ int usbgc_rx_list_max;
+
+ /* time out parameters */
+ clock_t usbgc_tx_timeout;
+ clock_t usbgc_tx_timeout_interval;
+
+ /* flow control */
+ int usbgc_flow_control;
+
+ /* MII timeout parameters */
+ clock_t usbgc_mii_linkdown_timeout;
+ clock_t usbgc_mii_link_watch_interval;
+ clock_t usbgc_mii_reset_timeout;
+
+ clock_t usbgc_mii_an_watch_interval;
+ clock_t usbgc_mii_an_timeout;
+ clock_t usbgc_mii_an_wait;
+ clock_t usbgc_mii_an_delay;
+
+ /* MII configuration */
+ int usbgc_mii_addr_min;
+ int usbgc_mii_linkdown_action;
+ int usbgc_mii_linkdown_timeout_action;
+#define MII_ACTION_NONE 0
+#define MII_ACTION_RESET 1
+#define MII_ACTION_RSA 2
+ boolean_t usbgc_mii_dont_reset:1;
+ boolean_t usbgc_mii_an_oneshot:1;
+ boolean_t usbgc_mii_hw_link_detection:1;
+ boolean_t usbgc_mii_stop_mac_on_linkdown:1;
+ uint16_t usbgc_mii_an_cmd;
+
+ /* I/O methods */
+
+ /* mac operation */
+ int (*usbgc_attach_chip)(struct usbgem_dev *dp);
+ int (*usbgc_reset_chip)(struct usbgem_dev *dp);
+ int (*usbgc_init_chip)(struct usbgem_dev *dp);
+ int (*usbgc_start_chip)(struct usbgem_dev *dp);
+ int (*usbgc_stop_chip)(struct usbgem_dev *dp);
+ uint32_t (*usbgc_multicast_hash)(struct usbgem_dev *dp,
+ const uint8_t *);
+ int (*usbgc_set_rx_filter)(struct usbgem_dev *dp);
+ int (*usbgc_set_media)(struct usbgem_dev *dp);
+ int (*usbgc_get_stats)(struct usbgem_dev *dp);
+ void (*usbgc_interrupt)(struct usbgem_dev *dp, mblk_t *mp);
+
+ /* packet manupilation */
+ mblk_t *(*usbgc_tx_make_packet)(struct usbgem_dev *dp, mblk_t *mp);
+ mblk_t *(*usbgc_rx_make_packet)(struct usbgem_dev *dp, mblk_t *mp);
+ /* mii operations */
+ int (*usbgc_mii_probe)(struct usbgem_dev *dp);
+ int (*usbgc_mii_init)(struct usbgem_dev *dp);
+ int (*usbgc_mii_config)(struct usbgem_dev *dp, int *errp);
+ uint16_t (*usbgc_mii_read)(struct usbgem_dev *dp, uint_t reg, int *errp);
+ void (*usbgc_mii_write)(struct usbgem_dev *dp, uint_t reg, uint16_t val, int *errp);
+
+ /* jumbo frame */
+ int usbgc_max_mtu;
+ int usbgc_default_mtu;
+ int usbgc_min_mtu;
+ } ugc;
+
+ int misc_flag;
+#define USBGEM_VLAN 0x0001
+ timeout_id_t intr_watcher_id;
+
+ /* buffer size */
+ uint_t mtu;
+
+ /* performance tuning parameters */
+ uint_t txthr; /* tx fifo threshoold */
+ uint_t txmaxdma; /* tx max dma burst size */
+ uint_t rxthr; /* rx fifo threshoold */
+ uint_t rxmaxdma; /* tx max dma burst size */
+
+ /* kstat stuff */
+ kstat_t *ksp;
+
+ /* ndd stuff */
+ caddr_t nd_data_p;
+ caddr_t nd_arg_p;
+
+#ifdef USBGEM_DEBUG_LEVEL
+ int tx_cnt;
+#endif
+};
+
+/*
+ * Exported functions
+ */
+int usbgem_ctrl_out(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *bp, int size);
+
+int usbgem_ctrl_in(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *bp, int size);
+
+int usbgem_ctrl_out_val(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ uint32_t v);
+
+int usbgem_ctrl_in_val(struct usbgem_dev *dp,
+ uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
+ void *valp);
+
+void usbgem_generate_macaddr(struct usbgem_dev *, uint8_t *);
+boolean_t usbgem_get_mac_addr_conf(struct usbgem_dev *);
+int usbgem_mii_probe_default(struct usbgem_dev *);
+int usbgem_mii_init_default(struct usbgem_dev *);
+int usbgem_mii_config_default(struct usbgem_dev *, int *errp);
+void usbgem_mii_update_link(struct usbgem_dev *);
+void usbgem_restart_tx(struct usbgem_dev *);
+boolean_t usbgem_tx_done(struct usbgem_dev *, int);
+void usbgem_receive(struct usbgem_dev *);
+struct usbgem_dev *usbgem_do_attach(dev_info_t *,
+ struct usbgem_conf *, void *, int);
+int usbgem_do_detach(dev_info_t *);
+
+uint32_t usbgem_ether_crc_le(const uint8_t *addr);
+uint32_t usbgem_ether_crc_be(const uint8_t *addr);
+
+int usbgem_resume(dev_info_t *);
+int usbgem_suspend(dev_info_t *);
+int usbgem_quiesce(dev_info_t *);
+
+#ifdef USBGEM_CONFIG_GLDv3
+#if DEVO_REV < 4
+#define USBGEM_STREAM_OPS(dev_ops, attach, detach) \
+ DDI_DEFINE_STREAM_OPS(dev_ops, nulldev, nulldev, attach, detach, \
+ nodev, NULL, D_MP, NULL)
+#else
+#define USBGEM_STREAM_OPS(dev_ops, attach, detach) \
+ DDI_DEFINE_STREAM_OPS(dev_ops, nulldev, nulldev, attach, detach, \
+ nodev, NULL, D_MP, NULL, usbgem_quiesce)
+#endif
+#else
+#define usbgem_getinfo gld_getinfo
+#define usbgem_open gld_open
+#define usbgem_close gld_close
+#define usbgem_wput gld_wput
+#define usbgem_wsrv gld_wsrv
+#define usbgem_rsrv gld_rsrv
+#define usbgem_power NULL
+#endif
+int usbgem_mod_init(struct dev_ops *, char *);
+void usbgem_mod_fini(struct dev_ops *);
+
+#define USBGEM_GET_DEV(dip) \
+ ((struct usbgem_dev *)(ddi_get_driver_private(dip)))
+
+#endif /* __USBGEM_H__ */
diff --git a/usr/src/uts/common/io/usbgem/usbgem_mii.h b/usr/src/uts/common/io/usbgem/usbgem_mii.h
new file mode 100644
index 0000000000..2b4176a340
--- /dev/null
+++ b/usr/src/uts/common/io/usbgem/usbgem_mii.h
@@ -0,0 +1,242 @@
+/*
+ * gem_mii.h: mii header for gem
+ *
+ * Copyright (c) 2002-2007 Masayuki Murayama. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+#pragma ident "@(#)gem_mii.h 1.4 07/11/30"
+
+/*
+ * gem_mii.h : MII registers
+ */
+#ifndef _GEM_MII_H_
+#define _GEM_MII_H_
+
+#ifdef GEM_CONFIG_GLDv3
+#include <sys/miiregs.h>
+#else
+#define MII_CONTROL 0
+#define MII_STATUS 1
+#define MII_PHYIDH 2
+#define MII_PHYIDL 3
+#define MII_AN_ADVERT 4
+#define MII_AN_LPABLE 5
+#define MII_AN_EXPANSION 6
+#define MII_AN_NXTPGXMIT 7
+#endif /* GEM_CONFIG_GLDv3 */
+
+#define MII_AN_LPANXT 8
+#define MII_MS_CONTROL 9
+#define MII_MS_STATUS 10
+#define MII_XSTATUS 15
+
+/* for 1000BaseT support */
+#define MII_1000TC MII_MS_CONTROL
+#define MII_1000TS MII_MS_STATUS
+#ifndef GEM_CONFIG_GLDv3
+#define MII_CONTROL_RESET 0x8000
+#define MII_CONTROL_LOOPBACK 0x4000
+#define MII_CONTROL_100MB 0x2000
+#define MII_CONTROL_ANE 0x1000
+#define MII_CONTROL_PWRDN 0x0800
+#define MII_CONTROL_ISOLATE 0x0400
+#define MII_CONTROL_RSAN 0x0200
+#define MII_CONTROL_FDUPLEX 0x0100
+#define MII_CONTROL_COLTST 0x0080
+#endif /* !GEM_CONFIG_GLDv3 */
+#define MII_CONTROL_SPEED 0x2040
+
+#define MII_CONTROL_10MB 0x0000
+#define MII_CONTROL_1000MB 0x0040
+
+#define MII_CONTROL_BITS \
+ "\020" \
+ "\020RESET" \
+ "\017LOOPBACK" \
+ "\016100MB" \
+ "\015ANE" \
+ "\014PWRDN" \
+ "\013ISOLATE" \
+ "\012RSAN" \
+ "\011FDUPLEX" \
+ "\010COLTST" \
+ "\0071000M"
+#ifndef GEM_CONFIG_GLDv3
+#define MII_STATUS_100_BASE_T4 0x8000
+#define MII_STATUS_100_BASEX_FD 0x4000
+#define MII_STATUS_100_BASEX 0x2000
+#define MII_STATUS_10_FD 0x1000
+#define MII_STATUS_10 0x0800
+#define MII_STATUS_MFPRMBLSUPR 0x0040
+#define MII_STATUS_ANDONE 0x0020
+#define MII_STATUS_REMFAULT 0x0010
+#define MII_STATUS_CANAUTONEG 0x0008
+#define MII_STATUS_LINKUP 0x0004
+#define MII_STATUS_JABBERING 0x0002
+#define MII_STATUS_EXTENDED 0x0001
+#endif /* !GEM_CONFIG_GLDv3 */
+#define MII_STATUS_XSTATUS 0x0100
+#define MII_STATUS_100_BASE_T2_FD 0x0400
+#define MII_STATUS_100_BASE_T2 0x0200
+
+#define MII_STATUS_ABILITY_TECH \
+ (MII_STATUS_100_BASE_T4 | \
+ MII_STATUS_100_BASEX_FD | \
+ MII_STATUS_100_BASEX | \
+ MII_STATUS_10 | \
+ MII_STATUS_10_FD)
+
+
+#define MII_STATUS_BITS \
+ "\020" \
+ "\020100_BASE_T4" \
+ "\017100_BASEX_FD" \
+ "\016100_BASEX" \
+ "\01510_BASE_FD" \
+ "\01410_BASE" \
+ "\013100_BASE_T2_FD" \
+ "\012100_BASE_T2" \
+ "\011XSTATUS" \
+ "\007MFPRMBLSUPR" \
+ "\006ANDONE" \
+ "\005REMFAULT" \
+ "\004CANAUTONEG" \
+ "\003LINKUP" \
+ "\002JABBERING" \
+ "\001EXTENDED"
+#ifndef GEM_CONFIG_GLDv3
+#define MII_AN_ADVERT_NP 0x8000
+#define MII_AN_ADVERT_REMFAULT 0x2000
+#define MII_AN_ADVERT_SELECTOR 0x001f
+#endif /* !GEM_CONFIG_GLDv3 */
+
+#define MII_ABILITY_ASM_DIR 0x0800 /* for annex 28B */
+#ifndef MII_ABILITY_PAUSE
+#define MII_ABILITY_PAUSE 0x0400 /* for IEEE 802.3x */
+#endif
+#ifndef GEM_CONFIG_GLDv3
+#define MII_ABILITY_100BASE_T4 0x0200
+#define MII_ABILITY_100BASE_TX_FD 0x0100
+#define MII_ABILITY_100BASE_TX 0x0080
+#define MII_ABILITY_10BASE_T_FD 0x0040
+#define MII_ABILITY_10BASE_T 0x0020
+#endif /* !GEM_CONFIG_GLDv3 */
+
+#define MII_AN_LPABLE_NP 0x8000
+
+#define MII_ABILITY_TECH \
+ (MII_ABILITY_100BASE_T4 | \
+ MII_ABILITY_100BASE_TX_FD | \
+ MII_ABILITY_100BASE_TX | \
+ MII_ABILITY_10BASE_T | \
+ MII_ABILITY_10BASE_T_FD)
+
+#define MII_ABILITY_ALL \
+ (MII_AN_ADVERT_REMFAULT | \
+ MII_ABILITY_ASM_DIR | \
+ MII_ABILITY_PAUSE | \
+ MII_ABILITY_TECH)
+
+
+#define MII_ABILITY_BITS \
+ "\020" \
+ "\016REMFAULT" \
+ "\014ASM_DIR" \
+ "\013PAUSE" \
+ "\012100BASE_T4" \
+ "\011100BASE_TX_FD" \
+ "\010100BASE_TX" \
+ "\00710BASE_T_FD" \
+ "\00610BASE_T"
+#ifndef GEM_CONFIG_GLDv3
+#define MII_AN_EXP_PARFAULT 0x0010
+#define MII_AN_EXP_LPCANNXTP 0x0008
+#define MII_AN_EXP_CANNXTPP 0x0004
+#define MII_AN_EXP_PAGERCVD 0x0002
+#define MII_AN_EXP_LPCANAN 0x0001
+#endif /* !GEM_CONFIG_GLDv3 */
+
+#define MII_AN_EXP_BITS \
+ "\020" \
+ "\005PARFAULT" \
+ "\004LPCANNXTP" \
+ "\003CANNXTPP" \
+ "\002PAGERCVD" \
+ "\001LPCANAN"
+
+#define MII_1000TC_TESTMODE 0xe000
+#define MII_1000TC_CFG_EN 0x1000
+#define MII_1000TC_CFG_VAL 0x0800
+#define MII_1000TC_PORTTYPE 0x0400
+#define MII_1000TC_ADV_FULL 0x0200
+#define MII_1000TC_ADV_HALF 0x0100
+
+#define MII_1000TC_BITS \
+ "\020" \
+ "\015CFG_EN" \
+ "\014CFG_VAL" \
+ "\013PORTTYPE" \
+ "\012FULL" \
+ "\011HALF"
+
+#define MII_1000TS_CFG_FAULT 0x8000
+#define MII_1000TS_CFG_MASTER 0x4000
+#define MII_1000TS_LOCALRXOK 0x2000
+#define MII_1000TS_REMOTERXOK 0x1000
+#define MII_1000TS_LP_FULL 0x0800
+#define MII_1000TS_LP_HALF 0x0400
+
+#define MII_1000TS_BITS \
+ "\020" \
+ "\020CFG_FAULT" \
+ "\017CFG_MASTER" \
+ "\014CFG_LOCALRXOK" \
+ "\013CFG_REMOTERXOK" \
+ "\012LP_FULL" \
+ "\011LP_HALF"
+
+#define MII_XSTATUS_1000BASEX_FD 0x8000
+#define MII_XSTATUS_1000BASEX 0x4000
+#define MII_XSTATUS_1000BASET_FD 0x2000
+#define MII_XSTATUS_1000BASET 0x1000
+
+#define MII_XSTATUS_BITS \
+ "\020" \
+ "\0201000BASEX_FD" \
+ "\0171000BASEX" \
+ "\0161000BASET_FD" \
+ "\0151000BASET"
+
+#define MII_READ_CMD(p, r) \
+ ((6<<(18+5+5)) | ((p)<<(18+5)) | ((r)<<18))
+
+#define MII_WRITE_CMD(p, r, v) \
+ ((5<<(18+5+5)) | ((p)<<(18+5)) | ((r)<<18) | (2 << 16) | (v))
+
+#endif /* _GEM_MII_H_ */
diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c
index 4a243a2edd..ef677d791d 100644
--- a/usr/src/uts/common/io/vioif/vioif.c
+++ b/usr/src/uts/common/io/vioif/vioif.c
@@ -11,6 +11,7 @@
/*
* Copyright 2013 Nexenta Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
*/
@@ -284,6 +285,13 @@ struct vioif_softc {
unsigned int sc_tx_csum:1;
unsigned int sc_tx_tso4:1;
+ /*
+ * For debugging, it is useful to know whether the MAC address we
+ * are using came from the host (via VIRTIO_NET_CONFIG_MAC) or
+ * was otherwise generated or set from within the guest.
+ */
+ unsigned int sc_mac_from_host:1;
+
int sc_mtu;
uint8_t sc_mac[ETHERADDRL];
/*
@@ -311,7 +319,10 @@ struct vioif_softc {
/* Copying small packets turns out to be faster then mapping them. */
unsigned long sc_rxcopy_thresh;
unsigned long sc_txcopy_thresh;
- /* Some statistic coming here */
+
+ /*
+ * Statistics visible through mac:
+ */
uint64_t sc_ipackets;
uint64_t sc_opackets;
uint64_t sc_rbytes;
@@ -324,6 +335,18 @@ struct vioif_softc {
uint64_t sc_notxbuf;
uint64_t sc_ierrors;
uint64_t sc_oerrors;
+
+ /*
+ * Internal debugging statistics:
+ */
+ uint64_t sc_rxfail_dma_handle;
+ uint64_t sc_rxfail_dma_buffer;
+ uint64_t sc_rxfail_dma_bind;
+ uint64_t sc_rxfail_chain_undersize;
+ uint64_t sc_rxfail_no_descriptors;
+ uint64_t sc_txfail_dma_handle;
+ uint64_t sc_txfail_dma_bind;
+ uint64_t sc_txfail_indirect_limit;
};
#define ETHER_HEADER_LEN sizeof (struct ether_header)
@@ -473,8 +496,7 @@ vioif_rx_construct(void *buffer, void *user_arg, int kmflags)
if (ddi_dma_alloc_handle(sc->sc_dev, &vioif_mapped_buf_dma_attr,
DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmah)) {
- dev_err(sc->sc_dev, CE_WARN,
- "Can't allocate dma handle for rx buffer");
+ sc->sc_rxfail_dma_handle++;
goto exit_handle;
}
@@ -482,8 +504,7 @@ vioif_rx_construct(void *buffer, void *user_arg, int kmflags)
VIOIF_RX_SIZE + sizeof (struct virtio_net_hdr),
&vioif_bufattr, DDI_DMA_STREAMING, DDI_DMA_SLEEP,
NULL, &buf->rb_mapping.vbm_buf, &len, &buf->rb_mapping.vbm_acch)) {
- dev_err(sc->sc_dev, CE_WARN,
- "Can't allocate rx buffer");
+ sc->sc_rxfail_dma_buffer++;
goto exit_alloc;
}
ASSERT(len >= VIOIF_RX_SIZE);
@@ -492,8 +513,7 @@ vioif_rx_construct(void *buffer, void *user_arg, int kmflags)
buf->rb_mapping.vbm_buf, len, DDI_DMA_READ | DDI_DMA_STREAMING,
DDI_DMA_SLEEP, NULL, &buf->rb_mapping.vbm_dmac,
&buf->rb_mapping.vbm_ncookies)) {
- dev_err(sc->sc_dev, CE_WARN, "Can't bind tx buffer");
-
+ sc->sc_rxfail_dma_bind++;
goto exit_bind;
}
@@ -725,6 +745,7 @@ vioif_add_rx(struct vioif_softc *sc, int kmflag)
* but MAC does not ask for this info, hence we
* update sc_norecvbuf.
*/
+ sc->sc_rxfail_no_descriptors++;
sc->sc_norecvbuf++;
break;
}
@@ -737,9 +758,7 @@ vioif_add_rx(struct vioif_softc *sc, int kmflag)
}
/* Still nothing? Bye. */
- if (!buf) {
- dev_err(sc->sc_dev, CE_WARN,
- "Can't allocate rx buffer");
+ if (buf == NULL) {
sc->sc_norecvbuf++;
vq_free_entry(sc->sc_rx_vq, ve);
break;
@@ -813,8 +832,7 @@ vioif_process_rx(struct vioif_softc *sc)
ASSERT(buf);
if (len < sizeof (struct virtio_net_hdr)) {
- dev_err(sc->sc_dev, CE_WARN, "RX: Cnain too small: %u",
- len - (uint32_t)sizeof (struct virtio_net_hdr));
+ sc->sc_rxfail_chain_undersize++;
sc->sc_ierrors++;
virtio_free_chain(ve);
continue;
@@ -828,7 +846,7 @@ vioif_process_rx(struct vioif_softc *sc)
*/
if (len < sc->sc_rxcopy_thresh) {
mp = allocb(len, 0);
- if (!mp) {
+ if (mp == NULL) {
sc->sc_norecvbuf++;
sc->sc_ierrors++;
@@ -845,7 +863,7 @@ vioif_process_rx(struct vioif_softc *sc)
buf->rb_mapping.vbm_buf +
sizeof (struct virtio_net_hdr) +
VIOIF_IP_ALIGN, len, 0, &buf->rb_frtn);
- if (!mp) {
+ if (mp == NULL) {
sc->sc_norecvbuf++;
sc->sc_ierrors++;
@@ -905,22 +923,23 @@ vioif_reclaim_used_tx(struct vioif_softc *sc)
while ((ve = virtio_pull_chain(sc->sc_tx_vq, &len))) {
/* We don't chain descriptors for tx, so don't expect any. */
- ASSERT(!ve->qe_next);
+ ASSERT(ve->qe_next == NULL);
buf = &sc->sc_txbufs[ve->qe_index];
mp = buf->tb_mp;
buf->tb_mp = NULL;
- if (mp) {
- for (int i = 0; i < buf->tb_external_num; i++)
+ if (mp != NULL) {
+ for (int i = 0; i < buf->tb_external_num; i++) {
(void) ddi_dma_unbind_handle(
buf->tb_external_mapping[i].vbm_dmah);
+ }
}
virtio_free_chain(ve);
/* External mapping used, mp was not freed in vioif_send() */
- if (mp)
+ if (mp != NULL)
freemsg(mp);
num_reclaimed++;
}
@@ -964,8 +983,7 @@ vioif_tx_lazy_handle_alloc(struct vioif_softc *sc, struct vioif_tx_buf *buf,
&vioif_mapped_buf_dma_attr, DDI_DMA_SLEEP, NULL,
&buf->tb_external_mapping[i].vbm_dmah);
if (ret != DDI_SUCCESS) {
- dev_err(sc->sc_dev, CE_WARN,
- "Can't allocate dma handle for external tx buffer");
+ sc->sc_txfail_dma_handle++;
}
}
@@ -1019,17 +1037,14 @@ vioif_tx_external(struct vioif_softc *sc, struct vq_entry *ve, mblk_t *mp,
DDI_DMA_SLEEP, NULL, &dmac, &ncookies);
if (ret != DDI_SUCCESS) {
+ sc->sc_txfail_dma_bind++;
sc->sc_oerrors++;
- dev_err(sc->sc_dev, CE_NOTE,
- "TX: Failed to bind external handle");
goto exit_bind;
}
/* Check if we still fit into the indirect table. */
if (virtio_ve_indirect_available(ve) < ncookies) {
- dev_err(sc->sc_dev, CE_NOTE,
- "TX: Indirect descriptor table limit reached."
- " It took %d fragments.", i);
+ sc->sc_txfail_indirect_limit++;
sc->sc_notxbuf++;
sc->sc_oerrors++;
@@ -1088,7 +1103,7 @@ vioif_send(struct vioif_softc *sc, mblk_t *mp)
ve = vq_alloc_entry(sc->sc_tx_vq);
- if (!ve) {
+ if (ve == NULL) {
sc->sc_notxbuf++;
/* Out of free descriptors - try later. */
return (B_FALSE);
@@ -1140,9 +1155,9 @@ vioif_send(struct vioif_softc *sc, mblk_t *mp)
/* meanwhile update the statistic */
if (mp->b_rptr[0] & 0x1) {
if (bcmp(mp->b_rptr, vioif_broadcast, ETHERADDRL) != 0)
- sc->sc_multixmt++;
- else
- sc->sc_brdcstxmt++;
+ sc->sc_multixmt++;
+ else
+ sc->sc_brdcstxmt++;
}
/*
@@ -1206,8 +1221,7 @@ vioif_start(void *arg)
struct vq_entry *ve;
uint32_t len;
- mac_link_update(sc->sc_mac_handle,
- vioif_link_state(sc));
+ mac_link_update(sc->sc_mac_handle, vioif_link_state(sc));
virtio_start_vq_intr(sc->sc_rx_vq);
@@ -1423,10 +1437,8 @@ vioif_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
case MAC_PROP_PRIVATE:
bzero(valstr, sizeof (valstr));
if (strcmp(pr_name, vioif_txcopy_thresh) == 0) {
-
value = sc->sc_txcopy_thresh;
- } else if (strcmp(pr_name,
- vioif_rxcopy_thresh) == 0) {
+ } else if (strcmp(pr_name, vioif_rxcopy_thresh) == 0) {
value = sc->sc_rxcopy_thresh;
} else {
return;
@@ -1502,7 +1514,6 @@ vioif_show_features(struct vioif_softc *sc, const char *prefix,
bufp += virtio_show_features(features, bufp, bufend - bufp);
*bufp = '\0';
-
/* Using '!' to only CE_NOTE this to the system log. */
dev_err(sc->sc_dev, CE_NOTE, "!%s Vioif (%b)", buf, features,
VIRTIO_NET_FEATURE_BITS);
@@ -1531,8 +1542,8 @@ vioif_dev_features(struct vioif_softc *sc)
sc->sc_virtio.sc_features);
if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) {
- dev_err(sc->sc_dev, CE_NOTE,
- "Host does not support RING_INDIRECT_DESC, bye.");
+ dev_err(sc->sc_dev, CE_WARN,
+ "Host does not support RING_INDIRECT_DESC. Cannot attach.");
return (DDI_FAILURE);
}
@@ -1554,6 +1565,7 @@ vioif_set_mac(struct vioif_softc *sc)
virtio_write_device_config_1(&sc->sc_virtio,
VIRTIO_NET_CONFIG_MAC + i, sc->sc_mac[i]);
}
+ sc->sc_mac_from_host = 0;
}
/* Get the mac address out of the hardware, or make up one. */
@@ -1567,8 +1579,7 @@ vioif_get_mac(struct vioif_softc *sc)
&sc->sc_virtio,
VIRTIO_NET_CONFIG_MAC + i);
}
- dev_err(sc->sc_dev, CE_NOTE, "Got MAC address from host: %s",
- ether_sprintf((struct ether_addr *)sc->sc_mac));
+ sc->sc_mac_from_host = 1;
} else {
/* Get a few random bytes */
(void) random_get_pseudo_bytes(sc->sc_mac, ETHERADDRL);
@@ -1580,7 +1591,7 @@ vioif_get_mac(struct vioif_softc *sc)
vioif_set_mac(sc);
dev_err(sc->sc_dev, CE_NOTE,
- "Generated a random MAC address: %s",
+ "!Generated a random MAC address: %s",
ether_sprintf((struct ether_addr *)sc->sc_mac));
}
}
@@ -1653,7 +1664,7 @@ vioif_check_features(struct vioif_softc *sc)
if (!vioif_has_feature(sc, VIRTIO_NET_F_GUEST_CSUM)) {
sc->sc_rx_csum = 0;
}
- cmn_err(CE_NOTE, "Csum enabled.");
+ dev_err(sc->sc_dev, CE_NOTE, "!Csum enabled.");
if (vioif_has_feature(sc, VIRTIO_NET_F_HOST_TSO4)) {
@@ -1667,11 +1678,11 @@ vioif_check_features(struct vioif_softc *sc)
*/
if (!vioif_has_feature(sc, VIRTIO_NET_F_HOST_ECN)) {
dev_err(sc->sc_dev, CE_NOTE,
- "TSO4 supported, but not ECN. "
+ "!TSO4 supported, but not ECN. "
"Not using LSO.");
sc->sc_tx_tso4 = 0;
} else {
- cmn_err(CE_NOTE, "LSO enabled");
+ dev_err(sc->sc_dev, CE_NOTE, "!LSO enabled");
}
}
}
@@ -1795,7 +1806,7 @@ vioif_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
vioif_check_features(sc);
- if (vioif_alloc_mems(sc))
+ if (vioif_alloc_mems(sc) != 0)
goto exit_alloc_mems;
if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
@@ -1883,7 +1894,7 @@ vioif_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
return (DDI_FAILURE);
}
- if (sc->sc_rxloan) {
+ if (sc->sc_rxloan > 0) {
dev_err(devinfo, CE_WARN, "!Some rx buffers are still upstream,"
" not detaching.");
return (DDI_FAILURE);
diff --git a/usr/src/uts/common/io/vnd/frameio.c b/usr/src/uts/common/io/vnd/frameio.c
new file mode 100644
index 0000000000..e4e700fa12
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/frameio.c
@@ -0,0 +1,464 @@
+/*
+ * 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) 2014, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Frame I/O utility functions
+ */
+
+#include <sys/frameio.h>
+
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/sysmacros.h>
+#include <sys/inttypes.h>
+
+static kmem_cache_t *frameio_cache;
+
+int
+frameio_init(void)
+{
+ frameio_cache = kmem_cache_create("frameio_cache",
+ sizeof (frameio_t) + sizeof (framevec_t) * FRAMEIO_NVECS_MAX,
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (frameio_cache == NULL)
+ return (1);
+
+ return (0);
+}
+
+void
+frameio_fini(void)
+{
+ if (frameio_cache != NULL)
+ kmem_cache_destroy(frameio_cache);
+}
+
+frameio_t *
+frameio_alloc(int kmflags)
+{
+ return (kmem_cache_alloc(frameio_cache, kmflags));
+}
+
+void
+frameio_free(frameio_t *fio)
+{
+ return (kmem_cache_free(frameio_cache, fio));
+}
+
+/*
+ * Ensure that we don't see any garbage in the framevecs that we're nominally
+ * supposed to work with. Specifically we want to make sure that the buflen and
+ * the address are not zero.
+ */
+static int
+frameio_hdr_check_vecs(frameio_t *fio)
+{
+ int i;
+ for (i = 0; i < fio->fio_nvecs; i++)
+ if (fio->fio_vecs[i].fv_buf == NULL ||
+ fio->fio_vecs[i].fv_buflen == 0)
+ return (EINVAL);
+
+ return (0);
+}
+
+/*
+ * We have to copy in framevec32_t's. To work around the data model issues and
+ * trying not to copy memory we first copy in the framevec32_t data into the
+ * standard fio_vec space. Next we work backwards copying a given framevec32_t
+ * to a temporaory framevec_t and then overwrite the frameio_t's data. Note that
+ * it is important that we do this in reverse so as to ensure that we don't
+ * clobber data as the framevec_t is larger than the framevec32_t.
+ */
+static int
+frameio_hdr_copyin_ilp32(frameio_t *fio, const void *addr)
+{
+ framevec32_t *vec32p;
+ framevec_t fv;
+ int i;
+
+ vec32p = (framevec32_t *)&fio->fio_vecs[0];
+
+ if (ddi_copyin(addr, vec32p, sizeof (framevec32_t) * fio->fio_nvecs,
+ 0) != 0)
+ return (EFAULT);
+
+ for (i = fio->fio_nvecs - 1; i >= 0; i--) {
+ fv.fv_buf = (void *)(uintptr_t)vec32p[i].fv_buf;
+ fv.fv_buflen = vec32p[i].fv_buflen;
+ fv.fv_actlen = vec32p[i].fv_actlen;
+ fio->fio_vecs[i].fv_buf = fv.fv_buf;
+ fio->fio_vecs[i].fv_buflen = fv.fv_buflen;
+ fio->fio_vecs[i].fv_actlen = fv.fv_actlen;
+ }
+
+ return (frameio_hdr_check_vecs(fio));
+}
+
+/*
+ * Copy in a frame io header into fio with space for up to nvecs. If the frameio
+ * contains more vectors than specified it will be ignored. mode should contain
+ * information about the datamodel.
+ */
+int
+frameio_hdr_copyin(frameio_t *fio, int max_vecs, const void *addr, uint_t mode)
+{
+ int model = ddi_model_convert_from(mode & FMODELS);
+ int cpf = mode & FKIOCTL ? FKIOCTL : 0;
+ size_t fsize = model == DDI_MODEL_ILP32 ?
+ sizeof (frameio32_t) : sizeof (frameio_t);
+
+ /*
+ * The start of the header is the same in all data models for the
+ * current verison.
+ */
+ if (ddi_copyin(addr, fio, fsize, cpf) != 0)
+ return (EFAULT);
+
+ if (fio->fio_version != FRAMEIO_VERSION_ONE)
+ return (EINVAL);
+
+ if (fio->fio_nvecs > FRAMEIO_NVECS_MAX || fio->fio_nvecs == 0)
+ return (EINVAL);
+
+ if (fio->fio_nvpf == 0)
+ return (EINVAL);
+
+ if (fio->fio_nvecs % fio->fio_nvpf != 0)
+ return (EINVAL);
+
+ if (fio->fio_nvecs > max_vecs)
+ return (EOVERFLOW);
+
+ addr = (void *)((uintptr_t)addr + fsize);
+ if (model == DDI_MODEL_ILP32) {
+ if (cpf != 0)
+ return (EINVAL);
+ return (frameio_hdr_copyin_ilp32(fio, addr));
+ }
+
+ if (ddi_copyin(addr, &fio->fio_vecs[0],
+ sizeof (framevec_t) * fio->fio_nvecs, cpf) != 0)
+ return (EFAULT);
+
+ return (frameio_hdr_check_vecs(fio));
+}
+
+static mblk_t *
+frameio_allocb(size_t sz)
+{
+ mblk_t *mp;
+
+ mp = allocb(sz, 0);
+ if (mp == NULL)
+ return (NULL);
+
+ mp->b_datap->db_type = M_DATA;
+ return (mp);
+}
+
+static int
+framevec_mblk_read(framevec_t *fv, mblk_t **mpp, int cpf)
+{
+ mblk_t *mp;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ mp = frameio_allocb(fv->fv_buflen);
+
+ if (mp == NULL) {
+ freemsg(mp);
+ return (EAGAIN);
+ }
+
+ if (ddi_copyin(fv->fv_buf, mp->b_wptr, fv->fv_buflen,
+ cpf) != 0) {
+ freemsg(mp);
+ return (EFAULT);
+ }
+
+ mp->b_wptr += fv->fv_buflen;
+ *mpp = mp;
+ return (0);
+}
+
+/*
+ * Read a set of frame vectors that make up a single message boundary and return
+ * that as a single message in *mpp that consists of multiple data parts.
+ */
+static int
+frameio_mblk_read(frameio_t *fio, framevec_t *fv, mblk_t **mpp, int cpf)
+{
+ int nparts = fio->fio_nvpf;
+ int part, error;
+ mblk_t *mp;
+
+ *mpp = NULL;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ /*
+ * Construct the initial frame
+ */
+ for (part = 0; part < nparts; part++) {
+ error = framevec_mblk_read(fv, &mp, cpf);
+ if (error != 0) {
+ freemsg(*mpp);
+ return (error);
+ }
+
+ if (*mpp == NULL)
+ *mpp = mp;
+ else
+ linkb(*mpp, mp);
+ fv++;
+ }
+
+ return (0);
+}
+
+/*
+ * Read data from a series of frameio vectors into a message block chain. A
+ * given frameio request has a number of discrete messages divided into
+ * individual vectors based on fio->fio_nvcspframe. Each discrete message will
+ * be constructed into a message block chain pointed to by b_next.
+ *
+ * If we get an EAGAIN while trying to construct a given message block what we
+ * return depends on what else we've done so far. If we have succesfully
+ * completed at least one message then we free everything else we've done so
+ * far and return that. If no messages have been completed we return EAGAIN. If
+ * instead we encounter a different error, say EFAULT, then all of the fv_actlen
+ * entries values are undefined.
+ */
+int
+frameio_mblk_chain_read(frameio_t *fio, mblk_t **mpp, int *nvecs, int cpf)
+{
+ int error = ENOTSUP;
+ int nframes = fio->fio_nvecs / fio->fio_nvpf;
+ int frame;
+ framevec_t *fv;
+ mblk_t *mp, *bmp = NULL;
+
+ /*
+ * Protect against bogus kernel subsystems.
+ */
+ VERIFY(fio->fio_nvecs > 0);
+ VERIFY(fio->fio_nvecs % fio->fio_nvpf == 0);
+
+ *mpp = NULL;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ fv = &fio->fio_vecs[0];
+ for (frame = 0; frame < nframes; frame++) {
+ error = frameio_mblk_read(fio, fv, &mp, cpf);
+ if (error != 0)
+ goto failed;
+
+ if (bmp != NULL)
+ bmp->b_next = mp;
+ else
+ *mpp = mp;
+ bmp = mp;
+ }
+
+ *nvecs = nframes;
+ return (0);
+failed:
+ /*
+ * On EAGAIN we've already taken care of making sure that we have no
+ * leftover messages, eg. they were never linked in.
+ */
+ if (error == EAGAIN) {
+ if (frame != 0)
+ error = 0;
+ if (*nvecs != NULL)
+ *nvecs = frame;
+ ASSERT(*mpp != NULL);
+ } else {
+ for (mp = *mpp; mp != NULL; mp = bmp) {
+ bmp = mp->b_next;
+ freemsg(mp);
+ }
+ if (nvecs != NULL)
+ *nvecs = 0;
+ *mpp = NULL;
+ }
+ return (error);
+}
+
+size_t
+frameio_frame_length(frameio_t *fio, framevec_t *fv)
+{
+ int i;
+ size_t len = 0;
+
+ for (i = 0; i < fio->fio_nvpf; i++, fv++)
+ len += fv->fv_buflen;
+
+ return (len);
+}
+
+/*
+ * Write a portion of an mblk to the current.
+ */
+static int
+framevec_write_mblk_part(framevec_t *fv, mblk_t *mp, size_t len, size_t moff,
+ size_t foff, int cpf)
+{
+ ASSERT(len <= MBLKL(mp) - moff);
+ ASSERT(len <= fv->fv_buflen - fv->fv_actlen);
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ if (ddi_copyout(mp->b_rptr + moff, fv->fv_buf + foff, len, cpf) != 0)
+ return (EFAULT);
+ fv->fv_actlen += len;
+
+ return (0);
+}
+
+/*
+ * Because copying this out to the user might fail we don't want to update the
+ * b_rptr in case we need to copy it out again.
+ */
+static int
+framevec_map_blk(frameio_t *fio, framevec_t *fv, mblk_t *mp, int cpf)
+{
+ int err;
+ size_t msize, blksize, len, moff, foff;
+
+ msize = msgsize(mp);
+ if (msize > frameio_frame_length(fio, fv))
+ return (EOVERFLOW);
+
+ moff = 0;
+ foff = 0;
+ blksize = MBLKL(mp);
+ fv->fv_actlen = 0;
+ while (msize != 0) {
+ len = MIN(blksize, fv->fv_buflen - fv->fv_actlen);
+ err = framevec_write_mblk_part(fv, mp, len, moff, foff, cpf);
+ if (err != 0)
+ return (err);
+
+ msize -= len;
+ blksize -= len;
+ moff += len;
+ foff += len;
+
+ if (blksize == 0 && msize != 0) {
+ mp = mp->b_cont;
+ ASSERT(mp != NULL);
+ moff = 0;
+ blksize = MBLKL(mp);
+ }
+
+ if (fv->fv_buflen == fv->fv_actlen && msize != 0) {
+ fv++;
+ fv->fv_actlen = 0;
+ foff = 0;
+ }
+ }
+
+ return (0);
+}
+
+int
+frameio_mblk_chain_write(frameio_t *fio, frameio_write_mblk_map_t map,
+ mblk_t *mp, int *nwrite, int cpf)
+{
+ int mcount = 0;
+ int ret = 0;
+
+ if (map != MAP_BLK_FRAME)
+ return (EINVAL);
+
+ while (mp != NULL && mcount < fio->fio_nvecs) {
+ ret = framevec_map_blk(fio, &fio->fio_vecs[mcount], mp, cpf);
+ if (ret != 0)
+ break;
+ mcount += fio->fio_nvpf;
+ mp = mp->b_next;
+ }
+
+ if (ret != 0 && mcount == 0) {
+ if (nwrite != NULL)
+ *nwrite = 0;
+ return (ret);
+ }
+
+ if (nwrite != NULL)
+ *nwrite = mcount / fio->fio_nvpf;
+
+ return (0);
+}
+
+/*
+ * Copy out nframes worth of frameio header data back to userland.
+ */
+int
+frameio_hdr_copyout(frameio_t *fio, int nframes, void *addr, uint_t mode)
+{
+ int i;
+ int model = ddi_model_convert_from(mode & FMODELS);
+ framevec32_t *vec32p;
+ framevec32_t f;
+
+ if (fio->fio_nvecs / fio->fio_nvpf < nframes)
+ return (EINVAL);
+
+ fio->fio_nvecs = nframes * fio->fio_nvpf;
+
+ if (model == DDI_MODEL_NONE) {
+ if (ddi_copyout(fio, addr,
+ sizeof (frameio_t) + fio->fio_nvecs * sizeof (framevec_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+ }
+
+ ASSERT(model == DDI_MODEL_ILP32);
+
+ vec32p = (framevec32_t *)&fio->fio_vecs[0];
+ for (i = 0; i < fio->fio_nvecs; i++) {
+ f.fv_buf = (caddr32_t)(uintptr_t)fio->fio_vecs[i].fv_buf;
+ if (fio->fio_vecs[i].fv_buflen > UINT_MAX ||
+ fio->fio_vecs[i].fv_actlen > UINT_MAX)
+ return (EOVERFLOW);
+ f.fv_buflen = fio->fio_vecs[i].fv_buflen;
+ f.fv_actlen = fio->fio_vecs[i].fv_actlen;
+ vec32p[i].fv_buf = f.fv_buf;
+ vec32p[i].fv_buflen = f.fv_buflen;
+ vec32p[i].fv_actlen = f.fv_actlen;
+ }
+
+ if (ddi_copyout(fio, addr,
+ sizeof (frameio32_t) + fio->fio_nvecs * sizeof (framevec32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+}
+
+void
+frameio_mark_consumed(frameio_t *fio, int nframes)
+{
+ int i;
+
+ ASSERT(fio->fio_nvecs / fio->fio_nvpf >= nframes);
+ for (i = 0; i < nframes * fio->fio_nvpf; i++)
+ fio->fio_vecs[i].fv_actlen = fio->fio_vecs[i].fv_buflen;
+}
diff --git a/usr/src/uts/common/io/vnd/vnd.c b/usr/src/uts/common/io/vnd/vnd.c
new file mode 100644
index 0000000000..264212cfb4
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/vnd.c
@@ -0,0 +1,5802 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * vnd - virtual (machine) networking datapath
+ *
+ * vnd's purpose is to provide a highly performant data path for Layer 2 network
+ * traffic and exist side by side an active IP netstack, each servicing
+ * different datalinks. vnd provides many of the same capabilities as the
+ * current TCP/IP stack does and some specific to layer two. Specifically:
+ *
+ * o Use of the DLD fastpath
+ * o Packet capture hooks
+ * o Ability to use hardware capabilities
+ * o Useful interfaces for handling multiple frames
+ *
+ * The following image shows where vnd fits into today's networking stack:
+ *
+ * +---------+----------+----------+
+ * | libdlpi | libvnd | libsocket|
+ * +---------+----------+----------+
+ * | · · VFS |
+ * | VFS · VFS +----------+
+ * | · | sockfs |
+ * +---------+----------+----------+
+ * | | VND | IP |
+ * | +----------+----------+
+ * | DLD/DLS |
+ * +-------------------------------+
+ * | MAC |
+ * +-------------------------------+
+ * | GLDv3 |
+ * +-------------------------------+
+ *
+ * -----------------------------------------
+ * A Tale of Two Devices - DDI Device Basics
+ * -----------------------------------------
+ *
+ * vnd presents itself to userland as a character device; however, it also is a
+ * STREAMS device so that it can interface with dld and the rest of the
+ * networking stack. Users never interface with the STREAMs devices directly and
+ * they are purely an implementation detail of vnd. Opening the STREAMS device
+ * require kcred and as such userland cannot interact with it or push it onto
+ * the stream head.
+ *
+ * The main vnd character device, /dev/vnd/ctl, is a self-cloning device. Every
+ * clone gets its own minor number; however, minor nodes are not created in the
+ * devices tree for these instances. In this state a user may do two different
+ * things. They may issue ioctls that affect global state or they may issue
+ * ioctls that try to attach it to a given datalink. Once a minor device has
+ * been attached to a datalink, all operations on it are scoped to that context,
+ * therefore subsequent global operations are not permitted.
+ *
+ * A given device can be linked into the /devices and /dev name space via a link
+ * ioctl. That ioctl causes a minor node to be created in /devices and then it
+ * will also appear under /dev/vnd/ due to vnd's sdev plugin. This is similar
+ * to, but simpler than, IP's persistence mechanism.
+ *
+ * ---------------------
+ * Binding to a datalink
+ * ---------------------
+ *
+ * Datalinks are backed by the dld (datalink device) and dls (datalink services)
+ * drivers. These drivers provide a STREAMS device for datalinks on the system
+ * which are exposed through /dev/net. Userland generally manipulates datalinks
+ * through libdlpi. When an IP interface is being plumbed up what actually
+ * happens is that someone does a dlpi_open(3DLPI) of the underlying datalink
+ * and then pushes on the ip STREAMS module with an I_PUSH ioctl. Modules may
+ * then can negotiate with dld and dls to obtain access to various capabilities
+ * and fast paths via a series of STREAMS messages.
+ *
+ * In vnd, we do the same thing, but we leave our STREAMS module as an
+ * implementation detail of the system. We don't want users to be able to
+ * arbitrarily push vnd STREAMS module onto any stream, so we explicitly require
+ * kcred to manipulate it. Thus, when a user issues a request to attach a
+ * datalink to a minor instance of the character device, that vnd minor instance
+ * itself does a layered open (ldi_open_by_name(9F)) of the specified datalink.
+ * vnd does that open using the passed in credentials from the ioctl, not kcred.
+ * This ensures that users who doesn't have permissions to open the device
+ * cannot. Once that's been opened, we push on the vnd streams module.
+ *
+ * Once the vnd STREAMS instance has been created for this device, eg. the
+ * I_PUSH ioctl returns, we explicitly send a STREAMS ioctl
+ * (VND_STRIOC_ASSOCIATE) to associate the vnd STREAMS and character devices.
+ * This association begins the STREAM device's initialization. We start up an
+ * asynchronous state machine that takes care of all the different aspects of
+ * plumbing up the device with dld and dls and enabling the MAC fast path. We
+ * need to guarantee to consumers of the character device that by the time their
+ * ioctl returns, the data path has been fully initialized.
+ *
+ * The state progression is fairly linear. There are two general steady states.
+ * The first is VND_S_ONLINE, which means that everything is jacked up and good
+ * to go. The alternative is VND_S_ZOMBIE, which means that the streams device
+ * encountered an error or we have finished tearing it down and the character
+ * device can clean it up. The following is our state progression and the
+ * meaning of each state:
+ *
+ * |
+ * |
+ * V
+ * +---------------+
+ * | VNS_S_INITIAL | This is our initial state. Every
+ * +---------------+ vnd STREAMS device starts here.
+ * | While in this state, only dlpi
+ * | M_PROTO and M_IOCTL messages can be
+ * | sent or received. All STREAMS based
+ * | data messages are dropped.
+ * | We transition out of this state by
+ * | sending a DL_INFO_REQ to obtain
+ * | information about the underlying
+ * | link.
+ * v
+ * +-----------------+
+ * +--<-| VNS_S_INFO_SENT | In this state, we verify and
+ * | +-----------------+ record information about the
+ * | | underlying device. If the device is
+ * | | not suitable, eg. not of type
+ * v | DL_ETHER, then we immediately
+ * | | become a ZOMBIE. To leave this
+ * | | state we request exclusive active
+ * | | access to the device via
+ * v | DL_EXCLUSIVE_REQ.
+ * | v
+ * | +----------------------+
+ * +--<-| VNS_S_EXCLUSIVE_SENT | In this state, we verify whether
+ * | +----------------------+ or not we were able to obtain
+ * | | | exclusive access to the device. If
+ * | | | we were not able to, then we leave,
+ * v | | as that means that something like
+ * | | | IP is already plumbed up on top of
+ * | | | the datalink. We leave this state
+ * | | | by progressing through to the
+ * | | | appropriate DLPI primitive, either
+ * v | | DLPI_ATTACH_REQ or DLPI_BIND_REQ
+ * | | | depending on the style of the
+ * | | | datalink.
+ * | | v
+ * | | +-------------------+
+ * +------ |--<-| VNS_S_ATTACH_SENT | In this state, we verify we were
+ * | | +-------------------+ able to perform a standard DLPI
+ * | | | attach and if so, go ahead and
+ * v | | send a DLPI_BIND_REQ.
+ * | v v
+ * | +-------------------+
+ * +--<-| VNS_S_BIND_SENT | In this state we see the result of
+ * | +-------------------+ our attempt to bind to PPA 0 of the
+ * v | underlying device. Because we're
+ * | | trying to be a layer two datapath,
+ * | | the specific attachment point isn't
+ * | | too important as we're going to
+ * v | have to enable promiscuous mode. We
+ * | | transition out of this by sending
+ * | | our first of three promiscuous mode
+ * | | requests.
+ * v v
+ * | +------------------------+
+ * +--<-| VNS_S_SAP_PROMISC_SENT | In this state we verify that we
+ * | +------------------------+ were able to enable promiscuous
+ * | | mode at the physical level. We
+ * | | transition out of this by enabling
+ * | | multicast and broadcast promiscuous
+ * v | mode.
+ * | v
+ * | +--------------------------+
+ * +--<-| VNS_S_MULTI_PROMISC_SENT | In this state we verify that we
+ * | +--------------------------+ have enabled DL_PROMISC_MULTI and
+ * v | move onto the second promiscuous
+ * | | mode request.
+ * | v
+ * | +----------------------------+
+ * +--<-| VNS_S_RX_ONLY_PROMISC_SENT | In this state we verify that we
+ * | +----------------------------+ enabled RX_ONLY promiscuous mode.
+ * | | We specifically do this as we don't
+ * v | want to receive our own traffic
+ * | | that we'll send out. We leave this
+ * | | state by enabling the final flag
+ * | | DL_PROMISC_FIXUPS.
+ * | v
+ * | +--------------------------+
+ * +--<-| VNS_S_FIXUP_PROMISC_SENT | In this state we verify that we
+ * | +--------------------------+ enabled FIXUP promiscuous mode.
+ * | | We specifically do this as we need
+ * v | to ensure that traffic which is
+ * | | received by being looped back to us
+ * | | correctly has checksums fixed. We
+ * | | leave this state by requesting the
+ * | | dld/dls capabilities that we can
+ * v | process.
+ * | v
+ * | +--------------------+
+ * +--<-| VNS_S_CAPAB_Q_SENT | We loop over the set of
+ * | +--------------------+ capabilities that dld advertised
+ * | | and enable the ones that currently
+ * v | support for use. See the section
+ * | | later on regarding capabilities
+ * | | for more information. We leave this
+ * | | state by sending an enable request.
+ * v v
+ * | +--------------------+
+ * +--<-| VNS_S_CAPAB_E_SENT | Here we finish all capability
+ * | +--------------------+ initialization. Once finished, we
+ * | | transition to the next state. If
+ * v | the dld fast path is not available,
+ * | | we become a zombie.
+ * | v
+ * | +--------------+
+ * | | VNS_S_ONLINE | This is a vnd STREAMS device's
+ * | +--------------+ steady state. It will normally
+ * | | reside in this state while it is in
+ * | | active use. It will only transition
+ * v | to the next state when the STREAMS
+ * | | device is closed by the character
+ * | | device. In this state, all data
+ * | | flows over the dld fast path.
+ * | v
+ * | +---------------------+
+ * +--->| VNS_S_SHUTTING_DOWN | This vnd state takes care of
+ * | +---------------------+ disabling capabilities and
+ * | | flushing all data. At this point
+ * | | any additional data that we receive
+ * | | will be dropped. We leave this
+ * v | state by trying to remove multicast
+ * | | promiscuity.
+ * | |
+ * | v
+ * | +---------------------------------+
+ * +-->| VNS_S_MULTICAST_PROMISCOFF_SENT | In this state, we check if we have
+ * | +---------------------------------+ successfully removed multicast
+ * | | promiscuous mode. If we have
+ * | | failed, we still carry on but only
+ * | | warn. We leave this state by trying
+ * | | to disable SAP level promiscuous
+ * | | mode.
+ * | v
+ * | +---------------------------+
+ * +-->| VNS_S_SAP_PROMISCOFF_SENT | In this state, we check if we have
+ * | +---------------------------+ successfully removed SAP level
+ * | | promiscuous mode. If we have
+ * | | failed, we still carry on but only
+ * | | warn. Note that we don't worry
+ * | | about either of
+ * | | DL_PROMISC_FIXUPS or
+ * | | DL_PROMISC_RX_ONLY. If these are
+ * | | the only two entries left, then we
+ * | | should have anything that MAC is
+ * | | doing for us at this point,
+ * | | therefore it's safe for us to
+ * | | proceed to unbind, which is how we
+ * | | leave this state via a
+ * | v DL_UNBIND_REQ.
+ * | +-------------------+
+ * +--->| VNS_S_UNBIND_SENT | Here, we check how the unbind
+ * | +-------------------+ request went. Regardless of its
+ * | | success, we always transition to
+ * | | a zombie state.
+ * | v
+ * | +--------------+
+ * +--->| VNS_S_ZOMBIE | In this state, the vnd STREAMS
+ * +--------------+ device is waiting to finish being
+ * reaped. Because we have no more
+ * ways to receive data it should be
+ * safe to destroy all remaining data
+ * structures.
+ *
+ * If the stream association fails for any reason the state machine reaches
+ * VNS_S_ZOMBIE. A more detailed vnd_errno_t will propagate back through the
+ * STREAMS ioctl to the character device. That will fail the user ioctl and
+ * propagate the vnd_errno_t back to userland. If, on the other hand, the
+ * association succeeds, then the vnd STREAMS device will be fully plumbed up
+ * and ready to transmit and receive message blocks. Consumers will be able to
+ * start using the other cbops(9E) entry points once the attach has fully
+ * finished, which will occur after the original user attach ioctl to the
+ * character device returns.
+ *
+ * It's quite important that we end up sending the full series of STREAMS
+ * messages when tearing down. While it's tempting to say that we should just
+ * rely on the STREAMS device being closed to properly ensure that we have no
+ * more additional data, that's not sufficient due to our use of direct
+ * callbacks. DLS does not ensure that by the time we change the direct
+ * callback (vnd_mac_input) that all callers to it will have been quiesced.
+ * However, it does guarantee that if we disable promiscuous mode ourselves and
+ * we turn off the main data path via DL_UNBIND_REQ that it will work.
+ * Therefore, we make sure to do this ourselves rather than letting DLS/DLD do
+ * it as part of tearing down the STREAMS device. This ensures that we'll
+ * quiesce all data before we destroy our data structures and thus we should
+ * eliminate the race in changing the data function.
+ *
+ * --------------------
+ * General Architecture
+ * --------------------
+ *
+ * There are several different devices and structures in the vnd driver. There
+ * is a per-netstack component, pieces related to the character device that
+ * consumers see, the internal STREAMS device state, and the data queues
+ * themselves. The following ASCII art picture describes their relationships and
+ * some of the major pieces of data that contain them. These are not exhaustive,
+ * e.g. synchronization primitives are left out.
+ *
+ * +----------------+ +-----------------+
+ * | global | | global |
+ * | device list | | netstack list |
+ * | vnd_dev_list | | vnd_nsd_list |
+ * +----------------+ +-----------------+
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+
+ * | | per-netstack data | ---> | per-netstack data | --> ...
+ * | | vnd_pnsd_t | | vnd_pnsd_t |
+ * | | | +-------------------+
+ * | | |
+ * | | nestackid_t ---+----> Netstack ID
+ * | | vnd_pnsd_flags_t -+----> Status flags
+ * | | zoneid_t ---+----> Zone ID for this netstack
+ * | | hook_family_t ---+----> VND IPv4 Hooks
+ * | | hook_family_t ---+----> VND IPv6 Hooks
+ * | | list_t ----+ |
+ * | +------------+------+
+ * | |
+ * | v
+ * | +------------------+ +------------------+
+ * | | character device | ---> | character device | -> ...
+ * +---------->| vnd_dev_t | | vnd_dev_t |
+ * | | +------------------+
+ * | |
+ * | minor_t ---+--> device minor number
+ * | ldi_handle_t ---+--> handle to /dev/net/%datalink
+ * | vnd_dev_flags_t -+--> device flags, non blocking, etc.
+ * | char[] ---+--> name if linked
+ * | vnd_str_t * -+ |
+ * +--------------+---+
+ * |
+ * v
+ * +-------------------------+
+ * | STREAMS device |
+ * | vnd_str_t |
+ * | |
+ * | vnd_str_state_t ---+---> State machine state
+ * | gsqueue_t * ---+---> mblk_t Serialization queue
+ * | vnd_str_stat_t ---+---> per-device kstats
+ * | vnd_str_capab_t ---+----------------------------+
+ * | vnd_data_queue_t ---+ | |
+ * | vnd_data_queue_t -+ | | v
+ * +-------------------+-+---+ +---------------------+
+ * | | | Stream capabilities |
+ * | | | vnd_str_capab_t |
+ * | | | |
+ * | | supported caps <--+-- vnd_capab_flags_t |
+ * | | dld cap handle <--+-- void * |
+ * | | direct tx func <--+-- vnd_dld_tx_t |
+ * | | +---------------------+
+ * | |
+ * +----------------+ +-------------+
+ * | |
+ * v v
+ * +-------------------+ +-------------------+
+ * | Read data queue | | Write data queue |
+ * | vnd_data_queue_t | | vnd_data_queue_t |
+ * | | | |
+ * | size_t ----+--> Current size | size_t ----+--> Current size
+ * | size_t ----+--> Max size | size_t ----+--> Max size
+ * | mblk_t * ----+--> Queue head | mblk_t * ----+--> Queue head
+ * | mblk_t * ----+--> Queue tail | mblk_t * ----+--> Queue tail
+ * +-------------------+ +-------------------+
+ *
+ *
+ * Globally, we maintain two lists. One list contains all of the character
+ * device soft states. The other maintains a list of all our netstack soft
+ * states. Each netstack maintains a list of active devices that have been
+ * associated with a datalink in its netstack.
+ *
+ * Recall that a given minor instance of the character device exists in one of
+ * two modes. It can either be a cloned open of /dev/vnd/ctl, the control node,
+ * or it can be associated with a given datalink. When minor instances are in
+ * the former state, they do not exist in a given vnd_pnsd_t's list of devices.
+ * As part of attaching to a datalink, the given vnd_dev_t will be inserted into
+ * the appropriate vnd_pnsd_t. In addition, this will cause a STREAMS device, a
+ * vnd_str_t, to be created and associated to a vnd_dev_t.
+ *
+ * The character device, and its vnd_dev_t, is the interface to the rest of the
+ * system. The vnd_dev_t keeps track of various aspects like whether various
+ * operations, such as read, write and the frameio ioctls, are considered
+ * blocking or non-blocking in the O_NONBLOCK sense. It also is responsible for
+ * keeping track of things like the name of the device, if any, in /dev. The
+ * vnd_str_t, on the other hand manages aspects like buffer sizes and the actual
+ * data queues. However, ioctls that manipulate these properties all go through
+ * the vnd_dev_t to its associated vnd_str_t.
+ *
+ * Each of the STREAMS devices, the vnd_str_t, maintains two data queues. One
+ * for frames to transmit (write queue) and one for frames received (read
+ * queue). These data queues have a maximum size and attempting to add data
+ * beyond that maximum size will result in data being dropped. The sizes are
+ * configurable via ioctls VND_IOC_SETTXBUF, VND_IOC_SETRXBUF. Data either sits
+ * in those buffers or has a reservation in those buffers while they are in vnd
+ * and waiting to be consumed by the user or by mac.
+ *
+ * Finally, the vnd_str_t also has a vnd_str_capab_t which we use to manage the
+ * available, negotiated, and currently active features.
+ *
+ * ----------------------
+ * Data Path and gsqueues
+ * ----------------------
+ *
+ * There's a lot of plumbing in vnd to get to the point where we can send data,
+ * but vnd's bread and butter is the data path, so it's worth diving into it in
+ * more detail. Data enters and exits the system from two ends.
+ *
+ * The first end is the vnd consumer. This comes in the form of read and write
+ * system calls as well as the frame I/O ioctls. The read and write system calls
+ * operate on a single frame at a time. Think of a frame as a single message
+ * that has come in off the wire, which may itself comprise multiple mblk_t's
+ * linked together in the kernel. readv(2) and writev(2) have the same
+ * limitations as read(2) and write(2). We enforce this as the system is
+ * required to fill up every uio(9S) buffer before moving onto the next one.
+ * This means that if you have a MTU sized buffer and two frames come in which
+ * are less than half of the MTU they must fill up the given iovec. Even if we
+ * didn't want to do this, we have no way of informing the supplier of the
+ * iovecs that they were only partially filled or where one frame ends and
+ * another begins. That's life, as such we have frame I/O which solves this
+ * problem. It allows for multiple frames to be consumed as well as for frames
+ * to be broken down into multiple vector components.
+ *
+ * The second end is the mac direct calls. As part of negotiating capabilities
+ * via dld, we give mac a function of ours to call when packets are received
+ * [vnd_mac_input()] and a callback to indicate that flow has been restored
+ * [vnd_mac_flow_control()]. In turn, we also get a function pointer that we can
+ * transmit data with. As part of the contract with mac, mac is allowed to flow
+ * control us by returning a cookie to the transmit function. When that happens,
+ * all outbound traffic is halted until our callback function is called and we
+ * can schedule drains.
+ *
+ * It's worth looking at these in further detail. We'll start with the rx path.
+ *
+ *
+ * |
+ * * . . . packets from gld
+ * |
+ * v
+ * +-------------+
+ * | mac |
+ * +-------------+
+ * |
+ * v
+ * +-------------+
+ * | dld |
+ * +-------------+
+ * |
+ * * . . . dld direct callback
+ * |
+ * v
+ * +---------------+
+ * | vnd_mac_input |
+ * +---------------+
+ * |
+ * v
+ * +---------+ +-------------+
+ * | dropped |<--*---------| vnd_hooks |
+ * | by | . +-------------+
+ * | hooks | . drop probe |
+ * +---------+ kstat bump * . . . Do we have free
+ * | buffer space?
+ * |
+ * no . | . yes
+ * . + .
+ * +---*--+------*-------+
+ * | |
+ * * . . drop probe * . . recv probe
+ * | kstat bump | kstat bump
+ * v |
+ * +---------+ * . . fire pollin
+ * | freemsg | v
+ * +---------+ +-----------------------+
+ * | vnd_str_t`vns_dq_read |
+ * +-----------------------+
+ * ^ ^
+ * +----------+ | | +---------+
+ * | read(9E) |-->-+ +--<--| frameio |
+ * +----------+ +---------+
+ *
+ * The rx path is rather linear. Packets come into us from mac. We always run
+ * them through the various hooks, and if they come out of that, we inspect the
+ * read data queue. If there is not enough space for a packet, we drop it.
+ * Otherwise, we append it to the data queue, and fire read notifications
+ * targetting anyone polling or doing blocking I/O on this device. Those
+ * consumers then drain the head of the data queue.
+ *
+ * The tx path is more complicated due to mac flow control. After any call into
+ * mac, we may have to potentially suspend writes and buffer data for an
+ * arbitrary amount of time. As such, we need to carefully track the total
+ * amount of outstanding data so that we don't waste kernel memory. This is
+ * further complicated by the fact that mac will asynchronously tell us when our
+ * flow has been resumed.
+ *
+ * For data to be able to enter the system, it needs to be able to take a
+ * reservation from the write data queue. Once the reservation has been
+ * obtained, we enter the gsqueue so that we can actually append it. We use
+ * gsqueues (serialization queues) to ensure that packets are manipulated in
+ * order as we deal with the draining and appending packets. We also leverage
+ * its worker thread to help us do draining after mac has restorted our flow.
+ *
+ * The following image describes the flow:
+ *
+ * +-----------+ +--------------+ +-------------------------+ +------+
+ * | write(9E) |-->| Space in the |--*--->| gsqueue_enter_one() |-->| Done |
+ * | frameio | | write queue? | . | +->vnd_squeue_tx_append | +------+
+ * +-----------+ +--------------+ . +-------------------------+
+ * | ^ .
+ * | | . reserve space from gsqueue
+ * | | |
+ * queue . . . * | space v
+ * full | * . . . avail +------------------------+
+ * v | | vnd_squeue_tx_append() |
+ * +--------+ +------------+ +------------------------+
+ * | EAGAIN |<--*------| Non-block? |<-+ |
+ * +--------+ . +------------+ | v
+ * . yes v | wait +--------------+
+ * no . .* * . . for | append chain |
+ * +----+ space | to outgoing |
+ * | mblk chain |
+ * from gsqueue +--------------+
+ * | |
+ * | +-------------------------------------------------+
+ * | |
+ * | | yes . . .
+ * v v .
+ * +-----------------------+ +--------------+ . +------+
+ * | vnd_squeue_tx_drain() |--->| mac blocked? |----*---->| Done |
+ * +-----------------------+ +--------------+ +------+
+ * | |
+ * +---------------------------------|---------------------+
+ * | | tx |
+ * | no . . * queue . . *
+ * | flow controlled . | empty * . fire pollout
+ * | . v | if mblk_t's
+ * +-------------+ . +---------------------+ | sent
+ * | set blocked |<----*------| vnd_squeue_tx_one() |--------^-------+
+ * | flags | +---------------------+ |
+ * +-------------+ More data | | | More data |
+ * and limit ^ v * . . and limit ^
+ * not reached . . * | | reached |
+ * +----+ | |
+ * v |
+ * +----------+ +-------------+ +---------------------------+
+ * | mac flow |--------->| remove mac |--->| gsqueue_enter_one() with |
+ * | control | | block flags | | vnd_squeue_tx_drain() and |
+ * | callback | +-------------+ | GSQUEUE_FILL flag, iff |
+ * +----------+ | not already scheduled |
+ * +---------------------------+
+ *
+ * The final path taken for a given write(9E)/frameio ioctl depends on whether
+ * or not the vnd_dev_t is non-blocking. That controls the initial path of
+ * trying to take a reservation in write data queue. If the device is in
+ * non-blocking mode, we'll return EAGAIN when there is not enough space
+ * available, otherwise, the calling thread blocks on the data queue.
+ *
+ * Today when we call into vnd_squeue_tx_drain() we will not try to drain the
+ * entire queue, as that could be quite large and we don't want to necessarily
+ * keep the thread that's doing the drain until it's been finished. Not only
+ * could more data be coming in, but the draining thread could be a userland
+ * thread that has more work to do. We have two limits today. There is an upper
+ * bound on the total amount of data and the total number of mblk_t chains. If
+ * we hit either limit, then we will schedule another drain in the gsqueue and
+ * go from there.
+ *
+ * It's worth taking some time to describe how we interact with gsqueues. vnd
+ * has a gsqueue_set_t for itself. It's important that it has its own set, as
+ * the profile of work that vnd does is different from other sub-systems in the
+ * kernel. When we open a STREAMS device in vnd_s_open, we get a random gsqueue.
+ * Unlike TCP/IP which uses an gsqueue for per TCP connection, we end up
+ * maintaining one for a given device. Because of that, we want to use a
+ * pseudo-random one to try and spread out the load, and picking one at random
+ * is likely to be just as good as any fancy algorithm we might come up with,
+ * especially as any two devices could have radically different transmit
+ * profiles.
+ *
+ * While some of the write path may seem complicated, it does allow us to
+ * maintain an important property. Once we have acknowledged a write(9E) or
+ * frameio ioctl, we will not drop the packet, excepting something like ipf via
+ * the firewall hooks.
+ *
+ * There is one other source of flow control that can exist in the system which
+ * is in the form of a barrier. The barrier is an internal mechanism used for
+ * ensuring that an gsqueue is drained for a given device. We use this as part
+ * of tearing down. Specifically we disable the write path so nothing new can be
+ * inserted into the gsqueue and then insert a barrier block. Once the barrier
+ * block comes out of the gsqueue, then we know nothing else in the gsqueue that
+ * could refer to the vnd_str_t, being destroyed, exists.
+ *
+ * ---------------------
+ * vnd, zones, netstacks
+ * ---------------------
+ *
+ * vnd devices are scoped to datalinks and datalinks are scoped to a netstack.
+ * Because of that, vnd is also a netstack module. It registers with the
+ * netstack sub-system and receives callbacks every time a netstack is created,
+ * being shutdown, and destroyed. The netstack callbacks drive the creation and
+ * destruction of the vnd_pnsd_t structures.
+ *
+ * Recall from the earlier architecture diagrams that every vnd device is scoped
+ * to a netstack and known about by a given vnd_pnsd_t. When that netstack is
+ * torn down, we also tear down any vnd devices that are hanging around. When
+ * the netstack is torn down, we know that any zones that are scoped to that
+ * netstack are being shut down and have no processes remaining. This is going
+ * to be the case whether they are shared or exclusive stack zones. We have to
+ * perform a careful dance.
+ *
+ * There are two different callbacks that happen on tear down, the first is a
+ * shutdown callback, the second is a destroy callback. When the shutdown
+ * callback is fired we need to prepare for the netstack to go away and ensure
+ * that nothing can continue to persist itself.
+ *
+ * More specifically, when we get notice of a stack being shutdown we first
+ * remove the netstack from the global netstack list to ensure that no one new
+ * can come in and find the netstack and get a reference to it. After that, we
+ * notify the neti hooks that they're going away. Once that's all done, we get
+ * to the heart of the matter.
+ *
+ * When shutting down there could be any number of outstanding contexts that
+ * have a reference on the vnd_pnsd_t and on the individual links. However, we
+ * know that no one new will be able to find the vnd_pnsd_t. To account for
+ * things that have existing references we mark the vnd_pnsd_t`vpnd_flags with
+ * VND_NS_CONDEMNED. This is checked by code paths that wish to append a device
+ * to the netstack's list. If this is set, then they must not append to it.
+ * Once this is set, we know that the netstack's list of devices can never grow,
+ * only shrink.
+ *
+ * Next, for each device we tag it with VND_D_ZONE_DYING. This indicates that
+ * the container for the device is being destroyed and that we should not allow
+ * additional references to the device to be created, whether via open, or
+ * linking. The presence of this bit also allows things like the list ioctl and
+ * sdev to know not to consider its existence. At the conclusion of this being
+ * set, we know that no one else should be able to obtain a new reference to the
+ * device.
+ *
+ * Once that has been set for all devices, we go through and remove any existing
+ * links that have been established in sdev. Because doing that may cause the
+ * final reference for the device to be dropped, which still has a reference to
+ * the netstack, we have to restart our walk due to dropped locks. We know that
+ * this walk will eventually complete because the device cannot be relinked and
+ * no new devices will be attached in this netstack due to VND_NS_CONDEMNED.
+ * Once that's finished, the shutdown callback returns.
+ *
+ * When we reach the destroy callback, we simply wait for references on the
+ * netstack to disappear. Because the zone has been shut down, all processes in
+ * it that have open references have been terminated and reaped. Any threads
+ * that are newly trying to reference it will fail. However, there is one thing
+ * that can halt this that we have no control over, which is the global zone
+ * holding open a reference to the device. In this case the zone halt will hang
+ * in vnd_stack_destroy. Once the last references is dropped we finish destroy
+ * the netinfo hooks and free the vnd_pnsd_t.
+ *
+ * ----
+ * sdev
+ * ----
+ *
+ * vnd registers a sdev plugin which allows it to dynamically fill out /dev/vnd
+ * for both the global and non-global zones. In any given zone we always supply
+ * a control node via /dev/vnd/ctl. This is the self-cloning node. Each zone
+ * will also have an entry per-link in that zone under /dev/vnd/%datalink, eg.
+ * if a link was named net0, there would be a /dev/vnd/net0. The global zone can
+ * also see every link for every zone, ala /dev/net, under
+ * /dev/vnd/%zonename/%datalink, eg. if a zone named 'turin' had a vnd device
+ * named net0, the global zone would have /dev/vnd/turin/net0.
+ *
+ * The sdev plugin has three interfaces that it supplies back to sdev. One is to
+ * validate that a given node is still valid. The next is a callback from sdev
+ * to say that it is no longer using the node. The third and final one is from
+ * sdev where it asks us to fill a directory. All of the heavy lifting is done
+ * in directory filling and in valiation. We opt not to maintain a reference on
+ * the device while there is an sdev node present. This makes the removal of
+ * nodes much simpler and most of the possible failure modes shouldn't cause any
+ * real problems. For example, the open path has to handle both dev_t's which no
+ * longer exist and which are no longer linked.
+ *
+ * -----
+ * hooks
+ * -----
+ *
+ * Like IP, vnd sends all L3 packets through its firewall hooks. Currently vnd
+ * provides these for L3 IP and IPv6 traffic. Each netstack provides these hooks
+ * in a minimal fashion. While we will allow traffic to be filtered through the
+ * hooks, we do not provide means for packet injection or additional inspection
+ * at this time. There are a total of four different events created:
+ *
+ * o IPv4 physical in
+ * o IPv4 physical out
+ * o IPv6 physical in
+ * o IPv6 physical out
+ *
+ * ---------------
+ * Synchronization
+ * ---------------
+ *
+ * To make our synchronization simpler, we've put more effort into making the
+ * metadata/setup paths do more work. That work allows the data paths to make
+ * assumptions around synchronization that simplify the general case. Each major
+ * structure, the vnd_pnsd_t, vnd_dev_t, vnd_str_t, and vnd_data_queue_t is
+ * annotated with the protection that its members receives. The following
+ * annotations are used:
+ *
+ * A Atomics; these values are only modified using atomics values.
+ * Currently this only applies to kstat values.
+ * E Existence; no lock is needed to access this member, it does not
+ * change while the structure is valid.
+ * GL Global Lock; these members are protected by the global
+ * vnd_dev_lock.
+ * L Locked; access to the member is controlled by a lock that is in
+ * the structure.
+ * NSL netstack lock; this member is protected by the containing
+ * netstack. This only applies to the vnd_dev_t`vdd_nslink.
+ * X This member is special, and is discussed in this section.
+ *
+ * In addition to locking, we also have reference counts on the vnd_dev_t and
+ * the vnd_pnsd_t. The reference counts describe the lifetimes of the structure.
+ * With rare exception, once a reference count is decremented, the consumer
+ * should not assume that the data is valid any more. The only exception to this
+ * is the case where we're removing an extant reference count from a link into
+ * /devices or /dev. Reference counts are obtained on these structures as a part
+ * of looking them up.
+ *
+ * # Global Lock Ordering
+ * ######################
+ *
+ * The following is the order that you must take locks in vnd:
+ *
+ * 1) vnd`vnd_dev_lock
+ * 2) vnd_pnsd_t`vpnd_lock
+ * 3) vnd_dev_t`vnd_lock
+ * 4) vnd_str_t`vns_lock
+ * 5) vnd_data_queue_t`vdq_lock
+ *
+ * One must adhere to the following rules:
+ *
+ * o You must acquire a lower numbered lock before a high numbered lock.
+ * o It is NOT legal to hold two locks of the same level concurrently, eg. you
+ * can not hold two different vnd_dev_t's vnd_lock at the same time.
+ * o You may release locks in any order.
+ * o If you release a lock, you must honor the locking rules before acquiring
+ * it again.
+ * o You should not hold any locks when calling any of the rele functions.
+ *
+ * # Special Considerations
+ * ########################
+ *
+ * While most of the locking is what's expected, it's worth going into the
+ * special nature that a few members hold. Today, only two structures have
+ * special considerations: the vnd_dev_t and the vnd_str_t. All members with
+ * special considerations have an additional annotation that describes how you
+ * should interact with it.
+ *
+ * vnd_dev_t: The vdd_nsd and vdd_cr are only valid when the minor node is
+ * attached or in the process of attaching. If the code path that goes through
+ * requires an attached vnd_dev_t, eg. the data path and tear down path, then it
+ * is always legal to dereference that member without a lock held. When they are
+ * added to the system, they should be done under the vdd_lock and done as part
+ * of setting the VND_D_ATTACH_INFLIGHT flag. These should not change during the
+ * lifetime of the vnd_dev_t.
+ *
+ * vnd_dev_t: The vdd_ldih is similar to the vdd_nsd and vdd_cr, except that it
+ * always exists as it is a part of the structure. The only time that it's valid
+ * to be using it is during the attach path with the VND_D_ATTACH_INFLIGHT flag
+ * set or during tear down. Outside of those paths which are naturally
+ * serialized, there is no explicit locking around the member.
+ *
+ * vnd_str_t: The vns_dev and vns_nsd work in similar ways. They are not
+ * initially set as part of creating the structure, but are set as part of
+ * responding to the association ioctl. Anything in the data path or metadata
+ * path that requires association may assume that they exist, as we do not kick
+ * off the state machine until they're set.
+ *
+ * vnd_str_t: The vns_drainblk and vns_barrierblk are similarly special. The
+ * members are designed to be used as part of various operations with the
+ * gsqueues. A lock isn't needed to use them, but to work with them, the
+ * appropriate flag in the vnd_str_t`vns_flags must have been set by the current
+ * thread. Otherwise, it is always fair game to refer to their addresses. Their
+ * contents are ignored by vnd, but some members are manipulated by the gsqueue
+ * subsystem.
+ */
+
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/ddi.h>
+#include <sys/ethernet.h>
+#include <sys/stropts.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/ksynch.h>
+#include <sys/taskq_impl.h>
+#include <sys/sdt.h>
+#include <sys/debug.h>
+#include <sys/sysmacros.h>
+#include <sys/dlpi.h>
+#include <sys/cred.h>
+#include <sys/id_space.h>
+#include <sys/list.h>
+#include <sys/ctype.h>
+#include <sys/policy.h>
+#include <sys/sunldi.h>
+#include <sys/cred.h>
+#include <sys/strsubr.h>
+#include <sys/poll.h>
+#include <sys/neti.h>
+#include <sys/hook.h>
+#include <sys/hook_event.h>
+#include <sys/vlan.h>
+#include <sys/dld.h>
+#include <sys/mac_client.h>
+#include <sys/netstack.h>
+#include <sys/fs/sdev_plugin.h>
+#include <sys/kstat.h>
+#include <sys/atomic.h>
+#include <sys/disp.h>
+#include <sys/random.h>
+#include <sys/gsqueue.h>
+
+#include <inet/ip.h>
+#include <inet/ip6.h>
+
+#include <sys/vnd.h>
+
+/*
+ * Globals
+ */
+static dev_info_t *vnd_dip;
+static taskq_t *vnd_taskq;
+static kmem_cache_t *vnd_str_cache;
+static kmem_cache_t *vnd_dev_cache;
+static kmem_cache_t *vnd_pnsd_cache;
+static id_space_t *vnd_minors;
+static int vnd_list_init = 0;
+static sdev_plugin_hdl_t vnd_sdev_hdl;
+static gsqueue_set_t *vnd_sqset;
+
+static kmutex_t vnd_dev_lock;
+static list_t vnd_dev_list; /* Protected by the vnd_dev_lock */
+static list_t vnd_nsd_list; /* Protected by the vnd_dev_lock */
+
+/*
+ * STREAMs ioctls
+ *
+ * The STREAMs ioctls are internal to vnd. No one should be seeing them, as such
+ * they aren't a part of the header file.
+ */
+#define VND_STRIOC (('v' << 24) | ('n' << 16) | ('d' << 8) | 0x80)
+
+/*
+ * Private ioctl to associate a given streams instance with a minor instance of
+ * the character device.
+ */
+#define VND_STRIOC_ASSOCIATE (VND_STRIOC | 0x1)
+
+typedef struct vnd_strioc_associate {
+ minor_t vsa_minor; /* minor device node */
+ netstackid_t vsa_nsid; /* netstack id */
+ vnd_errno_t vsa_errno; /* errno */
+} vnd_strioc_associate_t;
+
+typedef enum vnd_strioc_state {
+ VSS_UNKNOWN = 0,
+ VSS_COPYIN = 1,
+ VSS_COPYOUT = 2,
+} vnd_strioc_state_t;
+
+typedef struct vnd_strioc {
+ vnd_strioc_state_t vs_state;
+ caddr_t vs_addr;
+} vnd_strioc_t;
+
+/*
+ * VND SQUEUE TAGS, start at 0x42 so we don't overlap with extent tags. Though
+ * really, overlap is at the end of the day, inevitable.
+ */
+#define VND_SQUEUE_TAG_TX_DRAIN 0x42
+#define VND_SQUEUE_TAG_MAC_FLOW_CONTROL 0x43
+#define VND_SQUEUE_TAG_VND_WRITE 0x44
+#define VND_SQUEUE_TAG_ND_FRAMEIO_WRITE 0x45
+#define VND_SQUEUE_TAG_STRBARRIER 0x46
+
+/*
+ * vnd reserved names. These are names which are reserved by vnd and thus
+ * shouldn't be used by some external program.
+ */
+static char *vnd_reserved_names[] = {
+ "ctl",
+ "zone",
+ NULL
+};
+
+/*
+ * vnd's DTrace probe macros
+ *
+ * DTRACE_VND* are all for a stable provider. We also have an unstable internal
+ * set of probes for reference count manipulation.
+ */
+#define DTRACE_VND3(name, type1, arg1, type2, arg2, type3, arg3) \
+ DTRACE_PROBE3(__vnd_##name, type1, arg1, type2, arg2, type3, arg3);
+
+#define DTRACE_VND4(name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \
+ DTRACE_PROBE4(__vnd_##name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4);
+
+#define DTRACE_VND5(name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5) \
+ DTRACE_PROBE5(__vnd_##name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5);
+
+#define DTRACE_VND_REFINC(vdp) \
+ DTRACE_PROBE2(vnd__ref__inc, vnd_dev_t *, vdp, int, vdp->vdd_ref);
+#define DTRACE_VND_REFDEC(vdp) \
+ DTRACE_PROBE2(vnd__ref__dec, vnd_dev_t *, vdp, int, vdp->vdd_ref);
+
+
+/*
+ * Tunables
+ */
+size_t vnd_vdq_default_size = 1024 * 64; /* 64 KB */
+size_t vnd_vdq_hard_max = 1024 * 1024 * 4; /* 4 MB */
+
+/*
+ * These numbers are designed as per-device tunables that are applied when a new
+ * vnd device is attached. They're a rough stab at what may be a reasonable
+ * amount of work to do in one burst in an squeue.
+ */
+size_t vnd_flush_burst_size = 1520 * 10; /* 10 1500 MTU packets */
+size_t vnd_flush_nburst = 10; /* 10 frames */
+
+/*
+ * Constants related to our sdev plugins
+ */
+#define VND_SDEV_NAME "vnd"
+#define VND_SDEV_ROOT "/dev/vnd"
+#define VND_SDEV_ZROOT "/dev/vnd/zone"
+
+/*
+ * Statistic macros
+ */
+#define VND_STAT_INC(vsp, field, val) \
+ atomic_add_64(&(vsp)->vns_ksdata.field.value.ui64, val)
+#define VND_LATENCY_1MS 1000000
+#define VND_LATENCY_10MS 10000000
+#define VND_LATENCY_100MS 100000000
+#define VND_LATENCY_1S 1000000000
+#define VND_LATENCY_10S 10000000000
+
+/*
+ * Constants for vnd hooks
+ */
+static uint8_t vnd_bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+#define IPV4_MCAST_LEN 3
+static uint8_t vnd_ipv4_mcast[3] = { 0x01, 0x00, 0x5E };
+#define IPV6_MCAST_LEN 2
+static uint8_t vnd_ipv6_mcast[2] = { 0x33, 0x33 };
+
+/*
+ * vnd internal data structures and types
+ */
+
+struct vnd_str;
+struct vnd_dev;
+struct vnd_pnsd;
+
+/*
+ * As part of opening the device stream we need to properly communicate with our
+ * underlying stream. This is a bit of an asynchronous dance and we need to
+ * properly work with dld to get everything set up. We have to initiate the
+ * conversation with dld and as such we keep track of our state here.
+ */
+typedef enum vnd_str_state {
+ VNS_S_INITIAL = 0,
+ VNS_S_INFO_SENT,
+ VNS_S_EXCLUSIVE_SENT,
+ VNS_S_ATTACH_SENT,
+ VNS_S_BIND_SENT,
+ VNS_S_SAP_PROMISC_SENT,
+ VNS_S_MULTI_PROMISC_SENT,
+ VNS_S_RX_ONLY_PROMISC_SENT,
+ VNS_S_FIXUP_PROMISC_SENT,
+ VNS_S_CAPAB_Q_SENT,
+ VNS_S_CAPAB_E_SENT,
+ VNS_S_ONLINE,
+ VNS_S_SHUTTING_DOWN,
+ VNS_S_MULTICAST_PROMISCOFF_SENT,
+ VNS_S_SAP_PROMISCOFF_SENT,
+ VNS_S_UNBIND_SENT,
+ VNS_S_ZOMBIE
+} vnd_str_state_t;
+
+typedef enum vnd_str_flags {
+ VNS_F_NEED_ZONE = 0x1,
+ VNS_F_TASKQ_DISPATCHED = 0x2,
+ VNS_F_CONDEMNED = 0x4,
+ VNS_F_FLOW_CONTROLLED = 0x8,
+ VNS_F_DRAIN_SCHEDULED = 0x10,
+ VNS_F_BARRIER = 0x20,
+ VNS_F_BARRIER_DONE = 0x40
+} vnd_str_flags_t;
+
+typedef enum vnd_capab_flags {
+ VNS_C_HCKSUM = 0x1,
+ VNS_C_DLD = 0x2,
+ VNS_C_DIRECT = 0x4,
+ VNS_C_HCKSUM_BADVERS = 0x8
+} vnd_capab_flags_t;
+
+/*
+ * Definitions to interact with direct callbacks
+ */
+typedef void (*vnd_rx_t)(struct vnd_str *, mac_resource_t *, mblk_t *,
+ mac_header_info_t *);
+typedef uintptr_t vnd_mac_cookie_t;
+/* DLD Direct capability function */
+typedef int (*vnd_dld_cap_t)(void *, uint_t, void *, uint_t);
+/* DLD Direct tx function */
+typedef vnd_mac_cookie_t (*vnd_dld_tx_t)(void *, mblk_t *, uint64_t, uint16_t);
+/* DLD Direct function to set flow control callback */
+typedef void *(*vnd_dld_set_fcb_t)(void *, void (*)(void *, vnd_mac_cookie_t),
+ void *);
+/* DLD Direct function to see if flow controlled still */
+typedef int (*vnd_dld_is_fc_t)(void *, vnd_mac_cookie_t);
+
+/*
+ * The vnd_str_capab_t is always protected by the vnd_str_t it's a member of.
+ */
+typedef struct vnd_str_capab {
+ vnd_capab_flags_t vsc_flags;
+ t_uscalar_t vsc_hcksum_opts;
+ vnd_dld_cap_t vsc_capab_f;
+ void *vsc_capab_hdl;
+ vnd_dld_tx_t vsc_tx_f;
+ void *vsc_tx_hdl;
+ vnd_dld_set_fcb_t vsc_set_fcb_f;
+ void *vsc_set_fcb_hdl;
+ vnd_dld_is_fc_t vsc_is_fc_f;
+ void *vsc_is_fc_hdl;
+ vnd_mac_cookie_t vsc_fc_cookie;
+ void *vsc_tx_fc_hdl;
+} vnd_str_capab_t;
+
+/*
+ * The vnd_data_queue is a simple construct for storing a series of messages in
+ * a queue.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_data_queue {
+ struct vnd_str *vdq_vns; /* E */
+ kmutex_t vdq_lock;
+ kcondvar_t vdq_ready; /* Uses vdq_lock */
+ ssize_t vdq_max; /* L */
+ ssize_t vdq_cur; /* L */
+ mblk_t *vdq_head; /* L */
+ mblk_t *vdq_tail; /* L */
+} vnd_data_queue_t;
+
+typedef struct vnd_str_stat {
+ kstat_named_t vks_rbytes;
+ kstat_named_t vks_rpackets;
+ kstat_named_t vks_obytes;
+ kstat_named_t vks_opackets;
+ kstat_named_t vks_nhookindrops;
+ kstat_named_t vks_nhookoutdrops;
+ kstat_named_t vks_ndlpidrops;
+ kstat_named_t vks_ndataindrops;
+ kstat_named_t vks_ndataoutdrops;
+ kstat_named_t vks_tdrops;
+ kstat_named_t vks_linkname;
+ kstat_named_t vks_zonename;
+ kstat_named_t vks_nmacflow;
+ kstat_named_t vks_tmacflow;
+ kstat_named_t vks_mac_flow_1ms;
+ kstat_named_t vks_mac_flow_10ms;
+ kstat_named_t vks_mac_flow_100ms;
+ kstat_named_t vks_mac_flow_1s;
+ kstat_named_t vks_mac_flow_10s;
+} vnd_str_stat_t;
+
+/*
+ * vnd stream structure
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_str {
+ kmutex_t vns_lock;
+ kcondvar_t vns_cancelcv; /* Uses vns_lock */
+ kcondvar_t vns_barriercv; /* Uses vns_lock */
+ kcondvar_t vns_stcv; /* Uses vns_lock */
+ vnd_str_state_t vns_state; /* L */
+ vnd_str_state_t vns_laststate; /* L */
+ vnd_errno_t vns_errno; /* L */
+ vnd_str_flags_t vns_flags; /* L */
+ vnd_str_capab_t vns_caps; /* L */
+ taskq_ent_t vns_tqe; /* L */
+ vnd_data_queue_t vns_dq_read; /* E */
+ vnd_data_queue_t vns_dq_write; /* E */
+ mblk_t *vns_dlpi_inc; /* L */
+ queue_t *vns_rq; /* E */
+ queue_t *vns_wq; /* E */
+ queue_t *vns_lrq; /* E */
+ t_uscalar_t vns_dlpi_style; /* L */
+ t_uscalar_t vns_minwrite; /* L */
+ t_uscalar_t vns_maxwrite; /* L */
+ hrtime_t vns_fclatch; /* L */
+ hrtime_t vns_fcupdate; /* L */
+ kstat_t *vns_kstat; /* E */
+ gsqueue_t *vns_squeue; /* E */
+ mblk_t vns_drainblk; /* E + X */
+ mblk_t vns_barrierblk; /* E + X */
+ vnd_str_stat_t vns_ksdata; /* A */
+ size_t vns_nflush; /* L */
+ size_t vns_bsize; /* L */
+ struct vnd_dev *vns_dev; /* E + X */
+ struct vnd_pnsd *vns_nsd; /* E + X */
+} vnd_str_t;
+
+typedef enum vnd_dev_flags {
+ VND_D_ATTACH_INFLIGHT = 0x001,
+ VND_D_ATTACHED = 0x002,
+ VND_D_LINK_INFLIGHT = 0x004,
+ VND_D_LINKED = 0x008,
+ VND_D_CONDEMNED = 0x010,
+ VND_D_ZONE_DYING = 0x020,
+ VND_D_OPENED = 0x040
+} vnd_dev_flags_t;
+
+/*
+ * This represents the data associated with a minor device instance.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_dev {
+ kmutex_t vdd_lock;
+ list_node_t vdd_link; /* GL */
+ list_node_t vdd_nslink; /* NSL */
+ int vdd_ref; /* L */
+ vnd_dev_flags_t vdd_flags; /* L */
+ minor_t vdd_minor; /* E */
+ dev_t vdd_devid; /* E */
+ ldi_ident_t vdd_ldiid; /* E */
+ ldi_handle_t vdd_ldih; /* X */
+ cred_t *vdd_cr; /* X */
+ vnd_str_t *vdd_str; /* L */
+ struct pollhead vdd_ph; /* E */
+ struct vnd_pnsd *vdd_nsd; /* E + X */
+ char vdd_datalink[VND_NAMELEN]; /* L */
+ char vdd_lname[VND_NAMELEN]; /* L */
+} vnd_dev_t;
+
+typedef enum vnd_pnsd_flags {
+ VND_NS_CONDEMNED = 0x1
+} vnd_pnsd_flags_t;
+
+/*
+ * Per netstack data structure.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_pnsd {
+ list_node_t vpnd_link; /* protected by global dev lock */
+ zoneid_t vpnd_zid; /* E */
+ netstackid_t vpnd_nsid; /* E */
+ boolean_t vpnd_hooked; /* E */
+ net_handle_t vpnd_neti_v4; /* E */
+ hook_family_t vpnd_family_v4; /* E */
+ hook_event_t vpnd_event_in_v4; /* E */
+ hook_event_t vpnd_event_out_v4; /* E */
+ hook_event_token_t vpnd_token_in_v4; /* E */
+ hook_event_token_t vpnd_token_out_v4; /* E */
+ net_handle_t vpnd_neti_v6; /* E */
+ hook_family_t vpnd_family_v6; /* E */
+ hook_event_t vpnd_event_in_v6; /* E */
+ hook_event_t vpnd_event_out_v6; /* E */
+ hook_event_token_t vpnd_token_in_v6; /* E */
+ hook_event_token_t vpnd_token_out_v6; /* E */
+ kmutex_t vpnd_lock; /* Protects remaining members */
+ kcondvar_t vpnd_ref_change; /* Uses vpnd_lock */
+ int vpnd_ref; /* L */
+ vnd_pnsd_flags_t vpnd_flags; /* L */
+ list_t vpnd_dev_list; /* L */
+} vnd_pnsd_t;
+
+static void vnd_squeue_tx_drain(void *, mblk_t *, gsqueue_t *, void *);
+
+/*
+ * Drop function signature.
+ */
+typedef void (*vnd_dropper_f)(vnd_str_t *, mblk_t *, const char *);
+
+static void
+vnd_drop_ctl(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__ctl, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_in(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__in, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndataindrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_out(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__out, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndataoutdrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_hook_in(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__in, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_nhookindrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_hook_out(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__out, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_nhookoutdrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_panic(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ panic("illegal vnd drop");
+}
+
+static void
+vnd_mac_drop_input(vnd_str_t *vsp, mac_resource_t *unused, mblk_t *mp_chain,
+ mac_header_info_t *mhip)
+{
+ mblk_t *mp;
+
+ while (mp_chain != NULL) {
+ mp = mp_chain;
+ mp_chain = mp->b_next;
+ vnd_drop_hook_in(vsp, mp, "stream not associated");
+ }
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup(netstackid_t nsid)
+{
+ vnd_pnsd_t *nsp;
+
+ mutex_enter(&vnd_dev_lock);
+ for (nsp = list_head(&vnd_nsd_list); nsp != NULL;
+ nsp = list_next(&vnd_nsd_list, nsp)) {
+ if (nsp->vpnd_nsid == nsid) {
+ mutex_enter(&nsp->vpnd_lock);
+ VERIFY(nsp->vpnd_ref >= 0);
+ nsp->vpnd_ref++;
+ mutex_exit(&nsp->vpnd_lock);
+ break;
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (nsp);
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup_by_zid(zoneid_t zid)
+{
+ netstack_t *ns;
+ vnd_pnsd_t *nsp;
+ ns = netstack_find_by_zoneid(zid);
+ if (ns == NULL)
+ return (NULL);
+ nsp = vnd_nsd_lookup(ns->netstack_stackid);
+ netstack_rele(ns);
+ return (nsp);
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup_by_zonename(char *zname)
+{
+ zone_t *zonep;
+ vnd_pnsd_t *nsp;
+
+ zonep = zone_find_by_name(zname);
+ if (zonep == NULL)
+ return (NULL);
+
+ nsp = vnd_nsd_lookup_by_zid(zonep->zone_id);
+ zone_rele(zonep);
+ return (nsp);
+}
+
+static void
+vnd_nsd_ref(vnd_pnsd_t *nsp)
+{
+ mutex_enter(&nsp->vpnd_lock);
+ /*
+ * This can only be used on something that has been obtained through
+ * some other means. As such, the caller should already have a reference
+ * before adding another one. This function should not be used as a
+ * means of creating the initial reference.
+ */
+ VERIFY(nsp->vpnd_ref > 0);
+ nsp->vpnd_ref++;
+ mutex_exit(&nsp->vpnd_lock);
+ cv_broadcast(&nsp->vpnd_ref_change);
+}
+
+static void
+vnd_nsd_rele(vnd_pnsd_t *nsp)
+{
+ mutex_enter(&nsp->vpnd_lock);
+ VERIFY(nsp->vpnd_ref > 0);
+ nsp->vpnd_ref--;
+ mutex_exit(&nsp->vpnd_lock);
+ cv_broadcast(&nsp->vpnd_ref_change);
+}
+
+static vnd_dev_t *
+vnd_dev_lookup(minor_t m)
+{
+ vnd_dev_t *vdp;
+ mutex_enter(&vnd_dev_lock);
+ for (vdp = list_head(&vnd_dev_list); vdp != NULL;
+ vdp = list_next(&vnd_dev_list, vdp)) {
+ if (vdp->vdd_minor == m) {
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ mutex_exit(&vdp->vdd_lock);
+ break;
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (vdp);
+}
+
+static void
+vnd_dev_free(vnd_dev_t *vdp)
+{
+ /*
+ * When the STREAM exists we need to go through and make sure
+ * communication gets torn down. As part of closing the stream, we
+ * guarantee that nothing else should be able to enter the stream layer
+ * at this point. That means no one should be able to call
+ * read(),write() or one of the frameio ioctls.
+ */
+ if (vdp->vdd_flags & VND_D_ATTACHED) {
+ ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ crfree(vdp->vdd_cr);
+ vdp->vdd_cr = NULL;
+
+ /*
+ * We have to remove ourselves from our parents list now. It is
+ * really quite important that we have already set the condemend
+ * flag here so that our containing netstack basically knows
+ * that we're on the way down and knows not to wait for us. It's
+ * also important that we do that before we put a rele on the
+ * the device as that is the point at which it will check again.
+ */
+ mutex_enter(&vdp->vdd_nsd->vpnd_lock);
+ list_remove(&vdp->vdd_nsd->vpnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ vnd_nsd_rele(vdp->vdd_nsd);
+ vdp->vdd_nsd = NULL;
+ }
+ ASSERT(vdp->vdd_flags & VND_D_CONDEMNED);
+ id_free(vnd_minors, vdp->vdd_minor);
+ mutex_destroy(&vdp->vdd_lock);
+ kmem_cache_free(vnd_dev_cache, vdp);
+}
+
+static void
+vnd_dev_ref(vnd_dev_t *vdp)
+{
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ mutex_exit(&vdp->vdd_lock);
+}
+
+/*
+ * As part of releasing the hold on this we may tear down a given vnd_dev_t As
+ * such we need to make sure that we grab the list lock first before grabbing
+ * the vnd_dev_t's lock to ensure proper lock ordering.
+ */
+static void
+vnd_dev_rele(vnd_dev_t *vdp)
+{
+ mutex_enter(&vnd_dev_lock);
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref--;
+ DTRACE_VND_REFDEC(vdp);
+ if (vdp->vdd_ref > 0) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ return;
+ }
+
+ /*
+ * Now that we've removed this from the list, we can go ahead and
+ * drop the list lock. No one else can find this device and reference
+ * it. As its reference count is zero, it by definition does not have
+ * any remaining entries in /devices that could lead someone back to
+ * this.
+ */
+ vdp->vdd_flags |= VND_D_CONDEMNED;
+ list_remove(&vnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+
+ vnd_dev_free(vdp);
+}
+
+/*
+ * Insert a mesage block chain if there's space, otherwise drop it. Return one
+ * so someone who was waiting for data would now end up having found it. eg.
+ * caller should consider a broadcast.
+ */
+static int
+vnd_dq_push(vnd_data_queue_t *vqp, mblk_t *mp, boolean_t reserved,
+ vnd_dropper_f dropf)
+{
+ size_t msize;
+
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ if (reserved == B_FALSE) {
+ msize = msgsize(mp);
+ if (vqp->vdq_cur + msize > vqp->vdq_max) {
+ dropf(vqp->vdq_vns, mp, "buffer full");
+ return (0);
+ }
+ vqp->vdq_cur += msize;
+ }
+
+ if (vqp->vdq_head == NULL) {
+ ASSERT(vqp->vdq_tail == NULL);
+ vqp->vdq_head = mp;
+ vqp->vdq_tail = mp;
+ } else {
+ vqp->vdq_tail->b_next = mp;
+ vqp->vdq_tail = mp;
+ }
+
+ return (1);
+}
+
+/*
+ * Remove a message message block chain. If the amount of space in the buffer
+ * has changed we return 1. We have no way of knowing whether or not there is
+ * enough space overall for a given writer who is blocked, so we always end up
+ * having to return true and thus tell consumers that they should consider
+ * signalling.
+ */
+static int
+vnd_dq_pop(vnd_data_queue_t *vqp, mblk_t **mpp)
+{
+ size_t msize;
+ mblk_t *mp;
+
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(mpp != NULL);
+ if (vqp->vdq_head == NULL) {
+ ASSERT(vqp->vdq_tail == NULL);
+ *mpp = NULL;
+ return (0);
+ }
+
+ mp = vqp->vdq_head;
+ msize = msgsize(mp);
+
+ vqp->vdq_cur -= msize;
+ if (mp->b_next == NULL) {
+ vqp->vdq_head = NULL;
+ vqp->vdq_tail = NULL;
+ /*
+ * We can't be certain that this is always going to be zero.
+ * Someone may have basically taken a reservation of space on
+ * the data queue, eg. claimed spae but not yet pushed it on
+ * yet.
+ */
+ ASSERT(vqp->vdq_cur >= 0);
+ } else {
+ vqp->vdq_head = mp->b_next;
+ ASSERT(vqp->vdq_cur > 0);
+ }
+ mp->b_next = NULL;
+ *mpp = mp;
+ return (1);
+}
+
+/*
+ * Reserve space in the queue. This will bump up the size of the queue and
+ * entitle the user to push something on later without bumping the space.
+ */
+static int
+vnd_dq_reserve(vnd_data_queue_t *vqp, ssize_t size)
+{
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(size >= 0);
+
+ if (size == 0)
+ return (0);
+
+ if (size + vqp->vdq_cur > vqp->vdq_max)
+ return (0);
+
+ vqp->vdq_cur += size;
+ return (1);
+}
+
+static void
+vnd_dq_unreserve(vnd_data_queue_t *vqp, ssize_t size)
+{
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(size > 0);
+ ASSERT(size <= vqp->vdq_cur);
+
+ vqp->vdq_cur -= size;
+}
+
+static void
+vnd_dq_flush(vnd_data_queue_t *vqp, vnd_dropper_f dropf)
+{
+ mblk_t *mp, *next;
+
+ mutex_enter(&vqp->vdq_lock);
+ for (mp = vqp->vdq_head; mp != NULL; mp = next) {
+ next = mp->b_next;
+ mp->b_next = NULL;
+ dropf(vqp->vdq_vns, mp, "vnd_dq_flush");
+ }
+ vqp->vdq_cur = 0;
+ vqp->vdq_head = NULL;
+ vqp->vdq_tail = NULL;
+ mutex_exit(&vqp->vdq_lock);
+}
+
+static boolean_t
+vnd_dq_is_empty(vnd_data_queue_t *vqp)
+{
+ boolean_t ret;
+
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_head == NULL)
+ ret = B_TRUE;
+ else
+ ret = B_FALSE;
+ mutex_exit(&vqp->vdq_lock);
+
+ return (ret);
+}
+
+/*
+ * Get a network uint16_t from the message and translate it into something the
+ * host understands.
+ */
+static int
+vnd_mbc_getu16(mblk_t *mp, off_t off, uint16_t *out)
+{
+ size_t mpsize;
+ uint8_t *bp;
+
+ mpsize = msgsize(mp);
+ /* Check for overflow */
+ if (off + sizeof (uint16_t) > mpsize)
+ return (1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+
+ /*
+ * Data is in network order. Note the second byte of data might be in
+ * the next mp.
+ */
+ bp = mp->b_rptr + off;
+ *out = *bp << 8;
+ if (off + 1 == mpsize) {
+ mp = mp->b_cont;
+ bp = mp->b_rptr;
+ } else {
+ bp++;
+ }
+
+ *out |= *bp;
+ return (0);
+}
+
+/*
+ * Given an mblk chain find the mblk and address of a particular offset.
+ */
+static int
+vnd_mbc_getoffset(mblk_t *mp, off_t off, mblk_t **mpp, uintptr_t *offp)
+{
+ size_t mpsize;
+
+ if (off >= msgsize(mp))
+ return (1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+ *mpp = mp;
+ *offp = (uintptr_t)mp->b_rptr + off;
+
+ return (0);
+}
+
+/*
+ * Fetch the destination mac address. Set *dstp to that mac address. If the data
+ * is not contiguous in the first mblk_t, fill in datap and set *dstp to it.
+ */
+static int
+vnd_mbc_getdstmac(mblk_t *mp, uint8_t **dstpp, uint8_t *datap)
+{
+ int i;
+
+ if (MBLKL(mp) >= ETHERADDRL) {
+ *dstpp = mp->b_rptr;
+ return (0);
+ }
+
+ *dstpp = datap;
+ for (i = 0; i < ETHERADDRL; i += 2, datap += 2) {
+ if (vnd_mbc_getu16(mp, i, (uint16_t *)datap) != 0)
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+vnd_hook(vnd_str_t *vsp, mblk_t **mpp, net_handle_t netiv4, hook_event_t hev4,
+ hook_event_token_t hetv4, net_handle_t netiv6, hook_event_t hev6,
+ hook_event_token_t hetv6, vnd_dropper_f hdrop, vnd_dropper_f ddrop)
+{
+ uint16_t etype;
+ int vlan = 0;
+ hook_pkt_event_t info;
+ size_t offset, mblen;
+ uint8_t *dstp;
+ uint8_t dstaddr[6];
+ hook_event_t he;
+ hook_event_token_t het;
+ net_handle_t neti;
+
+ /*
+ * Before we can ask if we're interested we have to do enough work to
+ * determine the ethertype.
+ */
+
+ /* Byte 12 is either the VLAN tag or the ethertype */
+ if (vnd_mbc_getu16(*mpp, 12, &etype) != 0) {
+ ddrop(vsp, *mpp, "packet has incomplete ethernet header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ if (etype == ETHERTYPE_VLAN) {
+ vlan = 1;
+ /* Actual ethertype is another four bytes in */
+ if (vnd_mbc_getu16(*mpp, 16, &etype) != 0) {
+ ddrop(vsp, *mpp,
+ "packet has incomplete ethernet vlan header");
+ *mpp = NULL;
+ return (1);
+ }
+ offset = sizeof (struct ether_vlan_header);
+ } else {
+ offset = sizeof (struct ether_header);
+ }
+
+ /*
+ * At the moment we only hook on the kinds of things that the IP module
+ * would normally.
+ */
+ if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6)
+ return (0);
+
+ if (etype == ETHERTYPE_IP) {
+ neti = netiv4;
+ he = hev4;
+ het = hetv4;
+ } else {
+ neti = netiv6;
+ he = hev6;
+ het = hetv6;
+ }
+
+ if (!he.he_interested)
+ return (0);
+
+
+ if (vnd_mbc_getdstmac(*mpp, &dstp, dstaddr) != 0) {
+ ddrop(vsp, *mpp, "packet has incomplete ethernet header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ /*
+ * Now that we know we're interested, we have to do some additional
+ * sanity checking for IPF's sake, ala ip_check_length(). Specifically
+ * we need to check to make sure that the remaining packet size,
+ * excluding MAC, is at least the size of an IP header.
+ */
+ mblen = msgsize(*mpp);
+ if ((etype == ETHERTYPE_IP &&
+ mblen - offset < IP_SIMPLE_HDR_LENGTH) ||
+ (etype == ETHERTYPE_IPV6 && mblen - offset < IPV6_HDR_LEN)) {
+ ddrop(vsp, *mpp, "packet has invalid IP header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ info.hpe_protocol = neti;
+ info.hpe_ifp = (phy_if_t)vsp;
+ info.hpe_ofp = (phy_if_t)vsp;
+ info.hpe_mp = mpp;
+ info.hpe_flags = 0;
+
+ if (bcmp(vnd_bcast_addr, dstp, ETHERADDRL) == 0)
+ info.hpe_flags |= HPE_BROADCAST;
+ else if (etype == ETHERTYPE_IP &&
+ bcmp(vnd_ipv4_mcast, vnd_bcast_addr, IPV4_MCAST_LEN) == 0)
+ info.hpe_flags |= HPE_MULTICAST;
+ else if (etype == ETHERTYPE_IPV6 &&
+ bcmp(vnd_ipv6_mcast, vnd_bcast_addr, IPV6_MCAST_LEN) == 0)
+ info.hpe_flags |= HPE_MULTICAST;
+
+ if (vnd_mbc_getoffset(*mpp, offset, &info.hpe_mb,
+ (uintptr_t *)&info.hpe_hdr) != 0) {
+ ddrop(vsp, *mpp, "packet too small -- "
+ "unable to find payload");
+ *mpp = NULL;
+ return (1);
+ }
+
+ if (hook_run(neti->netd_hooks, het, (hook_data_t)&info) != 0) {
+ hdrop(vsp, *mpp, "drooped by hooks");
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * This should not be used for DL_INFO_REQ.
+ */
+static mblk_t *
+vnd_dlpi_alloc(size_t len, t_uscalar_t prim)
+{
+ mblk_t *mp;
+ mp = allocb(len, BPRI_MED);
+ if (mp == NULL)
+ return (NULL);
+
+ mp->b_datap->db_type = M_PROTO;
+ mp->b_wptr = mp->b_rptr + len;
+ bzero(mp->b_rptr, len);
+ ((dl_unitdata_req_t *)mp->b_rptr)->dl_primitive = prim;
+
+ return (mp);
+}
+
+static void
+vnd_dlpi_inc_push(vnd_str_t *vsp, mblk_t *mp)
+{
+ mblk_t **mpp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ ASSERT(mp->b_next == NULL);
+ mpp = &vsp->vns_dlpi_inc;
+ while (*mpp != NULL)
+ mpp = &((*mpp)->b_next);
+ *mpp = mp;
+}
+
+static mblk_t *
+vnd_dlpi_inc_pop(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vsp->vns_dlpi_inc;
+ if (mp != NULL) {
+ VERIFY(mp->b_next == NULL || mp->b_next != mp);
+ vsp->vns_dlpi_inc = mp->b_next;
+ mp->b_next = NULL;
+ }
+ return (mp);
+}
+
+static int
+vnd_st_sinfo(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_info_req_t *dlir;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = allocb(MAX(sizeof (dl_info_req_t), sizeof (dl_info_ack_t)),
+ BPRI_HI);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+ vsp->vns_state = VNS_S_INFO_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+
+ mp->b_datap->db_type = M_PCPROTO;
+ dlir = (dl_info_req_t *)mp->b_rptr;
+ mp->b_wptr = (uchar_t *)&dlir[1];
+ dlir->dl_primitive = DL_INFO_REQ;
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_info(vnd_str_t *vsp)
+{
+ dl_info_ack_t *dlia;
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ dlia = (dl_info_ack_t *)mp->b_rptr;
+ vsp->vns_dlpi_style = dlia->dl_provider_style;
+ vsp->vns_minwrite = dlia->dl_min_sdu;
+ vsp->vns_maxwrite = dlia->dl_max_sdu;
+
+ /*
+ * At this time we only support DL_ETHER devices.
+ */
+ if (dlia->dl_mac_type != DL_ETHER) {
+ freemsg(mp);
+ vsp->vns_errno = VND_E_NOTETHER;
+ return (1);
+ }
+
+ /*
+ * Because vnd operates on entire packets, we need to manually account
+ * for the ethernet header information. We add the size of the
+ * ether_vlan_header to account for this, regardless if it is using
+ * vlans or not.
+ */
+ vsp->vns_maxwrite += sizeof (struct ether_vlan_header);
+
+ freemsg(mp);
+ return (0);
+}
+
+static int
+vnd_st_sexclusive(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_attach_req_t), DL_EXCLUSIVE_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ vsp->vns_state = VNS_S_EXCLUSIVE_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+ return (0);
+}
+
+static int
+vnd_st_exclusive(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_error_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_exclusive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_EXCLUSIVE_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_exclusive: got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_DLEXCL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+/*
+ * Send down a DLPI_ATTACH_REQ.
+ */
+static int
+vnd_st_sattach(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_attach_req_t), DL_ATTACH_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ ((dl_attach_req_t *)mp->b_rptr)->dl_ppa = 0;
+ vsp->vns_state = VNS_S_ATTACH_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_attach(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp, "vnd_st_attach: unknown primitive type");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_ATTACH_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_attach: Got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_ATTACHFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_sbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_bind_req_t *dbrp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_bind_req_t) + sizeof (long),
+ DL_BIND_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+ dbrp = (dl_bind_req_t *)(mp->b_rptr);
+ dbrp->dl_sap = 0;
+ dbrp->dl_service_mode = DL_CLDLS;
+
+ vsp->vns_state = VNS_S_BIND_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_bind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_error_ack_t *)mp->b_rptr)->dl_primitive;
+
+ if (prim != DL_BIND_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp, "wrong dlpi primitive for vnd_st_bind");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_BINDFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_spromisc(vnd_str_t *vsp, int type, vnd_str_state_t next)
+{
+ mblk_t *mp;
+ dl_promiscon_req_t *dprp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_promiscon_req_t), DL_PROMISCON_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ dprp = (dl_promiscon_req_t *)mp->b_rptr;
+ dprp->dl_level = type;
+
+ vsp->vns_state = next;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_promisc(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_promisc");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_PROMISCON_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_promisc: Got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_PROMISCFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_scapabq(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ mp = vnd_dlpi_alloc(sizeof (dl_capability_req_t), DL_CAPABILITY_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ vsp->vns_state = VNS_S_CAPAB_Q_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static void
+vnd_mac_input(vnd_str_t *vsp, mac_resource_t *unused, mblk_t *mp_chain,
+ mac_header_info_t *mhip)
+{
+ int signal = 0;
+ mblk_t *mp;
+ vnd_pnsd_t *nsp = vsp->vns_nsd;
+
+ ASSERT(vsp != NULL);
+ ASSERT(mp_chain != NULL);
+
+ for (mp = mp_chain; mp != NULL; mp = mp_chain) {
+ uint16_t vid;
+ mp_chain = mp->b_next;
+ mp->b_next = NULL;
+
+ /*
+ * If we were operating in a traditional dlpi context then we
+ * would have enabled DLIOCRAW and rather than the fast path, we
+ * would come through dld_str_rx_raw. That function does two
+ * things that we have to consider doing ourselves. The first is
+ * that it adjusts the b_rptr back to account for dld bumping us
+ * past the mac header. It also tries to account for cases where
+ * mac provides an illusion of the mac header. Fortunately, dld
+ * only allows the fastpath when the media type is the same as
+ * the native type. Therefore all we have to do here is adjust
+ * the b_rptr.
+ */
+ ASSERT(mp->b_rptr >= DB_BASE(mp) + mhip->mhi_hdrsize);
+ mp->b_rptr -= mhip->mhi_hdrsize;
+ vid = VLAN_ID(mhip->mhi_tci);
+ if (mhip->mhi_istagged && vid != VLAN_ID_NONE) {
+ /*
+ * This is an overlapping copy. Do not use bcopy(9F).
+ */
+ memmove(mp->b_rptr + 4, mp->b_rptr, 12);
+ mp->b_rptr += 4;
+ }
+
+ if (nsp->vpnd_hooked && vnd_hook(vsp, &mp, nsp->vpnd_neti_v4,
+ nsp->vpnd_event_in_v4, nsp->vpnd_token_in_v4,
+ nsp->vpnd_neti_v6, nsp->vpnd_event_in_v6,
+ nsp->vpnd_token_in_v6, vnd_drop_hook_in, vnd_drop_in) != 0)
+ continue;
+
+ VND_STAT_INC(vsp, vks_rpackets, 1);
+ VND_STAT_INC(vsp, vks_rbytes, msgsize(mp));
+ DTRACE_VND5(recv, mblk_t *, mp, void *, NULL, void *, NULL,
+ vnd_str_t *, vsp, mblk_t *, mp);
+ mutex_enter(&vsp->vns_dq_read.vdq_lock);
+ signal |= vnd_dq_push(&vsp->vns_dq_read, mp, B_FALSE,
+ vnd_drop_in);
+ mutex_exit(&vsp->vns_dq_read.vdq_lock);
+ }
+
+ if (signal != 0) {
+ cv_broadcast(&vsp->vns_dq_read.vdq_ready);
+ pollwakeup(&vsp->vns_dev->vdd_ph, POLLIN | POLLRDNORM);
+ }
+
+}
+
+static void
+vnd_mac_flow_control_stat(vnd_str_t *vsp, hrtime_t diff)
+{
+ VND_STAT_INC(vsp, vks_nmacflow, 1);
+ VND_STAT_INC(vsp, vks_tmacflow, diff);
+ if (diff >= VND_LATENCY_1MS)
+ VND_STAT_INC(vsp, vks_mac_flow_1ms, 1);
+ if (diff >= VND_LATENCY_10MS)
+ VND_STAT_INC(vsp, vks_mac_flow_10ms, 1);
+ if (diff >= VND_LATENCY_100MS)
+ VND_STAT_INC(vsp, vks_mac_flow_100ms, 1);
+ if (diff >= VND_LATENCY_1S)
+ VND_STAT_INC(vsp, vks_mac_flow_1s, 1);
+ if (diff >= VND_LATENCY_10S)
+ VND_STAT_INC(vsp, vks_mac_flow_10s, 1);
+}
+
+/*
+ * This is a callback from MAC that indicates that we are allowed to send
+ * packets again.
+ */
+static void
+vnd_mac_flow_control(void *arg, vnd_mac_cookie_t cookie)
+{
+ vnd_str_t *vsp = arg;
+ hrtime_t now, diff;
+
+ mutex_enter(&vsp->vns_lock);
+ now = gethrtime();
+
+ /*
+ * Check for the case that we beat vnd_squeue_tx_one to the punch.
+ * There's also an additional case here that we got notified because
+ * we're sharing a device that ran out of tx descriptors, even though it
+ * wasn't because of us.
+ */
+ if (!(vsp->vns_flags & VNS_F_FLOW_CONTROLLED)) {
+ vsp->vns_fcupdate = now;
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ ASSERT(vsp->vns_flags & VNS_F_FLOW_CONTROLLED);
+ ASSERT(vsp->vns_caps.vsc_fc_cookie == cookie);
+ vsp->vns_flags &= ~VNS_F_FLOW_CONTROLLED;
+ vsp->vns_caps.vsc_fc_cookie = NULL;
+ diff = now - vsp->vns_fclatch;
+ vsp->vns_fclatch = 0;
+ DTRACE_VND3(flow__resumed, vnd_str_t *, vsp, uint64_t,
+ vsp->vns_dq_write.vdq_cur, uintptr_t, cookie);
+ /*
+ * If someone has asked to flush the squeue and thus inserted a barrier,
+ * than we shouldn't schedule a drain.
+ */
+ if (!(vsp->vns_flags & (VNS_F_DRAIN_SCHEDULED | VNS_F_BARRIER))) {
+ vsp->vns_flags |= VNS_F_DRAIN_SCHEDULED;
+ gsqueue_enter_one(vsp->vns_squeue, &vsp->vns_drainblk,
+ vnd_squeue_tx_drain, vsp, GSQUEUE_FILL,
+ VND_SQUEUE_TAG_MAC_FLOW_CONTROL);
+ }
+ mutex_exit(&vsp->vns_lock);
+}
+
+static void
+vnd_mac_enter(vnd_str_t *vsp, mac_perim_handle_t *mphp)
+{
+ ASSERT(MUTEX_HELD(&vsp->vns_lock));
+ VERIFY(vsp->vns_caps.vsc_capab_f(vsp->vns_caps.vsc_capab_hdl,
+ DLD_CAPAB_PERIM, mphp, DLD_ENABLE) == 0);
+}
+
+static void
+vnd_mac_exit(vnd_str_t *vsp, mac_perim_handle_t mph)
+{
+ ASSERT(MUTEX_HELD(&vsp->vns_lock));
+ VERIFY(vsp->vns_caps.vsc_capab_f(vsp->vns_caps.vsc_capab_hdl,
+ DLD_CAPAB_PERIM, mph, DLD_DISABLE) == 0);
+}
+
+static int
+vnd_dld_cap_enable(vnd_str_t *vsp, vnd_rx_t rxfunc)
+{
+ int ret;
+ dld_capab_direct_t d;
+ mac_perim_handle_t mph;
+ vnd_str_capab_t *c = &vsp->vns_caps;
+
+ bzero(&d, sizeof (d));
+ d.di_rx_cf = (uintptr_t)rxfunc;
+ d.di_rx_ch = vsp;
+ d.di_flags = DI_DIRECT_RAW;
+
+ vnd_mac_enter(vsp, &mph);
+
+ /*
+ * If we're coming in here for a second pass, we need to make sure that
+ * we remove an existing flow control notification callback, otherwise
+ * we'll create a duplicate that will remain with garbage data.
+ */
+ if (c->vsc_tx_fc_hdl != NULL) {
+ ASSERT(c->vsc_set_fcb_hdl != NULL);
+ (void) c->vsc_set_fcb_f(c->vsc_set_fcb_hdl, NULL,
+ c->vsc_tx_fc_hdl);
+ c->vsc_tx_fc_hdl = NULL;
+ }
+
+ if (vsp->vns_caps.vsc_capab_f(c->vsc_capab_hdl,
+ DLD_CAPAB_DIRECT, &d, DLD_ENABLE) == 0) {
+ c->vsc_tx_f = (vnd_dld_tx_t)d.di_tx_df;
+ c->vsc_tx_hdl = d.di_tx_dh;
+ c->vsc_set_fcb_f = (vnd_dld_set_fcb_t)d.di_tx_cb_df;
+ c->vsc_set_fcb_hdl = d.di_tx_cb_dh;
+ c->vsc_is_fc_f = (vnd_dld_is_fc_t)d.di_tx_fctl_df;
+ c->vsc_is_fc_hdl = d.di_tx_fctl_dh;
+ c->vsc_tx_fc_hdl = c->vsc_set_fcb_f(c->vsc_set_fcb_hdl,
+ vnd_mac_flow_control, vsp);
+ c->vsc_flags |= VNS_C_DIRECT;
+ ret = 0;
+ } else {
+ vsp->vns_errno = VND_E_DIRECTFAIL;
+ ret = 1;
+ }
+ vnd_mac_exit(vsp, mph);
+ return (ret);
+}
+
+static int
+vnd_st_capabq(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_capability_ack_t *cap;
+ dl_capability_sub_t *subp;
+ dl_capab_hcksum_t *hck;
+ dl_capab_dld_t *dld;
+ unsigned char *rp;
+ int ret = 0;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+
+ rp = mp->b_rptr;
+ cap = (dl_capability_ack_t *)rp;
+ if (cap->dl_sub_length == 0)
+ goto done;
+
+ /* Don't try to process something too big */
+ if (sizeof (dl_capability_ack_t) + cap->dl_sub_length > MBLKL(mp)) {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vsp->vns_errno = VND_E_CAPACKINVAL;
+ ret = 1;
+ goto done;
+ }
+
+ rp += cap->dl_sub_offset;
+
+ while (cap->dl_sub_length > 0) {
+ subp = (dl_capability_sub_t *)rp;
+ /* Sanity check something crazy from down below */
+ if (subp->dl_length + sizeof (dl_capability_sub_t) >
+ cap->dl_sub_length) {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vsp->vns_errno = VND_E_SUBCAPINVAL;
+ ret = 1;
+ goto done;
+ }
+
+ switch (subp->dl_cap) {
+ case DL_CAPAB_HCKSUM:
+ hck = (dl_capab_hcksum_t *)(rp +
+ sizeof (dl_capability_sub_t));
+ if (hck->hcksum_version != HCKSUM_CURRENT_VERSION) {
+ vsp->vns_caps.vsc_flags |= VNS_C_HCKSUM_BADVERS;
+ break;
+ }
+ if (dlcapabcheckqid(&hck->hcksum_mid, vsp->vns_lrq) !=
+ B_TRUE) {
+ vsp->vns_errno = VND_E_CAPABPASS;
+ ret = 1;
+ goto done;
+ }
+ vsp->vns_caps.vsc_flags |= VNS_C_HCKSUM;
+ vsp->vns_caps.vsc_hcksum_opts = hck->hcksum_txflags;
+ break;
+ case DL_CAPAB_DLD:
+ dld = (dl_capab_dld_t *)(rp +
+ sizeof (dl_capability_sub_t));
+ if (dld->dld_version != DLD_CURRENT_VERSION) {
+ vsp->vns_errno = VND_E_DLDBADVERS;
+ ret = 1;
+ goto done;
+ }
+ if (dlcapabcheckqid(&dld->dld_mid, vsp->vns_lrq) !=
+ B_TRUE) {
+ vsp->vns_errno = VND_E_CAPABPASS;
+ ret = 1;
+ goto done;
+ }
+ vsp->vns_caps.vsc_flags |= VNS_C_DLD;
+ vsp->vns_caps.vsc_capab_f =
+ (vnd_dld_cap_t)dld->dld_capab;
+ vsp->vns_caps.vsc_capab_hdl =
+ (void *)dld->dld_capab_handle;
+ /*
+ * At this point in time, we have to set up a direct
+ * function that drops all input. This validates that
+ * we'll be able to set up direct input and that we can
+ * easily switch it earlier to the real data function
+ * when we've plumbed everything up.
+ */
+ if (vnd_dld_cap_enable(vsp, vnd_mac_drop_input) != 0) {
+ /* vns_errno set by vnd_dld_cap_enable */
+ ret = 1;
+ goto done;
+ }
+ break;
+ default:
+ /* Ignore unsupported cap */
+ break;
+ }
+
+ rp += sizeof (dl_capability_sub_t) + subp->dl_length;
+ cap->dl_sub_length -= sizeof (dl_capability_sub_t) +
+ subp->dl_length;
+ }
+
+done:
+ /* Make sure we enabled direct callbacks */
+ if (ret == 0 && !(vsp->vns_caps.vsc_flags & VNS_C_DIRECT)) {
+ vsp->vns_errno = VND_E_DIRECTNOTSUP;
+ ret = 1;
+ }
+
+ freemsg(mp);
+ return (ret);
+}
+
+static void
+vnd_st_sonline(vnd_str_t *vsp)
+{
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ vsp->vns_state = VNS_S_ONLINE;
+ cv_broadcast(&vsp->vns_stcv);
+}
+
+static void
+vnd_st_shutdown(vnd_str_t *vsp)
+{
+ mac_perim_handle_t mph;
+ vnd_str_capab_t *vsc = &vsp->vns_caps;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ /*
+ * At this point in time we know that there is no one transmitting as
+ * our final reference has been torn down and that vnd_s_close inserted
+ * a barrier to validate that everything is flushed.
+ */
+ if (vsc->vsc_flags & VNS_C_DIRECT) {
+ vnd_mac_enter(vsp, &mph);
+ vsc->vsc_flags &= ~VNS_C_DIRECT;
+ (void) vsc->vsc_set_fcb_f(vsc->vsc_set_fcb_hdl, NULL,
+ vsc->vsc_tx_fc_hdl);
+ vsc->vsc_tx_fc_hdl = NULL;
+ (void) vsc->vsc_capab_f(vsc->vsc_capab_hdl, DLD_CAPAB_DIRECT,
+ NULL, DLD_DISABLE);
+ vnd_mac_exit(vsp, mph);
+ }
+}
+
+static boolean_t
+vnd_st_spromiscoff(vnd_str_t *vsp, int type, vnd_str_state_t next)
+{
+ boolean_t ret = B_TRUE;
+ mblk_t *mp;
+ dl_promiscoff_req_t *dprp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_promiscon_req_t), DL_PROMISCOFF_REQ);
+ if (mp == NULL) {
+ cmn_err(CE_NOTE, "!vnd failed to allocate mblk_t for "
+ "promiscoff request");
+ ret = B_FALSE;
+ goto next;
+ }
+
+ dprp = (dl_promiscoff_req_t *)mp->b_rptr;
+ dprp->dl_level = type;
+
+ putnext(vsp->vns_wq, mp);
+next:
+ vsp->vns_state = next;
+ cv_broadcast(&vsp->vns_stcv);
+ return (ret);
+}
+
+static void
+vnd_st_promiscoff(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ /*
+ * Unlike other cases where we guard against the incoming packet being
+ * NULL, during tear down we try to keep driving and therefore we may
+ * have gotten here due to an earlier failure, so there's nothing to do.
+ */
+ mp = vnd_dlpi_inc_pop(vsp);
+ if (mp == NULL)
+ return;
+
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_promiscoff");
+ return;
+ }
+
+ if (cprim != DL_PROMISCOFF_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_promiscoff: Got ack/nack for wrong primitive");
+ return;
+ }
+
+ if (prim == DL_ERROR_ACK) {
+ cmn_err(CE_WARN, "!failed to disable promiscuos mode during "
+ "vnd teardown");
+ }
+}
+
+static boolean_t
+vnd_st_sunbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ boolean_t ret = B_TRUE;
+
+ mp = vnd_dlpi_alloc(sizeof (dl_unbind_req_t), DL_UNBIND_REQ);
+ if (mp == NULL) {
+ cmn_err(CE_NOTE, "!vnd failed to allocate mblk_t for "
+ "unbind request");
+ ret = B_FALSE;
+ goto next;
+ }
+
+ putnext(vsp->vns_wq, mp);
+next:
+ vsp->vns_state = VNS_S_UNBIND_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ return (ret);
+}
+
+static void
+vnd_st_unbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ /*
+ * Unlike other cases where we guard against the incoming packet being
+ * NULL, during tear down we try to keep driving and therefore we may
+ * have gotten here due to an earlier failure, so there's nothing to do.
+ */
+ mp = vnd_dlpi_inc_pop(vsp);
+ if (mp == NULL)
+ goto next;
+
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_unbind");
+ goto next;
+ }
+
+ if (cprim != DL_UNBIND_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_unbind: Got ack/nack for wrong primitive");
+ goto next;
+ }
+
+ if (prim == DL_ERROR_ACK) {
+ cmn_err(CE_WARN, "!failed to unbind stream during vnd "
+ "teardown");
+ }
+
+next:
+ vsp->vns_state = VNS_S_ZOMBIE;
+ cv_broadcast(&vsp->vns_stcv);
+}
+
+/*
+ * Perform state transitions. This is a one way shot down the flow chart
+ * described in the big theory statement.
+ */
+static void
+vnd_str_state_transition(void *arg)
+{
+ boolean_t died = B_FALSE;
+ vnd_str_t *vsp = arg;
+ mblk_t *mp;
+
+ mutex_enter(&vsp->vns_lock);
+ if (vsp->vns_dlpi_inc == NULL && (vsp->vns_state != VNS_S_INITIAL &&
+ vsp->vns_state != VNS_S_SHUTTING_DOWN)) {
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ /*
+ * When trying to shut down, or unwinding from a failed enabling, rather
+ * than immediately entering the ZOMBIE state, we may instead opt to try
+ * and enter the next state in the progression. This is especially
+ * important when trying to tear everything down.
+ */
+loop:
+ DTRACE_PROBE2(vnd__state__transition, uintptr_t, vsp,
+ vnd_str_state_t, vsp->vns_state);
+ switch (vsp->vns_state) {
+ case VNS_S_INITIAL:
+ VERIFY(vsp->vns_dlpi_inc == NULL);
+ if (vnd_st_sinfo(vsp) != 0)
+ died = B_TRUE;
+ break;
+ case VNS_S_INFO_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_info(vsp) == 0) {
+ if (vnd_st_sexclusive(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_EXCLUSIVE_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_exclusive(vsp) == 0) {
+ if (vsp->vns_dlpi_style == DL_STYLE2) {
+ if (vnd_st_sattach(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ if (vnd_st_sbind(vsp) != 0)
+ died = B_TRUE;
+ }
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_ATTACH_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_attach(vsp) == 0) {
+ if (vnd_st_sbind(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_BIND_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_bind(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_SAP,
+ VNS_S_SAP_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_SAP_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_MULTI,
+ VNS_S_MULTI_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_MULTI_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_RX_ONLY,
+ VNS_S_RX_ONLY_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_RX_ONLY_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_FIXUPS,
+ VNS_S_FIXUP_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_FIXUP_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_scapabq(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_CAPAB_Q_SENT:
+ if (vnd_st_capabq(vsp) != 0)
+ died = B_TRUE;
+ else
+ vnd_st_sonline(vsp);
+ break;
+ case VNS_S_SHUTTING_DOWN:
+ vnd_st_shutdown(vsp);
+ if (vnd_st_spromiscoff(vsp, DL_PROMISC_MULTI,
+ VNS_S_MULTICAST_PROMISCOFF_SENT) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_MULTICAST_PROMISCOFF_SENT:
+ vnd_st_promiscoff(vsp);
+ if (vnd_st_spromiscoff(vsp, DL_PROMISC_SAP,
+ VNS_S_SAP_PROMISCOFF_SENT) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_SAP_PROMISCOFF_SENT:
+ vnd_st_promiscoff(vsp);
+ if (vnd_st_sunbind(vsp) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_UNBIND_SENT:
+ vnd_st_unbind(vsp);
+ break;
+ case VNS_S_ZOMBIE:
+ while ((mp = vnd_dlpi_inc_pop(vsp)) != NULL)
+ vnd_drop_ctl(vsp, mp, "vsp received data as a zombie");
+ break;
+ default:
+ panic("vnd_str_t entered an unknown state");
+ }
+
+ if (died == B_TRUE) {
+ ASSERT(vsp->vns_errno != VND_E_SUCCESS);
+ vsp->vns_laststate = vsp->vns_state;
+ vsp->vns_state = VNS_S_ZOMBIE;
+ cv_broadcast(&vsp->vns_stcv);
+ }
+
+ mutex_exit(&vsp->vns_lock);
+}
+
+static void
+vnd_dlpi_taskq_dispatch(void *arg)
+{
+ vnd_str_t *vsp = arg;
+ int run = 1;
+
+ while (run != 0) {
+ vnd_str_state_transition(vsp);
+ mutex_enter(&vsp->vns_lock);
+ if (vsp->vns_flags & VNS_F_CONDEMNED ||
+ vsp->vns_dlpi_inc == NULL) {
+ run = 0;
+ vsp->vns_flags &= ~VNS_F_TASKQ_DISPATCHED;
+ }
+ if (vsp->vns_flags & VNS_F_CONDEMNED)
+ cv_signal(&vsp->vns_cancelcv);
+ mutex_exit(&vsp->vns_lock);
+ }
+}
+
+static int
+vnd_neti_getifname(net_handle_t neti, phy_if_t phy, char *buf, const size_t len)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_getmtu(net_handle_t neti, phy_if_t phy, lif_if_t ifdata)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_getptmue(net_handle_t neti)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_getlifaddr(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ size_t nelem, net_ifaddr_t type[], void *storage)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_getlifzone(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ zoneid_t *zid)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_getlifflags(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ uint64_t *flags)
+{
+ return (-1);
+}
+
+static phy_if_t
+vnd_neti_phygetnext(net_handle_t neti, phy_if_t phy)
+{
+ return (-1);
+}
+
+static phy_if_t
+vnd_neti_phylookup(net_handle_t neti, const char *name)
+{
+ return (-1);
+}
+
+static lif_if_t
+vnd_neti_lifgetnext(net_handle_t neti, phy_if_t phy, lif_if_t ifdata)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_inject(net_handle_t neti, inject_t style, net_inject_t *packet)
+{
+ return (-1);
+}
+
+static phy_if_t
+vnd_neti_route(net_handle_t neti, struct sockaddr *address,
+ struct sockaddr *next)
+{
+ return ((phy_if_t)-1);
+}
+
+static int
+vnd_neti_ispchksum(net_handle_t neti, mblk_t *mp)
+{
+ return (-1);
+}
+
+static int
+vnd_neti_isvchksum(net_handle_t neti, mblk_t *mp)
+{
+ return (-1);
+}
+
+static net_protocol_t vnd_neti_info_v4 = {
+ NETINFO_VERSION,
+ NHF_VND_INET,
+ vnd_neti_getifname,
+ vnd_neti_getmtu,
+ vnd_neti_getptmue,
+ vnd_neti_getlifaddr,
+ vnd_neti_getlifzone,
+ vnd_neti_getlifflags,
+ vnd_neti_phygetnext,
+ vnd_neti_phylookup,
+ vnd_neti_lifgetnext,
+ vnd_neti_inject,
+ vnd_neti_route,
+ vnd_neti_ispchksum,
+ vnd_neti_isvchksum
+};
+
+static net_protocol_t vnd_neti_info_v6 = {
+ NETINFO_VERSION,
+ NHF_VND_INET6,
+ vnd_neti_getifname,
+ vnd_neti_getmtu,
+ vnd_neti_getptmue,
+ vnd_neti_getlifaddr,
+ vnd_neti_getlifzone,
+ vnd_neti_getlifflags,
+ vnd_neti_phygetnext,
+ vnd_neti_phylookup,
+ vnd_neti_lifgetnext,
+ vnd_neti_inject,
+ vnd_neti_route,
+ vnd_neti_ispchksum,
+ vnd_neti_isvchksum
+};
+
+
+static int
+vnd_netinfo_init(vnd_pnsd_t *nsp)
+{
+ nsp->vpnd_neti_v4 = net_protocol_register(nsp->vpnd_nsid,
+ &vnd_neti_info_v4);
+ ASSERT(nsp->vpnd_neti_v4 != NULL);
+
+ nsp->vpnd_neti_v6 = net_protocol_register(nsp->vpnd_nsid,
+ &vnd_neti_info_v6);
+ ASSERT(nsp->vpnd_neti_v6 != NULL);
+
+ nsp->vpnd_family_v4.hf_version = HOOK_VERSION;
+ nsp->vpnd_family_v4.hf_name = "vnd_inet";
+
+ if (net_family_register(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4) != 0) {
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_family_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_family_v6.hf_version = HOOK_VERSION;
+ nsp->vpnd_family_v6.hf_name = "vnd_inet6";
+
+ if (net_family_register(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6) != 0) {
+ net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_family_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_in_v4.he_version = HOOK_VERSION;
+ nsp->vpnd_event_in_v4.he_name = NH_PHYSICAL_IN;
+ nsp->vpnd_event_in_v4.he_flags = 0;
+ nsp->vpnd_event_in_v4.he_interested = B_FALSE;
+
+ nsp->vpnd_token_in_v4 = net_event_register(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ if (nsp->vpnd_token_in_v4 == NULL) {
+ net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_in_v6.he_version = HOOK_VERSION;
+ nsp->vpnd_event_in_v6.he_name = NH_PHYSICAL_IN;
+ nsp->vpnd_event_in_v6.he_flags = 0;
+ nsp->vpnd_event_in_v6.he_interested = B_FALSE;
+
+ nsp->vpnd_token_in_v6 = net_event_register(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ if (nsp->vpnd_token_in_v6 == NULL) {
+ net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_out_v4.he_version = HOOK_VERSION;
+ nsp->vpnd_event_out_v4.he_name = NH_PHYSICAL_OUT;
+ nsp->vpnd_event_out_v4.he_flags = 0;
+ nsp->vpnd_event_out_v4.he_interested = B_FALSE;
+
+ nsp->vpnd_token_out_v4 = net_event_register(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_out_v4);
+ if (nsp->vpnd_token_out_v4 == NULL) {
+ net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_out_v6.he_version = HOOK_VERSION;
+ nsp->vpnd_event_out_v6.he_name = NH_PHYSICAL_OUT;
+ nsp->vpnd_event_out_v6.he_flags = 0;
+ nsp->vpnd_event_out_v6.he_interested = B_FALSE;
+
+ nsp->vpnd_token_out_v6 = net_event_register(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_out_v6);
+ if (nsp->vpnd_token_out_v6 == NULL) {
+ net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ net_protocol_unregister(nsp->vpnd_neti_v4);
+ net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+vnd_netinfo_shutdown(vnd_pnsd_t *nsp)
+{
+ int ret;
+
+ ret = net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_out_v4);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_out_v6);
+ VERIFY(ret == 0);
+}
+
+static void
+vnd_netinfo_fini(vnd_pnsd_t *nsp)
+{
+ int ret;
+
+ ret = net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_out_v4);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_out_v6);
+ VERIFY(ret == 0);
+ ret = net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ VERIFY(ret == 0);
+ ret = net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ VERIFY(ret == 0);
+ ret = net_protocol_unregister(nsp->vpnd_neti_v4);
+ VERIFY(ret == 0);
+ ret = net_protocol_unregister(nsp->vpnd_neti_v6);
+ VERIFY(ret == 0);
+}
+
+static void
+vnd_strbarrier_cb(void *arg, mblk_t *bmp, gsqueue_t *gsp, void *dummy)
+{
+ vnd_str_t *vsp = arg;
+
+ VERIFY(bmp == &vsp->vns_barrierblk);
+ mutex_enter(&vsp->vns_lock);
+ VERIFY(vsp->vns_flags & VNS_F_BARRIER);
+ VERIFY(!(vsp->vns_flags & VNS_F_BARRIER_DONE));
+ vsp->vns_flags |= VNS_F_BARRIER_DONE;
+ mutex_exit(&vsp->vns_lock);
+
+ /*
+ * For better or worse, we have to broadcast here as we could have a
+ * thread that's blocked for completion as well as one that's blocked
+ * waiting to do a barrier itself.
+ */
+ cv_broadcast(&vsp->vns_barriercv);
+}
+
+/*
+ * This is a data barrier for the stream while it is in fastpath mode. It blocks
+ * and ensures that there is nothing else in the squeue.
+ */
+static void
+vnd_strbarrier(vnd_str_t *vsp)
+{
+ mutex_enter(&vsp->vns_lock);
+ while (vsp->vns_flags & VNS_F_BARRIER)
+ cv_wait(&vsp->vns_barriercv, &vsp->vns_lock);
+ vsp->vns_flags |= VNS_F_BARRIER;
+ mutex_exit(&vsp->vns_lock);
+
+ gsqueue_enter_one(vsp->vns_squeue, &vsp->vns_barrierblk,
+ vnd_strbarrier_cb, vsp, GSQUEUE_PROCESS, VND_SQUEUE_TAG_STRBARRIER);
+
+ mutex_enter(&vsp->vns_lock);
+ while (!(vsp->vns_flags & VNS_F_BARRIER_DONE))
+ cv_wait(&vsp->vns_barriercv, &vsp->vns_lock);
+ vsp->vns_flags &= ~VNS_F_BARRIER;
+ vsp->vns_flags &= ~VNS_F_BARRIER_DONE;
+ mutex_exit(&vsp->vns_lock);
+
+ /*
+ * We have to broadcast in case anyone is waiting for the barrier
+ * themselves.
+ */
+ cv_broadcast(&vsp->vns_barriercv);
+}
+
+/*
+ * Based on the type of message that we're dealing with we're going to want to
+ * do one of several things. Basically if it looks like it's something we know
+ * about, we should probably handle it in one of our transition threads.
+ * Otherwise, we should just simply putnext.
+ */
+static int
+vnd_s_rput(queue_t *q, mblk_t *mp)
+{
+ t_uscalar_t prim;
+ int dispatch = 0;
+ vnd_str_t *vsp = q->q_ptr;
+
+ switch (DB_TYPE(mp)) {
+ case M_PROTO:
+ case M_PCPROTO:
+ if (MBLKL(mp) < sizeof (t_uscalar_t)) {
+ vnd_drop_ctl(vsp, mp, "PROTO message too short");
+ break;
+ }
+
+ prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
+ if (prim == DL_UNITDATA_REQ || prim == DL_UNITDATA_IND) {
+ vnd_drop_ctl(vsp, mp,
+ "recieved an unsupported dlpi DATA req");
+ break;
+ }
+
+ /*
+ * Enqueue the entry and fire off a taskq dispatch.
+ */
+ mutex_enter(&vsp->vns_lock);
+ vnd_dlpi_inc_push(vsp, mp);
+ if (!(vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)) {
+ dispatch = 1;
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ }
+ mutex_exit(&vsp->vns_lock);
+ if (dispatch != 0)
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch,
+ vsp, 0, &vsp->vns_tqe);
+ break;
+ case M_DATA:
+ vnd_drop_in(vsp, mp, "M_DATA via put(9E)");
+ break;
+ default:
+ putnext(vsp->vns_rq, mp);
+ }
+ return (0);
+}
+
+static void
+vnd_strioctl(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct iocblk *iocp)
+{
+ int error;
+ vnd_strioc_t *visp;
+
+ if (iocp->ioc_cmd != VND_STRIOC_ASSOCIATE ||
+ iocp->ioc_count != TRANSPARENT) {
+ error = EINVAL;
+ goto nak;
+ }
+
+ /*
+ * All streams ioctls that we support must use kcred as a means to
+ * distinguish that this is a layered open by the kernel as opposed to
+ * one by a user who has done an I_PUSH of the module.
+ */
+ if (iocp->ioc_cr != kcred) {
+ error = EPERM;
+ goto nak;
+ }
+
+ if (mp->b_cont == NULL) {
+ error = EAGAIN;
+ goto nak;
+ }
+
+ visp = kmem_alloc(sizeof (vnd_strioc_t), KM_SLEEP);
+ ASSERT(MBLKL(mp->b_cont) == sizeof (caddr_t));
+ visp->vs_addr = *(caddr_t *)mp->b_cont->b_rptr;
+ visp->vs_state = VSS_COPYIN;
+
+ mcopyin(mp, (void *)visp, sizeof (vnd_strioc_associate_t), NULL);
+ qreply(q, mp);
+
+ return;
+
+nak:
+ if (mp->b_cont != NULL) {
+ freemsg(mp->b_cont);
+ mp->b_cont = NULL;
+ }
+
+ iocp->ioc_error = error;
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_count = 0;
+ qreply(q, mp);
+}
+
+static void
+vnd_striocdata(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct copyresp *csp)
+{
+ int error;
+ vnd_str_state_t state;
+ struct copyreq *crp;
+ vnd_strioc_associate_t *vss;
+ vnd_dev_t *vdp = NULL;
+ vnd_pnsd_t *nsp = NULL;
+ char iname[2*VND_NAMELEN];
+ zone_t *zone;
+ vnd_strioc_t *visp;
+
+ visp = (vnd_strioc_t *)csp->cp_private;
+
+ /* If it's not ours, it's not our problem */
+ if (csp->cp_cmd != VND_STRIOC_ASSOCIATE) {
+ if (q->q_next != NULL) {
+ putnext(q, mp);
+ } else {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vnd_drop_ctl(vsp, mp, "uknown cmd for M_IOCDATA");
+ }
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ return;
+ }
+
+ /* The nak is already sent for us */
+ if (csp->cp_rval != 0) {
+ vnd_drop_ctl(vsp, mp, "M_COPYIN failed");
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ return;
+ }
+
+ /* Data is sitting for us in b_cont */
+ if (mp->b_cont == NULL ||
+ MBLKL(mp->b_cont) != sizeof (vnd_strioc_associate_t)) {
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ miocnak(q, mp, 0, EINVAL);
+ return;
+ }
+
+ vss = (vnd_strioc_associate_t *)mp->b_cont->b_rptr;
+ vdp = vnd_dev_lookup(vss->vsa_minor);
+ if (vdp == NULL) {
+ error = EIO;
+ vss->vsa_errno = VND_E_NODEV;
+ goto nak;
+ }
+
+ nsp = vnd_nsd_lookup(vss->vsa_nsid);
+ if (nsp == NULL) {
+ error = EIO;
+ vss->vsa_errno = VND_E_NONETSTACK;
+ goto nak;
+ }
+
+ mutex_enter(&vsp->vns_lock);
+ if (!(vsp->vns_flags & VNS_F_NEED_ZONE)) {
+ mutex_exit(&vsp->vns_lock);
+ error = EEXIST;
+ vss->vsa_errno = VND_E_ASSOCIATED;
+ goto nak;
+ }
+
+ vsp->vns_nsd = nsp;
+ vsp->vns_flags &= ~VNS_F_NEED_ZONE;
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ mutex_exit(&vsp->vns_lock);
+
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch, vsp, 0,
+ &vsp->vns_tqe);
+
+
+ /* At this point we need to wait until we have transitioned to ONLINE */
+ mutex_enter(&vsp->vns_lock);
+ while (vsp->vns_state != VNS_S_ONLINE && vsp->vns_state != VNS_S_ZOMBIE)
+ cv_wait(&vsp->vns_stcv, &vsp->vns_lock);
+ state = vsp->vns_state;
+ mutex_exit(&vsp->vns_lock);
+
+ if (state == VNS_S_ZOMBIE) {
+ vss->vsa_errno = vsp->vns_errno;
+ error = EIO;
+ goto nak;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ mutex_enter(&vsp->vns_lock);
+ VERIFY(vdp->vdd_str == NULL);
+ /*
+ * Now initialize the remaining kstat properties and let's go ahead and
+ * create it.
+ */
+ (void) snprintf(iname, sizeof (iname), "z%d_%d",
+ vdp->vdd_nsd->vpnd_zid, vdp->vdd_minor);
+ vsp->vns_kstat = kstat_create_zone("vnd", vdp->vdd_minor, iname, "net",
+ KSTAT_TYPE_NAMED, sizeof (vnd_str_stat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
+ if (vsp->vns_kstat == NULL) {
+ error = EIO;
+ vss->vsa_errno = VND_E_KSTATCREATE;
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ goto nak;
+ }
+ vdp->vdd_str = vsp;
+ vsp->vns_dev = vdp;
+
+ /*
+ * Now, it's time to do the las thing that can fail, changing out the
+ * input function. After this we know that we can receive data, so we
+ * should make sure that we're ready.
+ */
+ if (vnd_dld_cap_enable(vsp, vnd_mac_input) != 0) {
+ error = EIO;
+ vss->vsa_errno = VND_E_DIRECTFAIL;
+ vdp->vdd_str = NULL;
+ vsp->vns_dev = NULL;
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ goto nak;
+ }
+
+ zone = zone_find_by_id(vdp->vdd_nsd->vpnd_zid);
+ ASSERT(zone != NULL);
+ vsp->vns_kstat->ks_data = &vsp->vns_ksdata;
+ /* Account for zone name */
+ vsp->vns_kstat->ks_data_size += strlen(zone->zone_name) + 1;
+ /* Account for eventual link name */
+ vsp->vns_kstat->ks_data_size += VND_NAMELEN;
+ kstat_named_setstr(&vsp->vns_ksdata.vks_zonename, zone->zone_name);
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ zone_rele(zone);
+ kstat_install(vsp->vns_kstat);
+
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * Note that the vnd_str_t does not keep a permanent hold on the
+ * vnd_pnsd_t. We leave that up to the vnd_dev_t as that's also what
+ * the nestack goes through to take care of everything.
+ */
+ vss->vsa_errno = VND_E_SUCCESS;
+nak:
+ if (vdp != NULL)
+ vnd_dev_rele(vdp);
+ if (nsp != NULL)
+ vnd_nsd_rele(nsp);
+ /*
+ * Change the copyin request to a copyout. Note that we can't use
+ * mcopyout here as it only works when the DB_TYPE is M_IOCTL. That's
+ * okay, as the copyin vs. copyout is basically the same.
+ */
+ DB_TYPE(mp) = M_COPYOUT;
+ visp->vs_state = VSS_COPYOUT;
+ crp = (struct copyreq *)mp->b_rptr;
+ crp->cq_private = (void *)visp;
+ crp->cq_addr = visp->vs_addr;
+ crp->cq_size = sizeof (vnd_strioc_associate_t);
+ qreply(q, mp);
+}
+
+static void
+vnd_stroutdata(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct copyresp *csp)
+{
+ ASSERT(csp->cp_private != NULL);
+ kmem_free(csp->cp_private, sizeof (vnd_strioc_t));
+ if (csp->cp_cmd != VND_STRIOC_ASSOCIATE) {
+ if (q->q_next != NULL) {
+ putnext(q, mp);
+ } else {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vnd_drop_ctl(vsp, mp, "uknown cmd for M_IOCDATA");
+ }
+ return;
+ }
+
+ /* The nak is already sent for us */
+ if (csp->cp_rval != 0) {
+ vnd_drop_ctl(vsp, mp, "M_COPYOUT failed");
+ return;
+ }
+
+ /* Ack and let's be done with it all */
+ miocack(q, mp, 0, 0);
+}
+
+static int
+vnd_s_wput(queue_t *q, mblk_t *mp)
+{
+ vnd_str_t *vsp = q->q_ptr;
+ struct copyresp *crp;
+ vnd_strioc_state_t vstate;
+ vnd_strioc_t *visp;
+
+ switch (DB_TYPE(mp)) {
+ case M_IOCTL:
+ vnd_strioctl(q, vsp, mp, (struct iocblk *)mp->b_rptr);
+ return (0);
+ case M_IOCDATA:
+ crp = (struct copyresp *)mp->b_rptr;
+ ASSERT(crp->cp_private != NULL);
+ visp = (vnd_strioc_t *)crp->cp_private;
+ vstate = visp->vs_state;
+ ASSERT(vstate == VSS_COPYIN || vstate == VSS_COPYOUT);
+ if (vstate == VSS_COPYIN)
+ vnd_striocdata(q, vsp, mp,
+ (struct copyresp *)mp->b_rptr);
+ else
+ vnd_stroutdata(q, vsp, mp,
+ (struct copyresp *)mp->b_rptr);
+ return (0);
+ default:
+ break;
+ }
+ if (q->q_next != NULL)
+ putnext(q, mp);
+ else
+ vnd_drop_ctl(vsp, mp, "!M_IOCTL in wput");
+
+ return (0);
+}
+
+static int
+vnd_s_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
+{
+ vnd_str_t *vsp;
+ uint_t rand;
+
+ if (q->q_ptr != NULL)
+ return (EINVAL);
+
+ if (!(sflag & MODOPEN))
+ return (ENXIO);
+
+ if (credp != kcred)
+ return (EPERM);
+
+ vsp = kmem_cache_alloc(vnd_str_cache, KM_SLEEP);
+ bzero(vsp, sizeof (*vsp));
+ mutex_init(&vsp->vns_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&vsp->vns_cancelcv, NULL, CV_DRIVER, NULL);
+ cv_init(&vsp->vns_barriercv, NULL, CV_DRIVER, NULL);
+ cv_init(&vsp->vns_stcv, NULL, CV_DRIVER, NULL);
+ vsp->vns_state = VNS_S_INITIAL;
+
+ mutex_init(&vsp->vns_dq_read.vdq_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&vsp->vns_dq_write.vdq_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_enter(&vnd_dev_lock);
+ vsp->vns_dq_read.vdq_max = vnd_vdq_default_size;
+ vsp->vns_dq_read.vdq_vns = vsp;
+ vsp->vns_dq_write.vdq_max = vnd_vdq_default_size;
+ vsp->vns_dq_write.vdq_vns = vsp;
+ mutex_exit(&vnd_dev_lock);
+ vsp->vns_rq = q;
+ vsp->vns_wq = WR(q);
+ q->q_ptr = WR(q)->q_ptr = vsp;
+ vsp->vns_flags = VNS_F_NEED_ZONE;
+ vsp->vns_nflush = vnd_flush_nburst;
+ vsp->vns_bsize = vnd_flush_burst_size;
+
+ (void) random_get_pseudo_bytes((uint8_t *)&rand, sizeof (rand));
+ vsp->vns_squeue = gsqueue_set_get(vnd_sqset, rand);
+
+ /*
+ * We create our kstat and initialize all of its fields now, but we
+ * don't install it until we actually do the zone association so we can
+ * get everything.
+ */
+ kstat_named_init(&vsp->vns_ksdata.vks_rbytes, "rbytes",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_rpackets, "rpackets",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_obytes, "obytes",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_opackets, "opackets",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_nhookindrops, "nhookindrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_nhookoutdrops, "nhookoutdrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndlpidrops, "ndlpidrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndataindrops, "ndataindrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndataoutdrops, "ndataoutdrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_tdrops, "total_drops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_linkname, "linkname",
+ KSTAT_DATA_STRING);
+ kstat_named_init(&vsp->vns_ksdata.vks_zonename, "zonename",
+ KSTAT_DATA_STRING);
+ kstat_named_init(&vsp->vns_ksdata.vks_nmacflow, "flowcontrol_events",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_tmacflow, "flowcontrol_time",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_1ms, "flowcontrol_1ms",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_10ms, "flowcontrol_10ms",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_100ms,
+ "flowcontrol_100ms", KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_1s, "flowcontrol_1s",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_10s, "flowcontrol_10s",
+ KSTAT_DATA_UINT64);
+ qprocson(q);
+ /*
+ * Now that we've called qprocson, grab the lower module for making sure
+ * that we don't have any pass through modules.
+ */
+ vsp->vns_lrq = RD(vsp->vns_wq->q_next);
+
+ return (0);
+}
+
+static int
+vnd_s_close(queue_t *q, int flag, cred_t *credp)
+{
+ vnd_str_t *vsp;
+ mblk_t *mp;
+
+ VERIFY(WR(q)->q_next != NULL);
+
+ vsp = q->q_ptr;
+ ASSERT(vsp != NULL);
+
+ /*
+ * We need to transition ourselves down. This means that we have a few
+ * important different things to do in the process of tearing down our
+ * input and output buffers, making sure we've drained the current
+ * squeue, and disabling the fast path. Before we disable the fast path,
+ * we should make sure the squeue is drained. Because we're in streams
+ * close, we know that no packets can come into us from userland, but we
+ * can receive more. As such, the following is the exact order of things
+ * that we do:
+ *
+ * 1) flush the vns_dq_read
+ * 2) Insert the drain mblk
+ * 3) When it's been received, tear down the fast path by kicking
+ * off the state machine.
+ * 4) One final flush of both the vns_dq_read,vns_dq_write
+ */
+
+ vnd_dq_flush(&vsp->vns_dq_read, vnd_drop_in);
+ vnd_strbarrier(vsp);
+ mutex_enter(&vsp->vns_lock);
+ vsp->vns_state = VNS_S_SHUTTING_DOWN;
+ if (!(vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)) {
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch, vsp,
+ 0, &vsp->vns_tqe);
+ }
+ while (vsp->vns_state != VNS_S_ZOMBIE)
+ cv_wait(&vsp->vns_stcv, &vsp->vns_lock);
+ mutex_exit(&vsp->vns_lock);
+
+ qprocsoff(q);
+ mutex_enter(&vsp->vns_lock);
+ vsp->vns_flags |= VNS_F_CONDEMNED;
+ while (vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)
+ cv_wait(&vsp->vns_cancelcv, &vsp->vns_lock);
+
+ while ((mp = vnd_dlpi_inc_pop(vsp)) != NULL)
+ vnd_drop_ctl(vsp, mp, "vnd_s_close");
+ mutex_exit(&vsp->vns_lock);
+
+ q->q_ptr = NULL;
+ vnd_dq_flush(&vsp->vns_dq_read, vnd_drop_in);
+ vnd_dq_flush(&vsp->vns_dq_write, vnd_drop_out);
+ mutex_destroy(&vsp->vns_dq_read.vdq_lock);
+ mutex_destroy(&vsp->vns_dq_write.vdq_lock);
+
+ if (vsp->vns_kstat != NULL)
+ kstat_delete(vsp->vns_kstat);
+ mutex_destroy(&vsp->vns_lock);
+ cv_destroy(&vsp->vns_stcv);
+ cv_destroy(&vsp->vns_barriercv);
+ cv_destroy(&vsp->vns_cancelcv);
+ kmem_cache_free(vnd_str_cache, vsp);
+
+ return (0);
+}
+
+static vnd_mac_cookie_t
+vnd_squeue_tx_one(vnd_str_t *vsp, mblk_t *mp)
+{
+ hrtime_t txtime;
+ vnd_mac_cookie_t vc;
+
+ VND_STAT_INC(vsp, vks_opackets, 1);
+ VND_STAT_INC(vsp, vks_obytes, msgsize(mp));
+ DTRACE_VND5(send, mblk_t *, mp, void *, NULL, void *, NULL,
+ vnd_str_t *, vsp, mblk_t *, mp);
+ /* Actually tx now */
+ txtime = gethrtime();
+ vc = vsp->vns_caps.vsc_tx_f(vsp->vns_caps.vsc_tx_hdl,
+ mp, 0, MAC_DROP_ON_NO_DESC);
+
+ /*
+ * We need to check two different conditions before we immediately set
+ * the flow control lock. The first thing that we need to do is verify
+ * that this is an instance of hard flow control, so to say. The flow
+ * control callbacks won't always fire in cases where we still get a
+ * cookie returned. The explicit check for flow control will guarantee
+ * us that we'll get a subsequent notification callback.
+ *
+ * The second case comes about because we do not hold the
+ * vnd_str_t`vns_lock across calls to tx, we need to determine if a flow
+ * control notification already came across for us in a different thread
+ * calling vnd_mac_flow_control(). To deal with this, we record a
+ * timestamp every time that we change the flow control state. We grab
+ * txtime here before we transmit because that guarantees that the
+ * hrtime_t of the call to vnd_mac_flow_control() will be after txtime.
+ *
+ * If the flow control notification beat us to the punch, the value of
+ * vns_fcupdate will be larger than the value of txtime, and we should
+ * just record the statistics. However, if we didn't beat it to the
+ * punch (txtime > vns_fcupdate), then we know that it's safe to wait
+ * for a notification.
+ */
+ if (vc != NULL) {
+ hrtime_t diff;
+
+ if (vsp->vns_caps.vsc_is_fc_f(vsp->vns_caps.vsc_is_fc_hdl,
+ vc) == 0)
+ return (NULL);
+ mutex_enter(&vsp->vns_lock);
+ diff = vsp->vns_fcupdate - txtime;
+ if (diff > 0) {
+ mutex_exit(&vsp->vns_lock);
+ vnd_mac_flow_control_stat(vsp, diff);
+ return (NULL);
+ }
+ vsp->vns_flags |= VNS_F_FLOW_CONTROLLED;
+ vsp->vns_caps.vsc_fc_cookie = vc;
+ vsp->vns_fclatch = txtime;
+ vsp->vns_fcupdate = txtime;
+ DTRACE_VND3(flow__blocked, vnd_str_t *, vsp,
+ uint64_t, vsp->vns_dq_write.vdq_cur, uintptr_t, vc);
+ mutex_exit(&vsp->vns_lock);
+ }
+
+ return (vc);
+}
+
+static void
+vnd_squeue_tx_drain(void *arg, mblk_t *drain_mp, gsqueue_t *gsp, void *dummy)
+{
+ mblk_t *mp;
+ int nmps;
+ size_t mptot, nflush, bsize;
+ boolean_t blocked, empty;
+ vnd_data_queue_t *vqp;
+ vnd_str_t *vsp = arg;
+
+ mutex_enter(&vsp->vns_lock);
+ /*
+ * We either enter here via an squeue or via vnd_squeue_tx_append(). In
+ * the former case we need to mark that there is no longer an active
+ * user of the drain block.
+ */
+ if (drain_mp != NULL) {
+ VERIFY(drain_mp == &vsp->vns_drainblk);
+ VERIFY(vsp->vns_flags & VNS_F_DRAIN_SCHEDULED);
+ vsp->vns_flags &= ~VNS_F_DRAIN_SCHEDULED;
+ }
+
+ /*
+ * If we're still flow controlled or under a flush barrier, nothing to
+ * do.
+ */
+ if (vsp->vns_flags & (VNS_F_FLOW_CONTROLLED | VNS_F_BARRIER)) {
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ nflush = vsp->vns_nflush;
+ bsize = vsp->vns_bsize;
+ mutex_exit(&vsp->vns_lock);
+
+ nmps = 0;
+ mptot = 0;
+ blocked = B_FALSE;
+ vqp = &vsp->vns_dq_write;
+ while (nmps < nflush && mptot <= bsize) {
+ mutex_enter(&vqp->vdq_lock);
+ if (vnd_dq_pop(vqp, &mp) == 0) {
+ mutex_exit(&vqp->vdq_lock);
+ break;
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ nmps++;
+ mptot += msgsize(mp);
+ if (vnd_squeue_tx_one(vsp, mp) != NULL) {
+ blocked = B_TRUE;
+ break;
+ }
+ }
+
+ empty = vnd_dq_is_empty(&vsp->vns_dq_write);
+
+ /*
+ * If the queue is not empty, we're not blocked, and there isn't a drain
+ * scheduled, put it into the squeue with the drain block and
+ * GSQUEUE_FILL.
+ */
+ if (blocked == B_FALSE && empty == B_FALSE) {
+ mutex_enter(&vsp->vns_lock);
+ if (!(vsp->vns_flags & VNS_F_DRAIN_SCHEDULED)) {
+ mblk_t *mp = &vsp->vns_drainblk;
+ vsp->vns_flags |= VNS_F_DRAIN_SCHEDULED;
+ gsqueue_enter_one(vsp->vns_squeue,
+ mp, vnd_squeue_tx_drain, vsp,
+ GSQUEUE_FILL, VND_SQUEUE_TAG_TX_DRAIN);
+ }
+ mutex_exit(&vsp->vns_lock);
+ }
+
+ /*
+ * If we drained some amount of data, we need to signal the data queue.
+ */
+ if (nmps > 0) {
+ cv_broadcast(&vsp->vns_dq_write.vdq_ready);
+ pollwakeup(&vsp->vns_dev->vdd_ph, POLLOUT);
+ }
+}
+
+static void
+vnd_squeue_tx_append(void *arg, mblk_t *mp, gsqueue_t *gsp, void *dummy)
+{
+ vnd_str_t *vsp = arg;
+ vnd_data_queue_t *vqp = &vsp->vns_dq_write;
+ vnd_pnsd_t *nsp = vsp->vns_nsd;
+ size_t len = msgsize(mp);
+
+ /*
+ * Before we append this packet, we should run it through the firewall
+ * rules.
+ */
+ if (nsp->vpnd_hooked && vnd_hook(vsp, &mp, nsp->vpnd_neti_v4,
+ nsp->vpnd_event_out_v4, nsp->vpnd_token_out_v4, nsp->vpnd_neti_v6,
+ nsp->vpnd_event_out_v6, nsp->vpnd_token_out_v6, vnd_drop_hook_out,
+ vnd_drop_out) != 0) {
+ /*
+ * Because we earlier reserved space for this packet and it's
+ * not making the cut, we need to go through and unreserve that
+ * space. Also note that the message block will likely be freed
+ * by the time we return from vnd_hook so we cannot rely on it.
+ */
+ mutex_enter(&vqp->vdq_lock);
+ vnd_dq_unreserve(vqp, len);
+ mutex_exit(&vqp->vdq_lock);
+ return;
+ }
+
+ /*
+ * We earlier reserved space for this packet. So for now simply append
+ * it and call drain. We know that no other drain can be going on right
+ * now thanks to the squeue.
+ */
+ mutex_enter(&vqp->vdq_lock);
+ (void) vnd_dq_push(&vsp->vns_dq_write, mp, B_TRUE, vnd_drop_panic);
+ mutex_exit(&vqp->vdq_lock);
+ vnd_squeue_tx_drain(vsp, NULL, NULL, NULL);
+}
+
+/*
+ * We need to see if this is a valid name of sorts for us. That means a few
+ * things. First off, we can't assume that what we've been given has actually
+ * been null terminated. More importantly, that it's a valid name as far as
+ * ddi_create_minor_node is concerned (that means no '@', '/', or ' '). We
+ * further constrain ourselves to simply alphanumeric characters and a few
+ * additional ones, ':', '-', and '_'.
+ */
+static int
+vnd_validate_name(const char *buf, size_t buflen)
+{
+ int i, len;
+
+ /* First make sure a null terminator exists */
+ for (i = 0; i < buflen; i++)
+ if (buf[i] == '\0')
+ break;
+ len = i;
+ if (i == 0 || i == buflen)
+ return (0);
+
+ for (i = 0; i < len; i++)
+ if (!isalnum(buf[i]) && buf[i] != ':' && buf[i] != '-' &&
+ buf[i] != '_')
+ return (0);
+
+ return (1);
+}
+
+static int
+vnd_ioctl_attach(vnd_dev_t *vdp, uintptr_t arg, cred_t *credp, int cpflag)
+{
+ vnd_ioc_attach_t via;
+ vnd_strioc_associate_t vss;
+ vnd_pnsd_t *nsp;
+ zone_t *zonep;
+ zoneid_t zid;
+ char buf[2*VND_NAMELEN];
+ int ret, rp;
+
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ if (secpolicy_net_rawaccess(credp) != 0)
+ return (EPERM);
+
+ if (ddi_copyin((void *)arg, &via, sizeof (via), cpflag) != 0)
+ return (EFAULT);
+ via.via_errno = VND_E_SUCCESS;
+
+ if (vnd_validate_name(via.via_name, VND_NAMELEN) == 0) {
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ /*
+ * Only the global zone can request to create a device in a different
+ * zone.
+ */
+ zid = crgetzoneid(credp);
+ if (zid != GLOBAL_ZONEID && via.via_zoneid != -1 &&
+ zid != via.via_zoneid) {
+ via.via_errno = VND_E_PERM;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ if (via.via_zoneid == -1)
+ via.via_zoneid = zid;
+
+ /*
+ * Establish the name we'll use now. We want to be extra paranoid about
+ * the device we're opening so check that now.
+ */
+ if (zid == GLOBAL_ZONEID && via.via_zoneid != zid) {
+ zonep = zone_find_by_id(via.via_zoneid);
+ if (zonep == NULL) {
+ via.via_errno = VND_E_NOZONE;
+ ret = EIO;
+ goto errcopyout;
+ }
+ if (snprintf(NULL, 0, "/dev/net/zone/%s/%s", zonep->zone_name,
+ via.via_name) >= sizeof (buf)) {
+ zone_rele(zonep);
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+ (void) snprintf(buf, sizeof (buf), "/dev/net/zone/%s/%s",
+ zonep->zone_name, via.via_name);
+ zone_rele(zonep);
+ zonep = NULL;
+ } else {
+ if (snprintf(NULL, 0, "/dev/net/%s", via.via_name) >=
+ sizeof (buf)) {
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+ (void) snprintf(buf, sizeof (buf), "/dev/net/%s", via.via_name);
+ }
+
+ /*
+ * If our zone is dying then the netstack will have been removed from
+ * this list.
+ */
+ nsp = vnd_nsd_lookup_by_zid(via.via_zoneid);
+ if (nsp == NULL) {
+ via.via_errno = VND_E_NOZONE;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ /*
+ * Note we set the attached handle even though we haven't actually
+ * finished the process of attaching the ldi handle.
+ */
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & (VND_D_ATTACHED | VND_D_ATTACH_INFLIGHT)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_nsd_rele(nsp);
+ via.via_errno = VND_E_ATTACHED;
+ ret = EIO;
+ goto errcopyout;
+ }
+ vdp->vdd_flags |= VND_D_ATTACH_INFLIGHT;
+ ASSERT(vdp->vdd_cr == NULL);
+ crhold(credp);
+ vdp->vdd_cr = credp;
+ ASSERT(vdp->vdd_nsd == NULL);
+ vdp->vdd_nsd = nsp;
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * Place an additional hold on the vnd_pnsd_t as we go through and do
+ * all of the rest of our work. This will be the hold that we keep for
+ * as long as this thing is attached.
+ */
+ vnd_nsd_ref(nsp);
+
+ ret = ldi_open_by_name(buf, FREAD | FWRITE, vdp->vdd_cr,
+ &vdp->vdd_ldih, vdp->vdd_ldiid);
+ if (ret != 0) {
+ if (ret == ENODEV)
+ via.via_errno = VND_E_NODATALINK;
+ goto err;
+ }
+
+ /*
+ * Unfortunately the I_PUSH interface doesn't allow us a way to detect
+ * whether or not we're coming in from a layered device. We really want
+ * to make sure that a normal user can't push on our streams module.
+ * Currently the only idea I have for this is to make sure that the
+ * credp is kcred which is really terrible.
+ */
+ ret = ldi_ioctl(vdp->vdd_ldih, I_PUSH, (intptr_t)"vnd", FKIOCTL,
+ kcred, &rp);
+ if (ret != 0) {
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ via.via_errno = VND_E_STRINIT;
+ ret = EIO;
+ goto err;
+ }
+
+ vss.vsa_minor = vdp->vdd_minor;
+ vss.vsa_nsid = nsp->vpnd_nsid;
+
+ ret = ldi_ioctl(vdp->vdd_ldih, VND_STRIOC_ASSOCIATE, (intptr_t)&vss,
+ FKIOCTL, kcred, &rp);
+ if (ret != 0 || vss.vsa_errno != VND_E_SUCCESS) {
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ if (ret == 0) {
+ via.via_errno = vss.vsa_errno;
+ ret = EIO;
+ }
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_nsd->vpnd_lock);
+
+ /*
+ * There's a chance that our netstack was condemned while we've had a
+ * hold on it. As such we need to check and if so, error out.
+ */
+ if (vdp->vdd_nsd->vpnd_flags & VND_NS_CONDEMNED) {
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ ret = EIO;
+ via.via_errno = VND_E_NOZONE;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_str != NULL);
+ vdp->vdd_flags &= ~VND_D_ATTACH_INFLIGHT;
+ vdp->vdd_flags |= VND_D_ATTACHED;
+ (void) strlcpy(vdp->vdd_datalink, via.via_name,
+ sizeof (vdp->vdd_datalink));
+ list_insert_tail(&vdp->vdd_nsd->vpnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ vnd_nsd_rele(nsp);
+
+ return (0);
+
+err:
+ mutex_enter(&vdp->vdd_lock);
+ vdp->vdd_flags &= ~VND_D_ATTACH_INFLIGHT;
+ crfree(vdp->vdd_cr);
+ vdp->vdd_cr = NULL;
+ vdp->vdd_nsd = NULL;
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * We have two holds to drop here. One for our original reference and
+ * one for the hold this operation would have represented.
+ */
+ vnd_nsd_rele(nsp);
+ vnd_nsd_rele(nsp);
+errcopyout:
+ if (ddi_copyout(&via, (void *)arg, sizeof (via), cpflag) != 0)
+ ret = EFAULT;
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_link(vnd_dev_t *vdp, intptr_t arg, cred_t *credp, int cpflag)
+{
+ int ret = 0;
+ vnd_ioc_link_t vil;
+ char mname[2*VND_NAMELEN];
+ char **c;
+ vnd_dev_t *v;
+ zoneid_t zid;
+
+ /* Not anyone can link something */
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ if (ddi_copyin((void *)arg, &vil, sizeof (vil), cpflag) != 0)
+ return (EFAULT);
+
+ if (vnd_validate_name(vil.vil_name, VND_NAMELEN) == 0) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+
+ c = vnd_reserved_names;
+ while (*c != NULL) {
+ if (strcmp(vil.vil_name, *c) == 0) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+ c++;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOTATTACHED;
+ goto errcopyout;
+ }
+
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOZONE;
+ goto errcopyout;
+ }
+
+ if (vdp->vdd_flags & (VND_D_LINK_INFLIGHT | VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_LINKED;
+ goto errcopyout;
+ }
+ vdp->vdd_flags |= VND_D_LINK_INFLIGHT;
+ zid = vdp->vdd_nsd->vpnd_zid;
+ mutex_exit(&vdp->vdd_lock);
+
+ if (snprintf(NULL, 0, "z%d:%s", zid, vil.vil_name) >=
+ sizeof (mname)) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+
+ mutex_enter(&vnd_dev_lock);
+ for (v = list_head(&vnd_dev_list); v != NULL;
+ v = list_next(&vnd_dev_list, v)) {
+ if (!(v->vdd_flags & VND_D_LINKED))
+ continue;
+
+ if (v->vdd_nsd->vpnd_zid == zid &&
+ strcmp(v->vdd_lname, vil.vil_name) == 0) {
+ mutex_exit(&vnd_dev_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_LINKEXISTS;
+ goto error;
+ }
+ }
+
+ /*
+ * We set the name and mark ourselves attached while holding the list
+ * lock to ensure that no other user can mistakingly find our name.
+ */
+ (void) snprintf(mname, sizeof (mname), "z%d:%s", zid,
+ vil.vil_name);
+ mutex_enter(&vdp->vdd_lock);
+
+ /*
+ * Because we dropped our lock, we need to double check whether or not
+ * the zone was marked as dying while we were here. If it hasn't, then
+ * it's safe for us to link it in.
+ */
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOZONE;
+ goto error;
+ }
+
+ (void) strlcpy(vdp->vdd_lname, vil.vil_name, sizeof (vdp->vdd_lname));
+ if (ddi_create_minor_node(vnd_dip, mname, S_IFCHR, vdp->vdd_minor,
+ DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ ret = EIO;
+ vil.vil_errno = VND_E_MINORNODE;
+ } else {
+ vdp->vdd_flags &= ~VND_D_LINK_INFLIGHT;
+ vdp->vdd_flags |= VND_D_LINKED;
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ ret = 0;
+ }
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+
+ if (ret == 0) {
+ /*
+ * Add a reference to represent that this device is linked into
+ * the file system name space to ensure that it doesn't
+ * disappear.
+ */
+ vnd_dev_ref(vdp);
+ return (0);
+ }
+
+error:
+ mutex_enter(&vdp->vdd_lock);
+ vdp->vdd_flags &= ~VND_D_LINK_INFLIGHT;
+ vdp->vdd_lname[0] = '\0';
+ mutex_exit(&vdp->vdd_lock);
+
+errcopyout:
+ if (ddi_copyout(&vil, (void *)arg, sizeof (vil), cpflag) != 0)
+ ret = EFAULT;
+ return (ret);
+}
+
+/*
+ * Common unlink function. This is used both from the ioctl path and from the
+ * netstack shutdown path. The caller is required to hold the mutex on the
+ * vnd_dev_t, but they basically will have it relinquished for them. The only
+ * thing the caller is allowed to do afterward is to potentially rele the
+ * vnd_dev_t if they have their own hold. Note that only the ioctl path has its
+ * own hold.
+ */
+static void
+vnd_dev_unlink(vnd_dev_t *vdp)
+{
+ char mname[2*VND_NAMELEN];
+
+ ASSERT(MUTEX_HELD(&vdp->vdd_lock));
+
+ (void) snprintf(mname, sizeof (mname), "z%d:%s",
+ vdp->vdd_nsd->vpnd_zid, vdp->vdd_lname);
+ ddi_remove_minor_node(vnd_dip, mname);
+ vdp->vdd_lname[0] = '\0';
+ vdp->vdd_flags &= ~VND_D_LINKED;
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * This rele corresponds to the reference that we took in
+ * vnd_ioctl_link.
+ */
+ vnd_dev_rele(vdp);
+}
+
+static int
+vnd_ioctl_unlink(vnd_dev_t *vdp, intptr_t arg, cred_t *credp, int cpflag)
+{
+ int ret;
+ zoneid_t zid;
+ vnd_ioc_unlink_t viu;
+
+ /* Not anyone can unlink something */
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ zid = crgetzoneid(credp);
+
+ if (ddi_copyin((void *)arg, &viu, sizeof (viu), cpflag) != 0)
+ return (EFAULT);
+
+ viu.viu_errno = VND_E_SUCCESS;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ viu.viu_errno = VND_E_NOTLINKED;
+ goto err;
+ }
+ VERIFY(vdp->vdd_flags & VND_D_ATTACHED);
+
+ if (zid != GLOBAL_ZONEID && zid != vdp->vdd_nsd->vpnd_zid) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ viu.viu_errno = VND_E_PERM;
+ goto err;
+ }
+
+ /* vnd_dev_unlink releases the vdp mutex for us */
+ vnd_dev_unlink(vdp);
+ ret = 0;
+err:
+ if (ddi_copyout(&viu, (void *)arg, sizeof (viu), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_setrxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ if (ddi_copyin((void *)arg, &vib, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ mutex_enter(&vnd_dev_lock);
+ if (vib.vib_size > vnd_vdq_hard_max) {
+ mutex_exit(&vnd_dev_lock);
+ vib.vib_errno = VND_E_BUFTOOBIG;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (vib.vib_size < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_BUFTOOSMALL;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_enter(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ vdp->vdd_str->vns_dq_read.vdq_max = vib.vib_size;
+ mutex_exit(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_getrxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ vib.vib_size = vdp->vdd_str->vns_dq_read.vdq_max;
+ mutex_exit(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_getmaxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vnd_dev_lock);
+ vib.vib_size = vnd_vdq_hard_max;
+ mutex_exit(&vnd_dev_lock);
+
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+vnd_ioctl_gettxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ vib.vib_size = vdp->vdd_str->vns_dq_write.vdq_max;
+ mutex_exit(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_settxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ if (ddi_copyin((void *)arg, &vib, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ mutex_enter(&vnd_dev_lock);
+ if (vib.vib_size > vnd_vdq_hard_max) {
+ mutex_exit(&vnd_dev_lock);
+ vib.vib_errno = VND_E_BUFTOOBIG;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (vib.vib_size < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_BUFTOOSMALL;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vdp->vdd_str->vns_lock);
+
+ mutex_enter(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ vdp->vdd_str->vns_dq_write.vdq_max = vib.vib_size;
+ mutex_exit(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_gettu(vnd_dev_t *vdp, intptr_t arg, int mode, boolean_t min)
+{
+ vnd_ioc_buf_t vib;
+
+ vib.vib_errno = 0;
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & VND_D_ATTACHED) {
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (min == B_TRUE)
+ vib.vib_size = vdp->vdd_str->vns_minwrite;
+ else
+ vib.vib_size = vdp->vdd_str->vns_maxwrite;
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ } else {
+ vib.vib_errno = VND_E_NOTATTACHED;
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+vnd_frameio_read(vnd_dev_t *vdp, intptr_t addr, int mode)
+{
+ int ret, nonblock, nwrite;
+ frameio_t *fio;
+ vnd_data_queue_t *vqp;
+ mblk_t *mp;
+
+ fio = frameio_alloc(KM_NOSLEEP | KM_NORMALPRI);
+ if (fio == NULL)
+ return (EAGAIN);
+
+ ret = frameio_hdr_copyin(fio, FRAMEIO_NVECS_MAX, (const void *)addr,
+ mode);
+ if (ret != 0) {
+ frameio_free(fio);
+ return (ret);
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ frameio_free(fio);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ nonblock = mode & (FNONBLOCK | FNDELAY);
+
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+
+ /* Check empty case */
+ if (vqp->vdq_cur == 0) {
+ if (nonblock != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EWOULDBLOCK);
+ }
+ while (vqp->vdq_cur == 0) {
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EINTR);
+ }
+ }
+ }
+
+ ret = frameio_mblk_chain_write(fio, MAP_BLK_FRAME, vqp->vdq_head,
+ &nwrite, mode & FKIOCTL);
+ if (ret != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (ret);
+ }
+
+ ret = frameio_hdr_copyout(fio, nwrite, (void *)addr, mode);
+ if (ret != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (ret);
+ }
+
+ while (nwrite > 0) {
+ (void) vnd_dq_pop(vqp, &mp);
+ freemsg(mp);
+ nwrite--;
+ }
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+
+ return (0);
+}
+
+static int
+vnd_frameio_write(vnd_dev_t *vdp, intptr_t addr, int mode)
+{
+ frameio_t *fio;
+ int ret, nonblock, nframes, i, nread;
+ size_t maxwrite, minwrite, total, flen;
+ mblk_t *mp_chain, *mp, *nmp;
+ vnd_data_queue_t *vqp;
+
+ fio = frameio_alloc(KM_NOSLEEP | KM_NORMALPRI);
+ if (fio == NULL)
+ return (EAGAIN);
+
+ ret = frameio_hdr_copyin(fio, FRAMEIO_NVECS_MAX, (void *)addr, mode);
+ if (ret != 0) {
+ frameio_free(fio);
+ return (ret);
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ frameio_free(fio);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ nonblock = mode & (FNONBLOCK | FNDELAY);
+
+ /*
+ * Make sure no single frame is larger than we can accept.
+ */
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ minwrite = vdp->vdd_str->vns_minwrite;
+ maxwrite = vdp->vdd_str->vns_maxwrite;
+ mutex_exit(&vdp->vdd_str->vns_lock);
+
+ nframes = fio->fio_nvpf / fio->fio_nvecs;
+ total = 0;
+ for (i = 0; i < nframes; i++) {
+ flen = frameio_frame_length(fio,
+ &fio->fio_vecs[i*fio->fio_nvpf]);
+ if (flen < minwrite || flen > maxwrite) {
+ frameio_free(fio);
+ return (ERANGE);
+ }
+ total += flen;
+ }
+
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ while (vnd_dq_reserve(vqp, total) == 0) {
+ if (nonblock != 0) {
+ frameio_free(fio);
+ mutex_exit(&vqp->vdq_lock);
+ return (EAGAIN);
+ }
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ /*
+ * We've reserved our space, let's copyin and go from here.
+ */
+ ret = frameio_mblk_chain_read(fio, &mp_chain, &nread, mode & FKIOCTL);
+ if (ret != 0) {
+ frameio_free(fio);
+ vnd_dq_unreserve(vqp, total);
+ cv_broadcast(&vqp->vdq_ready);
+ pollwakeup(&vdp->vdd_ph, POLLOUT);
+ return (ret);
+ }
+
+ for (mp = mp_chain; mp != NULL; mp = nmp) {
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+ gsqueue_enter_one(vdp->vdd_str->vns_squeue, mp,
+ vnd_squeue_tx_append, vdp->vdd_str, GSQUEUE_PROCESS,
+ VND_SQUEUE_TAG_VND_WRITE);
+ }
+
+ /*
+ * Update the frameio structure to indicate that we wrote those frames.
+ */
+ frameio_mark_consumed(fio, nread);
+ ret = frameio_hdr_copyout(fio, nread, (void *)addr, mode);
+ frameio_free(fio);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_list_copy_info(vnd_dev_t *vdp, vnd_ioc_info_t *arg, int mode)
+{
+ const char *link;
+ uint32_t vers = 1;
+ ASSERT(MUTEX_HELD(&vdp->vdd_lock));
+
+ /*
+ * Copy all of the members out to userland.
+ */
+ if (ddi_copyout(&vers, &arg->vii_version, sizeof (uint32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (vdp->vdd_flags & VND_D_LINKED)
+ link = vdp->vdd_lname;
+ else
+ link = "<anonymous>";
+ if (ddi_copyout(link, arg->vii_name, sizeof (arg->vii_name),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (ddi_copyout(vdp->vdd_datalink, arg->vii_datalink,
+ sizeof (arg->vii_datalink), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (ddi_copyout(&vdp->vdd_nsd->vpnd_zid, &arg->vii_zone,
+ sizeof (zoneid_t), mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+}
+
+static int
+vnd_ioctl_list(intptr_t arg, cred_t *credp, int mode)
+{
+ vnd_ioc_list_t vl;
+ vnd_ioc_list32_t vl32;
+ zoneid_t zid;
+ vnd_dev_t *vdp;
+ vnd_ioc_info_t *vip;
+ int found, cancopy, ret;
+
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ if (ddi_copyin((void *)arg, &vl32, sizeof (vnd_ioc_list32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ vl.vl_nents = vl32.vl_nents;
+ vl.vl_actents = vl32.vl_actents;
+ vl.vl_ents = (void *)(uintptr_t)vl32.vl_ents;
+ } else {
+ if (ddi_copyin((void *)arg, &vl, sizeof (vnd_ioc_list_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ }
+
+ cancopy = vl.vl_nents;
+ vip = vl.vl_ents;
+ found = 0;
+ zid = crgetzoneid(credp);
+ mutex_enter(&vnd_dev_lock);
+ for (vdp = list_head(&vnd_dev_list); vdp != NULL;
+ vdp = list_next(&vnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & VND_D_ATTACHED &&
+ !(vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING)) &&
+ (zid == GLOBAL_ZONEID || zid == vdp->vdd_nsd->vpnd_zid)) {
+ found++;
+ if (cancopy > 0) {
+ ret = vnd_ioctl_list_copy_info(vdp, vip, mode);
+ if (ret != 0) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ return (ret);
+ }
+ cancopy--;
+ vip++;
+ }
+ }
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ if (ddi_copyout(&found, &((vnd_ioc_list_t *)arg)->vl_actents,
+ sizeof (uint_t), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+
+static int
+vnd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ int ret;
+ minor_t m;
+ vnd_dev_t *vdp;
+
+ m = getminor(dev);
+ ASSERT(m != 0);
+
+ /*
+ * Make sure no one has come in on an ioctl from the strioc case.
+ */
+ if ((cmd & VND_STRIOC) == VND_STRIOC)
+ return (ENOTTY);
+
+ /*
+ * Like close, seems like if this minor isn't found, it's a programmer
+ * error somehow.
+ */
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENXIO);
+
+ switch (cmd) {
+ case VND_IOC_ATTACH:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_attach(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_LINK:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_link(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_UNLINK:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_unlink(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_GETRXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_getrxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_SETRXBUF:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_setrxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETTXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_SETTXBUF:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_settxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETMAXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ if (crgetzoneid(credp) != GLOBAL_ZONEID) {
+ ret = EPERM;
+ break;
+ }
+ ret = vnd_ioctl_getmaxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETMINTU:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettu(vdp, arg, mode, B_TRUE);
+ break;
+ case VND_IOC_GETMAXTU:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettu(vdp, arg, mode, B_FALSE);
+ break;
+ case VND_IOC_FRAMEIO_READ:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_frameio_read(vdp, arg, mode);
+ break;
+ case VND_IOC_FRAMEIO_WRITE:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_frameio_write(vdp, arg, mode);
+ break;
+ case VND_IOC_LIST:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_list(arg, credp, mode);
+ break;
+ default:
+ ret = ENOTTY;
+ break;
+ }
+
+ vnd_dev_rele(vdp);
+ return (ret);
+}
+
+static int
+vnd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ vnd_dev_t *vdp;
+ minor_t m;
+ zoneid_t zid;
+
+ if (flag & (FEXCL | FNDELAY))
+ return (ENOTSUP);
+
+ if (otyp & OTYP_BLK)
+ return (ENOTSUP);
+
+ zid = crgetzoneid(credp);
+ m = getminor(*devp);
+
+ /*
+ * If we have an open of a non-zero instance then we need to look that
+ * up in our list of entries.
+ */
+ if (m != 0) {
+
+ /*
+ * We don't check for rawaccess globally as a user could be
+ * doing a list ioctl on the control node which doesn't require
+ * this privilege.
+ */
+ if (secpolicy_net_rawaccess(credp) != 0)
+ return (EPERM);
+
+
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENOENT);
+
+ /*
+ * We need to check to make sure that the user is allowed to
+ * open this node. At this point it should be an attached handle
+ * as that's all we're allowed to access.
+ */
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if (zid != GLOBAL_ZONEID && zid != vdp->vdd_nsd->vpnd_zid) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if ((flag & FEXCL) && (vdp->vdd_flags & VND_D_OPENED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (EBUSY);
+ }
+
+ if (!(vdp->vdd_flags & VND_D_OPENED)) {
+ vdp->vdd_flags |= VND_D_OPENED;
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+
+ return (0);
+ }
+
+ if (flag & FEXCL)
+ return (ENOTSUP);
+
+ /*
+ * We need to clone ourselves and set up new a state.
+ */
+ vdp = kmem_cache_alloc(vnd_dev_cache, KM_SLEEP);
+ bzero(vdp, sizeof (vnd_dev_t));
+
+ if (ldi_ident_from_dev(*devp, &vdp->vdd_ldiid) != 0) {
+ kmem_cache_free(vnd_dev_cache, vdp);
+ return (EINVAL);
+ }
+
+ vdp->vdd_minor = id_alloc(vnd_minors);
+ mutex_init(&vdp->vdd_lock, NULL, MUTEX_DRIVER, NULL);
+ list_link_init(&vdp->vdd_link);
+ vdp->vdd_ref = 1;
+ *devp = makedevice(getmajor(*devp), vdp->vdd_minor);
+ vdp->vdd_devid = *devp;
+ DTRACE_VND_REFINC(vdp);
+ vdp->vdd_flags |= VND_D_OPENED;
+
+ mutex_enter(&vnd_dev_lock);
+ list_insert_head(&vnd_dev_list, vdp);
+ mutex_exit(&vnd_dev_lock);
+
+ return (0);
+}
+
+static int
+vnd_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ minor_t m;
+ vnd_dev_t *vdp;
+
+ m = getminor(dev);
+ if (m == 0)
+ return (ENXIO);
+
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_flags & VND_D_OPENED);
+ vdp->vdd_flags &= ~VND_D_OPENED;
+ mutex_exit(&vdp->vdd_lock);
+
+ /* Remove the hold from the previous open. */
+ vnd_dev_rele(vdp);
+
+ /* And now from lookup */
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+static int
+vnd_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int nonblock, error = 0;
+ size_t mpsize;
+ vnd_dev_t *vdp;
+ vnd_data_queue_t *vqp;
+ mblk_t *mp = NULL;
+ offset_t u_loffset;
+
+ /*
+ * If we have more than one uio we refuse to do anything. That's for
+ * frameio.
+ */
+ if (uiop->uio_iovcnt > 1)
+ return (EINVAL);
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ nonblock = uiop->uio_fmode & (FNONBLOCK | FNDELAY);
+
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+
+ /* Check empty case */
+ if (vqp->vdq_cur == 0) {
+ if (nonblock != 0) {
+ error = EWOULDBLOCK;
+ goto err;
+ }
+ while (vqp->vdq_cur == 0) {
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ error = EINTR;
+ goto err;
+ }
+ }
+ }
+
+ /* Ensure our buffer is big enough */
+ mp = vqp->vdq_head;
+ ASSERT(mp != NULL);
+ mpsize = msgsize(mp);
+ if (mpsize > uiop->uio_resid) {
+ error = EOVERFLOW;
+ goto err;
+ }
+
+ u_loffset = uiop->uio_loffset;
+ while (mp != NULL) {
+ if (uiomove(mp->b_rptr, MBLKL(mp), UIO_READ, uiop) != 0) {
+ error = EFAULT;
+ uiop->uio_loffset = u_loffset;
+ mp = NULL;
+ goto err;
+ }
+ mpsize -= MBLKL(mp);
+ mp = mp->b_cont;
+ }
+ ASSERT(mpsize == 0);
+ (void) vnd_dq_pop(vqp, &mp);
+ freemsg(mp);
+err:
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+
+ return (error);
+}
+
+static int
+vnd_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int nonblock, error;
+ vnd_dev_t *vdp;
+ mblk_t *mp;
+ ssize_t iosize, origsize;
+ vnd_data_queue_t *vqp;
+
+ if (uiop->uio_iovcnt > 1)
+ return (EINVAL);
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ nonblock = uiop->uio_fmode & (FNONBLOCK | FNDELAY);
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (uiop->uio_resid > vdp->vdd_str->vns_maxwrite ||
+ uiop->uio_resid < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ vnd_dev_rele(vdp);
+ return (ERANGE);
+ }
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ VERIFY(vdp->vdd_str != NULL);
+
+ /*
+ * Reserve space in the data queue if we can. If we can't, block or
+ * return EAGAIN. If we can, go and squeue_enter.
+ */
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ while (vnd_dq_reserve(vqp, uiop->uio_resid) == 0) {
+ if (nonblock != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+ return (EAGAIN);
+ }
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ /*
+ * Now that we've reserved the space, try to allocate kernel space for
+ * and copy in the block. To take care of all this we use the
+ * strmakedata subroutine for now.
+ */
+ origsize = iosize = uiop->uio_resid;
+ error = strmakedata(&iosize, uiop, vdp->vdd_str->vns_wq->q_stream, 0,
+ &mp);
+
+ /*
+ * strmakedata() will return an error or it may only consume a portion
+ * of the data.
+ */
+ if (error != 0 || uiop->uio_resid != 0) {
+ vnd_dq_unreserve(vqp, origsize);
+ cv_broadcast(&vqp->vdq_ready);
+ pollwakeup(&vdp->vdd_ph, POLLOUT);
+ vnd_dev_rele(vdp);
+ return (ENOSR);
+ }
+
+ gsqueue_enter_one(vdp->vdd_str->vns_squeue, mp,
+ vnd_squeue_tx_append, vdp->vdd_str, GSQUEUE_PROCESS,
+ VND_SQUEUE_TAG_VND_WRITE);
+
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+static int
+vnd_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ int ready = 0;
+ vnd_dev_t *vdp;
+ vnd_data_queue_t *vqp;
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ if ((events & POLLIN) || (events & POLLRDNORM)) {
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_head != NULL)
+ ready |= events & (POLLIN | POLLRDNORM);
+ mutex_exit(&vqp->vdq_lock);
+ }
+
+ if (events & POLLOUT) {
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_cur != vqp->vdq_max)
+ ready |= POLLOUT;
+ mutex_exit(&vqp->vdq_lock);
+ }
+
+ if (ready != 0) {
+ *reventsp = ready;
+ vnd_dev_rele(vdp);
+ return (0);
+ }
+
+ *reventsp = 0;
+ if (!anyyet)
+ *phpp = &vdp->vdd_ph;
+
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+static void *
+vnd_stack_init(netstackid_t stackid, netstack_t *ns)
+{
+ vnd_pnsd_t *nsp;
+
+ nsp = kmem_cache_alloc(vnd_pnsd_cache, KM_SLEEP);
+ bzero(nsp, sizeof (*nsp));
+ nsp->vpnd_nsid = stackid;
+ nsp->vpnd_zid = netstackid_to_zoneid(stackid);
+ nsp->vpnd_flags = 0;
+ mutex_init(&nsp->vpnd_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&nsp->vpnd_dev_list, sizeof (vnd_dev_t),
+ offsetof(vnd_dev_t, vdd_nslink));
+ if (vnd_netinfo_init(nsp) == 0)
+ nsp->vpnd_hooked = B_TRUE;
+
+ mutex_enter(&vnd_dev_lock);
+ list_insert_tail(&vnd_nsd_list, nsp);
+ mutex_exit(&vnd_dev_lock);
+
+ return (nsp);
+}
+
+static void
+vnd_stack_shutdown(netstackid_t stackid, void *arg)
+{
+ vnd_pnsd_t *nsp = arg;
+ vnd_dev_t *vdp;
+
+ ASSERT(nsp != NULL);
+ /*
+ * After shut down no one should be able to find their way to this
+ * netstack again.
+ */
+ mutex_enter(&vnd_dev_lock);
+ list_remove(&vnd_nsd_list, nsp);
+ mutex_exit(&vnd_dev_lock);
+
+ /*
+ * Make sure hooks know that they're going away.
+ */
+ if (nsp->vpnd_hooked == B_TRUE)
+ vnd_netinfo_shutdown(nsp);
+
+ /*
+ * Now we need to go through and notify each zone that they are in
+ * teardown phase. See the big theory statement section on vnd, zones,
+ * netstacks, and sdev for more information about this.
+ */
+ mutex_enter(&nsp->vpnd_lock);
+ nsp->vpnd_flags |= VND_NS_CONDEMNED;
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_CONDEMNED))
+ vdp->vdd_flags |= VND_D_ZONE_DYING;
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&nsp->vpnd_lock);
+
+ /*
+ * Next we remove all the links as we know nothing new can be added to
+ * the list and that none of the extent devices can obtain additional
+ * links.
+ */
+restart:
+ mutex_enter(&nsp->vpnd_lock);
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if ((vdp->vdd_flags & VND_D_CONDEMNED) ||
+ !(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ continue;
+ }
+
+ /*
+ * We drop our lock here and restart afterwards. Note that as
+ * part of unlinking we end up doing a rele of the vnd_dev_t. If
+ * this is the final hold on the vnd_dev_t then it might try and
+ * remove itself. Our locking rules requires not to be holding
+ * any locks when we call any of the rele functions.
+ *
+ * Note that the unlink function requires holders to call into
+ * it with the vnd_dev_t->vdd_lock held and will take care of it
+ * for us. Because we don't have a hold on it, we're done at
+ * this point.
+ */
+ mutex_exit(&nsp->vpnd_lock);
+ /* Forcibly unlink */
+ vnd_dev_unlink(vdp);
+ goto restart;
+ }
+ mutex_exit(&nsp->vpnd_lock);
+}
+
+static void
+vnd_stack_destroy(netstackid_t stackid, void *arg)
+{
+ vnd_pnsd_t *nsp = arg;
+
+ ASSERT(nsp != NULL);
+
+ /*
+ * Now that we've unlinked everything we just have to hang out for
+ * it to finish exiting. Now that it's no longer the kernel itself
+ * that's doing this we just need to wait for our reference count to
+ * equal zero and then we're free. If the global zone is holding open a
+ * reference to a vnd device for another zone, that's bad, but there's
+ * nothing much we can do. See the section on 'vnd, zones, netstacks' in
+ * the big theory statement for more information.
+ */
+ mutex_enter(&nsp->vpnd_lock);
+ while (nsp->vpnd_ref != 0)
+ cv_wait(&nsp->vpnd_ref_change, &nsp->vpnd_lock);
+ mutex_exit(&nsp->vpnd_lock);
+
+ /*
+ * During shutdown we removed ourselves from the list and now we have no
+ * more references so we can safely say that there is nothing left and
+ * destroy everything that we had sitting around.
+ */
+ if (nsp->vpnd_hooked == B_TRUE)
+ vnd_netinfo_fini(nsp);
+
+ mutex_destroy(&nsp->vpnd_lock);
+ list_destroy(&nsp->vpnd_dev_list);
+ kmem_cache_free(vnd_pnsd_cache, nsp);
+}
+
+/*
+ * Convert a node with a name of the form /dev/vnd/zone/%zonename and
+ * /dev/vnd/zone/%zonename/%linkname to the corresponding vnd netstack.
+ */
+static vnd_pnsd_t *
+vnd_sdev_ctx_to_ns(sdev_ctx_t ctx)
+{
+ enum vtype vt;
+ const char *path = sdev_ctx_path(ctx);
+ char *zstart, *dup;
+ size_t duplen;
+ vnd_pnsd_t *nsp;
+
+ vt = sdev_ctx_vtype(ctx);
+ ASSERT(strncmp(path, VND_SDEV_ZROOT, strlen(VND_SDEV_ZROOT)) == 0);
+
+ if (vt == VDIR) {
+ zstart = strrchr(path, '/');
+ ASSERT(zstart != NULL);
+ zstart++;
+ return (vnd_nsd_lookup_by_zonename(zstart));
+ }
+
+ ASSERT(vt == VCHR);
+
+ dup = strdup(path);
+ duplen = strlen(dup) + 1;
+ zstart = strrchr(dup, '/');
+ *zstart = '\0';
+ zstart--;
+ zstart = strrchr(dup, '/');
+ zstart++;
+ nsp = vnd_nsd_lookup_by_zonename(zstart);
+ kmem_free(dup, duplen);
+
+ return (nsp);
+}
+
+static sdev_plugin_validate_t
+vnd_sdev_validate_dir(sdev_ctx_t ctx)
+{
+ vnd_pnsd_t *nsp;
+
+ if (strcmp(sdev_ctx_path(ctx), VND_SDEV_ROOT) == 0)
+ return (SDEV_VTOR_VALID);
+
+ if (strcmp(sdev_ctx_path(ctx), VND_SDEV_ZROOT) == 0) {
+ ASSERT(getzoneid() == GLOBAL_ZONEID);
+ ASSERT(sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL);
+ return (SDEV_VTOR_VALID);
+ }
+
+ nsp = vnd_sdev_ctx_to_ns(ctx);
+ if (nsp == NULL)
+ return (SDEV_VTOR_INVALID);
+ vnd_nsd_rele(nsp);
+
+ return (SDEV_VTOR_VALID);
+}
+
+static sdev_plugin_validate_t
+vnd_sdev_validate(sdev_ctx_t ctx)
+{
+ enum vtype vt;
+ dev_t dev;
+ vnd_dev_t *vdp;
+
+ vt = sdev_ctx_vtype(ctx);
+ if (vt == VDIR)
+ return (vnd_sdev_validate_dir(ctx));
+ ASSERT(vt == VCHR);
+
+ if (strcmp("ctl", sdev_ctx_name(ctx)) == 0)
+ return (SDEV_VTOR_VALID);
+
+ dev = (uintptr_t)sdev_ctx_vtype_data(ctx);
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (SDEV_VTOR_STALE);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED) ||
+ (vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING))) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_STALE);
+ }
+
+ if (strcmp(sdev_ctx_name(ctx), vdp->vdd_lname) != 0) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_STALE);
+ }
+
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_VALID);
+}
+
+/*
+ * This function is a no-op. sdev never has holds on our devices as they can go
+ * away at any time and specfs has to deal with that fact.
+ */
+static void
+vnd_sdev_inactive(sdev_ctx_t ctx)
+{
+}
+
+static int
+vnd_sdev_fillzone(vnd_pnsd_t *nsp, sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_dev_t *vdp;
+
+ mutex_enter(&nsp->vpnd_lock);
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if ((vdp->vdd_flags & VND_D_LINKED) &&
+ !(vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING))) {
+ ret = sdev_plugin_mknod(ctx, vdp->vdd_lname, S_IFCHR,
+ vdp->vdd_devid);
+ if (ret != 0 && ret != EEXIST) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&nsp->vpnd_lock);
+ vnd_nsd_rele(nsp);
+ return (ret);
+ }
+ }
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&nsp->vpnd_lock);
+
+ return (0);
+}
+
+static int
+vnd_sdev_filldir_root(sdev_ctx_t ctx)
+{
+ zoneid_t zid;
+ vnd_pnsd_t *nsp;
+ int ret;
+
+ zid = getzoneid();
+ nsp = vnd_nsd_lookup(zoneid_to_netstackid(zid));
+ ASSERT(nsp != NULL);
+ ret = vnd_sdev_fillzone(nsp, ctx);
+ vnd_nsd_rele(nsp);
+ if (ret != 0)
+ return (ret);
+
+ /*
+ * Checking the zone id is not sufficient as the global zone could be
+ * reaching down into a non-global zone's mounted /dev.
+ */
+ if (zid == GLOBAL_ZONEID && (sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL)) {
+ ret = sdev_plugin_mkdir(ctx, "zone");
+ if (ret != 0 && ret != EEXIST)
+ return (ret);
+ }
+
+ /*
+ * Always add a reference to the control node. There's no need to
+ * reference it since it always exists and is always what we clone from.
+ */
+ ret = sdev_plugin_mknod(ctx, "ctl", S_IFCHR,
+ makedevice(ddi_driver_major(vnd_dip), 0));
+ if (ret != 0 && ret != EEXIST)
+ return (ret);
+
+ return (0);
+}
+
+static int
+vnd_sdev_filldir_zroot(sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_pnsd_t *nsp;
+ zone_t *zonep;
+
+ ASSERT(getzoneid() == GLOBAL_ZONEID);
+ ASSERT(sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL);
+
+ mutex_enter(&vnd_dev_lock);
+ for (nsp = list_head(&vnd_nsd_list); nsp != NULL;
+ nsp = list_next(&vnd_nsd_list, nsp)) {
+ mutex_enter(&nsp->vpnd_lock);
+ if (list_is_empty(&nsp->vpnd_dev_list)) {
+ mutex_exit(&nsp->vpnd_lock);
+ continue;
+ }
+ mutex_exit(&nsp->vpnd_lock);
+ zonep = zone_find_by_id(nsp->vpnd_zid);
+ /*
+ * This zone must be being torn down, so skip it.
+ */
+ if (zonep == NULL)
+ continue;
+ ret = sdev_plugin_mkdir(ctx, zonep->zone_name);
+ zone_rele(zonep);
+ if (ret != 0 && ret != EEXIST) {
+ mutex_exit(&vnd_dev_lock);
+ return (ret);
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (0);
+}
+
+static int
+vnd_sdev_filldir(sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_pnsd_t *nsp;
+
+ ASSERT(sdev_ctx_vtype(ctx) == VDIR);
+ if (strcmp(VND_SDEV_ROOT, sdev_ctx_path(ctx)) == 0)
+ return (vnd_sdev_filldir_root(ctx));
+
+ if (strcmp(VND_SDEV_ZROOT, sdev_ctx_path(ctx)) == 0)
+ return (vnd_sdev_filldir_zroot(ctx));
+
+ ASSERT(strncmp(VND_SDEV_ZROOT, sdev_ctx_path(ctx),
+ strlen(VND_SDEV_ZROOT)) == 0);
+ nsp = vnd_sdev_ctx_to_ns(ctx);
+ if (nsp == NULL)
+ return (0);
+
+ ret = vnd_sdev_fillzone(nsp, ctx);
+ vnd_nsd_rele(nsp);
+
+ return (ret);
+}
+
+static sdev_plugin_ops_t vnd_sdev_ops = {
+ SDEV_PLUGIN_VERSION,
+ SDEV_PLUGIN_SUBDIR,
+ vnd_sdev_validate,
+ vnd_sdev_filldir,
+ vnd_sdev_inactive
+};
+
+static int
+vnd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int errp = 0;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ /*
+ * Only allow one instance.
+ */
+ if (vnd_dip != NULL)
+ return (DDI_FAILURE);
+
+ vnd_dip = dip;
+ if (ddi_create_minor_node(vnd_dip, "vnd", S_IFCHR, 0, DDI_PSEUDO, 0) !=
+ DDI_SUCCESS) {
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ DDI_KERNEL_IOCTL, NULL, 0) != DDI_PROP_SUCCESS) {
+ ddi_remove_minor_node(vnd_dip, NULL);
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ vnd_sdev_hdl = sdev_plugin_register(VND_SDEV_NAME, &vnd_sdev_ops,
+ &errp);
+ if (vnd_sdev_hdl == NULL) {
+ ddi_remove_minor_node(vnd_dip, NULL);
+ ddi_prop_remove_all(vnd_dip);
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ vnd_sqset = gsqueue_set_create(GSQUEUE_DEFAULT_WAIT,
+ GSQUEUE_DEFAULT_PRIORITY);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+vnd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ mutex_enter(&vnd_dev_lock);
+ if (!list_is_empty(&vnd_dev_list)) {
+ mutex_exit(&vnd_dev_lock);
+ return (DDI_FAILURE);
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ return (DDI_FAILURE);
+}
+
+static int
+vnd_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ int error;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)vnd_dip;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ break;
+ }
+ return (error);
+}
+
+
+
+static void
+vnd_ddi_fini(void)
+{
+ netstack_unregister(NS_VND);
+ if (vnd_taskq != NULL)
+ taskq_destroy(vnd_taskq);
+ if (vnd_str_cache != NULL)
+ kmem_cache_destroy(vnd_str_cache);
+ if (vnd_dev_cache != NULL)
+ kmem_cache_destroy(vnd_dev_cache);
+ if (vnd_pnsd_cache != NULL)
+ kmem_cache_destroy(vnd_pnsd_cache);
+ if (vnd_minors != NULL)
+ id_space_destroy(vnd_minors);
+ if (vnd_list_init != 0) {
+ list_destroy(&vnd_nsd_list);
+ list_destroy(&vnd_dev_list);
+ mutex_destroy(&vnd_dev_lock);
+ vnd_list_init = 0;
+ }
+ frameio_fini();
+}
+
+static int
+vnd_ddi_init(void)
+{
+ if (frameio_init() != 0)
+ return (DDI_FAILURE);
+
+ vnd_str_cache = kmem_cache_create("vnd_str_cache", sizeof (vnd_str_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_str_cache == NULL) {
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+ vnd_dev_cache = kmem_cache_create("vnd_dev_cache", sizeof (vnd_dev_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_dev_cache == NULL) {
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+ vnd_pnsd_cache = kmem_cache_create("vnd_pnsd_cache",
+ sizeof (vnd_pnsd_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_pnsd_cache == NULL) {
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ vnd_taskq = taskq_create_instance("vnd", -1, 1, minclsyspri, 0, 0, 0);
+ if (vnd_taskq == NULL) {
+ kmem_cache_destroy(vnd_pnsd_cache);
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ vnd_minors = id_space_create("vnd_minors", 1, INT32_MAX);
+ if (vnd_minors == NULL) {
+ taskq_destroy(vnd_taskq);
+ kmem_cache_destroy(vnd_pnsd_cache);
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ mutex_init(&vnd_dev_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&vnd_dev_list, sizeof (vnd_dev_t),
+ offsetof(vnd_dev_t, vdd_link));
+ list_create(&vnd_nsd_list, sizeof (vnd_pnsd_t),
+ offsetof(vnd_pnsd_t, vpnd_link));
+ vnd_list_init = 1;
+
+ netstack_register(NS_VND, vnd_stack_init, vnd_stack_shutdown,
+ vnd_stack_destroy);
+
+ return (DDI_SUCCESS);
+}
+
+static struct module_info vnd_minfo = {
+ 0, /* module id */
+ "vnd", /* module name */
+ 1, /* smallest packet size */
+ INFPSZ, /* largest packet size (infinite) */
+ 1, /* high watermark */
+ 0 /* low watermark */
+};
+
+static struct qinit vnd_r_qinit = {
+ vnd_s_rput,
+ NULL,
+ vnd_s_open,
+ vnd_s_close,
+ NULL,
+ &vnd_minfo,
+ NULL
+};
+
+static struct qinit vnd_w_qinit = {
+ vnd_s_wput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &vnd_minfo,
+ NULL
+};
+
+static struct streamtab vnd_strtab = {
+ &vnd_r_qinit,
+ &vnd_w_qinit,
+ NULL,
+ NULL
+};
+
+
+static struct cb_ops vnd_cb_ops = {
+ vnd_open, /* open */
+ vnd_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ vnd_read, /* read */
+ vnd_write, /* write */
+ vnd_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ vnd_chpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops vnd_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ vnd_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ vnd_attach, /* attach */
+ vnd_detach, /* detach */
+ nodev, /* reset */
+ &vnd_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
+static struct modldrv vnd_modldrv = {
+ &mod_driverops,
+ "Virtual Networking Datapath Driver",
+ &vnd_dev_ops
+};
+
+static struct fmodsw vnd_fmodfsw = {
+ "vnd",
+ &vnd_strtab,
+ D_NEW | D_MP
+};
+
+static struct modlstrmod vnd_modlstrmod = {
+ &mod_strmodops,
+ "Virtual Networking Datapath Driver",
+ &vnd_fmodfsw
+};
+
+static struct modlinkage vnd_modlinkage = {
+ MODREV_1,
+ &vnd_modldrv,
+ &vnd_modlstrmod,
+ NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ /*
+ * We need to do all of our global initialization in init as opposed to
+ * attach and detach. The problem here is that because vnd can be used
+ * from a stream context while being detached, we can not rely on having
+ * run attach to create everything, alas. so it goes in _init, just like
+ * our friend ip.
+ */
+ if ((error = vnd_ddi_init()) != DDI_SUCCESS)
+ return (error);
+ error = mod_install((&vnd_modlinkage));
+ if (error != 0)
+ vnd_ddi_fini();
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&vnd_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&vnd_modlinkage);
+ if (error == 0)
+ vnd_ddi_fini();
+ return (error);
+}
diff --git a/usr/src/uts/common/io/vnd/vnd.conf b/usr/src/uts/common/io/vnd/vnd.conf
new file mode 100644
index 0000000000..65872e1ddf
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/vnd.conf
@@ -0,0 +1,16 @@
+#
+# 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) 2014, Joyent, Inc. All rights reserved.
+#
+
+name="vnd" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/vnic/vnic_dev.c b/usr/src/uts/common/io/vnic/vnic_dev.c
index 3cacbe395b..3cb7e7660a 100644
--- a/usr/src/uts/common/io/vnic/vnic_dev.c
+++ b/usr/src/uts/common/io/vnic/vnic_dev.c
@@ -53,6 +53,7 @@
#include <sys/vlan.h>
#include <sys/vnic.h>
#include <sys/vnic_impl.h>
+#include <sys/mac_impl.h>
#include <sys/mac_flow_impl.h>
#include <inet/ip_impl.h>
@@ -369,6 +370,7 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
bzero(vnic, sizeof (*vnic));
+ vnic->vn_ls = LINK_STATE_UNKNOWN;
vnic->vn_id = vnic_id;
vnic->vn_link_id = linkid;
vnic->vn_vrid = vrid;
@@ -579,11 +581,12 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
vnic->vn_enabled = B_TRUE;
if (is_anchor) {
- mac_link_update(vnic->vn_mh, LINK_STATE_UP);
+ vnic->vn_ls = LINK_STATE_UP;
} else {
- mac_link_update(vnic->vn_mh,
- mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
+ vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
+ MAC_STAT_LINK_STATE);
}
+ mac_link_update(vnic->vn_mh, vnic->vn_ls);
rw_exit(&vnic_lock);
@@ -1072,6 +1075,18 @@ vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
err = mac_maxsdu_update(vn->vn_mh, mtu);
break;
}
+ case MAC_PROP_VN_PROMISC_FILTERED: {
+ boolean_t filtered;
+
+ if (pr_valsize < sizeof (filtered)) {
+ err = EINVAL;
+ break;
+ }
+
+ bcopy(pr_val, &filtered, sizeof (filtered));
+ mac_set_promisc_filtered(vn->vn_mch, filtered);
+ break;
+ }
case MAC_PROP_SECONDARY_ADDRS: {
mac_secondary_addr_t msa;
@@ -1079,6 +1094,34 @@ vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
err = vnic_set_secondary_macs(vn, &msa);
break;
}
+ case MAC_PROP_PRIVATE: {
+ long val, i;
+ const char *v;
+
+ if (vn->vn_link_id != DATALINK_INVALID_LINKID ||
+ strcmp(pr_name, "_linkstate") != 0) {
+ err = ENOTSUP;
+ break;
+ }
+
+ for (v = pr_val, i = 0; i < pr_valsize; i++, v++) {
+ if (*v == '\0')
+ break;
+ }
+ if (i == pr_valsize) {
+ err = EINVAL;
+ break;
+ }
+
+ (void) ddi_strtol(pr_val, (char **)NULL, 0, &val);
+ if (val != LINK_STATE_UP && val != LINK_STATE_DOWN) {
+ err = EINVAL;
+ break;
+ }
+ vn->vn_ls = val;
+ mac_link_update(vn->vn_mh, vn->vn_ls);
+ break;
+ }
default:
err = ENOTSUP;
break;
@@ -1093,11 +1136,29 @@ vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
{
vnic_t *vn = arg;
int ret = 0;
+ boolean_t out;
switch (pr_num) {
+ case MAC_PROP_VN_PROMISC_FILTERED:
+ out = mac_get_promisc_filtered(vn->vn_mch);
+ ASSERT(pr_valsize >= sizeof (boolean_t));
+ bcopy(&out, pr_val, sizeof (boolean_t));
+ break;
case MAC_PROP_SECONDARY_ADDRS:
ret = vnic_get_secondary_macs(vn, pr_valsize, pr_val);
break;
+ case MAC_PROP_PRIVATE:
+ if (vn->vn_link_id != DATALINK_INVALID_LINKID) {
+ ret = EINVAL;
+ break;
+ }
+
+ if (strcmp(pr_name, "_linkstate") != 0) {
+ ret = EINVAL;
+ break;
+ }
+ (void) snprintf(pr_val, pr_valsize, "%d", vn->vn_ls);
+ break;
default:
ret = ENOTSUP;
break;
@@ -1107,7 +1168,8 @@ vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
}
/* ARGSUSED */
-static void vnic_m_propinfo(void *m_driver, const char *pr_name,
+static void
+vnic_m_propinfo(void *m_driver, const char *pr_name,
mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
{
vnic_t *vn = m_driver;
@@ -1150,6 +1212,18 @@ static void vnic_m_propinfo(void *m_driver, const char *pr_name,
mac_perim_exit(mph);
}
break;
+ case MAC_PROP_PRIVATE:
+ if (vn->vn_link_id != DATALINK_INVALID_LINKID)
+ break;
+
+ if (strcmp(pr_name, "_linkstate") == 0) {
+ char buf[16];
+
+ mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
+ (void) snprintf(buf, sizeof (buf), "%d", vn->vn_ls);
+ mac_prop_info_set_default_str(prh, buf);
+ }
+ break;
}
}
@@ -1222,8 +1296,9 @@ vnic_notify_cb(void *arg, mac_notify_type_t type)
break;
case MAC_NOTE_LINK:
- mac_link_update(vnic->vn_mh,
- mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
+ vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
+ MAC_STAT_LINK_STATE);
+ mac_link_update(vnic->vn_mh, vnic->vn_ls);
break;
default:
diff --git a/usr/src/uts/common/io/vscan/vscan_svc.c b/usr/src/uts/common/io/vscan/vscan_svc.c
index a9817f571f..92eb0901c2 100644
--- a/usr/src/uts/common/io/vscan/vscan_svc.c
+++ b/usr/src/uts/common/io/vscan/vscan_svc.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <sys/stat.h>
@@ -461,7 +462,7 @@ vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
boolean_t allow;
clock_t timeout, time_left;
- if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
+ if ((vp == NULL) || (vp->v_path == vn_vpath_empty) || cr == NULL)
return (0);
DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
@@ -1080,7 +1081,6 @@ vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
struct vattr attr;
ASSERT(vp != NULL);
- ASSERT(vp->v_path != NULL);
attr.va_mask = AT_SIZE;
diff --git a/usr/src/uts/common/io/zfd.c b/usr/src/uts/common/io/zfd.c
new file mode 100644
index 0000000000..2da310ab8d
--- /dev/null
+++ b/usr/src/uts/common/io/zfd.c
@@ -0,0 +1,1154 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Zone File Descriptor Driver.
+ *
+ * This driver is derived from the zcons driver which is in turn derived from
+ * the pts/ptm drivers. The purpose is to expose file descriptors within the
+ * zone which are connected to zoneadmd and used for logging or an interactive
+ * connection to a process within the zone.
+ *
+ * Its implementation is straightforward. Each instance of the driver
+ * represents a global-zone/local-zone pair. Unlike the zcons device, zoneadmd
+ * uses these devices unidirectionally to provide stdin, stdout and stderr to
+ * the process within the zone.
+ *
+ * Instances of zfd are onlined as children of /pseudo/zfdnex@2/ by zoneadmd,
+ * using the devctl framework; thus the driver does not need to maintain any
+ * sort of "admin" node.
+ *
+ * The driver shuttles I/O from master side to slave side and back. In a break
+ * from the pts/ptm semantics, if one side is not open, I/O directed towards
+ * it will simply be discarded. This is so that if zoneadmd is not holding the
+ * master side fd open (i.e. it has died somehow), processes in the zone do not
+ * experience any errors and I/O to the fd does not cause the process to hang.
+ *
+ * The driver can also act as a multiplexer so that data written to the
+ * slave side within the zone is also redirected back to another zfd device
+ * inside the zone for consumption (i.e. it can be read). The intention is
+ * that a logging process within the zone can consume data that is being
+ * written by an application onto the primary stream. This is essentially
+ * a tee off of the primary stream into a log stream. This tee can also be
+ * configured to be flow controlled via an ioctl. Flow control happens on the
+ * primary stream and is used to ensure that the log stream receives all of
+ * the messages off the primary stream when consumption of the data off of
+ * the log stream gets behind. Configuring for flow control implies that the
+ * application writing to the primary stream will be blocked when the log
+ * consumer gets behind. Note that closing the log stream (e.g. when the zone
+ * halts) will cause the loss of all messages queued in the stream.
+ *
+ * The zone's zfd device configuration is driven by zoneadmd and a zone mode.
+ * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat
+ * of a misnomer since its purpose has evolved. The attribute can have a
+ * variety of values, but the lowest two positions are used to control how many
+ * zfd devices are created inside the zone and if the primary stream is a tty.
+ *
+ * Here is a summary of how the 4 modes control what zfd devices are created
+ * and how they're used:
+ *
+ * t-: 1 stdio zdev (0) configured as a tty
+ * --: 3 stdio zdevs (0, 1, 2), not configured as a tty
+ * tn: 1 stdio zdev (0) configured as a tty, 1 additional zdev (1)
+ * -n: 3 stdio zdevs (0, 1, 2), not tty, 2 additional zdevs (3, 4)
+ *
+ * With the 't' flag set, stdin/out/err is multiplexed onto a single full-duplex
+ * stream which is configured as a tty. That is, ptem, ldterm and ttycompat are
+ * autopushed onto the stream when the slave side is opened. There is only a
+ * single zfd dev (0) needed for the primary stream.
+ *
+ * When the 'n' flag is set, it is assumed that output logging will be done
+ * within the zone itself. In this configuration 1 or 2 additional zfd devices,
+ * depending on tty mode ('t' flag) are created within the zone. An application
+ * can then configure the zfd streams driver into a multiplexer. Output from
+ * the stdout/stderr zfd(s) will be teed into the correspond logging zfd(s)
+ * within the zone.
+ *
+ * The following is a diagram of how this works for a '-n' configuration:
+ *
+ *
+ * zoneadmd (for zlogin -I stdout)
+ * GZ: ^
+ * |
+ * --------------------------
+ * ^
+ * NGZ: |
+ * app >1 -> zfd1 -> zfd3 -> logger (for logger to consume app's stdout)
+ *
+ * There would be a similar path for the app's stderr into zfd4 for the logger
+ * to consume stderr.
+ */
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+#include <sys/debug.h>
+#include <sys/devops.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kstr.h>
+#include <sys/modctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/zfd.h>
+#include <sys/vnode.h>
+#include <sys/fs/snode.h>
+#include <sys/zone.h>
+#include <sys/sdt.h>
+
+static kmutex_t zfd_mux_lock;
+
+static int zfd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int zfd_attach(dev_info_t *, ddi_attach_cmd_t);
+static int zfd_detach(dev_info_t *, ddi_detach_cmd_t);
+
+static int zfd_open(queue_t *, dev_t *, int, int, cred_t *);
+static int zfd_close(queue_t *, int, cred_t *);
+static void zfd_wput(queue_t *, mblk_t *);
+static void zfd_rsrv(queue_t *);
+static void zfd_wsrv(queue_t *);
+
+/*
+ * The instance number is encoded in the dev_t in the minor number; the lowest
+ * bit of the minor number is used to track the master vs. slave side of the
+ * fd. The rest of the bits in the minor number are the instance.
+ */
+#define ZFD_MASTER_MINOR 0
+#define ZFD_SLAVE_MINOR 1
+
+#define ZFD_INSTANCE(x) (getminor((x)) >> 1)
+#define ZFD_NODE(x) (getminor((x)) & 0x01)
+
+/*
+ * This macro converts a zfd_state_t pointer to the associated slave minor
+ * node's dev_t.
+ */
+#define ZFD_STATE_TO_SLAVEDEV(x) \
+ (makedevice(ddi_driver_major((x)->zfd_devinfo), \
+ (minor_t)(ddi_get_instance((x)->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR)))
+
+int zfd_debug = 0;
+#define DBG(a) if (zfd_debug) cmn_err(CE_NOTE, a)
+#define DBG1(a, b) if (zfd_debug) cmn_err(CE_NOTE, a, b)
+
+/*
+ * ZFD Pseudo Terminal Module: stream data structure definitions,
+ * based on zcons.
+ */
+static struct module_info zfd_info = {
+ 0x20FD, /* ZOFD - 8445 */
+ "zfd",
+ 0, /* min packet size */
+ INFPSZ, /* max packet size - infinity */
+ 2048, /* high water */
+ 128 /* low water */
+};
+
+static struct qinit zfd_rinit = {
+ NULL,
+ (int (*)()) zfd_rsrv,
+ zfd_open,
+ zfd_close,
+ NULL,
+ &zfd_info,
+ NULL
+};
+
+static struct qinit zfd_winit = {
+ (int (*)()) zfd_wput,
+ (int (*)()) zfd_wsrv,
+ NULL,
+ NULL,
+ NULL,
+ &zfd_info,
+ NULL
+};
+
+static struct streamtab zfd_tab_info = {
+ &zfd_rinit,
+ &zfd_winit,
+ NULL,
+ NULL
+};
+
+#define ZFD_CONF_FLAG (D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL)
+
+/*
+ * this will define (struct cb_ops cb_zfd_ops) and (struct dev_ops zfd_ops)
+ */
+DDI_DEFINE_STREAM_OPS(zfd_ops, nulldev, nulldev, zfd_attach, zfd_detach, \
+ nodev, zfd_getinfo, ZFD_CONF_FLAG, &zfd_tab_info, \
+ ddi_quiesce_not_needed);
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module (this is a pseudo driver) */
+ "Zone FD driver", /* description of module */
+ &zfd_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+typedef enum {
+ ZFD_NO_MUX,
+ ZFD_PRIMARY_STREAM,
+ ZFD_LOG_STREAM
+} zfd_mux_type_t;
+
+typedef struct zfd_state {
+ dev_info_t *zfd_devinfo; /* instance info */
+ queue_t *zfd_master_rdq; /* GZ read queue */
+ queue_t *zfd_slave_rdq; /* in-zone read queue */
+ int zfd_state; /* ZFD_STATE_MOPEN, ZFD_STATE_SOPEN */
+ int zfd_tty; /* ZFD_MAKETTY - strm mods will push */
+ boolean_t zfd_is_flowcon; /* primary stream flow stopped */
+ boolean_t zfd_allow_flowcon; /* use flow control */
+ zfd_mux_type_t zfd_muxt; /* state type: none, primary, log */
+ struct zfd_state *zfd_inst_pri; /* log state's primary ptr */
+ struct zfd_state *zfd_inst_log; /* primary state's log ptr */
+} zfd_state_t;
+
+#define ZFD_STATE_MOPEN 0x01
+#define ZFD_STATE_SOPEN 0x02
+
+static void *zfd_soft_state;
+
+/*
+ * List of STREAMS modules that are autopushed onto a slave instance when its
+ * opened, but only if the ZFD_MAKETTY ioctl has first been received by the
+ * master.
+ */
+static char *zfd_mods[] = {
+ "ptem",
+ "ldterm",
+ "ttcompat",
+ NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ if ((err = ddi_soft_state_init(&zfd_soft_state, sizeof (zfd_state_t),
+ 0)) != 0) {
+ return (err);
+ }
+
+ if ((err = mod_install(&modlinkage)) != 0)
+ ddi_soft_state_fini(zfd_soft_state);
+
+ mutex_init(&zfd_mux_lock, NULL, MUTEX_DEFAULT, NULL);
+ return (err);
+}
+
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = mod_remove(&modlinkage)) != 0) {
+ return (err);
+ }
+
+ ddi_soft_state_fini(&zfd_soft_state);
+ mutex_destroy(&zfd_mux_lock);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+zfd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ zfd_state_t *zfds;
+ int instance;
+ char masternm[ZFD_NAME_LEN], slavenm[ZFD_NAME_LEN];
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ instance = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(zfd_soft_state, instance) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ (void) snprintf(masternm, sizeof (masternm), "%s%d", ZFD_MASTER_NAME,
+ instance);
+ (void) snprintf(slavenm, sizeof (slavenm), "%s%d", ZFD_SLAVE_NAME,
+ instance);
+
+ /*
+ * Create the master and slave minor nodes.
+ */
+ if ((ddi_create_minor_node(dip, slavenm, S_IFCHR,
+ instance << 1 | ZFD_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
+ (ddi_create_minor_node(dip, masternm, S_IFCHR,
+ instance << 1 | ZFD_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(zfd_soft_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ VERIFY((zfds = ddi_get_soft_state(zfd_soft_state, instance)) != NULL);
+ zfds->zfd_devinfo = dip;
+ zfds->zfd_tty = 0;
+ zfds->zfd_muxt = ZFD_NO_MUX;
+ zfds->zfd_inst_log = NULL;
+ return (DDI_SUCCESS);
+}
+
+static int
+zfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ zfd_state_t *zfds;
+ int instance;
+
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ instance = ddi_get_instance(dip);
+ if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+ return (DDI_FAILURE);
+
+ if ((zfds->zfd_state & ZFD_STATE_MOPEN) ||
+ (zfds->zfd_state & ZFD_STATE_SOPEN)) {
+ DBG1("zfd_detach: device (dip=%p) still open\n", (void *)dip);
+ return (DDI_FAILURE);
+ }
+
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(zfd_soft_state, instance);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * zfd_getinfo()
+ * getinfo(9e) entrypoint.
+ */
+/*ARGSUSED*/
+static int
+zfd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ zfd_state_t *zfds;
+ int instance = ZFD_INSTANCE((dev_t)arg);
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if ((zfds = ddi_get_soft_state(zfd_soft_state,
+ instance)) == NULL)
+ return (DDI_FAILURE);
+ *result = zfds->zfd_devinfo;
+ return (DDI_SUCCESS);
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * Return the equivalent queue from the other side of the relationship.
+ * e.g.: given the slave's write queue, return the master's write queue.
+ */
+static queue_t *
+zfd_switch(queue_t *qp)
+{
+ zfd_state_t *zfds = qp->q_ptr;
+ ASSERT(zfds != NULL);
+
+ if (qp == zfds->zfd_master_rdq)
+ return (zfds->zfd_slave_rdq);
+ else if (OTHERQ(qp) == zfds->zfd_master_rdq && zfds->zfd_slave_rdq
+ != NULL)
+ return (OTHERQ(zfds->zfd_slave_rdq));
+ else if (qp == zfds->zfd_slave_rdq)
+ return (zfds->zfd_master_rdq);
+ else if (OTHERQ(qp) == zfds->zfd_slave_rdq && zfds->zfd_master_rdq
+ != NULL)
+ return (OTHERQ(zfds->zfd_master_rdq));
+ else
+ return (NULL);
+}
+
+/*
+ * For debugging and outputting messages. Returns the name of the side of
+ * the relationship associated with this queue.
+ */
+static const char *
+zfd_side(queue_t *qp)
+{
+ zfd_state_t *zfds = qp->q_ptr;
+ ASSERT(zfds != NULL);
+
+ if (qp == zfds->zfd_master_rdq ||
+ OTHERQ(qp) == zfds->zfd_master_rdq) {
+ return ("master");
+ }
+ ASSERT(qp == zfds->zfd_slave_rdq || OTHERQ(qp) == zfds->zfd_slave_rdq);
+ return ("slave");
+}
+
+/*ARGSUSED*/
+static int
+zfd_master_open(zfd_state_t *zfds,
+ queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ mblk_t *mop;
+ struct stroptions *sop;
+
+ /*
+ * Enforce exclusivity on the master side; the only consumer should
+ * be the zoneadmd for the zone.
+ */
+ if ((zfds->zfd_state & ZFD_STATE_MOPEN) != 0)
+ return (EBUSY);
+
+ if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+ DBG("zfd_master_open(): mop allocation failed\n");
+ return (ENOMEM);
+ }
+
+ zfds->zfd_state |= ZFD_STATE_MOPEN;
+
+ /*
+ * q_ptr stores driver private data; stash the soft state data on both
+ * read and write sides of the queue.
+ */
+ WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+ qprocson(rqp);
+
+ /*
+ * Following qprocson(), the master side is fully plumbed into the
+ * STREAM and may send/receive messages. Setting zfds->zfd_master_rdq
+ * will allow the slave to send messages to us (the master).
+ * This cannot occur before qprocson() because the master is not
+ * ready to process them until that point.
+ */
+ zfds->zfd_master_rdq = rqp;
+
+ /*
+ * set up hi/lo water marks on stream head read queue and add
+ * controlling tty as needed.
+ */
+ mop->b_datap->db_type = M_SETOPTS;
+ mop->b_wptr += sizeof (struct stroptions);
+ sop = (struct stroptions *)(void *)mop->b_rptr;
+ if (oflag & FNOCTTY)
+ sop->so_flags = SO_HIWAT | SO_LOWAT;
+ else
+ sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+ sop->so_hiwat = 512;
+ sop->so_lowat = 256;
+ putnext(rqp, mop);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+zfd_slave_open(zfd_state_t *zfds,
+ queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ mblk_t *mop;
+ struct stroptions *sop;
+ /*
+ * The slave side can be opened as many times as needed.
+ */
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zfds));
+ return (0);
+ }
+
+ /* A log stream is read-only */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ (oflag & (FREAD | FWRITE)) != FREAD)
+ return (EINVAL);
+
+ if (zfds->zfd_tty == 1) {
+ major_t major;
+ minor_t minor;
+ minor_t lastminor;
+ uint_t anchorindex;
+
+ /*
+ * Set up sad(7D) so that the necessary STREAMS modules will
+ * be in place. A wrinkle is that 'ptem' must be anchored
+ * in place (see streamio(7i)) because we always want the
+ * fd to have terminal semantics.
+ */
+ minor =
+ ddi_get_instance(zfds->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR;
+ major = ddi_driver_major(zfds->zfd_devinfo);
+ lastminor = 0;
+ anchorindex = 1;
+ if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor,
+ &anchorindex, zfd_mods) != 0) {
+ DBG("zfd_slave_open(): kstr_autopush() failed\n");
+ return (EIO);
+ }
+ }
+
+ if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+ DBG("zfd_slave_open(): mop allocation failed\n");
+ return (ENOMEM);
+ }
+
+ zfds->zfd_state |= ZFD_STATE_SOPEN;
+
+ /*
+ * q_ptr stores driver private data; stash the soft state data on both
+ * read and write sides of the queue.
+ */
+ WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+
+ qprocson(rqp);
+
+ /*
+ * Must follow qprocson(), since we aren't ready to process until then.
+ */
+ zfds->zfd_slave_rdq = rqp;
+
+ /*
+ * set up hi/lo water marks on stream head read queue and add
+ * controlling tty as needed.
+ */
+ mop->b_datap->db_type = M_SETOPTS;
+ mop->b_wptr += sizeof (struct stroptions);
+ sop = (struct stroptions *)(void *)mop->b_rptr;
+ sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+ sop->so_hiwat = 512;
+ sop->so_lowat = 256;
+ putnext(rqp, mop);
+
+ return (0);
+}
+
+/*
+ * open(9e) entrypoint; checks sflag, and rejects anything unordinary.
+ */
+static int
+zfd_open(queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ int instance = ZFD_INSTANCE(*devp);
+ int ret;
+ zfd_state_t *zfds;
+
+ if (sflag != 0)
+ return (EINVAL);
+
+ if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+ return (ENXIO);
+
+ switch (ZFD_NODE(*devp)) {
+ case ZFD_MASTER_MINOR:
+ ret = zfd_master_open(zfds, rqp, devp, oflag, sflag, credp);
+ break;
+ case ZFD_SLAVE_MINOR:
+ ret = zfd_slave_open(zfds, rqp, devp, oflag, sflag, credp);
+ /*
+ * If we just opened the log stream and flow control has
+ * been enabled, we want to make sure the primary stream can
+ * start flowing.
+ */
+ if (ret == 0 && zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ zfds->zfd_inst_pri->zfd_allow_flowcon) {
+ zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+ if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+ qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+ }
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * close(9e) entrypoint.
+ */
+/*ARGSUSED1*/
+static int
+zfd_close(queue_t *rqp, int flag, cred_t *credp)
+{
+ queue_t *wqp;
+ mblk_t *bp;
+ zfd_state_t *zfds;
+ major_t major;
+ minor_t minor;
+
+ zfds = (zfd_state_t *)rqp->q_ptr;
+
+ if (rqp == zfds->zfd_master_rdq) {
+ DBG("Closing master side");
+
+ zfds->zfd_master_rdq = NULL;
+ zfds->zfd_state &= ~ZFD_STATE_MOPEN;
+
+ /*
+ * qenable slave side write queue so that it can flush
+ * its messages as master's read queue is going away
+ */
+ if (zfds->zfd_slave_rdq != NULL) {
+ qenable(WR(zfds->zfd_slave_rdq));
+ }
+
+ qprocsoff(rqp);
+ WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+ } else if (rqp == zfds->zfd_slave_rdq) {
+
+ DBG("Closing slave side");
+ zfds->zfd_state &= ~ZFD_STATE_SOPEN;
+ zfds->zfd_slave_rdq = NULL;
+
+ wqp = WR(rqp);
+ while ((bp = getq(wqp)) != NULL) {
+ if (zfds->zfd_master_rdq != NULL)
+ putnext(zfds->zfd_master_rdq, bp);
+ else if (bp->b_datap->db_type == M_IOCTL)
+ miocnak(wqp, bp, 0, 0);
+ else
+ freemsg(bp);
+ }
+
+ /*
+ * Qenable master side write queue so that it can flush its
+ * messages as slaves's read queue is going away.
+ */
+ if (zfds->zfd_master_rdq != NULL)
+ qenable(WR(zfds->zfd_master_rdq));
+
+ /*
+ * Qenable primary stream if necessary.
+ */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ zfds->zfd_inst_pri->zfd_allow_flowcon) {
+ zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+ if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+ qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+ }
+
+ qprocsoff(rqp);
+ WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+ if (zfds->zfd_tty == 1) {
+ /*
+ * Clear the sad configuration so that reopening
+ * doesn't fail to set up sad configuration.
+ */
+ major = ddi_driver_major(zfds->zfd_devinfo);
+ minor = ddi_get_instance(zfds->zfd_devinfo) << 1 |
+ ZFD_SLAVE_MINOR;
+ (void) kstr_autopush(CLR_AUTOPUSH, &major, &minor,
+ NULL, NULL, NULL);
+ }
+ }
+
+ return (0);
+}
+
+static void
+handle_mflush(queue_t *qp, mblk_t *mp)
+{
+ mblk_t *nmp;
+ DBG1("M_FLUSH on %s side", zfd_side(qp));
+
+ if (*mp->b_rptr & FLUSHW) {
+ DBG1("M_FLUSH, FLUSHW, %s side", zfd_side(qp));
+ flushq(qp, FLUSHDATA);
+ *mp->b_rptr &= ~FLUSHW;
+ if ((*mp->b_rptr & FLUSHR) == 0) {
+ /*
+ * FLUSHW only. Change to FLUSHR and putnext other side,
+ * then we are done.
+ */
+ *mp->b_rptr |= FLUSHR;
+ if (zfd_switch(RD(qp)) != NULL) {
+ putnext(zfd_switch(RD(qp)), mp);
+ return;
+ }
+ } else if ((zfd_switch(RD(qp)) != NULL) &&
+ (nmp = copyb(mp)) != NULL) {
+ /*
+ * It is a FLUSHRW; we copy the mblk and send
+ * it to the other side, since we still need to use
+ * the mblk in FLUSHR processing, below.
+ */
+ putnext(zfd_switch(RD(qp)), nmp);
+ }
+ }
+
+ if (*mp->b_rptr & FLUSHR) {
+ DBG("qreply(qp) turning FLUSHR around\n");
+ qreply(qp, mp);
+ return;
+ }
+ freemsg(mp);
+}
+
+/*
+ * Evaluate the various conditionals to determine if we're teeing into a log
+ * stream and if the primary stream should be flow controlled. This function
+ * can set the zfd_is_flowcon flag as a side effect.
+ *
+ * When teeing with flow control, we always queue the teed msg here and if
+ * the queue is getting full, we set zfd_is_flowcon. The primary stream will
+ * always queue when zfd_is_flowcon and will also not be served when
+ * zfd_is_flowcon is set. This causes backpressure on the primary stream
+ * until the teed queue can drain.
+ */
+static void
+zfd_tee_handler(zfd_state_t *zfds, unsigned char type, mblk_t *mp)
+{
+ queue_t *log_qp;
+ zfd_state_t *log_zfds;
+ mblk_t *lmp;
+
+ if (zfds->zfd_muxt != ZFD_PRIMARY_STREAM)
+ return;
+
+ if (type != M_DATA)
+ return;
+
+ log_zfds = zfds->zfd_inst_log;
+ if (log_zfds == NULL)
+ return;
+
+ ASSERT(log_zfds->zfd_muxt == ZFD_LOG_STREAM);
+
+ if ((log_zfds->zfd_state & ZFD_STATE_SOPEN) == 0) {
+ if (zfds->zfd_allow_flowcon)
+ zfds->zfd_is_flowcon = B_TRUE;
+ return;
+ }
+
+ /* The zfd_slave_rdq is null until the log dev is opened in the zone */
+ log_qp = RD(log_zfds->zfd_slave_rdq);
+ DTRACE_PROBE2(zfd__tee__check, void *, log_qp, void *, zfds);
+
+ if (!zfds->zfd_allow_flowcon) {
+ /*
+ * We're not supposed to tee with flow control and the tee is
+ * full so we skip teeing into the log stream.
+ */
+ if ((log_qp->q_flag & QFULL) != 0)
+ return;
+ }
+
+ /*
+ * Tee the message into the log stream.
+ */
+ lmp = dupmsg(mp);
+ if (lmp == NULL) {
+ if (zfds->zfd_allow_flowcon)
+ zfds->zfd_is_flowcon = B_TRUE;
+ return;
+ }
+
+ if (log_qp->q_first == NULL && bcanputnext(log_qp, lmp->b_band)) {
+ putnext(log_qp, lmp);
+ } else {
+ if (putq(log_qp, lmp) == 0) {
+ /* The logger queue is full, free the msg. */
+ freemsg(lmp);
+ }
+ /*
+ * If we're supposed to tee with flow control and the tee is
+ * over the high water mark then we want the primary stream to
+ * stop flowing. We'll stop queueing the primary stream after
+ * the log stream has drained.
+ */
+ if (zfds->zfd_allow_flowcon &&
+ log_qp->q_count > log_qp->q_hiwat) {
+ zfds->zfd_is_flowcon = B_TRUE;
+ }
+ }
+}
+
+/*
+ * wput(9E) is symmetric for master and slave sides, so this handles both
+ * without splitting the codepath. (The only exception to this is the
+ * processing of zfd ioctls, which is restricted to the master side.)
+ *
+ * zfd_wput() looks at the other side; if there is no process holding that
+ * side open, it frees the message. This prevents processes from hanging
+ * if no one is holding open the fd. Otherwise, it putnext's high
+ * priority messages, putnext's normal messages if possible, and otherwise
+ * enqueues the messages; in the case that something is enqueued, wsrv(9E)
+ * will take care of eventually shuttling I/O to the other side.
+ *
+ * When configured as a multiplexer, then anything written to the stream
+ * from inside the zone is also teed off to the corresponding log stream
+ * for consumption within the zone (i.e. the log stream can be read, but never
+ * written to, by an application inside the zone).
+ */
+static void
+zfd_wput(queue_t *qp, mblk_t *mp)
+{
+ unsigned char type = mp->b_datap->db_type;
+ zfd_state_t *zfds;
+ struct iocblk *iocbp;
+ boolean_t must_queue = B_FALSE;
+
+ ASSERT(qp->q_ptr);
+
+ DBG1("entering zfd_wput, %s side", zfd_side(qp));
+
+ /*
+ * Process zfd ioctl messages if qp is the master side's write queue.
+ */
+ zfds = (zfd_state_t *)qp->q_ptr;
+
+ if (type == M_IOCTL) {
+ iocbp = (struct iocblk *)(void *)mp->b_rptr;
+
+ switch (iocbp->ioc_cmd) {
+ case ZFD_MAKETTY:
+ zfds->zfd_tty = 1;
+ miocack(qp, mp, 0, 0);
+ return;
+ case ZFD_EOF:
+ if (zfds->zfd_slave_rdq != NULL)
+ (void) putnextctl(zfds->zfd_slave_rdq,
+ M_HANGUP);
+ miocack(qp, mp, 0, 0);
+ return;
+ case ZFD_HAS_SLAVE:
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ miocack(qp, mp, 0, 0);
+ } else {
+ miocack(qp, mp, 0, ENOTTY);
+ }
+ return;
+ case ZFD_MUX: {
+ /*
+ * Setup the multiplexer configuration for the two
+ * streams.
+ *
+ * We expect to be called on the stream that will
+ * become the log stream and be passed one data block
+ * with the minor number of the slave side of the
+ * primary stream.
+ */
+ int to;
+ int instance;
+ zfd_state_t *prim_zfds;
+
+ if (iocbp->ioc_count != TRANSPARENT ||
+ mp->b_cont == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+
+ /* Get the primary slave minor device number */
+ to = *(int *)mp->b_cont->b_rptr;
+ instance = ZFD_INSTANCE(to);
+
+ if ((prim_zfds = ddi_get_soft_state(zfd_soft_state,
+ instance)) == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+
+ /* Disallow changing primary/log once set. */
+ mutex_enter(&zfd_mux_lock);
+ if (zfds->zfd_muxt != ZFD_NO_MUX ||
+ prim_zfds->zfd_muxt != ZFD_NO_MUX) {
+ mutex_exit(&zfd_mux_lock);
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+
+ zfds->zfd_muxt = ZFD_LOG_STREAM;
+ zfds->zfd_inst_pri = prim_zfds;
+ prim_zfds->zfd_muxt = ZFD_PRIMARY_STREAM;
+ prim_zfds->zfd_inst_log = zfds;
+ mutex_exit(&zfd_mux_lock);
+ DTRACE_PROBE2(zfd__mux__link, void *, prim_zfds,
+ void *, zfds);
+
+ miocack(qp, mp, 0, 0);
+ return;
+ }
+ case ZFD_MUX_FLOWCON: {
+ /*
+ * We expect this ioctl to be issued against the
+ * log stream. We don't use the primary stream since
+ * there can be other streams modules pushed onto that
+ * stream which would interfere with the ioctl.
+ */
+ int val;
+ zfd_state_t *prim_zfds;
+
+ if (iocbp->ioc_count != TRANSPARENT ||
+ mp->b_cont == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+
+ if (zfds->zfd_muxt != ZFD_LOG_STREAM) {
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+ prim_zfds = zfds->zfd_inst_pri;
+
+ /* Get the flow control setting */
+ val = *(int *)mp->b_cont->b_rptr;
+ if (val != 0 && val != 1) {
+ miocack(qp, mp, 0, EINVAL);
+ return;
+ }
+
+ prim_zfds->zfd_allow_flowcon = (boolean_t)val;
+ if (!prim_zfds->zfd_allow_flowcon)
+ prim_zfds->zfd_is_flowcon = B_FALSE;
+
+ DTRACE_PROBE1(zfd__mux__flowcon, void *, prim_zfds);
+ miocack(qp, mp, 0, 0);
+ return;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* if on the write side, may need to tee */
+ if (zfds->zfd_slave_rdq != NULL && qp == WR(zfds->zfd_slave_rdq)) {
+ /* tee output to any attached log stream */
+ zfd_tee_handler(zfds, type, mp);
+
+ /* high-priority msgs are not subject to flow control */
+ if (zfds->zfd_is_flowcon && type == M_DATA)
+ must_queue = B_TRUE;
+ }
+
+ if (zfd_switch(RD(qp)) == NULL) {
+ DBG1("wput to %s side (no one listening)", zfd_side(qp));
+ switch (type) {
+ case M_FLUSH:
+ handle_mflush(qp, mp);
+ break;
+ case M_IOCTL:
+ miocnak(qp, mp, 0, 0);
+ break;
+ default:
+ freemsg(mp);
+ break;
+ }
+ return;
+ }
+
+ if (type >= QPCTL) {
+ DBG1("(hipri) wput, %s side", zfd_side(qp));
+ switch (type) {
+ case M_READ: /* supposedly from ldterm? */
+ DBG("zfd_wput: tossing M_READ\n");
+ freemsg(mp);
+ break;
+ case M_FLUSH:
+ handle_mflush(qp, mp);
+ break;
+ default:
+ /*
+ * Put this to the other side.
+ */
+ ASSERT(zfd_switch(RD(qp)) != NULL);
+ putnext(zfd_switch(RD(qp)), mp);
+ break;
+ }
+ DBG1("done (hipri) wput, %s side", zfd_side(qp));
+ return;
+ }
+
+ /*
+ * If the primary stream has been stopped for flow control then
+ * enqueue the msg, otherwise only putnext if there isn't already
+ * something in the queue. If we don't do this then things would wind
+ * up out of order.
+ */
+ if (!must_queue && qp->q_first == NULL &&
+ bcanputnext(RD(zfd_switch(qp)), mp->b_band)) {
+ putnext(RD(zfd_switch(qp)), mp);
+ } else {
+ /*
+ * zfd_wsrv expects msgs queued on the primary queue. Those
+ * will be handled by zfd_wsrv after zfd_rsrv performs the
+ * qenable on the proper queue.
+ */
+ (void) putq(qp, mp);
+ }
+
+ DBG1("done wput, %s side", zfd_side(qp));
+}
+
+/*
+ * Read server
+ *
+ * For primary stream:
+ * Under normal execution rsrv(9E) is symmetric for master and slave, so
+ * zfd_rsrv() can handle both without splitting up the codepath. We do this by
+ * enabling the write side of the partner. This triggers the partner to send
+ * messages queued on its write side to this queue's read side.
+ *
+ * For log stream:
+ * Internally we've queued up the msgs that we've teed off to the log stream
+ * so when we're invoked we need to pass these along.
+ */
+static void
+zfd_rsrv(queue_t *qp)
+{
+ zfd_state_t *zfds;
+ zfds = (zfd_state_t *)qp->q_ptr;
+
+ /*
+ * log stream server
+ */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM && zfds->zfd_slave_rdq != NULL) {
+ queue_t *log_qp;
+ mblk_t *mp;
+
+ log_qp = RD(zfds->zfd_slave_rdq);
+
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ zfd_state_t *pzfds = zfds->zfd_inst_pri;
+
+ while ((mp = getq(qp)) != NULL) {
+ if (bcanputnext(log_qp, mp->b_band)) {
+ putnext(log_qp, mp);
+ } else {
+ (void) putbq(log_qp, mp);
+ break;
+ }
+ }
+
+ if (log_qp->q_count < log_qp->q_lowat) {
+ DTRACE_PROBE(zfd__flow__on);
+ pzfds->zfd_is_flowcon = B_FALSE;
+ if (pzfds->zfd_master_rdq != NULL)
+ qenable(RD(pzfds->zfd_master_rdq));
+ }
+ } else {
+ /* No longer open, drain the queue */
+ while ((mp = getq(qp)) != NULL) {
+ freemsg(mp);
+ }
+ flushq(qp, FLUSHALL);
+ }
+ return;
+ }
+
+ /*
+ * Care must be taken here, as either of the master or slave side
+ * qptr could be NULL.
+ */
+ ASSERT(qp == zfds->zfd_master_rdq || qp == zfds->zfd_slave_rdq);
+ if (zfd_switch(qp) == NULL) {
+ DBG("zfd_rsrv: other side isn't listening\n");
+ return;
+ }
+ qenable(WR(zfd_switch(qp)));
+}
+
+/*
+ * Write server
+ *
+ * This routine is symmetric for master and slave, so it handles both without
+ * splitting up the codepath.
+ *
+ * If there are messages on this queue that can be sent to the other, send
+ * them via putnext(). Else, if queued messages cannot be sent, leave them
+ * on this queue.
+ */
+static void
+zfd_wsrv(queue_t *qp)
+{
+ queue_t *swq;
+ mblk_t *mp;
+ zfd_state_t *zfds = (zfd_state_t *)qp->q_ptr;
+
+ ASSERT(zfds != NULL);
+
+ /*
+ * Partner has no read queue, so take the data, and throw it away.
+ */
+ if (zfd_switch(RD(qp)) == NULL) {
+ DBG("zfd_wsrv: other side isn't listening");
+ while ((mp = getq(qp)) != NULL) {
+ if (mp->b_datap->db_type == M_IOCTL)
+ miocnak(qp, mp, 0, 0);
+ else
+ freemsg(mp);
+ }
+ flushq(qp, FLUSHALL);
+ return;
+ }
+
+ swq = RD(zfd_switch(qp));
+
+ /*
+ * while there are messages on this write queue...
+ */
+ while (!zfds->zfd_is_flowcon && (mp = getq(qp)) != NULL) {
+ /*
+ * Due to the way zfd_wput is implemented, we should never
+ * see a high priority control message here.
+ */
+ ASSERT(mp->b_datap->db_type < QPCTL);
+
+ if (bcanputnext(swq, mp->b_band)) {
+ putnext(swq, mp);
+ } else {
+ (void) putbq(qp, mp);
+ break;
+ }
+ }
+}
diff --git a/usr/src/uts/common/mapfiles/README b/usr/src/uts/common/mapfiles/README
new file mode 100644
index 0000000000..5b65771325
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/README
@@ -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 2016 Joyent, Inc.
+#
+
+Kernel Module Build Time Symbol Verification
+--------------------------------------------
+
+Historically, kernel modules have all been built as relocatable objects.
+They are not dynamic objects and dependency information is always noted
+in individual makefiles. Along with this, there has never been any
+verification of the symbols that are being used. This means that it's
+possible for a kernel module author to refer to a symbol that doesn't
+exist and not find out until they try to install the module.
+
+To help find these problems at build time, we provide an opt-in system
+for modules to use, leveraging the link-editor's '-z defs' option. This
+option ensures that there are no unknown definitons at link-edit time.
+To supply these definitions we supply a series of mapfiles in this
+directory.
+
+These mapfiles are not the traditional versioning mapfiles like those in
+usr/src/lib/README.mapfiles! Please review the following differences
+closely:
+
+* These mapfiles do not declare any versions!
+* These mapfiles do not use the 'SYMBOL_VERSION' directive, instead they
+ use the 'SYMBOL_SCOPE' directive.
+* These mapfiles do not hide symbols! Library mapfiles always have
+ something to catch all local symbols. That should *never* be used
+ here. These mapfiles should not effect visibility.
+* All symbols in these mapfiles should be marked 'EXTERN' to indicate
+ that they are not provided by the kernel module but by another.
+* These mapfiles do not declare what is or isn't a public interface,
+ though they are often grouped around interfaces, to make it easier for
+ a driver author to get this right.
+
+Mapfiles are organized based on kernel module. For example the GLDv3
+device driver interface is provided by the 'mac' module and thus is
+found in the file 'mac.mapfile'. The DDI is currently in the 'ddi'
+mapfile. Functions that are found in genunix and unix that aren't in
+the DDI should not be put in that mapfile.
+
+Note, the existing files may not be complete. These are intended to only
+have the public interfaces provided by modules and thus should not
+include every symbol in them. As the need arises, add new symbols or
+modules as appropriate.
+
+To opt a module into this, first declare a series of MAPFILES that they
+should check against in the module. This should be a series of one or
+more files, for example:
+
+MAPFILES += ddi mac
+
+Next, you should add an include of Makefile.mapfile right before you
+include Makefile.targ. You can do this with the following line:
+
+include $(UTSBASE)/Makefile.mapfile
diff --git a/usr/src/uts/common/mapfiles/ddi.mapfile b/usr/src/uts/common/mapfiles/ddi.mapfile
new file mode 100644
index 0000000000..25aa8ab045
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/ddi.mapfile
@@ -0,0 +1,190 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object scoping must comply with the rules detailed in
+#
+# usr/src/uts/common/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
+#
+
+#
+# This file contains core functions provided by the DDI and also items
+# required as part of the platform's runime ABI (think compiler
+# functions).
+#
+
+$mapfile_version 2
+
+SYMBOL_SCOPE {
+ global:
+ __divdi3 { FLAGS = EXTERN };
+ __stack_chk_fail { FLAGS = EXTERN };
+ __stack_chk_guard { FLAGS = EXTERN };
+ allocb { FLAGS = EXTERN };
+ assfail { FLAGS = EXTERN };
+ assfail3 { FLAGS = EXTERN };
+ atomic_dec_32_nv { FLAGS = EXTERN };
+ bcmp { FLAGS = EXTERN };
+ bcopy { FLAGS = EXTERN };
+ bzero { FLAGS = EXTERN };
+ cmn_err { FLAGS = EXTERN };
+ cv_broadcast { FLAGS = EXTERN };
+ cv_destroy { FLAGS = EXTERN };
+ cv_init { FLAGS = EXTERN };
+ cv_reltimedwait { FLAGS = EXTERN };
+ ddi_cb_register { FLAGS = EXTERN };
+ ddi_cb_unregister { FLAGS = EXTERN };
+ ddi_dev_regsize { FLAGS = EXTERN };
+ ddi_dma_addr_bind_handle { FLAGS = EXTERN };
+ ddi_dma_alloc_handle { FLAGS = EXTERN };
+ ddi_dma_free_handle { FLAGS = EXTERN };
+ ddi_dma_mem_alloc { FLAGS = EXTERN };
+ ddi_dma_mem_free { FLAGS = EXTERN };
+ ddi_dma_nextcookie { FLAGS = EXTERN };
+ ddi_dma_sync { FLAGS = EXTERN };
+ ddi_dma_unbind_handle { FLAGS = EXTERN };
+ ddi_fm_acc_err_clear { FLAGS = EXTERN };
+ ddi_fm_acc_err_get { FLAGS = EXTERN };
+ ddi_fm_dma_err_get { FLAGS = EXTERN };
+ ddi_fm_ereport_post { FLAGS = EXTERN };
+ ddi_fm_fini { FLAGS = EXTERN };
+ ddi_fm_handler_register { FLAGS = EXTERN };
+ ddi_fm_handler_unregister { FLAGS = EXTERN };
+ ddi_fm_init { FLAGS = EXTERN };
+ ddi_fm_service_impact { FLAGS = EXTERN };
+ ddi_get_driver_private { FLAGS = EXTERN };
+ ddi_get_instance { FLAGS = EXTERN };
+ ddi_get_lbolt { FLAGS = EXTERN };
+ ddi_get_lbolt64 { FLAGS = EXTERN };
+ ddi_get_name { FLAGS = EXTERN };
+ ddi_get_parent { FLAGS = EXTERN };
+ ddi_get16 { FLAGS = EXTERN };
+ ddi_get32 { FLAGS = EXTERN };
+ ddi_get64 { FLAGS = EXTERN };
+ ddi_intr_add_handler { FLAGS = EXTERN };
+ ddi_intr_alloc { FLAGS = EXTERN };
+ ddi_intr_block_disable { FLAGS = EXTERN };
+ ddi_intr_block_enable { FLAGS = EXTERN };
+ ddi_intr_disable { FLAGS = EXTERN };
+ ddi_intr_enable { FLAGS = EXTERN };
+ ddi_intr_free { FLAGS = EXTERN };
+ ddi_intr_get_cap { FLAGS = EXTERN };
+ ddi_intr_get_navail { FLAGS = EXTERN };
+ ddi_intr_get_nintrs { FLAGS = EXTERN };
+ ddi_intr_get_pri { FLAGS = EXTERN };
+ ddi_intr_get_supported_types { FLAGS = EXTERN };
+ ddi_intr_remove_handler { FLAGS = EXTERN };
+ ddi_periodic_add { FLAGS = EXTERN };
+ ddi_periodic_delete { FLAGS = EXTERN };
+ ddi_power { FLAGS = EXTERN };
+ ddi_prop_free { FLAGS = EXTERN };
+ ddi_prop_get_int { FLAGS = EXTERN };
+ ddi_prop_lookup_int_array { FLAGS = EXTERN };
+ ddi_prop_op { FLAGS = EXTERN };
+ ddi_prop_remove_all { FLAGS = EXTERN };
+ ddi_prop_update_int_array { FLAGS = EXTERN };
+ ddi_prop_update_string { FLAGS = EXTERN };
+ ddi_ptob { FLAGS = EXTERN };
+ ddi_put16 { FLAGS = EXTERN };
+ ddi_put32 { FLAGS = EXTERN };
+ ddi_quiesce_not_supported { FLAGS = EXTERN };
+ ddi_regs_map_free { FLAGS = EXTERN };
+ ddi_regs_map_setup { FLAGS = EXTERN };
+ ddi_set_driver_private { FLAGS = EXTERN };
+ ddi_strtol { FLAGS = EXTERN };
+ ddi_taskq_create { FLAGS = EXTERN };
+ ddi_taskq_destroy { FLAGS = EXTERN };
+ ddi_taskq_dispatch { FLAGS = EXTERN };
+ delay { FLAGS = EXTERN };
+ desballoc { FLAGS = EXTERN };
+ dev_err { FLAGS = EXTERN };
+ drv_usectohz { FLAGS = EXTERN };
+ drv_usecwait { FLAGS = EXTERN };
+ fm_ena_generate { FLAGS = EXTERN };
+ freeb { FLAGS = EXTERN };
+ freemsg { FLAGS = EXTERN };
+ freemsgchain { FLAGS = EXTERN };
+ gethrtime { FLAGS = EXTERN };
+ kmem_alloc { FLAGS = EXTERN };
+ kmem_free { FLAGS = EXTERN };
+ kmem_zalloc { FLAGS = EXTERN };
+ kstat_create { FLAGS = EXTERN };
+ kstat_delete { FLAGS = EXTERN };
+ kstat_install { FLAGS = EXTERN };
+ kstat_named_init { FLAGS = EXTERN };
+ list_create { FLAGS = EXTERN };
+ list_destroy { FLAGS = EXTERN };
+ list_head { FLAGS = EXTERN };
+ list_insert_tail { FLAGS = EXTERN };
+ list_next { FLAGS = EXTERN };
+ list_remove { FLAGS = EXTERN };
+ memcpy { FLAGS = EXTERN };
+ memset { FLAGS = EXTERN };
+ miocack { FLAGS = EXTERN };
+ miocnak { FLAGS = EXTERN };
+ mod_driverops { FLAGS = EXTERN };
+ mod_info { FLAGS = EXTERN };
+ mod_install { FLAGS = EXTERN };
+ mod_remove { FLAGS = EXTERN };
+ msgpullup { FLAGS = EXTERN };
+ msgsize { FLAGS = EXTERN };
+ mutex_destroy { FLAGS = EXTERN };
+ mutex_enter { FLAGS = EXTERN };
+ mutex_exit { FLAGS = EXTERN };
+ mutex_init { FLAGS = EXTERN };
+ mutex_owned { FLAGS = EXTERN };
+ mutex_tryenter { FLAGS = EXTERN };
+ nochpoll { FLAGS = EXTERN };
+ nodev { FLAGS = EXTERN };
+ nulldev { FLAGS = EXTERN };
+ panic { FLAGS = EXTERN };
+ pci_config_get16 { FLAGS = EXTERN };
+ pci_config_get32 { FLAGS = EXTERN };
+ pci_config_get64 { FLAGS = EXTERN };
+ pci_config_get8 { FLAGS = EXTERN };
+ pci_config_put16 { FLAGS = EXTERN };
+ pci_config_put32 { FLAGS = EXTERN };
+ pci_config_put64 { FLAGS = EXTERN };
+ pci_config_put8 { FLAGS = EXTERN };
+ pci_config_setup { FLAGS = EXTERN };
+ pci_config_teardown { FLAGS = EXTERN };
+ pci_ereport_post { FLAGS = EXTERN };
+ pci_ereport_setup { FLAGS = EXTERN };
+ pci_ereport_teardown { FLAGS = EXTERN };
+ pci_lcap_locate { FLAGS = EXTERN };
+ qreply { FLAGS = EXTERN };
+ rw_destroy { FLAGS = EXTERN };
+ rw_enter { FLAGS = EXTERN };
+ rw_exit { FLAGS = EXTERN };
+ rw_init { FLAGS = EXTERN };
+ snprintf { FLAGS = EXTERN };
+ sprintf { FLAGS = EXTERN };
+ strcat { FLAGS = EXTERN };
+ strcmp { FLAGS = EXTERN };
+ strcpy { FLAGS = EXTERN };
+ strlen { FLAGS = EXTERN };
+ timeout { FLAGS = EXTERN };
+ untimeout { FLAGS = EXTERN };
+ vsnprintf { FLAGS = EXTERN };
+ vsprintf { FLAGS = EXTERN };
+};
diff --git a/usr/src/uts/common/mapfiles/dtrace.mapfile.awk b/usr/src/uts/common/mapfiles/dtrace.mapfile.awk
new file mode 100644
index 0000000000..b8a7e2d372
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/dtrace.mapfile.awk
@@ -0,0 +1,34 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# This script is designed to assemble a mapfile for DTrace probes.
+#
+BEGIN {
+ print "#"
+ print "# This file is autogenerated by dtrace.mapfile.awk"
+ print "#"
+ print "$mapfile_version 2"
+ print "SYMBOL_SCOPE {"
+ print " global:"
+}
+
+/__dtrace_probe_/ {
+ printf "\t%s\t{ FLAGS = EXTERN };\n", $1
+}
+
+END {
+ print "};"
+}
diff --git a/usr/src/uts/common/mapfiles/kernel.mapfile b/usr/src/uts/common/mapfiles/kernel.mapfile
new file mode 100644
index 0000000000..6bddb3c7ef
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/kernel.mapfile
@@ -0,0 +1,41 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object scoping must comply with the rules detailed in
+#
+# usr/src/uts/common/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
+#
+
+#
+# This file contains functions provided by the kernel that various
+# modules use. This is a combination of things in both unix and genunix.
+#
+
+$mapfile_version 2
+
+SYMBOL_SCOPE {
+ global:
+ bt_getlowbit { FLAGS = EXTERN };
+ servicing_interrupt { FLAGS = EXTERN };
+};
diff --git a/usr/src/uts/common/mapfiles/mac.mapfile b/usr/src/uts/common/mapfiles/mac.mapfile
new file mode 100644
index 0000000000..30462f80d5
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/mac.mapfile
@@ -0,0 +1,55 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object scoping must comply with the rules detailed in
+#
+# usr/src/uts/common/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_SCOPE {
+ global:
+ mac_alloc { FLAGS = EXTERN };
+ mac_fini_ops { FLAGS = EXTERN };
+ mac_free { FLAGS = EXTERN };
+ mac_hcksum_get { FLAGS = EXTERN };
+ mac_hcksum_set { FLAGS = EXTERN };
+ mac_init_ops { FLAGS = EXTERN };
+ mac_link_update { FLAGS = EXTERN };
+ mac_lso_get { FLAGS = EXTERN };
+ mac_maxsdu_update { FLAGS = EXTERN };
+ mac_prop_info_set_default_link_flowctrl { FLAGS = EXTERN };
+ mac_prop_info_set_default_str { FLAGS = EXTERN };
+ mac_prop_info_set_default_uint8 { FLAGS = EXTERN };
+ mac_prop_info_set_perm { FLAGS = EXTERN };
+ mac_prop_info_set_range_uint32 { FLAGS = EXTERN };
+ mac_ring_intr_set { FLAGS = EXTERN };
+ mac_register { FLAGS = EXTERN };
+ mac_rx { FLAGS = EXTERN };
+ mac_rx_ring { FLAGS = EXTERN };
+ mac_tx_ring_update { FLAGS = EXTERN };
+ mac_tx_update { FLAGS = EXTERN };
+ mac_unregister { FLAGS = EXTERN };
+};
diff --git a/usr/src/uts/common/mapfiles/random.mapfile b/usr/src/uts/common/mapfiles/random.mapfile
new file mode 100644
index 0000000000..d3d8bc89fa
--- /dev/null
+++ b/usr/src/uts/common/mapfiles/random.mapfile
@@ -0,0 +1,37 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object scoping must comply with the rules detailed in
+#
+# usr/src/uts/common/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_SCOPE {
+ global:
+ random_get_bytes { FLAGS = EXTERN };
+ random_get_blocking_bytes { FLAGS = EXTERN };
+ random_get_pseudo_bytes { FLAGS = EXTERN };
+};
diff --git a/usr/src/uts/common/netinet/in.h b/usr/src/uts/common/netinet/in.h
index d530b7f36e..7927cf5e24 100644
--- a/usr/src/uts/common/netinet/in.h
+++ b/usr/src/uts/common/netinet/in.h
@@ -3,6 +3,7 @@
* Use is subject to license terms.
*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
@@ -225,6 +226,7 @@ typedef uint16_t sa_family_t;
#define IPPORT_SLP 427
#define IPPORT_MIP 434
#define IPPORT_SMB 445 /* a.k.a. microsoft-ds */
+#define IPPORT_VXLAN 4789
/*
* Internet Key Exchange (IKE) ports
@@ -268,6 +270,11 @@ typedef uint16_t sa_family_t;
#define IPPORT_RESERVED 1024
#define IPPORT_USERRESERVED 5000
+#ifdef _KERNEL
+#define IPPORT_DYNAMIC_MIN 49152
+#define IPPORT_DYNAMIC_MAX 65535
+#endif
+
/*
* Link numbers
*/
diff --git a/usr/src/uts/common/netinet/udp.h b/usr/src/uts/common/netinet/udp.h
index c65a9bad3a..74cff75d43 100644
--- a/usr/src/uts/common/netinet/udp.h
+++ b/usr/src/uts/common/netinet/udp.h
@@ -1,6 +1,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -17,9 +18,6 @@
#ifndef _NETINET_UDP_H
#define _NETINET_UDP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-/* udp.h 1.7 88/08/19 SMI; from UCB 7.1 6/5/86 */
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -36,6 +34,16 @@ struct udphdr {
#define UDP_EXCLBIND 0x0101 /* for internal use only */
#define UDP_RCVHDR 0x0102 /* for internal use only */
#define UDP_NAT_T_ENDPOINT 0x0103 /* for internal use only */
+#define UDP_SRCPORT_HASH 0x0104 /* for internal use only */
+#define UDP_SND_TO_CONNECTED 0x0105 /* for internal use only */
+
+/*
+ * Hash definitions for UDP_SRCPORT_HASH that effectively tell UDP how to go
+ * handle UDP_SRCPORT_HASH.
+ */
+#define UDP_HASH_DISABLE 0x0000 /* for internal use only */
+#define UDP_HASH_VXLAN 0x0001 /* for internal use only */
+
/*
* Following option in UDP_ namespace required to be exposed through
* <xti.h> (It also requires exposing options not implemented). The options
diff --git a/usr/src/uts/common/os/brand.c b/usr/src/uts/common/os/brand.c
index 0af67f5d98..02901d023d 100644
--- a/usr/src/uts/common/os/brand.c
+++ b/usr/src/uts/common/os/brand.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/kmem.h>
@@ -45,7 +46,7 @@ struct brand_mach_ops native_mach_ops = {
};
#else /* !__sparcv9 */
struct brand_mach_ops native_mach_ops = {
- NULL, NULL, NULL, NULL
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
#endif /* !__sparcv9 */
@@ -53,7 +54,8 @@ brand_t native_brand = {
BRAND_VER_1,
"native",
NULL,
- &native_mach_ops
+ &native_mach_ops,
+ 0
};
/*
@@ -310,46 +312,112 @@ brand_unregister_zone(struct brand *bp)
mutex_exit(&brand_list_lock);
}
-void
-brand_setbrand(proc_t *p)
+int
+brand_setbrand(proc_t *p, boolean_t lwps_ok)
{
brand_t *bp = p->p_zone->zone_brand;
+ void *brand_data = NULL;
- ASSERT(bp != NULL);
- ASSERT(p->p_brand == &native_brand);
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ VERIFY(bp != NULL);
/*
- * We should only be called from exec(), when we know the process
- * is single-threaded.
+ * Process branding occurs during fork() and exec(). When it happens
+ * during fork(), the LWP count will always be 0 since branding is
+ * performed as part of getproc(), before LWPs have been associated.
+ * The same is not true during exec(), where a multi-LWP process may
+ * undergo branding just prior to gexec(). This is to ensure
+ * exec-related brand hooks are available. While it may seem
+ * complicated to brand a multi-LWP process, the two possible outcomes
+ * simplify things:
+ *
+ * 1. The exec() succeeds: LWPs besides the caller will be killed and
+ * any further branding will occur in a single-LWP context.
+ * 2. The exec() fails: The process will be promptly unbranded since
+ * the hooks are no longer needed.
+ *
+ * To prevent inconsistent brand state from being encountered during
+ * the exec(), LWPs beyond the caller which are associated with this
+ * process must be held temporarily. They will be released either when
+ * they are killed in the exec() success, or when the brand is cleared
+ * after exec() failure.
*/
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
+ if (lwps_ok) {
+ /*
+ * We've been called from a exec() context tolerating the
+ * existence of multiple LWPs during branding is necessary.
+ */
+ VERIFY(p == curproc);
+ VERIFY(p->p_tlist != NULL);
+ if (p->p_tlist != p->p_tlist->t_forw) {
+ /*
+ * Multiple LWPs are present. Hold all but the caller.
+ */
+ if (!holdlwps(SHOLDFORK1)) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * Processes branded during fork() should not have LWPs at all.
+ */
+ VERIFY(p->p_tlist == NULL);
+ }
+
+ if (bp->b_data_size > 0) {
+ brand_data = kmem_zalloc(bp->b_data_size, KM_SLEEP);
+ }
+
+ mutex_enter(&p->p_lock);
+ ASSERT(!PROC_IS_BRANDED(p));
p->p_brand = bp;
+ p->p_brand_data = brand_data;
ASSERT(PROC_IS_BRANDED(p));
BROP(p)->b_setbrand(p);
+ mutex_exit(&p->p_lock);
+ return (0);
}
void
-brand_clearbrand(proc_t *p, boolean_t no_lwps)
+brand_clearbrand(proc_t *p, boolean_t lwps_ok)
{
brand_t *bp = p->p_zone->zone_brand;
- klwp_t *lwp = NULL;
- ASSERT(bp != NULL);
- ASSERT(!no_lwps || (p->p_tlist == NULL));
+ void *brand_data;
- /*
- * If called from exec_common() or proc_exit(),
- * we know the process is single-threaded.
- * If called from fork_fail, p_tlist is NULL.
- */
- if (!no_lwps) {
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
- lwp = p->p_tlist->t_lwp;
- }
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ VERIFY(bp != NULL);
+ VERIFY(PROC_IS_BRANDED(p));
- ASSERT(PROC_IS_BRANDED(p));
- BROP(p)->b_proc_exit(p, lwp);
+ mutex_enter(&p->p_lock);
p->p_brand = &native_brand;
+ brand_data = p->p_brand_data;
+ p->p_brand_data = NULL;
+
+ if (lwps_ok) {
+ VERIFY(p == curproc);
+ /*
+ * A process with multiple LWPs is being de-branded after
+ * failing an exec. The other LWPs were held as part of the
+ * procedure, so they must be resumed now.
+ */
+ if (p->p_tlist != NULL && p->p_tlist != p->p_tlist->t_forw) {
+ continuelwps(p);
+ }
+ } else {
+ /*
+ * While clearing the brand, it's ok for one LWP to be present.
+ * This happens when a native binary is executed inside a
+ * branded zone, since the brand will be removed during the
+ * course of a successful exec.
+ */
+ VERIFY(p->p_tlist == NULL || p->p_tlist == p->p_tlist->t_forw);
+ }
+ mutex_exit(&p->p_lock);
+
+ if (brand_data != NULL) {
+ kmem_free(brand_data, bp->b_data_size);
+ }
}
#if defined(__sparcv9)
@@ -483,7 +551,7 @@ brand_solaris_cmd(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
return (ENOSYS);
/* For all other operations this must be a branded process. */
- if (p->p_brand == &native_brand)
+ if (!PROC_IS_BRANDED(p))
return (ENOSYS);
ASSERT(p->p_brand == pbrand);
@@ -601,15 +669,15 @@ restoreexecenv(struct execenv *ep, stack_t *sp)
int
brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
intpdata_t *idatap, int level, long *execsz, int setid, caddr_t exec_file,
- cred_t *cred, int brand_action, struct brand *pbrand, char *bname,
- char *brandlib, char *brandlib32, char *brandlinker, char *brandlinker32)
+ cred_t *cred, int *brand_action, struct brand *pbrand, char *bname,
+ char *brandlib, char *brandlib32)
{
vnode_t *nvp;
Ehdr ehdr;
Addr uphdr_vaddr;
intptr_t voffset;
- int interp;
+ char *interp;
int i, err;
struct execenv env;
struct execenv origenv;
@@ -619,7 +687,6 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
klwp_t *lwp = ttolwp(curthread);
brand_proc_data_t *spd;
brand_elf_data_t sed, *sedp;
- char *linker;
uintptr_t lddata; /* lddata of executable's linker */
ASSERT(curproc->p_brand == pbrand);
@@ -636,12 +703,10 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
*/
if (args->to_model == DATAMODEL_NATIVE) {
args->emulator = brandlib;
- linker = brandlinker;
}
#if defined(_LP64)
else {
args->emulator = brandlib32;
- linker = brandlinker32;
}
#endif /* _LP64 */
@@ -725,7 +790,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
if (args->to_model == DATAMODEL_NATIVE) {
err = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
&voffset, exec_file, &interp, &env.ex_bssbase,
- &env.ex_brkbase, &env.ex_brksize, NULL);
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
}
#if defined(_LP64)
else {
@@ -733,7 +798,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
Elf32_Addr uphdr_vaddr32;
err = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
&voffset, exec_file, &interp, &env.ex_bssbase,
- &env.ex_brkbase, &env.ex_brksize, NULL);
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
Ehdr32to64(&ehdr32, &ehdr);
if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -744,6 +809,10 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
#endif /* _LP64 */
if (err != 0) {
restoreexecenv(&origenv, &orig_sigaltstack);
+
+ if (interp != NULL)
+ kmem_free(interp, MAXPATHLEN);
+
return (err);
}
@@ -761,7 +830,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
sedp->sed_phent = ehdr.e_phentsize;
sedp->sed_phnum = ehdr.e_phnum;
- if (interp) {
+ if (interp != NULL) {
if (ehdr.e_type == ET_DYN) {
/*
* This is a shared object executable, so we
@@ -777,16 +846,20 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
* it in and store relevant information about it in the
* aux vector, where the brand library can find it.
*/
- if ((err = lookupname(linker, UIO_SYSSPACE,
+ if ((err = lookupname(interp, UIO_SYSSPACE,
FOLLOW, NULLVPP, &nvp)) != 0) {
- uprintf("%s: not found.", brandlinker);
+ uprintf("%s: not found.", interp);
restoreexecenv(&origenv, &orig_sigaltstack);
+ kmem_free(interp, MAXPATHLEN);
return (err);
}
+
+ kmem_free(interp, MAXPATHLEN);
+
if (args->to_model == DATAMODEL_NATIVE) {
err = mapexec_brand(nvp, args, &ehdr,
&uphdr_vaddr, &voffset, exec_file, &interp,
- NULL, NULL, NULL, &lddata);
+ NULL, NULL, NULL, &lddata, NULL);
}
#if defined(_LP64)
else {
@@ -794,7 +867,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
Elf32_Addr uphdr_vaddr32;
err = mapexec32_brand(nvp, args, &ehdr32,
&uphdr_vaddr32, &voffset, exec_file, &interp,
- NULL, NULL, NULL, &lddata);
+ NULL, NULL, NULL, &lddata, NULL);
Ehdr32to64(&ehdr32, &ehdr);
if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -934,9 +1007,9 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
/*
* Third, the /proc aux vectors set up by elfexec() point to
- * brand emulation library and it's linker. Copy these to the
+ * brand emulation library and its linker. Copy these to the
* /proc brand specific aux vector, and update the regular
- * /proc aux vectors to point to the executable (and it's
+ * /proc aux vectors to point to the executable (and its
* linker). This will enable debuggers to access the
* executable via the usual /proc or elf notes aux vectors.
*
@@ -1078,55 +1151,31 @@ brand_solaris_freelwp(klwp_t *l, struct brand *pbrand)
}
/*ARGSUSED*/
-int
+void
brand_solaris_initlwp(klwp_t *l, struct brand *pbrand)
{
ASSERT(l->lwp_procp->p_brand == pbrand);
ASSERT(l->lwp_procp->p_brand_data != NULL);
ASSERT(l->lwp_brand == NULL);
l->lwp_brand = (void *)-1;
- return (0);
}
/*ARGSUSED*/
void
brand_solaris_lwpexit(klwp_t *l, struct brand *pbrand)
{
- proc_t *p = l->lwp_procp;
-
ASSERT(l->lwp_procp->p_brand == pbrand);
ASSERT(l->lwp_procp->p_brand_data != NULL);
ASSERT(l->lwp_brand != NULL);
-
- /*
- * We should never be called for the last thread in a process.
- * (That case is handled by brand_solaris_proc_exit().)
- * Therefore this lwp must be exiting from a multi-threaded
- * process.
- */
- ASSERT(p->p_tlist != p->p_tlist->t_forw);
-
- l->lwp_brand = NULL;
}
/*ARGSUSED*/
void
-brand_solaris_proc_exit(struct proc *p, klwp_t *l, struct brand *pbrand)
+brand_solaris_proc_exit(struct proc *p, struct brand *pbrand)
{
ASSERT(p->p_brand == pbrand);
ASSERT(p->p_brand_data != NULL);
- /*
- * When called from proc_exit(), we know that process is
- * single-threaded and free our lwp brand data.
- * otherwise just free p_brand_data and return.
- */
- if (l != NULL) {
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
- ASSERT(p->p_tlist->t_lwp == l);
- (void) brand_solaris_freelwp(l, pbrand);
- }
-
/* upon exit, free our proc brand data */
kmem_free(p->p_brand_data, sizeof (brand_proc_data_t));
p->p_brand_data = NULL;
@@ -1145,5 +1194,4 @@ brand_solaris_setbrand(proc_t *p, struct brand *pbrand)
ASSERT(p->p_tlist == p->p_tlist->t_forw);
p->p_brand_data = kmem_zalloc(sizeof (brand_proc_data_t), KM_SLEEP);
- (void) brand_solaris_initlwp(p->p_tlist->t_lwp, pbrand);
}
diff --git a/usr/src/uts/common/os/clock_highres.c b/usr/src/uts/common/os/clock_highres.c
index 805813037d..1280c8a1b6 100644
--- a/usr/src/uts/common/os/clock_highres.c
+++ b/usr/src/uts/common/os/clock_highres.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2015, Joyent Inc. All rights reserved.
+ * Copyright 2016, Joyent Inc.
*/
#include <sys/timer.h>
@@ -41,6 +41,9 @@
static clock_backend_t clock_highres;
+/* minimum non-privileged interval (200us) */
+long clock_highres_interval_min = 200000;
+
/*ARGSUSED*/
static int
clock_highres_settime(timespec_t *ts)
@@ -68,17 +71,6 @@ clock_highres_getres(timespec_t *ts)
static int
clock_highres_timer_create(itimer_t *it, void (*fire)(itimer_t *))
{
- /*
- * CLOCK_HIGHRES timers of sufficiently high resolution can deny
- * service; only allow privileged users to create such timers.
- * Sites that do not wish to have this restriction should
- * give users the "proc_clock_highres" privilege.
- */
- if (secpolicy_clock_highres(CRED()) != 0) {
- it->it_arg = NULL;
- return (EPERM);
- }
-
it->it_arg = kmem_zalloc(sizeof (cyclic_id_t), KM_SLEEP);
it->it_fire = fire;
@@ -111,6 +103,49 @@ clock_highres_timer_settime(itimer_t *it, int flags,
cpu_t *cpu;
cpupart_t *cpupart;
int pset;
+ boolean_t value_need_clamp = B_FALSE;
+ boolean_t intval_need_clamp = B_FALSE;
+ cred_t *cr = CRED();
+ struct itimerspec clamped;
+
+ /*
+ * CLOCK_HIGHRES timers of sufficiently high resolution can deny
+ * service; only allow privileged users to create such timers.
+ * Non-privileged users (those without the "proc_clock_highres"
+ * privilege) can create timers with lower resolution but if they
+ * attempt to use a very low time value (< 200us) then their
+ * timer will be clamped at 200us.
+ */
+ if (when->it_value.tv_sec == 0 &&
+ when->it_value.tv_nsec > 0 &&
+ when->it_value.tv_nsec < clock_highres_interval_min)
+ value_need_clamp = B_TRUE;
+
+ if (when->it_interval.tv_sec == 0 &&
+ when->it_interval.tv_nsec > 0 &&
+ when->it_interval.tv_nsec < clock_highres_interval_min)
+ intval_need_clamp = B_TRUE;
+
+ if ((value_need_clamp || intval_need_clamp) &&
+ secpolicy_clock_highres(cr) != 0) {
+ clamped.it_value.tv_sec = when->it_value.tv_sec;
+ clamped.it_interval.tv_sec = when->it_interval.tv_sec;
+
+ if (value_need_clamp) {
+ clamped.it_value.tv_nsec = clock_highres_interval_min;
+ } else {
+ clamped.it_value.tv_nsec = when->it_value.tv_nsec;
+ }
+
+ if (intval_need_clamp) {
+ clamped.it_interval.tv_nsec =
+ clock_highres_interval_min;
+ } else {
+ clamped.it_interval.tv_nsec = when->it_interval.tv_nsec;
+ }
+
+ when = &clamped;
+ }
cyctime.cyt_when = ts2hrt(&when->it_value);
cyctime.cyt_interval = ts2hrt(&when->it_interval);
diff --git a/usr/src/uts/common/os/contract.c b/usr/src/uts/common/os/contract.c
index 249066674e..9ea08f5535 100644
--- a/usr/src/uts/common/os/contract.c
+++ b/usr/src/uts/common/os/contract.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -287,7 +288,10 @@ contract_ctor(contract_t *ct, ct_type_t *type, ct_template_t *tmpl, void *data,
avl_index_t where;
klwp_t *curlwp = ttolwp(curthread);
- ASSERT(author == curproc);
+ /*
+ * It's possible that author is not curproc if the zone is creating
+ * a new process as a child of zsched.
+ */
mutex_init(&ct->ct_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ct->ct_reflock, NULL, MUTEX_DEFAULT, NULL);
diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c
index 733fd03a92..b0098946b3 100644
--- a/usr/src/uts/common/os/cred.c
+++ b/usr/src/uts/common/os/cred.c
@@ -727,6 +727,14 @@ crgetzoneid(const cred_t *cr)
cr->cr_zone->zone_id);
}
+zoneid_t
+crgetzonedid(const cred_t *cr)
+{
+ return (cr->cr_zone == NULL ?
+ (cr->cr_uid == -1 ? (zoneid_t)-1 : GLOBAL_ZONEID) :
+ cr->cr_zone->zone_did);
+}
+
projid_t
crgetprojid(const cred_t *cr)
{
diff --git a/usr/src/uts/common/os/ddi_intr_irm.c b/usr/src/uts/common/os/ddi_intr_irm.c
index c3c0481e7f..a4b35dcb5b 100644
--- a/usr/src/uts/common/os/ddi_intr_irm.c
+++ b/usr/src/uts/common/os/ddi_intr_irm.c
@@ -1320,7 +1320,7 @@ i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
/* Log callback errors */
if (ret != DDI_SUCCESS) {
- cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
+ cmn_err(CE_WARN, "!%s%d: failed callback (action=%d, ret=%d)\n",
ddi_driver_name(req_p->ireq_dip),
ddi_get_instance(req_p->ireq_dip), (int)action, ret);
}
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index 172fce8d89..d46b8538a9 100644
--- a/usr/src/uts/common/os/exec.c
+++ b/usr/src/uts/common/os/exec.c
@@ -26,7 +26,7 @@
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/*
- * Copyright 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -69,6 +69,7 @@
#include <sys/sdt.h>
#include <sys/brand.h>
#include <sys/klpd.h>
+#include <sys/random.h>
#include <c2/audit.h>
@@ -97,6 +98,7 @@ uint_t auxv_hwcap32_2 = 0; /* 32-bit version of auxv_hwcap2 */
#endif
#define PSUIDFLAGS (SNOCD|SUGID)
+#define RANDOM_LEN 16 /* 16 bytes for AT_RANDOM aux entry */
/*
* exece() - system call wrapper around exec_common()
@@ -297,14 +299,43 @@ exec_common(const char *fname, const char **argp, const char **envp,
ua.argp = argp;
ua.envp = envp;
- /* If necessary, brand this process before we start the exec. */
- if (brandme)
- brand_setbrand(p);
+ /* If necessary, brand this process/lwp before we start the exec. */
+ if (brandme) {
+ void *brand_data = NULL;
+
+ /*
+ * Process branding may fail if multiple LWPs are present and
+ * holdlwps() cannot complete successfully.
+ */
+ error = brand_setbrand(p, B_TRUE);
+
+ if (error == 0 && BROP(p)->b_lwpdata_alloc != NULL) {
+ brand_data = BROP(p)->b_lwpdata_alloc(p);
+ if (brand_data == NULL) {
+ error = 1;
+ }
+ }
+
+ if (error == 0) {
+ mutex_enter(&p->p_lock);
+ BROP(p)->b_initlwp(lwp, brand_data);
+ mutex_exit(&p->p_lock);
+ } else {
+ VN_RELE(vp);
+ if (dir != NULL) {
+ VN_RELE(dir);
+ }
+ pn_free(&resolvepn);
+ goto fail;
+ }
+ }
if ((error = gexec(&vp, &ua, &args, NULL, 0, &execsz,
- exec_file, p->p_cred, brand_action)) != 0) {
- if (brandme)
- brand_clearbrand(p, B_FALSE);
+ exec_file, p->p_cred, &brand_action)) != 0) {
+ if (brandme) {
+ BROP(p)->b_freelwp(lwp);
+ brand_clearbrand(p, B_TRUE);
+ }
VN_RELE(vp);
if (dir != NULL)
VN_RELE(dir);
@@ -336,7 +367,7 @@ exec_common(const char *fname, const char **argp, const char **envp,
/*
* Clear contract template state
*/
- lwp_ctmpl_clear(lwp);
+ lwp_ctmpl_clear(lwp, B_TRUE);
/*
* Save the directory in which we found the executable for expanding
@@ -360,6 +391,8 @@ exec_common(const char *fname, const char **argp, const char **envp,
* pending held signals remain held, so don't clear t_hold.
*/
mutex_enter(&p->p_lock);
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, 0);
lwp->lwp_oldcontext = 0;
lwp->lwp_ustack = 0;
lwp->lwp_old_stk_ctl = 0;
@@ -419,8 +452,10 @@ exec_common(const char *fname, const char **argp, const char **envp,
TRACE_2(TR_FAC_PROC, TR_PROC_EXEC, "proc_exec:p %p up %p", p, up);
/* Unbrand ourself if necessary. */
- if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE))
+ if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE)) {
+ BROP(p)->b_freelwp(lwp);
brand_clearbrand(p, B_FALSE);
+ }
setregs(&args);
@@ -544,7 +579,7 @@ gexec(
long *execsz,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
struct vnode *vp, *execvp = NULL;
proc_t *pp = ttoproc(curthread);
@@ -858,8 +893,14 @@ gexec(
if (pp->p_plist || (pp->p_proc_flag & P_PR_TRACE))
args->traceinval = 1;
}
- if (pp->p_proc_flag & P_PR_PTRACE)
+
+ /*
+ * If legacy ptrace is enabled, generate the SIGTRAP.
+ */
+ if (pp->p_proc_flag & P_PR_PTRACE) {
psignal(pp, SIGTRAP);
+ }
+
if (args->traceinval)
prinvalidate(&pp->p_user);
}
@@ -1517,6 +1558,27 @@ stk_add(uarg_t *args, const char *sp, enum uio_seg segflg)
return (0);
}
+/*
+ * Add a fixed size byte array to the stack (only from kernel space).
+ */
+static int
+stk_byte_add(uarg_t *args, const uint8_t *sp, size_t len)
+{
+ int error;
+
+ if (STK_AVAIL(args) < sizeof (int))
+ return (E2BIG);
+ *--args->stk_offp = args->stk_strp - args->stk_base;
+
+ if (len > STK_AVAIL(args))
+ return (E2BIG);
+ bcopy(sp, args->stk_strp, len);
+
+ args->stk_strp += len;
+
+ return (0);
+}
+
static int
stk_getptr(uarg_t *args, char *src, char **dst)
{
@@ -1553,6 +1615,7 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
size_t size, pad;
char *argv = (char *)uap->argp;
char *envp = (char *)uap->envp;
+ uint8_t rdata[RANDOM_LEN];
/*
* Copy interpreter's name and argument to argv[0] and argv[1].
@@ -1635,8 +1698,9 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
args->ne = args->na - argc;
/*
- * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME, and
- * AT_SUN_EMULATOR strings to the stack.
+ * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME,
+ * AT_SUN_BRAND_NROOT, and AT_SUN_EMULATOR strings, as well as AT_RANDOM
+ * array, to the stack.
*/
if (auxvpp != NULL && *auxvpp != NULL) {
if ((error = stk_add(args, platform, UIO_SYSSPACE)) != 0)
@@ -1649,6 +1713,20 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
if (args->emulator != NULL &&
(error = stk_add(args, args->emulator, UIO_SYSSPACE)) != 0)
return (error);
+
+ /*
+ * For the AT_RANDOM aux vector we provide 16 bytes of random
+ * data.
+ */
+ (void) random_get_pseudo_bytes(rdata, sizeof (rdata));
+
+ if ((error = stk_byte_add(args, rdata, sizeof (rdata))) != 0)
+ return (error);
+
+ if (args->brand_nroot != NULL &&
+ (error = stk_add(args, args->brand_nroot,
+ UIO_SYSSPACE)) != 0)
+ return (error);
}
/*
@@ -1755,7 +1833,7 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
/*
* Fill in the aux vector now that we know the user stack addresses
* for the AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME and
- * AT_SUN_EMULATOR strings.
+ * AT_SUN_EMULATOR strings, as well as the AT_RANDOM array.
*/
if (auxvpp != NULL && *auxvpp != NULL) {
if (args->to_model == DATAMODEL_NATIVE) {
@@ -1768,6 +1846,11 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
if (args->emulator != NULL)
ADDAUX(*a,
AT_SUN_EMULATOR, (long)&ustrp[*--offp])
+ ADDAUX(*a, AT_RANDOM, (long)&ustrp[*--offp])
+ if (args->brand_nroot != NULL) {
+ ADDAUX(*a,
+ AT_SUN_BRAND_NROOT, (long)&ustrp[*--offp])
+ }
} else {
auxv32_t **a = (auxv32_t **)auxvpp;
ADDAUX(*a,
@@ -1780,6 +1863,11 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
if (args->emulator != NULL)
ADDAUX(*a, AT_SUN_EMULATOR,
(int)(uintptr_t)&ustrp[*--offp])
+ ADDAUX(*a, AT_RANDOM, (int)(uintptr_t)&ustrp[*--offp])
+ if (args->brand_nroot != NULL) {
+ ADDAUX(*a, AT_SUN_BRAND_NROOT,
+ (int)(uintptr_t)&ustrp[*--offp])
+ }
}
}
@@ -1868,6 +1956,9 @@ exec_args(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
usrstack = (char *)USRSTACK32;
}
+ if (args->maxstack != 0 && (uintptr_t)usrstack > args->maxstack)
+ usrstack = (char *)args->maxstack;
+
ASSERT(P2PHASE((uintptr_t)usrstack, args->stk_align) == 0);
#if defined(__sparc)
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
index f0c0983a3a..0e213deb21 100644
--- a/usr/src/uts/common/os/exit.c
+++ b/usr/src/uts/common/os/exit.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -230,7 +230,7 @@ restart_init(int what, int why)
siginfofree(lwp->lwp_curinfo);
lwp->lwp_curinfo = NULL;
}
- lwp_ctmpl_clear(lwp);
+ lwp_ctmpl_clear(lwp, B_FALSE);
/*
* Reset both the process root directory and the current working
@@ -366,19 +366,6 @@ proc_exit(int why, int what)
}
mutex_exit(&p->p_lock);
- DTRACE_PROC(lwp__exit);
- DTRACE_PROC1(exit, int, why);
-
- /*
- * Will perform any brand specific proc exit processing, since this
- * is always the last lwp, will also perform lwp_exit and free brand
- * data
- */
- if (PROC_IS_BRANDED(p)) {
- lwp_detach_brand_hdlrs(lwp);
- brand_clearbrand(p, B_FALSE);
- }
-
/*
* Don't let init exit unless zone_start_init() failed its exec, or
* we are shutting down the zone or the machine.
@@ -390,12 +377,35 @@ proc_exit(int why, int what)
if (z->zone_boot_err == 0 &&
zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN) {
- if (z->zone_restart_init == B_TRUE) {
- if (restart_init(what, why) == 0)
- return (0);
+
+ /*
+ * If the init process should be restarted, the
+ * "zone_restart_init" member will be set. Some init
+ * programs in branded zones do not tolerate a restart
+ * in the traditional manner; setting the
+ * "zone_reboot_on_init_exit" member will cause the
+ * entire zone to be rebooted instead. If neither of
+ * these flags is set the zone will shut down.
+ */
+ if (z->zone_reboot_on_init_exit == B_TRUE &&
+ z->zone_restart_init == B_TRUE) {
+ /*
+ * Trigger a zone reboot and continue
+ * with exit processing.
+ */
+ z->zone_init_status = wstat(why, what);
+ (void) zone_kadmin(A_REBOOT, 0, NULL,
+ zone_kcred());
+
} else {
+ if (z->zone_restart_init == B_TRUE) {
+ if (restart_init(what, why) == 0)
+ return (0);
+ }
+
+ z->zone_init_status = wstat(why, what);
(void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL,
- CRED());
+ zone_kcred());
}
}
@@ -407,6 +417,32 @@ proc_exit(int why, int what)
z->zone_proc_initpid = -1;
}
+ /*
+ * Delay firing probes (and performing brand cleanup) until after the
+ * zone_proc_initpid check. Cases which result in zone shutdown or
+ * restart via zone_kadmin eventually result in a call back to
+ * proc_exit.
+ */
+ DTRACE_PROC(lwp__exit);
+ DTRACE_PROC1(exit, int, why);
+
+ /*
+ * Will perform any brand specific proc exit processing. Since this
+ * is always the last lwp, will also perform lwp exit/free and proc
+ * exit. Brand data will be freed when the process is reaped.
+ */
+ if (PROC_IS_BRANDED(p)) {
+ BROP(p)->b_lwpexit(lwp);
+ BROP(p)->b_proc_exit(p);
+ /*
+ * To ensure that b_proc_exit has access to brand-specific data
+ * contained by the one remaining lwp, call the freelwp hook as
+ * the last part of this clean-up process.
+ */
+ BROP(p)->b_freelwp(lwp);
+ lwp_detach_brand_hdlrs(lwp);
+ }
+
lwp_pcb_exit();
/*
@@ -658,10 +694,22 @@ proc_exit(int why, int what)
if ((q = p->p_child) != NULL && p != proc_init) {
struct proc *np;
struct proc *initp = proc_init;
+ pid_t zone_initpid = 1;
+ struct proc *zoneinitp = NULL;
boolean_t setzonetop = B_FALSE;
- if (!INGLOBALZONE(curproc))
- setzonetop = B_TRUE;
+ if (!INGLOBALZONE(curproc)) {
+ zone_initpid = curproc->p_zone->zone_proc_initpid;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+ zoneinitp = prfind(zone_initpid);
+ if (zoneinitp != NULL) {
+ initp = zoneinitp;
+ } else {
+ zone_initpid = 1;
+ setzonetop = B_TRUE;
+ }
+ }
pgdetach(p);
@@ -673,7 +721,8 @@ proc_exit(int why, int what)
*/
delete_ns(q->p_parent, q);
- q->p_ppid = 1;
+ q->p_ppid = zone_initpid;
+
q->p_pidflag &= ~(CLDNOSIGCHLD | CLDWAITPID);
if (setzonetop) {
mutex_enter(&q->p_lock);
@@ -847,8 +896,50 @@ proc_exit(int why, int what)
mutex_exit(&p->p_lock);
if (!evaporate) {
- p->p_pidflag &= ~CLDPEND;
- sigcld(p, sqp);
+ /*
+ * The brand specific code only happens when the brand has a
+ * function to call in place of sigcld and the parent of the
+ * exiting process is not the global zone init. If the parent
+ * is the global zone init, then the process was reparented,
+ * and we don't want brand code delivering possibly strange
+ * signals to init. Also, init is not branded, so any brand
+ * specific exit data will not be picked up by init anyway.
+ */
+ if (PROC_IS_BRANDED(p) &&
+ BROP(p)->b_exit_with_sig != NULL &&
+ p->p_ppid != 1) {
+ /*
+ * The code for _fini that could unload the brand_t
+ * blocks until the count of zones using the module
+ * reaches zero. Zones decrement the refcount on their
+ * brands only after all user tasks in that zone have
+ * exited and been waited on. The decrement on the
+ * brand's refcount happen in zone_destroy(). That
+ * depends on zone_shutdown() having been completed.
+ * zone_shutdown() includes a call to zone_empty(),
+ * where the zone waits for itself to reach the state
+ * ZONE_IS_EMPTY. This state is only set in either
+ * zone_shutdown(), when there are no user processes as
+ * the zone enters this function, or in
+ * zone_task_rele(). zone_task_rele() is called from
+ * code triggered by waiting on processes, not by the
+ * processes exiting through proc_exit(). This means
+ * all the branded processes that could exist for a
+ * specific brand_t must exit and get reaped before the
+ * refcount on the brand_t can reach 0. _fini will
+ * never unload the corresponding brand module before
+ * proc_exit finishes execution for all processes
+ * branded with a particular brand_t, which makes the
+ * operation below safe to do. Brands that wish to use
+ * this mechanism must wait in _fini as described
+ * above.
+ */
+ BROP(p)->b_exit_with_sig(p, sqp);
+ } else {
+ p->p_pidflag &= ~CLDPEND;
+ sigcld(p, sqp);
+ }
+
} else {
/*
* Do what sigcld() would do if the disposition
@@ -927,10 +1018,9 @@ winfo(proc_t *pp, k_siginfo_t *ip, int waitflag)
int
waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
{
- int found;
proc_t *cp, *pp;
- int proc_gone;
int waitflag = !(options & WNOWAIT);
+ boolean_t have_brand_helper = B_FALSE;
/*
* Obsolete flag, defined here only for binary compatibility
@@ -958,7 +1048,8 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
pp = ttoproc(curthread);
/*
- * lock parent mutex so that sibling chain can be searched.
+ * Anytime you are looking for a process, you take pidlock to prevent
+ * things from changing as you look.
*/
mutex_enter(&pidlock);
@@ -978,10 +1069,37 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
return (ECHILD);
}
- while (pp->p_child != NULL) {
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_waitid_helper != NULL) {
+ have_brand_helper = B_TRUE;
+ }
+
+ while (pp->p_child != NULL || have_brand_helper) {
+ boolean_t brand_wants_wait = B_FALSE;
+ int proc_gone = 0;
+ int found = 0;
- proc_gone = 0;
+ /*
+ * Give the brand a chance to return synthetic results from
+ * this waitid() call before we do the real thing.
+ */
+ if (have_brand_helper) {
+ int ret;
+ if (BROP(pp)->b_waitid_helper(idtype, id, ip, options,
+ &brand_wants_wait, &ret) == 0) {
+ mutex_exit(&pidlock);
+ return (ret);
+ }
+
+ if (pp->p_child == NULL) {
+ goto no_real_children;
+ }
+ }
+
+ /*
+ * Look for interesting children in the newstate list.
+ */
+ VERIFY(pp->p_child != NULL);
for (cp = pp->p_child_ns; cp != NULL; cp = cp->p_sibling_ns) {
if (idtype != P_PID && (cp->p_pidflag & CLDWAITPID))
continue;
@@ -989,6 +1107,11 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
continue;
if (idtype == P_PGID && id != cp->p_pgrp)
continue;
+ if (PROC_IS_BRANDED(pp)) {
+ if (BROP(pp)->b_wait_filter != NULL &&
+ BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+ continue;
+ }
switch (cp->p_wcode) {
@@ -1033,12 +1156,16 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
* Wow! None of the threads on the p_sibling_ns list were
* interesting threads. Check all the kids!
*/
- found = 0;
for (cp = pp->p_child; cp != NULL; cp = cp->p_sibling) {
if (idtype == P_PID && id != cp->p_pid)
continue;
if (idtype == P_PGID && id != cp->p_pgrp)
continue;
+ if (PROC_IS_BRANDED(pp)) {
+ if (BROP(pp)->b_wait_filter != NULL &&
+ BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+ continue;
+ }
switch (cp->p_wcode) {
case CLD_TRAPPED:
@@ -1107,11 +1234,12 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
break;
}
+no_real_children:
/*
* If we found no interesting processes at all,
* break out and return ECHILD.
*/
- if (found + proc_gone == 0)
+ if (!brand_wants_wait && (found + proc_gone == 0))
break;
if (options & WNOHANG) {
@@ -1130,7 +1258,7 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
* change state while we wait, we don't wait at all.
* Get out with ECHILD according to SVID.
*/
- if (found == proc_gone)
+ if (!brand_wants_wait && (found == proc_gone))
break;
if (!cv_wait_sig_swap(&pp->p_cv, &pidlock)) {
@@ -1226,6 +1354,12 @@ freeproc(proc_t *p)
p->p_killsqp = NULL;
}
+ /* Clear any remaining brand data */
+ if (PROC_IS_BRANDED(p)) {
+ brand_clearbrand(p, B_FALSE);
+ }
+
+
prfree(p); /* inform /proc */
/*
diff --git a/usr/src/uts/common/os/fio.c b/usr/src/uts/common/os/fio.c
index 76eddd4e50..bfee77130d 100644
--- a/usr/src/uts/common/os/fio.c
+++ b/usr/src/uts/common/os/fio.c
@@ -852,7 +852,8 @@ flist_fork(uf_info_t *pfip, uf_info_t *cfip)
*/
cfip->fi_nfiles = nfiles = flist_minsize(pfip);
- cfip->fi_list = kmem_zalloc(nfiles * sizeof (uf_entry_t), KM_SLEEP);
+ cfip->fi_list = nfiles == 0 ? NULL :
+ kmem_zalloc(nfiles * sizeof (uf_entry_t), KM_SLEEP);
for (fd = 0, pufp = pfip->fi_list, cufp = cfip->fi_list; fd < nfiles;
fd++, pufp++, cufp++) {
diff --git a/usr/src/uts/common/os/flock.c b/usr/src/uts/common/os/flock.c
index 884de65fe8..0f92f1a38d 100644
--- a/usr/src/uts/common/os/flock.c
+++ b/usr/src/uts/common/os/flock.c
@@ -523,10 +523,10 @@ ofdcleanlock(file_t *fp)
* file descriptor the application loses its lock and does not know).
* 2) Locks are not preserved across fork(2).
*
- * Because these locks are only assoiciated with a pid they are per-process.
- * This is why any close will drop the lock and is also why once the process
- * forks then the lock is no longer related to the new process. These locks can
- * be considered as pid-ful.
+ * Because these locks are only associated with a PID, they are per-process.
+ * This is why any close will drop the lock and is also why, once the process
+ * forks, the lock is no longer related to the new process. These locks can
+ * be considered as PID-ful.
*
* See ofdlock() for the implementation of a similar but improved locking
* scheme.
@@ -1003,7 +1003,7 @@ flk_free_lock(lock_descriptor_t *lock)
ASSERT(IS_DEAD(lock));
- if ((fp = lock->l_ofd) != NULL)
+ if ((fp = lock->l_ofd) != NULL && fp->f_filock == (struct filock *)lock)
fp->f_filock = NULL;
if (IS_REFERENCED(lock)) {
diff --git a/usr/src/uts/common/os/fork.c b/usr/src/uts/common/os/fork.c
index fe3a362fa7..2dcfdc2e14 100644
--- a/usr/src/uts/common/os/fork.c
+++ b/usr/src/uts/common/os/fork.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -84,6 +84,7 @@ static int64_t cfork(int, int, int);
static int getproc(proc_t **, pid_t, uint_t);
#define GETPROC_USER 0x0
#define GETPROC_KERNEL 0x1
+#define GETPROC_ZSCHED 0x2
static void fork_fail(proc_t *);
static void forklwp_fail(proc_t *);
@@ -125,13 +126,36 @@ forksys(int subcode, int flags)
}
}
+/*
+ * Remove the associations of a child process from its parent and siblings.
+ */
+static void
+disown_proc(proc_t *pp, proc_t *cp)
+{
+ proc_t **orphpp;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ orphpp = &pp->p_orphan;
+ while (*orphpp != cp)
+ orphpp = &(*orphpp)->p_nextorph;
+ *orphpp = cp->p_nextorph;
+
+ if (pp->p_child == cp)
+ pp->p_child = cp->p_sibling;
+ if (cp->p_sibling)
+ cp->p_sibling->p_psibling = cp->p_psibling;
+ if (cp->p_psibling)
+ cp->p_psibling->p_sibling = cp->p_sibling;
+}
+
/* ARGSUSED */
static int64_t
cfork(int isvfork, int isfork1, int flags)
{
proc_t *p = ttoproc(curthread);
struct as *as;
- proc_t *cp, **orphpp;
+ proc_t *cp;
klwp_t *clone;
kthread_t *t;
task_t *tk;
@@ -266,16 +290,7 @@ cfork(int isvfork, int isfork1, int flags)
sprunlock(p);
fork_fail(cp);
mutex_enter(&pidlock);
- orphpp = &p->p_orphan;
- while (*orphpp != cp)
- orphpp = &(*orphpp)->p_nextorph;
- *orphpp = cp->p_nextorph;
- if (p->p_child == cp)
- p->p_child = cp->p_sibling;
- if (cp->p_sibling)
- cp->p_sibling->p_psibling = cp->p_psibling;
- if (cp->p_psibling)
- cp->p_psibling->p_sibling = cp->p_sibling;
+ disown_proc(p, cp);
mutex_enter(&cp->p_lock);
tk = cp->p_task;
task_detach(cp);
@@ -640,16 +655,7 @@ forklwperr:
atomic_dec_32(&cp->p_pool->pool_ref);
mutex_exit(&cp->p_lock);
- orphpp = &p->p_orphan;
- while (*orphpp != cp)
- orphpp = &(*orphpp)->p_nextorph;
- *orphpp = cp->p_nextorph;
- if (p->p_child == cp)
- p->p_child = cp->p_sibling;
- if (cp->p_sibling)
- cp->p_sibling->p_psibling = cp->p_psibling;
- if (cp->p_psibling)
- cp->p_psibling->p_sibling = cp->p_sibling;
+ disown_proc(p, cp);
pid_exit(cp, tk);
mutex_exit(&pidlock);
@@ -696,7 +702,7 @@ fork_fail(proc_t *cp)
if (PTOU(curproc)->u_cwd)
refstr_rele(PTOU(curproc)->u_cwd);
if (PROC_IS_BRANDED(cp)) {
- brand_clearbrand(cp, B_TRUE);
+ brand_clearbrand(cp, B_FALSE);
}
}
@@ -745,7 +751,7 @@ forklwp_fail(proc_t *p)
kmem_free(t->t_door, sizeof (door_data_t));
t->t_door = NULL;
}
- lwp_ctmpl_clear(ttolwp(t));
+ lwp_ctmpl_clear(ttolwp(t), B_FALSE);
/*
* Remove the thread from the all threads list.
@@ -782,6 +788,9 @@ extern struct as kas;
/*
* fork a kernel process.
+ *
+ * Passing a pid argument of -1 indicates that the new process should be
+ * launched as a child of 'zsched' within the zone.
*/
int
newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
@@ -800,6 +809,7 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
rctl_set_t *init_set;
ASSERT(pid != 1);
+ ASSERT(pid >= 0);
if (getproc(&p, pid, GETPROC_KERNEL) < 0)
return (EAGAIN);
@@ -843,8 +853,18 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
rctl_set_t *init_set;
task_t *tk, *tk_old;
klwp_t *lwp;
+ boolean_t pzsched = B_FALSE;
+ int flag = GETPROC_USER;
+
+ /* Handle a new user-level thread as child of zsched. */
+ if (pid < 0) {
+ VERIFY(curzone != global_zone);
+ flag = GETPROC_ZSCHED;
+ pzsched = B_TRUE;
+ pid = 0;
+ }
- if (getproc(&p, pid, GETPROC_USER) < 0)
+ if (getproc(&p, pid, flag) < 0)
return (EAGAIN);
/*
* init creates a new task, distinct from the task
@@ -886,23 +906,27 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
if ((lwp = lwp_create(pc, arg, 0, p, TS_STOPPED, pri,
&curthread->t_hold, cid, 1)) == NULL) {
task_t *tk;
+
fork_fail(p);
mutex_enter(&pidlock);
+ disown_proc(p->p_parent, p);
+
mutex_enter(&p->p_lock);
tk = p->p_task;
task_detach(p);
ASSERT(p->p_pool->pool_ref > 0);
atomic_add_32(&p->p_pool->pool_ref, -1);
mutex_exit(&p->p_lock);
+
pid_exit(p, tk);
mutex_exit(&pidlock);
task_rele(tk);
-
return (EAGAIN);
}
t = lwptot(lwp);
- ctp = contract_process_fork(sys_process_tmpl, p, curproc,
+ ctp = contract_process_fork(sys_process_tmpl, p,
+ (pzsched ? curproc->p_zone->zone_zsched : curproc),
B_FALSE);
ASSERT(ctp != NULL);
if (ct != NULL)
@@ -943,7 +967,11 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
return (-1); /* no point in starting new processes */
- pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+ if (flags & GETPROC_ZSCHED) {
+ pp = curproc->p_zone->zone_zsched;
+ } else {
+ pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+ }
task = pp->p_task;
proj = task->tk_proj;
zone = pp->p_zone;
@@ -1004,6 +1032,9 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
cp->p_t1_lgrpid = LGRP_NONE;
cp->p_tr_lgrpid = LGRP_NONE;
+ /* Default to native brand initially */
+ cp->p_brand = &native_brand;
+
if ((newpid = pid_allocate(cp, pid, PID_ALLOC_PROC)) == -1) {
if (nproc == v.v_proc) {
CPU_STATS_ADDQ(CPU, sys, procovf, 1);
@@ -1071,9 +1102,6 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
cp->p_flag = pp->p_flag & (SJCTL|SNOWAIT|SNOCD);
cp->p_sessp = pp->p_sessp;
sess_hold(pp);
- cp->p_brand = pp->p_brand;
- if (PROC_IS_BRANDED(pp))
- BROP(pp)->b_copy_procdata(cp, pp);
cp->p_bssbase = pp->p_bssbase;
cp->p_brkbase = pp->p_brkbase;
cp->p_brksize = pp->p_brksize;
@@ -1153,6 +1181,18 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
mutex_exit(&cp->p_lock);
mutex_exit(&pidlock);
+ if (PROC_IS_BRANDED(pp)) {
+ /*
+ * The only reason why process branding should fail is when
+ * the procedure is complicated by multiple LWPs on the scene.
+ * With an LWP count of 0, this newly allocated process has no
+ * reason to fail branding.
+ */
+ VERIFY0(brand_setbrand(cp, B_FALSE));
+
+ BROP(pp)->b_copy_procdata(cp, pp);
+ }
+
avl_create(&cp->p_ct_held, contract_compar, sizeof (contract_t),
offsetof(contract_t, ct_ctlist));
diff --git a/usr/src/uts/common/os/grow.c b/usr/src/uts/common/os/grow.c
index f5e92cfd94..0c4c0bcad6 100644
--- a/usr/src/uts/common/os/grow.c
+++ b/usr/src/uts/common/os/grow.c
@@ -19,7 +19,10 @@
* CDDL HEADER END
*/
-/* Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved. */
+/*
+ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
@@ -52,6 +55,7 @@
#include <sys/fcntl.h>
#include <sys/lwpchan_impl.h>
#include <sys/nbmlock.h>
+#include <sys/brand.h>
#include <vm/hat.h>
#include <vm/as.h>
@@ -522,6 +526,20 @@ choose_addr(struct as *as, caddr_t *addrp, size_t len, offset_t off,
return (0);
}
+caddr_t
+map_userlimit(proc_t *pp, struct as *as, int flags)
+{
+ if (flags & _MAP_LOW32) {
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_map32limit != NULL) {
+ return ((caddr_t)(uintptr_t)BROP(pp)->b_map32limit(pp));
+ } else {
+ return ((caddr_t)_userlimit32);
+ }
+ }
+
+ return (as->a_userlimit);
+}
+
/*
* Used for MAP_ANON - fast way to get anonymous pages
@@ -537,8 +555,6 @@ zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags,
return (EACCES);
if ((flags & MAP_FIXED) != 0) {
- caddr_t userlimit;
-
/*
* Use the user address. First verify that
* the address to be used is page aligned.
@@ -547,9 +563,8 @@ zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags,
if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
return (EINVAL);
- userlimit = flags & _MAP_LOW32 ?
- (caddr_t)USERLIMIT32 : as->a_userlimit;
- switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+ switch (valid_usr_range(*addrp, len, uprot, as,
+ map_userlimit(as->a_proc, as, flags))) {
case RANGE_OKAY:
break;
case RANGE_BADPROT:
@@ -717,8 +732,6 @@ smmap_common(caddr_t *addrp, size_t len,
* If the user specified an address, do some simple checks here
*/
if ((flags & MAP_FIXED) != 0) {
- caddr_t userlimit;
-
/*
* Use the user address. First verify that
* the address to be used is page aligned.
@@ -726,10 +739,8 @@ smmap_common(caddr_t *addrp, size_t len,
*/
if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
return (EINVAL);
-
- userlimit = flags & _MAP_LOW32 ?
- (caddr_t)USERLIMIT32 : as->a_userlimit;
- switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+ switch (valid_usr_range(*addrp, len, uprot, as,
+ map_userlimit(curproc, as, flags))) {
case RANGE_OKAY:
break;
case RANGE_BADPROT:
diff --git a/usr/src/uts/common/os/id_space.c b/usr/src/uts/common/os/id_space.c
deleted file mode 100644
index 2dad0cb940..0000000000
--- a/usr/src/uts/common/os/id_space.c
+++ /dev/null
@@ -1,159 +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) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#include <sys/types.h>
-#include <sys/id_space.h>
-#include <sys/debug.h>
-
-/*
- * ID Spaces
- *
- * The id_space_t provides a simple implementation of a managed range of
- * integer identifiers using a vmem arena. An ID space guarantees that the
- * next identifer returned by an allocation is larger than the previous one,
- * unless there are no larger slots remaining in the range. In this case,
- * the ID space will return the first available slot in the lower part of the
- * range (viewing the previous identifier as a partitioning element). If no
- * slots are available, id_alloc()/id_allocff() will sleep until an
- * identifier becomes available. Accordingly, id_space allocations must be
- * initiated from contexts where sleeping is acceptable. id_alloc_nosleep()/
- * id_allocff_nosleep() will return -1 if no slots are available or if the
- * system is low on memory. If id_alloc_nosleep() fails, callers should
- * not try to extend the ID space. This is to avoid making a possible
- * low-memory situation worse.
- *
- * As an ID space is designed for representing a range of id_t's, there
- * is a preexisting maximal range: [0, MAXUID]. ID space requests outside
- * that range will fail on a DEBUG kernel. The id_allocff*() functions
- * return the first available id, and should be used when there is benefit
- * to having a compact allocated range.
- *
- * (Presently, the id_space_t abstraction supports only direct allocations; ID
- * reservation, in which an ID is allocated but placed in a internal
- * dictionary for later use, should be added when a consuming subsystem
- * arrives.)
- */
-
-#define ID_TO_ADDR(id) ((void *)(uintptr_t)(id + 1))
-#define ADDR_TO_ID(addr) ((id_t)((uintptr_t)addr - 1))
-
-/*
- * Create an arena to represent the range [low, high).
- * Caller must be in a context in which VM_SLEEP is legal.
- */
-id_space_t *
-id_space_create(const char *name, id_t low, id_t high)
-{
- ASSERT(low >= 0);
- ASSERT(low < high);
-
- return (vmem_create(name, ID_TO_ADDR(low), high - low, 1,
- NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER));
-}
-
-/*
- * Destroy a previously created ID space.
- * No restrictions on caller's context.
- */
-void
-id_space_destroy(id_space_t *isp)
-{
- vmem_destroy(isp);
-}
-
-void
-id_space_extend(id_space_t *isp, id_t low, id_t high)
-{
- (void) vmem_add(isp, ID_TO_ADDR(low), high - low, VM_SLEEP);
-}
-
-/*
- * Allocate an id_t from specified ID space.
- * Caller must be in a context in which VM_SLEEP is legal.
- */
-id_t
-id_alloc(id_space_t *isp)
-{
- return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_SLEEP | VM_NEXTFIT)));
-}
-
-/*
- * Allocate an id_t from specified ID space.
- * Returns -1 on failure (see module block comments for more information on
- * failure modes).
- */
-id_t
-id_alloc_nosleep(id_space_t *isp)
-{
- return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_NEXTFIT)));
-}
-
-/*
- * Allocate an id_t from specified ID space using FIRSTFIT.
- * Caller must be in a context in which VM_SLEEP is legal.
- */
-id_t
-id_allocff(id_space_t *isp)
-{
- return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_SLEEP | VM_FIRSTFIT)));
-}
-
-/*
- * Allocate an id_t from specified ID space using FIRSTFIT
- * Returns -1 on failure (see module block comments for more information on
- * failure modes).
- */
-id_t
-id_allocff_nosleep(id_space_t *isp)
-{
- return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_FIRSTFIT)));
-}
-
-/*
- * Allocate a specific identifier if possible, returning the id if
- * successful, or -1 on failure.
- */
-id_t
-id_alloc_specific_nosleep(id_space_t *isp, id_t id)
-{
- void *minaddr = ID_TO_ADDR(id);
- void *maxaddr = ID_TO_ADDR(id + 1);
-
- /*
- * Note that even though we're vmem_free()ing this later, it
- * should be OK, since there's no quantum cache.
- */
- return (ADDR_TO_ID(vmem_xalloc(isp, 1, 1, 0, 0,
- minaddr, maxaddr, VM_NOSLEEP)));
-}
-
-/*
- * Free a previously allocated ID.
- * No restrictions on caller's context.
- */
-void
-id_free(id_space_t *isp, id_t id)
-{
- vmem_free(isp, ID_TO_ADDR(id), 1);
-}
diff --git a/usr/src/uts/common/os/ipc.c b/usr/src/uts/common/os/ipc.c
index 9381019cd1..6a6f5d84ef 100644
--- a/usr/src/uts/common/os/ipc.c
+++ b/usr/src/uts/common/os/ipc.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -1217,6 +1218,23 @@ ipc_remove(ipc_service_t *service, kipc_perm_t *perm)
(IPC_ZONE_USAGE(perm, service) == 0)));
}
+/*
+ * Perform actual IPC_RMID, either via ipc_rmid or due to a delayed *_RMID.
+ */
+void
+ipc_rmsvc(ipc_service_t *service, kipc_perm_t *perm)
+{
+ ASSERT(service->ipcs_count > 0);
+ ASSERT(MUTEX_HELD(&service->ipcs_lock));
+
+ ipc_remove(service, perm);
+ mutex_exit(&service->ipcs_lock);
+
+ /* perform any per-service removal actions */
+ service->ipcs_rmid(perm);
+
+ ipc_rele(service, perm);
+}
/*
* Common code to perform an IPC_RMID. Returns an errno value on
@@ -1247,13 +1265,7 @@ ipc_rmid(ipc_service_t *service, int id, cred_t *cr)
/*
* Nothing can fail from this point on.
*/
- ipc_remove(service, perm);
- mutex_exit(&service->ipcs_lock);
-
- /* perform any per-service removal actions */
- service->ipcs_rmid(perm);
-
- ipc_rele(service, perm);
+ ipc_rmsvc(service, perm);
return (0);
}
diff --git a/usr/src/uts/common/os/klpd.c b/usr/src/uts/common/os/klpd.c
index 55734ae757..8592b47021 100644
--- a/usr/src/uts/common/os/klpd.c
+++ b/usr/src/uts/common/os/klpd.c
@@ -39,6 +39,8 @@
#include <sys/pathname.h>
#include <sys/varargs.h>
#include <sys/zone.h>
+#include <sys/cmn_err.h>
+#include <sys/sdt.h>
#include <netinet/in.h>
#define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
@@ -860,8 +862,13 @@ pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
klpd_hold(pfd);
mutex_exit(&myzone->zone_lock);
- if (pfd == NULL)
+ if (pfd == NULL) {
+ DTRACE_PROBE2(pfexecd__not__running,
+ zone_t *, myzone, char *, rpnp->pn_path);
+ uprintf("pfexecd not running; pid %d privileges not "
+ "elevated\n", curproc->p_pid);
return (0);
+ }
if (pfd->klpd_door_pid == curproc->p_pid) {
klpd_rele(pfd);
@@ -896,6 +903,9 @@ pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
case EINTR:
/* FALLTHROUGH */
default:
+ DTRACE_PROBE4(pfexecd__failure,
+ int, dres, zone_t *, myzone,
+ char *, rpnp->pn_path, klpd_reg_t *, pfd);
goto out;
}
}
diff --git a/usr/src/uts/common/os/kmem.c b/usr/src/uts/common/os/kmem.c
index ccdbc59f21..e2d7fe988d 100644
--- a/usr/src/uts/common/os/kmem.c
+++ b/usr/src/uts/common/os/kmem.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -159,10 +160,22 @@
* find known objects and is about to free it, or
* c) the client has freed the object.
* In all these cases (a, b, and c) kmem frees the new object (the
- * unused copy destination) and searches for the old object in the
- * magazine layer. If found, the object is removed from the magazine
- * layer and freed to the slab layer so it will no longer hold the
- * slab hostage.
+ * unused copy destination). In the first case, the object is in
+ * use and the correct action is that for LATER; in the latter two
+ * cases, we know that the object is either freed or about to be
+ * freed, in which case it is either already in a magazine or about
+ * to be in one. In these cases, we know that the object will either
+ * be reallocated and reused, or it will end up in a full magazine
+ * that will be reaped (thereby liberating the slab). Because it
+ * is prohibitively expensive to differentiate these cases, and
+ * because the defrag code is executed when we're low on memory
+ * (thereby biasing the system to reclaim full magazines) we treat
+ * all DONT_KNOW cases as LATER and rely on cache reaping to
+ * generally clean up full magazines. While we take the same action
+ * for these cases, we maintain their semantic distinction: if
+ * defragmentation is not occurring, it is useful to know if this
+ * is due to objects in use (LATER) or objects in an unknown state
+ * of transition (DONT_KNOW).
*
* 2.3 Object States
*
@@ -285,10 +298,10 @@
* view of the slab layer, making it a candidate for the move callback. Most
* objects unrecognized by the client in the move callback fall into this
* category and are cheaply distinguished from known objects by the test
- * described earlier. Since recognition is cheap for the client, and searching
- * magazines is expensive for kmem, kmem defers searching until the client first
- * returns KMEM_CBRC_DONT_KNOW. As long as the needed effort is reasonable, kmem
- * elsewhere does what it can to avoid bothering the client unnecessarily.
+ * described earlier. Because searching magazines is prohibitively expensive
+ * for kmem, clients that do not mark freed objects (and therefore return
+ * KMEM_CBRC_DONT_KNOW for large numbers of objects) may find defragmentation
+ * efficacy reduced.
*
* Invalidating the designated pointer member before freeing the object marks
* the object to be avoided in the callback, and conversely, assigning a valid
@@ -998,6 +1011,7 @@ size_t kmem_transaction_log_size; /* transaction log size [2% of memory] */
size_t kmem_content_log_size; /* content log size [2% of memory] */
size_t kmem_failure_log_size; /* failure log [4 pages per CPU] */
size_t kmem_slab_log_size; /* slab create log [4 pages per CPU] */
+size_t kmem_zerosized_log_size; /* zero-sized log [4 pages per CPU] */
size_t kmem_content_maxsave = 256; /* KMF_CONTENTS max bytes to log */
size_t kmem_lite_minsize = 0; /* minimum buffer size for KMF_LITE */
size_t kmem_lite_maxalign = 1024; /* maximum buffer alignment for KMF_LITE */
@@ -1005,6 +1019,14 @@ int kmem_lite_pcs = 4; /* number of PCs to store in KMF_LITE mode */
size_t kmem_maxverify; /* maximum bytes to inspect in debug routines */
size_t kmem_minfirewall; /* hardware-enforced redzone threshold */
+#ifdef DEBUG
+int kmem_warn_zerosized = 1; /* whether to warn on zero-sized KM_SLEEP */
+#else
+int kmem_warn_zerosized = 0; /* whether to warn on zero-sized KM_SLEEP */
+#endif
+
+int kmem_panic_zerosized = 0; /* whether to panic on zero-sized KM_SLEEP */
+
#ifdef _LP64
size_t kmem_max_cached = KMEM_BIG_MAXBUF; /* maximum kmem_alloc cache */
#else
@@ -1038,21 +1060,7 @@ static vmem_t *kmem_default_arena;
static vmem_t *kmem_firewall_va_arena;
static vmem_t *kmem_firewall_arena;
-/*
- * Define KMEM_STATS to turn on statistic gathering. By default, it is only
- * turned on when DEBUG is also defined.
- */
-#ifdef DEBUG
-#define KMEM_STATS
-#endif /* DEBUG */
-
-#ifdef KMEM_STATS
-#define KMEM_STAT_ADD(stat) ((stat)++)
-#define KMEM_STAT_COND_ADD(cond, stat) ((void) (!(cond) || (stat)++))
-#else
-#define KMEM_STAT_ADD(stat) /* nothing */
-#define KMEM_STAT_COND_ADD(cond, stat) /* nothing */
-#endif /* KMEM_STATS */
+static int kmem_zerosized; /* # of zero-sized allocs */
/*
* kmem slab consolidator thresholds (tunables)
@@ -1071,47 +1079,6 @@ size_t kmem_reclaim_max_slabs = 1;
*/
size_t kmem_reclaim_scan_range = 12;
-#ifdef KMEM_STATS
-static struct {
- uint64_t kms_callbacks;
- uint64_t kms_yes;
- uint64_t kms_no;
- uint64_t kms_later;
- uint64_t kms_dont_need;
- uint64_t kms_dont_know;
- uint64_t kms_hunt_found_mag;
- uint64_t kms_hunt_found_slab;
- uint64_t kms_hunt_alloc_fail;
- uint64_t kms_hunt_lucky;
- uint64_t kms_notify;
- uint64_t kms_notify_callbacks;
- uint64_t kms_disbelief;
- uint64_t kms_already_pending;
- uint64_t kms_callback_alloc_fail;
- uint64_t kms_callback_taskq_fail;
- uint64_t kms_endscan_slab_dead;
- uint64_t kms_endscan_slab_destroyed;
- uint64_t kms_endscan_nomem;
- uint64_t kms_endscan_refcnt_changed;
- uint64_t kms_endscan_nomove_changed;
- uint64_t kms_endscan_freelist;
- uint64_t kms_avl_update;
- uint64_t kms_avl_noupdate;
- uint64_t kms_no_longer_reclaimable;
- uint64_t kms_notify_no_longer_reclaimable;
- uint64_t kms_notify_slab_dead;
- uint64_t kms_notify_slab_destroyed;
- uint64_t kms_alloc_fail;
- uint64_t kms_constructor_fail;
- uint64_t kms_dead_slabs_freed;
- uint64_t kms_defrags;
- uint64_t kms_scans;
- uint64_t kms_scan_depot_ws_reaps;
- uint64_t kms_debug_reaps;
- uint64_t kms_debug_scans;
-} kmem_move_stats;
-#endif /* KMEM_STATS */
-
/* consolidator knobs */
static boolean_t kmem_move_noreap;
static boolean_t kmem_move_blocked;
@@ -1142,6 +1109,7 @@ kmem_log_header_t *kmem_transaction_log;
kmem_log_header_t *kmem_content_log;
kmem_log_header_t *kmem_failure_log;
kmem_log_header_t *kmem_slab_log;
+kmem_log_header_t *kmem_zerosized_log;
static int kmem_lite_count; /* # of PCs in kmem_buftag_lite_t */
@@ -1922,15 +1890,7 @@ kmem_slab_free(kmem_cache_t *cp, void *buf)
cp->cache_complete_slab_count--;
avl_add(&cp->cache_partial_slabs, sp);
} else {
-#ifdef DEBUG
- if (avl_update_gt(&cp->cache_partial_slabs, sp)) {
- KMEM_STAT_ADD(kmem_move_stats.kms_avl_update);
- } else {
- KMEM_STAT_ADD(kmem_move_stats.kms_avl_noupdate);
- }
-#else
(void) avl_update_gt(&cp->cache_partial_slabs, sp);
-#endif
}
ASSERT((cp->cache_slab_create - cp->cache_slab_destroy) ==
@@ -2964,8 +2924,33 @@ kmem_alloc(size_t size, int kmflag)
/* fall through to kmem_cache_alloc() */
} else {
- if (size == 0)
+ if (size == 0) {
+ if (kmflag != KM_SLEEP && !(kmflag & KM_PANIC))
+ return (NULL);
+
+ /*
+ * If this is a sleeping allocation or one that has
+ * been specified to panic on allocation failure, we
+ * consider it to be deprecated behavior to allocate
+ * 0 bytes. If we have been configured to panic under
+ * this condition, we panic; if to warn, we warn -- and
+ * regardless, we log to the kmem_zerosized_log that
+ * that this condition has occurred (which gives us
+ * enough information to be able to debug it).
+ */
+ if (kmem_panic && kmem_panic_zerosized)
+ panic("attempted to kmem_alloc() size of 0");
+
+ if (kmem_warn_zerosized) {
+ cmn_err(CE_WARN, "kmem_alloc(): sleeping "
+ "allocation with size of 0; "
+ "see kmem_zerosized_log for details");
+ }
+
+ kmem_log_event(kmem_zerosized_log, NULL, NULL, NULL);
+
return (NULL);
+ }
buf = vmem_alloc(kmem_oversize_arena, size,
kmflag & KM_VMFLAGS);
@@ -3579,7 +3564,7 @@ kmem_cache_kstat_update(kstat_t *ksp, int rw)
kmcp->kmc_move_later.value.ui64 = kd->kmd_later;
kmcp->kmc_move_dont_need.value.ui64 = kd->kmd_dont_need;
kmcp->kmc_move_dont_know.value.ui64 = kd->kmd_dont_know;
- kmcp->kmc_move_hunt_found.value.ui64 = kd->kmd_hunt_found;
+ kmcp->kmc_move_hunt_found.value.ui64 = 0;
kmcp->kmc_move_slabs_freed.value.ui64 = kd->kmd_slabs_freed;
kmcp->kmc_defrag.value.ui64 = kd->kmd_defrags;
kmcp->kmc_scan.value.ui64 = kd->kmd_scans;
@@ -4150,7 +4135,8 @@ kmem_cache_destroy(kmem_cache_t *cp)
if (kmem_taskq != NULL)
taskq_wait(kmem_taskq);
- if (kmem_move_taskq != NULL)
+
+ if (kmem_move_taskq != NULL && cp->cache_defrag != NULL)
taskq_wait(kmem_move_taskq);
kmem_cache_magazine_purge(cp);
@@ -4488,8 +4474,8 @@ kmem_init(void)
}
kmem_failure_log = kmem_log_init(kmem_failure_log_size);
-
kmem_slab_log = kmem_log_init(kmem_slab_log_size);
+ kmem_zerosized_log = kmem_log_init(kmem_zerosized_log_size);
/*
* Initialize STREAMS message caches so allocb() is available.
@@ -4677,94 +4663,6 @@ kmem_slab_is_reclaimable(kmem_cache_t *cp, kmem_slab_t *sp, int flags)
(sp->slab_chunks * cp->cache_defrag->kmd_reclaim_numer));
}
-static void *
-kmem_hunt_mag(kmem_cache_t *cp, kmem_magazine_t *m, int n, void *buf,
- void *tbuf)
-{
- int i; /* magazine round index */
-
- for (i = 0; i < n; i++) {
- if (buf == m->mag_round[i]) {
- if (cp->cache_flags & KMF_BUFTAG) {
- (void) kmem_cache_free_debug(cp, tbuf,
- caller());
- }
- m->mag_round[i] = tbuf;
- return (buf);
- }
- }
-
- return (NULL);
-}
-
-/*
- * Hunt the magazine layer for the given buffer. If found, the buffer is
- * removed from the magazine layer and returned, otherwise NULL is returned.
- * The state of the returned buffer is freed and constructed.
- */
-static void *
-kmem_hunt_mags(kmem_cache_t *cp, void *buf)
-{
- kmem_cpu_cache_t *ccp;
- kmem_magazine_t *m;
- int cpu_seqid;
- int n; /* magazine rounds */
- void *tbuf; /* temporary swap buffer */
-
- ASSERT(MUTEX_NOT_HELD(&cp->cache_lock));
-
- /*
- * Allocated a buffer to swap with the one we hope to pull out of a
- * magazine when found.
- */
- tbuf = kmem_cache_alloc(cp, KM_NOSLEEP);
- if (tbuf == NULL) {
- KMEM_STAT_ADD(kmem_move_stats.kms_hunt_alloc_fail);
- return (NULL);
- }
- if (tbuf == buf) {
- KMEM_STAT_ADD(kmem_move_stats.kms_hunt_lucky);
- if (cp->cache_flags & KMF_BUFTAG) {
- (void) kmem_cache_free_debug(cp, buf, caller());
- }
- return (buf);
- }
-
- /* Hunt the depot. */
- mutex_enter(&cp->cache_depot_lock);
- n = cp->cache_magtype->mt_magsize;
- for (m = cp->cache_full.ml_list; m != NULL; m = m->mag_next) {
- if (kmem_hunt_mag(cp, m, n, buf, tbuf) != NULL) {
- mutex_exit(&cp->cache_depot_lock);
- return (buf);
- }
- }
- mutex_exit(&cp->cache_depot_lock);
-
- /* Hunt the per-CPU magazines. */
- for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) {
- ccp = &cp->cache_cpu[cpu_seqid];
-
- mutex_enter(&ccp->cc_lock);
- m = ccp->cc_loaded;
- n = ccp->cc_rounds;
- if (kmem_hunt_mag(cp, m, n, buf, tbuf) != NULL) {
- mutex_exit(&ccp->cc_lock);
- return (buf);
- }
- m = ccp->cc_ploaded;
- n = ccp->cc_prounds;
- if (kmem_hunt_mag(cp, m, n, buf, tbuf) != NULL) {
- mutex_exit(&ccp->cc_lock);
- return (buf);
- }
- mutex_exit(&ccp->cc_lock);
- }
-
- kmem_cache_free(cp, tbuf);
- return (NULL);
-}
-
/*
* May be called from the kmem_move_taskq, from kmem_cache_move_notify_task(),
* or when the buffer is freed.
@@ -4828,7 +4726,7 @@ static void kmem_move_end(kmem_cache_t *, kmem_move_t *);
* NO kmem frees the new buffer, marks the slab of the old buffer
* non-reclaimable to avoid bothering the client again
* LATER kmem frees the new buffer, increments slab_later_count
- * DONT_KNOW kmem frees the new buffer, searches mags for the old buffer
+ * DONT_KNOW kmem frees the new buffer
* DONT_NEED kmem frees both the old buffer and the new buffer
*
* The pending callback argument now being processed contains both of the
@@ -4862,19 +4760,14 @@ kmem_move_buffer(kmem_move_t *callback)
* another buffer on the same slab.
*/
if (!kmem_slab_is_reclaimable(cp, sp, callback->kmm_flags)) {
- KMEM_STAT_ADD(kmem_move_stats.kms_no_longer_reclaimable);
- KMEM_STAT_COND_ADD((callback->kmm_flags & KMM_NOTIFY),
- kmem_move_stats.kms_notify_no_longer_reclaimable);
kmem_slab_free(cp, callback->kmm_to_buf);
kmem_move_end(cp, callback);
return;
}
/*
- * Hunting magazines is expensive, so we'll wait to do that until the
- * client responds KMEM_CBRC_DONT_KNOW. However, checking the slab layer
- * is cheap, so we might as well do that here in case we can avoid
- * bothering the client.
+ * Checking the slab layer is easy, so we might as well do that here
+ * in case we can avoid bothering the client.
*/
mutex_enter(&cp->cache_lock);
free_on_slab = (kmem_slab_allocated(cp, sp,
@@ -4882,7 +4775,6 @@ kmem_move_buffer(kmem_move_t *callback)
mutex_exit(&cp->cache_lock);
if (free_on_slab) {
- KMEM_STAT_ADD(kmem_move_stats.kms_hunt_found_slab);
kmem_slab_free(cp, callback->kmm_to_buf);
kmem_move_end(cp, callback);
return;
@@ -4894,7 +4786,6 @@ kmem_move_buffer(kmem_move_t *callback)
*/
if (kmem_cache_alloc_debug(cp, callback->kmm_to_buf,
KM_NOSLEEP, 1, caller()) != 0) {
- KMEM_STAT_ADD(kmem_move_stats.kms_alloc_fail);
kmem_move_end(cp, callback);
return;
}
@@ -4902,15 +4793,11 @@ kmem_move_buffer(kmem_move_t *callback)
cp->cache_constructor(callback->kmm_to_buf, cp->cache_private,
KM_NOSLEEP) != 0) {
atomic_inc_64(&cp->cache_alloc_fail);
- KMEM_STAT_ADD(kmem_move_stats.kms_constructor_fail);
kmem_slab_free(cp, callback->kmm_to_buf);
kmem_move_end(cp, callback);
return;
}
- KMEM_STAT_ADD(kmem_move_stats.kms_callbacks);
- KMEM_STAT_COND_ADD((callback->kmm_flags & KMM_NOTIFY),
- kmem_move_stats.kms_notify_callbacks);
cp->cache_defrag->kmd_callbacks++;
cp->cache_defrag->kmd_thread = curthread;
cp->cache_defrag->kmd_from_buf = callback->kmm_from_buf;
@@ -4928,7 +4815,6 @@ kmem_move_buffer(kmem_move_t *callback)
cp->cache_defrag->kmd_to_buf = NULL;
if (response == KMEM_CBRC_YES) {
- KMEM_STAT_ADD(kmem_move_stats.kms_yes);
cp->cache_defrag->kmd_yes++;
kmem_slab_free_constructed(cp, callback->kmm_from_buf, B_FALSE);
/* slab safe to access until kmem_move_end() */
@@ -4943,14 +4829,12 @@ kmem_move_buffer(kmem_move_t *callback)
switch (response) {
case KMEM_CBRC_NO:
- KMEM_STAT_ADD(kmem_move_stats.kms_no);
cp->cache_defrag->kmd_no++;
mutex_enter(&cp->cache_lock);
kmem_slab_move_no(cp, sp, callback->kmm_from_buf);
mutex_exit(&cp->cache_lock);
break;
case KMEM_CBRC_LATER:
- KMEM_STAT_ADD(kmem_move_stats.kms_later);
cp->cache_defrag->kmd_later++;
mutex_enter(&cp->cache_lock);
if (!KMEM_SLAB_IS_PARTIAL(sp)) {
@@ -4959,7 +4843,6 @@ kmem_move_buffer(kmem_move_t *callback)
}
if (++sp->slab_later_count >= KMEM_DISBELIEF) {
- KMEM_STAT_ADD(kmem_move_stats.kms_disbelief);
kmem_slab_move_no(cp, sp, callback->kmm_from_buf);
} else if (!(sp->slab_flags & KMEM_SLAB_NOMOVE)) {
sp->slab_stuck_offset = KMEM_SLAB_OFFSET(sp,
@@ -4968,7 +4851,6 @@ kmem_move_buffer(kmem_move_t *callback)
mutex_exit(&cp->cache_lock);
break;
case KMEM_CBRC_DONT_NEED:
- KMEM_STAT_ADD(kmem_move_stats.kms_dont_need);
cp->cache_defrag->kmd_dont_need++;
kmem_slab_free_constructed(cp, callback->kmm_from_buf, B_FALSE);
if (sp->slab_refcnt == 0)
@@ -4978,19 +4860,21 @@ kmem_move_buffer(kmem_move_t *callback)
mutex_exit(&cp->cache_lock);
break;
case KMEM_CBRC_DONT_KNOW:
- KMEM_STAT_ADD(kmem_move_stats.kms_dont_know);
+ /*
+ * If we don't know if we can move this buffer or not, we'll
+ * just assume that we can't: if the buffer is in fact free,
+ * then it is sitting in one of the per-CPU magazines or in
+ * a full magazine in the depot layer. Either way, because
+ * defrag is induced in the same logic that reaps a cache,
+ * it's likely that full magazines will be returned to the
+ * system soon (thereby accomplishing what we're trying to
+ * accomplish here: return those magazines to their slabs).
+ * Given this, any work that we might do now to locate a buffer
+ * in a magazine is wasted (and expensive!) work; we bump
+ * a counter in this case and otherwise assume that we can't
+ * move it.
+ */
cp->cache_defrag->kmd_dont_know++;
- if (kmem_hunt_mags(cp, callback->kmm_from_buf) != NULL) {
- KMEM_STAT_ADD(kmem_move_stats.kms_hunt_found_mag);
- cp->cache_defrag->kmd_hunt_found++;
- kmem_slab_free_constructed(cp, callback->kmm_from_buf,
- B_TRUE);
- if (sp->slab_refcnt == 0)
- cp->cache_defrag->kmd_slabs_freed++;
- mutex_enter(&cp->cache_lock);
- kmem_slab_move_yes(cp, sp, callback->kmm_from_buf);
- mutex_exit(&cp->cache_lock);
- }
break;
default:
panic("'%s' (%p) unexpected move callback response %d\n",
@@ -5015,10 +4899,9 @@ kmem_move_begin(kmem_cache_t *cp, kmem_slab_t *sp, void *buf, int flags)
ASSERT(sp->slab_flags & KMEM_SLAB_MOVE_PENDING);
callback = kmem_cache_alloc(kmem_move_cache, KM_NOSLEEP);
- if (callback == NULL) {
- KMEM_STAT_ADD(kmem_move_stats.kms_callback_alloc_fail);
+
+ if (callback == NULL)
return (B_FALSE);
- }
callback->kmm_from_slab = sp;
callback->kmm_from_buf = buf;
@@ -5043,7 +4926,6 @@ kmem_move_begin(kmem_cache_t *cp, kmem_slab_t *sp, void *buf, int flags)
pending->kmm_flags |= KMM_DESPERATE;
}
mutex_exit(&cp->cache_lock);
- KMEM_STAT_ADD(kmem_move_stats.kms_already_pending);
kmem_cache_free(kmem_move_cache, callback);
return (B_TRUE);
}
@@ -5057,7 +4939,6 @@ kmem_move_begin(kmem_cache_t *cp, kmem_slab_t *sp, void *buf, int flags)
if (!taskq_dispatch(kmem_move_taskq, (task_func_t *)kmem_move_buffer,
callback, TQ_NOSLEEP)) {
- KMEM_STAT_ADD(kmem_move_stats.kms_callback_taskq_fail);
mutex_enter(&cp->cache_lock);
avl_remove(&cp->cache_defrag->kmd_moves_pending, callback);
mutex_exit(&cp->cache_lock);
@@ -5103,7 +4984,6 @@ kmem_move_end(kmem_cache_t *cp, kmem_move_t *callback)
cp->cache_slab_destroy++;
mutex_exit(&cp->cache_lock);
kmem_slab_destroy(cp, sp);
- KMEM_STAT_ADD(kmem_move_stats.kms_dead_slabs_freed);
mutex_enter(&cp->cache_lock);
}
}
@@ -5248,8 +5128,6 @@ kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs,
* pending move completes.
*/
list_insert_head(deadlist, sp);
- KMEM_STAT_ADD(kmem_move_stats.
- kms_endscan_slab_dead);
return (-1);
}
@@ -5264,10 +5142,6 @@ kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs,
cp->cache_slab_destroy++;
mutex_exit(&cp->cache_lock);
kmem_slab_destroy(cp, sp);
- KMEM_STAT_ADD(kmem_move_stats.
- kms_dead_slabs_freed);
- KMEM_STAT_ADD(kmem_move_stats.
- kms_endscan_slab_destroyed);
mutex_enter(&cp->cache_lock);
/*
* Since we can't pick up the scan where we left
@@ -5283,8 +5157,6 @@ kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs,
* for the request and say nothing about the
* number of reclaimable slabs.
*/
- KMEM_STAT_COND_ADD(s < max_slabs,
- kmem_move_stats.kms_endscan_nomem);
return (-1);
}
@@ -5300,16 +5172,10 @@ kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs,
* destination buffer on the same slab. In that
* case, we're not interested in counting it.
*/
- KMEM_STAT_COND_ADD(!(flags & KMM_DEBUG) &&
- (s < max_slabs),
- kmem_move_stats.kms_endscan_refcnt_changed);
return (-1);
}
- if ((sp->slab_flags & KMEM_SLAB_NOMOVE) != nomove) {
- KMEM_STAT_COND_ADD(s < max_slabs,
- kmem_move_stats.kms_endscan_nomove_changed);
+ if ((sp->slab_flags & KMEM_SLAB_NOMOVE) != nomove)
return (-1);
- }
/*
* Generating a move request allocates a destination
@@ -5336,11 +5202,6 @@ kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs,
}
end_scan:
- KMEM_STAT_COND_ADD(!(flags & KMM_DEBUG) &&
- (s < max_slabs) &&
- (sp == avl_first(&cp->cache_partial_slabs)),
- kmem_move_stats.kms_endscan_freelist);
-
return (s);
}
@@ -5400,8 +5261,6 @@ kmem_cache_move_notify_task(void *arg)
&cp->cache_defrag->kmd_moves_pending)) {
list_insert_head(deadlist, sp);
mutex_exit(&cp->cache_lock);
- KMEM_STAT_ADD(kmem_move_stats.
- kms_notify_slab_dead);
return;
}
@@ -5409,9 +5268,6 @@ kmem_cache_move_notify_task(void *arg)
cp->cache_slab_destroy++;
mutex_exit(&cp->cache_lock);
kmem_slab_destroy(cp, sp);
- KMEM_STAT_ADD(kmem_move_stats.kms_dead_slabs_freed);
- KMEM_STAT_ADD(kmem_move_stats.
- kms_notify_slab_destroyed);
return;
}
} else {
@@ -5425,7 +5281,6 @@ kmem_cache_move_notify(kmem_cache_t *cp, void *buf)
{
kmem_move_notify_args_t *args;
- KMEM_STAT_ADD(kmem_move_stats.kms_notify);
args = kmem_alloc(sizeof (kmem_move_notify_args_t), KM_NOSLEEP);
if (args != NULL) {
args->kmna_cache = cp;
@@ -5448,7 +5303,6 @@ kmem_cache_defrag(kmem_cache_t *cp)
n = avl_numnodes(&cp->cache_partial_slabs);
if (n > 1) {
/* kmem_move_buffers() drops and reacquires cache_lock */
- KMEM_STAT_ADD(kmem_move_stats.kms_defrags);
cp->cache_defrag->kmd_defrags++;
(void) kmem_move_buffers(cp, n, 0, KMM_DESPERATE);
}
@@ -5547,7 +5401,6 @@ kmem_cache_scan(kmem_cache_t *cp)
*
* kmem_move_buffers() drops and reacquires cache_lock.
*/
- KMEM_STAT_ADD(kmem_move_stats.kms_scans);
kmd->kmd_scans++;
slabs_found = kmem_move_buffers(cp, kmem_reclaim_scan_range,
kmem_reclaim_max_slabs, 0);
@@ -5588,12 +5441,9 @@ kmem_cache_scan(kmem_cache_t *cp)
if (!kmem_move_noreap &&
((debug_rand % kmem_mtb_reap) == 0)) {
mutex_exit(&cp->cache_lock);
- KMEM_STAT_ADD(kmem_move_stats.kms_debug_reaps);
kmem_cache_reap(cp);
return;
} else if ((debug_rand % kmem_mtb_move) == 0) {
- KMEM_STAT_ADD(kmem_move_stats.kms_scans);
- KMEM_STAT_ADD(kmem_move_stats.kms_debug_scans);
kmd->kmd_scans++;
(void) kmem_move_buffers(cp,
kmem_reclaim_scan_range, 1, KMM_DEBUG);
@@ -5604,8 +5454,6 @@ kmem_cache_scan(kmem_cache_t *cp)
mutex_exit(&cp->cache_lock);
- if (reap) {
- KMEM_STAT_ADD(kmem_move_stats.kms_scan_depot_ws_reaps);
+ if (reap)
kmem_depot_ws_reap(cp);
- }
}
diff --git a/usr/src/uts/common/os/logsubr.c b/usr/src/uts/common/os/logsubr.c
index 149f5f8a88..cbc4fa0000 100644
--- a/usr/src/uts/common/os/logsubr.c
+++ b/usr/src/uts/common/os/logsubr.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2013 Gary Mills
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Joyent, Inc.
*/
#include <sys/types.h>
@@ -249,8 +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) 1983, 2010, Oracle and/or its affiliates. "
- "All rights reserved.\n");
+ printf("Copyright (c) 2010-2016, Joyent Inc. All rights reserved.\n");
#ifdef DEBUG
printf("DEBUG enabled\n");
#endif
diff --git a/usr/src/uts/common/os/lwp.c b/usr/src/uts/common/os/lwp.c
index feb8e76c42..a7de7b513f 100644
--- a/usr/src/uts/common/os/lwp.c
+++ b/usr/src/uts/common/os/lwp.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#include <sys/param.h>
@@ -57,6 +57,8 @@
#include <sys/lgrp.h>
#include <sys/rctl.h>
#include <sys/contract_impl.h>
+#include <sys/contract/process.h>
+#include <sys/contract/process_impl.h>
#include <sys/cpc_impl.h>
#include <sys/sdt.h>
#include <sys/cmn_err.h>
@@ -115,7 +117,7 @@ lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
ret_tidhash_t *ret_tidhash = NULL;
int i;
int rctlfail = 0;
- boolean_t branded = 0;
+ void *brand_data = NULL;
struct ctxop *ctx = NULL;
ASSERT(cid != sysdccid); /* system threads must start in SYS */
@@ -283,6 +285,19 @@ lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
*/
lep = kmem_zalloc(sizeof (*lep), KM_SLEEP);
+ /*
+ * If necessary, speculatively allocate lwp brand data. This is done
+ * ahead of time so p_lock need not be dropped during lwp branding.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_lwpdata_alloc != NULL) {
+ if ((brand_data = BROP(p)->b_lwpdata_alloc(p)) == NULL) {
+ mutex_enter(&p->p_lock);
+ err = 1;
+ atomic_inc_32(&p->p_zone->zone_ffmisc);
+ goto error;
+ }
+ }
+
mutex_enter(&p->p_lock);
grow:
/*
@@ -630,18 +645,6 @@ grow:
} while (lwp_hash_lookup(p, t->t_tid) != NULL);
}
- /*
- * If this is a branded process, let the brand do any necessary lwp
- * initialization.
- */
- if (PROC_IS_BRANDED(p)) {
- if (BROP(p)->b_initlwp(lwp)) {
- err = 1;
- atomic_inc_32(&p->p_zone->zone_ffmisc);
- goto error;
- }
- branded = 1;
- }
if (t->t_tid == 1) {
kpreempt_disable();
@@ -654,7 +657,6 @@ grow:
}
}
- p->p_lwpcnt++;
t->t_waitfor = -1;
/*
@@ -696,8 +698,27 @@ grow:
t->t_post_sys = 1;
/*
+ * Perform lwp branding
+ *
+ * The b_initlwp hook is _not_ allowed to drop p->p_lock as it must be
+ * continuously held between when the tidhash is sized and when the lwp
+ * is inserted into it. Operations requiring p->p_lock to be
+ * temporarily dropped can be performed in b_initlwp_post.
+ */
+ if (PROC_IS_BRANDED(p)) {
+ BROP(p)->b_initlwp(lwp, brand_data);
+ /*
+ * The b_initlwp hook is expected to consume any preallocated
+ * brand_data in a way that prepares it for deallocation by the
+ * b_freelwp hook.
+ */
+ brand_data = NULL;
+ }
+
+ /*
* Insert the new thread into the list of all threads.
*/
+ p->p_lwpcnt++;
if ((tx = p->p_tlist) == NULL) {
t->t_back = t;
t->t_forw = t;
@@ -718,6 +739,13 @@ grow:
lep->le_start = t->t_start;
lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
+ /*
+ * Complete lwp branding
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_initlwp_post != NULL) {
+ BROP(p)->b_initlwp_post(lwp);
+ }
+
if (state == TS_RUN) {
/*
* We set the new lwp running immediately.
@@ -753,8 +781,9 @@ error:
if (cid != NOCLASS && bufp != NULL)
CL_FREE(cid, bufp);
- if (branded)
- BROP(p)->b_freelwp(lwp);
+ if (brand_data != NULL) {
+ BROP(p)->b_lwpdata_free(brand_data);
+ }
mutex_exit(&p->p_lock);
t->t_state = TS_FREE;
@@ -827,8 +856,27 @@ lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
int i;
for (i = 0; i < ct_ntypes; i++) {
- dst->lwp_ct_active[i] = ctmpl_dup(src->lwp_ct_active[i]);
+ ct_template_t *tmpl = src->lwp_ct_active[i];
+
+ /*
+ * If the process contract template is setup to be preserved
+ * across exec, then if we're forking, perform an implicit
+ * template_clear now. This ensures that future children of
+ * this child will remain in the same contract unless they're
+ * explicitly setup differently. We know we're forking if the
+ * two LWPs belong to different processes.
+ */
+ if (i == CTT_PROCESS && tmpl != NULL) {
+ ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+ if (dst->lwp_procp != src->lwp_procp &&
+ (ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+ tmpl = NULL;
+ }
+
+ dst->lwp_ct_active[i] = ctmpl_dup(tmpl);
dst->lwp_ct_latest[i] = NULL;
+
}
}
@@ -836,21 +884,33 @@ lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
* Clear an LWP's contract template state.
*/
void
-lwp_ctmpl_clear(klwp_t *lwp)
+lwp_ctmpl_clear(klwp_t *lwp, boolean_t is_exec)
{
ct_template_t *tmpl;
int i;
for (i = 0; i < ct_ntypes; i++) {
- if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
- ctmpl_free(tmpl);
- lwp->lwp_ct_active[i] = NULL;
- }
-
if (lwp->lwp_ct_latest[i] != NULL) {
contract_rele(lwp->lwp_ct_latest[i]);
lwp->lwp_ct_latest[i] = NULL;
}
+
+ if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
+ /*
+ * If we're exec-ing a new program and the process
+ * contract template is setup to be preserved across
+ * exec, then don't clear it.
+ */
+ if (is_exec && i == CTT_PROCESS) {
+ ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+ if ((ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+ continue;
+ }
+
+ ctmpl_free(tmpl);
+ lwp->lwp_ct_active[i] = NULL;
+ }
}
}
@@ -891,13 +951,6 @@ lwp_exit(void)
if (t->t_upimutex != NULL)
upimutex_cleanup();
- /*
- * Perform any brand specific exit processing, then release any
- * brand data associated with the lwp
- */
- if (PROC_IS_BRANDED(p))
- BROP(p)->b_lwpexit(lwp);
-
lwp_pcb_exit();
mutex_enter(&p->p_lock);
@@ -941,6 +994,18 @@ lwp_exit(void)
DTRACE_PROC(lwp__exit);
/*
+ * Perform any brand specific exit processing, then release any
+ * brand data associated with the lwp
+ */
+ if (PROC_IS_BRANDED(p)) {
+ mutex_exit(&p->p_lock);
+ BROP(p)->b_lwpexit(lwp);
+ BROP(p)->b_freelwp(lwp);
+ mutex_enter(&p->p_lock);
+ prbarrier(p);
+ }
+
+ /*
* If the lwp is a detached lwp or if the process is exiting,
* remove (lwp_hash_out()) the lwp from the lwp directory.
* Otherwise null out the lwp's le_thread pointer in the lwp
@@ -1101,7 +1166,7 @@ lwp_cleanup(void)
}
kpreempt_enable();
- lwp_ctmpl_clear(ttolwp(t));
+ lwp_ctmpl_clear(ttolwp(t), B_FALSE);
}
int
diff --git a/usr/src/uts/common/os/main.c b/usr/src/uts/common/os/main.c
index 7afc1cfe00..dda0b3e4a6 100644
--- a/usr/src/uts/common/os/main.c
+++ b/usr/src/uts/common/os/main.c
@@ -27,7 +27,7 @@
/* All Rights Reserved */
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -157,7 +157,7 @@ exec_init(const char *initpath, const char *args)
int error = 0, count = 0;
proc_t *p = ttoproc(curthread);
klwp_t *lwp = ttolwp(curthread);
- int brand_action;
+ int brand_action = EBA_NONE;
if (args == NULL)
args = "";
@@ -268,7 +268,15 @@ exec_init(const char *initpath, const char *args)
*/
sigemptyset(&curthread->t_hold);
- brand_action = ZONE_IS_BRANDED(p->p_zone) ? EBA_BRAND : EBA_NONE;
+ /*
+ * Only instruct exec_common to brand the process if necessary. It is
+ * possible that the init process is already properly branded due to the
+ * proc_exit -> restart_init -> exec_init call chain.
+ */
+ if (ZONE_IS_BRANDED(p->p_zone) &&
+ p->p_brand != p->p_zone->zone_brand) {
+ brand_action = EBA_BRAND;
+ }
again:
error = exec_common((const char *)(uintptr_t)exec_fnamep,
(const char **)(uintptr_t)uap, NULL, brand_action);
diff --git a/usr/src/uts/common/os/mmapobj.c b/usr/src/uts/common/os/mmapobj.c
index b6b5446d71..596c855a45 100644
--- a/usr/src/uts/common/os/mmapobj.c
+++ b/usr/src/uts/common/os/mmapobj.c
@@ -1360,10 +1360,15 @@ calc_loadable(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, size_t *len,
}
if (num_segs++ == 0) {
/*
- * The p_vaddr of the first PT_LOAD segment
- * must either be NULL or within the first
- * page in order to be interpreted.
- * Otherwise, its an invalid file.
+ * While ELF doesn't specify the meaning of
+ * p_vaddr for PT_LOAD segments in ET_DYN
+ * objects, we mandate that is either NULL or
+ * (to accommodate some historical binaries)
+ * within the first page. (Note that there
+ * exist non-native ET_DYN objects that violate
+ * this constraint that we nonetheless must be
+ * able to execute; see the ET_DYN handling in
+ * mapelfexec() for details.)
*/
if (e_type == ET_DYN &&
((caddr_t)((uintptr_t)vaddr &
diff --git a/usr/src/uts/common/os/netstack.c b/usr/src/uts/common/os/netstack.c
index 5c556bffef..1bed9d7406 100644
--- a/usr/src/uts/common/os/netstack.c
+++ b/usr/src/uts/common/os/netstack.c
@@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2016, Joyent, Inc. All rights reserved.
*/
#include <sys/param.h>
@@ -340,7 +340,7 @@ netstack_zone_create(zoneid_t zoneid)
/*
* Should never find a pre-existing exclusive stack
*/
- ASSERT(stackid == GLOBAL_NETSTACKID);
+ VERIFY(stackid == GLOBAL_NETSTACKID);
kmem_free(ns, sizeof (netstack_t));
ns = *nsp;
mutex_enter(&ns->netstack_lock);
@@ -1041,6 +1041,26 @@ netstack_find_by_stackid(netstackid_t stackid)
return (NULL);
}
+boolean_t
+netstack_inuse_by_stackid(netstackid_t stackid)
+{
+ netstack_t *ns;
+ boolean_t rval = B_FALSE;
+
+ mutex_enter(&netstack_g_lock);
+
+ for (ns = netstack_head; ns != NULL; ns = ns->netstack_next) {
+ if (ns->netstack_stackid == stackid) {
+ rval = B_TRUE;
+ break;
+ }
+ }
+
+ mutex_exit(&netstack_g_lock);
+
+ return (rval);
+}
+
void
netstack_rele(netstack_t *ns)
{
diff --git a/usr/src/uts/common/os/pid.c b/usr/src/uts/common/os/pid.c
index b555bb82b7..eba6147fab 100644
--- a/usr/src/uts/common/os/pid.c
+++ b/usr/src/uts/common/os/pid.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -112,6 +113,18 @@ pid_lookup(pid_t pid)
return (pidp);
}
+struct pid *
+pid_find(pid_t pid)
+{
+ struct pid *pidp;
+
+ mutex_enter(&pidlinklock);
+ pidp = pid_lookup(pid);
+ mutex_exit(&pidlinklock);
+
+ return (pidp);
+}
+
void
pid_setmin(void)
{
@@ -522,6 +535,20 @@ sprunlock(proc_t *p)
THREAD_KPRI_RELEASE();
}
+/*
+ * Undo effects of sprlock but without dropping p->p_lock
+ */
+void
+sprunprlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+ THREAD_KPRI_RELEASE();
+}
+
void
pid_init(void)
{
diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c
index 07bc2920da..d2bdb4ce37 100644
--- a/usr/src/uts/common/os/policy.c
+++ b/usr/src/uts/common/os/policy.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/types.h>
@@ -55,6 +55,7 @@
#include <sys/mntent.h>
#include <sys/contract_impl.h>
#include <sys/dld_ioc.h>
+#include <sys/brand.h>
/*
* There are two possible layers of privilege routines and two possible
@@ -1243,6 +1244,22 @@ secpolicy_vnode_owner(const cred_t *cr, uid_t owner)
void
secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
{
+ proc_t *p = curproc;
+
+ /*
+ * Allow the brand to override this behaviour.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_setid_clear != NULL) {
+ /*
+ * This brand hook will return 0 if handling is complete, or
+ * some other value if the brand would like us to fall back to
+ * the usual behaviour.
+ */
+ if (BROP(p)->b_setid_clear(vap, cr) == 0) {
+ return;
+ }
+ }
+
if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 &&
secpolicy_vnode_setid_retain(cr,
(vap->va_mode & S_ISUID) != 0 &&
@@ -2078,6 +2095,13 @@ secpolicy_meminfo(const cred_t *cr)
}
int
+secpolicy_fs_import(const cred_t *cr)
+{
+ return (PRIV_POLICY(cr, PRIV_SYS_FS_IMPORT, B_FALSE, EPERM, NULL));
+}
+
+
+int
secpolicy_pfexec_register(const cred_t *cr)
{
return (PRIV_POLICY(cr, PRIV_SYS_ADMIN, B_TRUE, EPERM, NULL));
@@ -2581,3 +2605,11 @@ secpolicy_ppp_config(const cred_t *cr)
return (secpolicy_net_config(cr, B_FALSE));
return (PRIV_POLICY(cr, PRIV_SYS_PPP_CONFIG, B_FALSE, EPERM, NULL));
}
+
+int
+secpolicy_hyprlofs_control(const cred_t *cr)
+{
+ if (PRIV_POLICY(cr, PRIV_HYPRLOFS_CONTROL, B_FALSE, EPERM, NULL))
+ return (EPERM);
+ return (0);
+}
diff --git a/usr/src/uts/common/os/priv_defs b/usr/src/uts/common/os/priv_defs
index a3cdaccc2a..cc1c5e03a6 100644
--- a/usr/src/uts/common/os/priv_defs
+++ b/usr/src/uts/common/os/priv_defs
@@ -177,6 +177,10 @@ privilege PRIV_GRAPHICS_MAP
Allows a process to perform privileged mappings through a
graphics device.
+privilege PRIV_HYPRLOFS_CONTROL
+
+ Allows a process to manage hyprlofs entries.
+
privilege PRIV_IPC_DAC_READ
Allows a process to read a System V IPC
@@ -372,6 +376,10 @@ privilege PRIV_SYS_DEVICES
Allows a process to open the real console device directly.
Allows a process to open devices that have been exclusively opened.
+privilege PRIV_SYS_FS_IMPORT
+
+ Allows a process to import a potentially untrusted file system.
+
privilege PRIV_SYS_IPC_CONFIG
Allows a process to increase the size of a System V IPC Message
diff --git a/usr/src/uts/common/os/sched.c b/usr/src/uts/common/os/sched.c
index c1d6569f11..15e77d39f7 100644
--- a/usr/src/uts/common/os/sched.c
+++ b/usr/src/uts/common/os/sched.c
@@ -27,6 +27,10 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
@@ -646,16 +650,17 @@ top:
klwp_t *lwp = ttolwp(tp);
/*
- * Swapout eligible lwps (specified by the scheduling
- * class) which don't have TS_DONT_SWAP set. Set the
- * "intent to swap" flag (TS_SWAPENQ) on threads
- * which have TS_DONT_SWAP set so that they can be
+ * Swapout eligible lwps (specified by the scheduling class)
+ * which don't have TS_DONT_SWAP set. Set the "intent to swap"
+ * flag (TS_SWAPENQ) on threads which have either TS_DONT_SWAP
+ * set or are currently on a split stack so that they can be
* swapped if and when they reach a safe point.
*/
thread_lock(tp);
thread_pri = CL_SWAPOUT(tp, swapflags);
if (thread_pri != -1) {
- if (tp->t_schedflag & TS_DONT_SWAP) {
+ if ((tp->t_schedflag & TS_DONT_SWAP) ||
+ (tp->t_flag & T_SPLITSTK)) {
tp->t_schedflag |= TS_SWAPENQ;
tp->t_trapret = 1;
aston(tp);
diff --git a/usr/src/uts/common/os/shm.c b/usr/src/uts/common/os/shm.c
index bacc595f78..5deae96d73 100644
--- a/usr/src/uts/common/os/shm.c
+++ b/usr/src/uts/common/os/shm.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -319,6 +320,7 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
size_t share_size;
struct shm_data ssd;
uintptr_t align_hint;
+ long curprot;
/*
* Pick a share pagesize to use, if (!isspt(sp)).
@@ -453,6 +455,7 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
}
}
+ curprot = sp->shm_opts & SHM_PROT_MASK;
if (!isspt(sp)) {
error = sptcreate(size, &segspt, sp->shm_amp, prot,
flags, share_szc);
@@ -462,8 +465,8 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
}
sp->shm_sptinfo->sptas = segspt->s_as;
sp->shm_sptseg = segspt;
- sp->shm_sptprot = prot;
- } else if ((prot & sp->shm_sptprot) != sp->shm_sptprot) {
+ sp->shm_opts = (sp->shm_opts & ~SHM_PROT_MASK) | prot;
+ } else if ((prot & curprot) != curprot) {
/*
* Ensure we're attaching to an ISM segment with
* fewer or equal permissions than what we're
@@ -748,6 +751,23 @@ shmctl(int shmid, int cmd, void *arg)
}
break;
+ /* Stage segment for removal, but don't remove until last detach */
+ case SHM_RMID:
+ if ((error = secpolicy_ipc_owner(cr, (kipc_perm_t *)sp)) != 0)
+ break;
+
+ /*
+ * If attached, just mark it as a pending remove, otherwise
+ * we must perform the normal ipc_rmid now.
+ */
+ if ((sp->shm_perm.ipc_ref - 1) > 0) {
+ sp->shm_opts |= SHM_RM_PENDING;
+ } else {
+ mutex_exit(lock);
+ return (ipc_rmid(shm_svc, shmid, cr));
+ }
+ break;
+
default:
error = EINVAL;
break;
@@ -778,6 +798,23 @@ shm_detach(proc_t *pp, segacct_t *sap)
sp->shm_ismattch--;
sp->shm_dtime = gethrestime_sec();
sp->shm_lpid = pp->p_pid;
+ if ((sp->shm_opts & SHM_RM_PENDING) != 0 &&
+ sp->shm_perm.ipc_ref == 2) {
+ /*
+ * If this is the last detach of the segment across the whole
+ * system then now we can perform the delayed IPC_RMID.
+ * The ipc_ref count has 1 for the original 'get' and one for
+ * each 'attach' (see 'stat' handling in shmctl).
+ */
+ sp->shm_opts &= ~SHM_RM_PENDING;
+ mutex_enter(&shm_svc->ipcs_lock);
+ ipc_rmsvc(shm_svc, (kipc_perm_t *)sp); /* Drops lock */
+ ASSERT(!MUTEX_HELD(&shm_svc->ipcs_lock));
+ ASSERT(((kipc_perm_t *)sp)->ipc_ref > 0);
+
+ /* Lock was dropped, need to retake it for following rele. */
+ (void) ipc_lock(shm_svc, sp->shm_perm.ipc_id);
+ }
ipc_rele(shm_svc, (kipc_perm_t *)sp); /* Drops lock */
kmem_free(sap, sizeof (segacct_t));
diff --git a/usr/src/uts/common/os/sig.c b/usr/src/uts/common/os/sig.c
index 453b1f22d4..5ef12f3ae4 100644
--- a/usr/src/uts/common/os/sig.c
+++ b/usr/src/uts/common/os/sig.c
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -60,6 +60,7 @@
#include <sys/cyclic.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
#include <sys/signalfd.h>
const k_sigset_t nullsmask = {0, 0, 0};
@@ -148,6 +149,21 @@ signal_is_blocked(kthread_t *t, int sig)
}
/*
+ * Return true if the signal can safely be ignored.
+ * That is, if the signal is included in the p_ignore mask and doing so is not
+ * forbidden by any process branding.
+ */
+static int
+sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+ return (sigismember(&p->p_ignore, sig) && /* sig in ignore mask */
+ !(PROC_IS_BRANDED(p) && /* allowed by brand */
+ BROP(p)->b_sig_ignorable != NULL &&
+ BROP(p)->b_sig_ignorable(p, lwp, sig) == B_FALSE));
+
+}
+
+/*
* Return true if the signal can safely be discarded on generation.
* That is, if there is no need for the signal on the receiving end.
* The answer is true if the process is a zombie or
@@ -159,12 +175,13 @@ signal_is_blocked(kthread_t *t, int sig)
* the signal is not being accepted via sigwait()
*/
static int
-sig_discardable(proc_t *p, int sig)
+sig_discardable(proc_t *p, kthread_t *tp, int sig)
{
kthread_t *t = p->p_tlist;
+ klwp_t *lwp = (tp == NULL) ? NULL : tp->t_lwp;
return (t == NULL || /* if zombie or ... */
- (sigismember(&p->p_ignore, sig) && /* signal is ignored */
+ (sig_ignorable(p, lwp, sig) && /* signal is ignored */
t->t_forw == t && /* and single-threaded */
!tracing(p, sig) && /* and no /proc tracing */
!signal_is_blocked(t, sig) && /* and signal not blocked */
@@ -200,7 +217,7 @@ eat_signal(kthread_t *t, int sig)
!(ttoproc(t)->p_proc_flag & P_PR_LOCK)) {
ttoproc(t)->p_stopsig = 0;
t->t_dtrace_stop = 0;
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
} else if (t != curthread && t->t_state == TS_ONPROC) {
aston(t); /* make it do issig promptly */
@@ -297,7 +314,7 @@ sigtoproc(proc_t *p, kthread_t *t, int sig)
}
}
- if (sig_discardable(p, sig)) {
+ if (sig_discardable(p, t, sig)) {
DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist,
proc_t *, p, int, sig);
return;
@@ -497,7 +514,7 @@ issig_justlooking(void)
if (sigismember(&set, sig) &&
(tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig))) {
+ !sig_ignorable(p, lwp, sig))) {
/*
* Don't promote a signal that will stop
* the process when lwp_nostop is set.
@@ -623,6 +640,21 @@ issig_forreal(void)
}
/*
+ * Allow the brand the chance to alter (or suppress) delivery
+ * of this signal.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) {
+ /*
+ * The brand hook will return 0 if it would like
+ * us to drive on, or -1 if we should restart
+ * the loop to check other conditions.
+ */
+ if (BROP(p)->b_issig_stop(p, lwp) != 0) {
+ continue;
+ }
+ }
+
+ /*
* Honor requested stop before dealing with the
* current signal; a debugger may change it.
* Do not want to go back to loop here since this is a special
@@ -656,7 +688,7 @@ issig_forreal(void)
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (sigismember(&t->t_sigwait, sig) ||
- (!sigismember(&p->p_ignore, sig) &&
+ (!sig_ignorable(p, lwp, sig) &&
!isjobstop(sig))) {
if (p->p_flag & (SEXITLWPS|SKILLED)) {
sig = SIGKILL;
@@ -708,7 +740,7 @@ issig_forreal(void)
toproc = 0;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&t->t_extsig, sig))
ext = 1;
break;
@@ -722,7 +754,7 @@ issig_forreal(void)
toproc = 1;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&p->p_extsig, sig))
ext = 1;
break;
@@ -954,6 +986,16 @@ stop(int why, int what)
}
break;
+ case PR_BRAND:
+ /*
+ * We have been stopped by the brand code for a brand-private
+ * reason. This is an asynchronous stop affecting only this
+ * LWP.
+ */
+ VERIFY(PROC_IS_BRANDED(p));
+ flags &= ~TS_BSTART;
+ break;
+
default: /* /proc stop */
flags &= ~TS_PSTART;
/*
@@ -1065,7 +1107,7 @@ stop(int why, int what)
}
}
- if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) {
+ if (why != PR_JOBCONTROL && why != PR_CHECKPOINT && why != PR_BRAND) {
/*
* Do process-level notification when all lwps are
* either stopped on events of interest to /proc
@@ -1171,6 +1213,13 @@ stop(int why, int what)
if (why == PR_CHECKPOINT)
del_one_utstop();
+ /*
+ * Allow the brand to post notification of this stop condition.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_stop_notify != NULL) {
+ BROP(p)->b_stop_notify(p, lwp, why, what);
+ }
+
thread_lock(t);
ASSERT((t->t_schedflag & TS_ALLSTART) == 0);
t->t_schedflag |= flags;
@@ -1192,7 +1241,7 @@ stop(int why, int what)
(p->p_flag & (SEXITLWPS|SKILLED))) {
p->p_stopsig = 0;
thread_lock(t);
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
thread_unlock_nopreempt(t);
} else if (why == PR_JOBCONTROL) {
@@ -1327,7 +1376,7 @@ psig(void)
* this signal from pending to current (we dropped p->p_lock).
* This can happen only in a multi-threaded process.
*/
- if (sigismember(&p->p_ignore, sig) ||
+ if (sig_ignorable(p, lwp, sig) ||
(func == SIG_DFL && sigismember(&stopdefault, sig))) {
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
@@ -1771,9 +1820,12 @@ post_sigcld(proc_t *cp, sigqueue_t *sqp)
/*
* This can only happen when the parent is init.
* (See call to sigcld(q, NULL) in exit().)
- * Use KM_NOSLEEP to avoid deadlock.
+ * Use KM_NOSLEEP to avoid deadlock. The child procs
+ * initpid can be 1 for zlogin.
*/
- ASSERT(pp == proc_init);
+ ASSERT(pp->p_pidp->pid_id ==
+ cp->p_zone->zone_proc_initpid ||
+ pp->p_pidp->pid_id == 1);
winfo(cp, &info, 0);
sigaddq(pp, NULL, &info, KM_NOSLEEP);
} else {
@@ -1804,6 +1856,15 @@ sigcld_repost()
sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
mutex_enter(&pidlock);
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_sigcld_repost != NULL) {
+ /*
+ * Allow the brand to inject synthetic SIGCLD signals.
+ */
+ if (BROP(pp)->b_sigcld_repost(pp, sqp) == 0) {
+ mutex_exit(&pidlock);
+ return;
+ }
+ }
for (cp = pp->p_child; cp; cp = cp->p_sibling) {
if (cp->p_pidflag & CLDPEND) {
post_sigcld(cp, sqp);
@@ -2115,7 +2176,7 @@ sigaddqa(proc_t *p, kthread_t *t, sigqueue_t *sigqp)
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(sig >= 1 && sig < NSIG);
- if (sig_discardable(p, sig))
+ if (sig_discardable(p, t, sig))
siginfofree(sigqp);
else
sigaddqins(p, t, sigqp);
@@ -2141,7 +2202,7 @@ sigaddq(proc_t *p, kthread_t *t, k_siginfo_t *infop, int km_flags)
* blocking the signal (it *could* change it's mind while
* the signal is pending) then don't bother creating one.
*/
- if (!sig_discardable(p, sig) &&
+ if (!sig_discardable(p, t, sig) &&
(sigismember(&p->p_siginfo, sig) ||
(curproc->p_ct_process != p->p_ct_process) ||
(sig == SIGCLD && SI_FROMKERNEL(infop))) &&
diff --git a/usr/src/uts/common/os/smb_subr.c b/usr/src/uts/common/os/smb_subr.c
index 6084676b17..6dc7230bed 100644
--- a/usr/src/uts/common/os/smb_subr.c
+++ b/usr/src/uts/common/os/smb_subr.c
@@ -25,7 +25,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
#include <sys/smbios_impl.h>
#include <sys/cmn_err.h>
@@ -43,13 +45,13 @@ smb_strerror(int err)
void *
smb_alloc(size_t len)
{
- return (kmem_alloc(len, KM_SLEEP));
+ return (len > 0 ? kmem_alloc(len, KM_SLEEP) : NULL);
}
void *
smb_zalloc(size_t len)
{
- return (kmem_zalloc(len, KM_SLEEP));
+ return (len > 0 ? kmem_zalloc(len, KM_SLEEP) : NULL);
}
void
diff --git a/usr/src/uts/common/os/streamio.c b/usr/src/uts/common/os/streamio.c
index 62f94729cf..0a1406e0cd 100644
--- a/usr/src/uts/common/os/streamio.c
+++ b/usr/src/uts/common/os/streamio.c
@@ -24,7 +24,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -77,6 +77,7 @@
#include <sys/policy.h>
#include <sys/dld.h>
#include <sys/zone.h>
+#include <sys/limits.h>
#include <c2/audit.h>
/*
@@ -985,12 +986,20 @@ strget(struct stdata *stp, queue_t *q, struct uio *uiop, int first,
* (registered in sd_wakeq).
*/
struiod_t uiod;
+ struct iovec buf[IOV_MAX_STACK];
+ int iovlen = 0;
if (first)
stp->sd_wakeq &= ~RSLEEP;
- (void) uiodup(uiop, &uiod.d_uio, uiod.d_iov,
- sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+ if (uiop->uio_iovcnt > IOV_MAX_STACK) {
+ iovlen = uiop->uio_iovcnt * sizeof (iovec_t);
+ uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
+ } else {
+ uiod.d_iov = buf;
+ }
+
+ (void) uiodup(uiop, &uiod.d_uio, uiod.d_iov, uiop->uio_iovcnt);
uiod.d_mp = 0;
/*
* Mark that a thread is in rwnext on the read side
@@ -1029,6 +1038,8 @@ strget(struct stdata *stp, queue_t *q, struct uio *uiop, int first,
if ((bp = uiod.d_mp) != NULL) {
*errorp = 0;
ASSERT(MUTEX_HELD(&stp->sd_lock));
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (bp);
}
error = 0;
@@ -1048,8 +1059,14 @@ strget(struct stdata *stp, queue_t *q, struct uio *uiop, int first,
} else {
*errorp = error;
ASSERT(MUTEX_HELD(&stp->sd_lock));
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (NULL);
}
+
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
+
/*
* Try a getq in case a rwnext() generated mblk
* has bubbled up via strrput().
@@ -2544,6 +2561,8 @@ strput(struct stdata *stp, mblk_t *mctl, struct uio *uiop, ssize_t *iosize,
int b_flag, int pri, int flags)
{
struiod_t uiod;
+ struct iovec buf[IOV_MAX_STACK];
+ int iovlen = 0;
mblk_t *mp;
queue_t *wqp = stp->sd_wrq;
int error = 0;
@@ -2635,13 +2654,21 @@ strput(struct stdata *stp, mblk_t *mctl, struct uio *uiop, ssize_t *iosize,
mp->b_flag |= b_flag;
mp->b_band = (uchar_t)pri;
- (void) uiodup(uiop, &uiod.d_uio, uiod.d_iov,
- sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+ if (uiop->uio_iovcnt > IOV_MAX_STACK) {
+ iovlen = uiop->uio_iovcnt * sizeof (iovec_t);
+ uiod.d_iov = (struct iovec *)kmem_alloc(iovlen, KM_SLEEP);
+ } else {
+ uiod.d_iov = buf;
+ }
+
+ (void) uiodup(uiop, &uiod.d_uio, uiod.d_iov, uiop->uio_iovcnt);
uiod.d_uio.uio_offset = 0;
uiod.d_mp = mp;
error = rwnext(wqp, &uiod);
if (! uiod.d_mp) {
uioskip(uiop, *iosize);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (error);
}
ASSERT(mp == uiod.d_mp);
@@ -2659,17 +2686,23 @@ strput(struct stdata *stp, mblk_t *mctl, struct uio *uiop, ssize_t *iosize,
error = 0;
} else {
freemsg(mp);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (error);
}
/* Have to check canput before consuming data from the uio */
if (pri == 0) {
if (!canputnext(wqp) && !(flags & MSG_IGNFLOW)) {
freemsg(mp);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (EWOULDBLOCK);
}
} else {
if (!bcanputnext(wqp, pri) && !(flags & MSG_IGNFLOW)) {
freemsg(mp);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (EWOULDBLOCK);
}
}
@@ -2677,6 +2710,8 @@ strput(struct stdata *stp, mblk_t *mctl, struct uio *uiop, ssize_t *iosize,
/* Copyin data from the uio */
if ((error = struioget(wqp, mp, &uiod, 0)) != 0) {
freemsg(mp);
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (error);
}
uioskip(uiop, *iosize);
@@ -2693,6 +2728,8 @@ strput(struct stdata *stp, mblk_t *mctl, struct uio *uiop, ssize_t *iosize,
putnext(wqp, mp);
stream_runservice(stp);
}
+ if (iovlen != 0)
+ kmem_free(uiod.d_iov, iovlen);
return (0);
}
@@ -3178,6 +3215,7 @@ job_control_type(int cmd)
case JAGENT: /* Obsolete */
case JTRUN: /* Obsolete */
case JXTPROTO: /* Obsolete */
+ case TIOCSETLD:
return (JCSETP);
}
diff --git a/usr/src/uts/common/os/sysent.c b/usr/src/uts/common/os/sysent.c
index 0d1bb6a8a1..1e61f3d24c 100644
--- a/usr/src/uts/common/os/sysent.c
+++ b/usr/src/uts/common/os/sysent.c
@@ -23,7 +23,7 @@
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -61,8 +61,7 @@ struct mmaplf32a;
int access(char *, int);
int alarm(int);
int auditsys(struct auditcalls *, rval_t *);
-int64_t brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t);
+int64_t brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
int brk(caddr_t);
int chdir(char *);
int chmod(char *, int);
@@ -646,7 +645,7 @@ struct sysent sysent[NSYSCALL] =
SYSENT_NOSYS(),
SYSENT_C("llseek", llseek32, 4)),
/* 176 */ SYSENT_LOADABLE(), /* inst_sync */
- /* 177 */ SYSENT_CI("brandsys", brandsys, 6),
+ /* 177 */ SYSENT_CI("brandsys", brandsys, 5),
/* 178 */ SYSENT_LOADABLE(), /* kaio */
/* 179 */ SYSENT_LOADABLE(), /* cpc */
/* 180 */ SYSENT_CI("lgrpsys", lgrpsys, 3),
@@ -1001,7 +1000,7 @@ struct sysent sysent32[NSYSCALL] =
/* 174 */ SYSENT_CI("pwrite", pwrite32, 4),
/* 175 */ SYSENT_C("llseek", llseek32, 4),
/* 176 */ SYSENT_LOADABLE32(), /* inst_sync */
- /* 177 */ SYSENT_CI("brandsys", brandsys, 6),
+ /* 177 */ SYSENT_CI("brandsys", brandsys, 5),
/* 178 */ SYSENT_LOADABLE32(), /* kaio */
/* 179 */ SYSENT_LOADABLE32(), /* cpc */
/* 180 */ SYSENT_CI("lgrpsys", lgrpsys, 3),
@@ -1093,18 +1092,20 @@ char **syscallnames;
systrace_sysent_t *systrace_sysent;
void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
/*ARGSUSED*/
void
systrace_stub(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7)
{}
/*ARGSUSED*/
int64_t
dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+ uintptr_t arg7)
{
systrace_sysent_t *sy = &systrace_sysent[curthread->t_sysnum];
dtrace_id_t id;
@@ -1112,7 +1113,8 @@ dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
proc_t *p;
if ((id = sy->stsy_entry) != DTRACE_IDNONE)
- (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+ (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
/*
* We want to explicitly allow DTrace consumers to stop a process
@@ -1126,14 +1128,15 @@ dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
}
mutex_exit(&p->p_lock);
- rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+ rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
if (ttolwp(curthread)->lwp_errno != 0)
rval = -1;
if ((id = sy->stsy_return) != DTRACE_IDNONE)
(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
- (uintptr_t)((int64_t)rval >> 32), 0, 0, 0);
+ (uintptr_t)((int64_t)rval >> 32), 0, 0, 0, 0, 0);
return (rval);
}
@@ -1145,7 +1148,8 @@ systrace_sysent_t *systrace_sysent32;
/*ARGSUSED*/
int64_t
dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+ uintptr_t arg7)
{
systrace_sysent_t *sy = &systrace_sysent32[curthread->t_sysnum];
dtrace_id_t id;
@@ -1153,7 +1157,8 @@ dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
proc_t *p;
if ((id = sy->stsy_entry) != DTRACE_IDNONE)
- (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+ (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+ arg7);
/*
* We want to explicitly allow DTrace consumers to stop a process
@@ -1167,14 +1172,15 @@ dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
}
mutex_exit(&p->p_lock);
- rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+ rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+ arg7);
if (ttolwp(curthread)->lwp_errno != 0)
rval = -1;
if ((id = sy->stsy_return) != DTRACE_IDNONE)
(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
- (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0);
+ (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0, 0, 0);
return (rval);
}
@@ -1202,5 +1208,5 @@ dtrace_systrace_rtt(void)
}
if ((id = sy->stsy_return) != DTRACE_IDNONE)
- (*systrace_probe)(id, 0, 0, 0, 0, 0, 0);
+ (*systrace_probe)(id, 0, 0, 0, 0, 0, 0, 0, 0);
}
diff --git a/usr/src/uts/common/os/timers.c b/usr/src/uts/common/os/timers.c
index 61acc6cf97..53be806026 100644
--- a/usr/src/uts/common/os/timers.c
+++ b/usr/src/uts/common/os/timers.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -1172,6 +1173,14 @@ timespectohz64(timespec_t *tv)
void
hrt2ts(hrtime_t hrt, timestruc_t *tsp)
{
+#if defined(__amd64)
+ /*
+ * The cleverness explained above is unecessary on x86_64 CPUs where
+ * modern compilers are able to optimize down to faster operations.
+ */
+ tsp->tv_sec = hrt / NANOSEC;
+ tsp->tv_nsec = hrt % NANOSEC;
+#else
uint32_t sec, nsec, tmp;
tmp = (uint32_t)(hrt >> 30);
@@ -1193,20 +1202,28 @@ hrt2ts(hrtime_t hrt, timestruc_t *tsp)
}
tsp->tv_sec = (time_t)sec;
tsp->tv_nsec = nsec;
+#endif /* defined(__amd64) */
}
/*
* Convert from timestruc_t to hrtime_t.
- *
- * The code below is equivalent to:
- *
- * hrt = tsp->tv_sec * NANOSEC + tsp->tv_nsec;
- *
- * but requires no integer multiply.
*/
hrtime_t
ts2hrt(const timestruc_t *tsp)
{
+#if defined(__amd64) || defined(__i386)
+ /*
+ * On modern x86 CPUs, the simple version is faster.
+ */
+ return ((tsp->tv_sec * NANOSEC) + tsp->tv_nsec);
+#else
+ /*
+ * The code below is equivalent to:
+ *
+ * hrt = tsp->tv_sec * NANOSEC + tsp->tv_nsec;
+ *
+ * but requires no integer multiply.
+ */
hrtime_t hrt;
hrt = tsp->tv_sec;
@@ -1215,6 +1232,7 @@ ts2hrt(const timestruc_t *tsp)
hrt = (hrt << 7) - hrt - hrt - hrt;
hrt = (hrt << 9) + tsp->tv_nsec;
return (hrt);
+#endif /* defined(__amd64) || defined(__i386) */
}
/*
@@ -1246,6 +1264,13 @@ tv2hrt(struct timeval *tvp)
void
hrt2tv(hrtime_t hrt, struct timeval *tvp)
{
+#if defined(__amd64)
+ /*
+ * Like hrt2ts, the simple version is faster on x86_64.
+ */
+ tvp->tv_sec = hrt / NANOSEC;
+ tvp->tv_usec = (hrt % NANOSEC) / (NANOSEC / MICROSEC);
+#else
uint32_t sec, nsec, tmp;
uint32_t q, r, t;
@@ -1267,17 +1292,17 @@ hrt2tv(hrtime_t hrt, struct timeval *tvp)
sec++;
}
tvp->tv_sec = (time_t)sec;
-/*
- * this routine is very similar to hr2ts, but requires microseconds
- * instead of nanoseconds, so an interger divide by 1000 routine
- * completes the conversion
- */
+ /*
+ * this routine is very similar to hr2ts, but requires microseconds
+ * instead of nanoseconds, so an interger divide by 1000 routine
+ * completes the conversion
+ */
t = (nsec >> 7) + (nsec >> 8) + (nsec >> 12);
q = (nsec >> 1) + t + (nsec >> 15) + (t >> 11) + (t >> 14);
q = q >> 9;
r = nsec - q*1000;
tvp->tv_usec = q + ((r + 24) >> 10);
-
+#endif /* defined(__amd64) */
}
int
diff --git a/usr/src/uts/common/os/vmem.c b/usr/src/uts/common/os/vmem.c
index f67b06993e..e1c86f5071 100644
--- a/usr/src/uts/common/os/vmem.c
+++ b/usr/src/uts/common/os/vmem.c
@@ -1618,7 +1618,7 @@ vmem_destroy(vmem_t *vmp)
leaked = vmem_size(vmp, VMEM_ALLOC);
if (leaked != 0)
- cmn_err(CE_WARN, "vmem_destroy('%s'): leaked %lu %s",
+ cmn_err(CE_WARN, "!vmem_destroy('%s'): leaked %lu %s",
vmp->vm_name, leaked, (vmp->vm_cflags & VMC_IDENTIFIER) ?
"identifiers" : "bytes");
diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c
index 1028f9ce3f..36d8b02031 100644
--- a/usr/src/uts/common/os/zone.c
+++ b/usr/src/uts/common/os/zone.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent Inc. All rights reserved.
+ * Copyright 2016, Joyent Inc.
*/
/*
@@ -250,6 +250,8 @@
#include <sys/cpucaps.h>
#include <vm/seg.h>
#include <sys/mac.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
/*
* This constant specifies the number of seconds that threads waiting for
@@ -370,8 +372,12 @@ static char *zone_ref_subsys_names[] = {
rctl_hndl_t rc_zone_cpu_shares;
rctl_hndl_t rc_zone_locked_mem;
rctl_hndl_t rc_zone_max_swap;
+rctl_hndl_t rc_zone_phys_mem;
rctl_hndl_t rc_zone_max_lofi;
rctl_hndl_t rc_zone_cpu_cap;
+rctl_hndl_t rc_zone_cpu_baseline;
+rctl_hndl_t rc_zone_cpu_burst_time;
+rctl_hndl_t rc_zone_zfs_io_pri;
rctl_hndl_t rc_zone_nlwps;
rctl_hndl_t rc_zone_nprocs;
rctl_hndl_t rc_zone_shmmax;
@@ -417,8 +423,9 @@ static boolean_t zsd_wait_for_inprogress(zone_t *, struct zsd_entry *,
* Version 5 alters the zone_boot system call, and converts its old
* bootargs parameter to be set by the zone_setattr API instead.
* Version 6 adds the flag argument to zone_create.
+ * Version 7 adds the requested zoneid to zone_create.
*/
-static const int ZONE_SYSCALL_API_VERSION = 6;
+static const int ZONE_SYSCALL_API_VERSION = 7;
/*
* Certain filesystems (such as NFS and autofs) need to know which zone
@@ -1377,6 +1384,114 @@ static rctl_ops_t zone_cpu_cap_ops = {
/*ARGSUSED*/
static rctl_qty_t
+zone_cpu_base_get(rctl_t *rctl, struct proc *p)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (cpucaps_zone_get_base(p->p_zone));
+}
+
+/*
+ * The zone cpu base is used to set the baseline CPU for the zone
+ * so we can track when the zone is bursting.
+ */
+/*ARGSUSED*/
+static int
+zone_cpu_base_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ return (cpucaps_zone_set_base(zone, nv));
+}
+
+static rctl_ops_t zone_cpu_base_ops = {
+ rcop_no_action,
+ zone_cpu_base_get,
+ zone_cpu_base_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
+zone_cpu_burst_time_get(rctl_t *rctl, struct proc *p)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (cpucaps_zone_get_burst_time(p->p_zone));
+}
+
+/*
+ * The zone cpu burst time is used to set the amount of time CPU(s) can be
+ * bursting for the zone.
+ */
+/*ARGSUSED*/
+static int
+zone_cpu_burst_time_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ return (cpucaps_zone_set_burst_time(zone, nv));
+}
+
+static rctl_ops_t zone_cpu_burst_time_ops = {
+ rcop_no_action,
+ zone_cpu_burst_time_get,
+ zone_cpu_burst_time_set,
+ rcop_no_test
+};
+
+/*
+ * zone.zfs-io-pri resource control support (IO priority).
+ */
+/*ARGSUSED*/
+static rctl_qty_t
+zone_zfs_io_pri_get(rctl_t *rctl, struct proc *p)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (p->p_zone->zone_zfs_io_pri);
+}
+
+/*ARGSUSED*/
+static int
+zone_zfs_io_pri_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ /*
+ * set priority to the new value.
+ */
+ zone->zone_zfs_io_pri = nv;
+ return (0);
+}
+
+static rctl_ops_t zone_zfs_io_pri_ops = {
+ rcop_no_action,
+ zone_zfs_io_pri_get,
+ zone_zfs_io_pri_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
zone_lwps_usage(rctl_t *r, proc_t *p)
{
rctl_qty_t nlwps;
@@ -1703,6 +1818,39 @@ static rctl_ops_t zone_max_swap_ops = {
/*ARGSUSED*/
static rctl_qty_t
+zone_phys_mem_usage(rctl_t *rctl, struct proc *p)
+{
+ rctl_qty_t q;
+ zone_t *z = p->p_zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ /* No additional lock because not enforced in the kernel */
+ q = z->zone_phys_mem;
+ return (q);
+}
+
+/*ARGSUSED*/
+static int
+zone_phys_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+ if (e->rcep_p.zone == NULL)
+ return (0);
+ e->rcep_p.zone->zone_phys_mem_ctl = nv;
+ return (0);
+}
+
+static rctl_ops_t zone_phys_mem_ops = {
+ rcop_no_action,
+ zone_phys_mem_usage,
+ zone_phys_mem_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
zone_max_lofi_usage(rctl_t *rctl, struct proc *p)
{
rctl_qty_t q;
@@ -1796,6 +1944,20 @@ zone_lockedmem_kstat_update(kstat_t *ksp, int rw)
}
static int
+zone_physmem_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_kstat_t *zk = ksp->ks_data;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ zk->zk_usage.value.ui64 = zone->zone_phys_mem;
+ zk->zk_value.value.ui64 = zone->zone_phys_mem_ctl;
+ return (0);
+}
+
+static int
zone_nprocs_kstat_update(kstat_t *ksp, int rw)
{
zone_t *zone = ksp->ks_private;
@@ -1824,7 +1986,7 @@ zone_swapresv_kstat_update(kstat_t *ksp, int rw)
}
static kstat_t *
-zone_kstat_create_common(zone_t *zone, char *name,
+zone_rctl_kstat_create_common(zone_t *zone, char *name,
int (*updatefunc) (kstat_t *, int))
{
kstat_t *ksp;
@@ -1849,6 +2011,160 @@ zone_kstat_create_common(zone_t *zone, char *name,
return (ksp);
}
+static int
+zone_vfs_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_vfs_kstat_t *zvp = ksp->ks_data;
+ kstat_io_t *kiop = &zone->zone_vfs_rwstats;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ /*
+ * Extract the VFS statistics from the kstat_io_t structure used by
+ * kstat_runq_enter() and related functions. Since the slow ops
+ * counters are updated directly by the VFS layer, there's no need to
+ * copy those statistics here.
+ *
+ * Note that kstat_runq_enter() and the related functions use
+ * gethrtime_unscaled(), so scale the time here.
+ */
+ zvp->zv_nread.value.ui64 = kiop->nread;
+ zvp->zv_reads.value.ui64 = kiop->reads;
+ zvp->zv_rtime.value.ui64 = kiop->rtime;
+ zvp->zv_rcnt.value.ui64 = kiop->rcnt;
+ zvp->zv_rlentime.value.ui64 = kiop->rlentime;
+ zvp->zv_nwritten.value.ui64 = kiop->nwritten;
+ zvp->zv_writes.value.ui64 = kiop->writes;
+ zvp->zv_wtime.value.ui64 = kiop->wtime;
+ zvp->zv_wcnt.value.ui64 = kiop->wcnt;
+ zvp->zv_wlentime.value.ui64 = kiop->wlentime;
+
+ scalehrtime((hrtime_t *)&zvp->zv_rtime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_rlentime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_wtime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_wlentime.value.ui64);
+
+ return (0);
+}
+
+static kstat_t *
+zone_vfs_kstat_create(zone_t *zone)
+{
+ kstat_t *ksp;
+ zone_vfs_kstat_t *zvp;
+
+ if ((ksp = kstat_create_zone("zone_vfs", zone->zone_id,
+ zone->zone_name, "zone_vfs", KSTAT_TYPE_NAMED,
+ sizeof (zone_vfs_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+ return (NULL);
+
+ if (zone->zone_id != GLOBAL_ZONEID)
+ kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+ zvp = ksp->ks_data = kmem_zalloc(sizeof (zone_vfs_kstat_t), KM_SLEEP);
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ ksp->ks_lock = &zone->zone_vfs_lock;
+ zone->zone_vfs_stats = zvp;
+
+ /* The kstat "name" field is not large enough for a full zonename */
+ kstat_named_init(&zvp->zv_zonename, "zonename", KSTAT_DATA_STRING);
+ kstat_named_setstr(&zvp->zv_zonename, zone->zone_name);
+ kstat_named_init(&zvp->zv_nread, "nread", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_reads, "reads", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rtime, "rtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rcnt, "rcnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rlentime, "rlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_nwritten, "nwritten", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_writes, "writes", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wtime, "wtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wcnt, "wcnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wlentime, "wlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_10ms_ops, "10ms_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_100ms_ops, "100ms_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_1s_ops, "1s_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_10s_ops, "10s_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_delay_cnt, "delay_cnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_delay_time, "delay_time", KSTAT_DATA_UINT64);
+
+ ksp->ks_update = zone_vfs_kstat_update;
+ ksp->ks_private = zone;
+
+ kstat_install(ksp);
+ return (ksp);
+}
+
+static int
+zone_zfs_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_zfs_kstat_t *zzp = ksp->ks_data;
+ kstat_io_t *kiop = &zone->zone_zfs_rwstats;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ /*
+ * Extract the ZFS statistics from the kstat_io_t structure used by
+ * kstat_runq_enter() and related functions. Since the I/O throttle
+ * counters are updated directly by the ZFS layer, there's no need to
+ * copy those statistics here.
+ *
+ * Note that kstat_runq_enter() and the related functions use
+ * gethrtime_unscaled(), so scale the time here.
+ */
+ zzp->zz_nread.value.ui64 = kiop->nread;
+ zzp->zz_reads.value.ui64 = kiop->reads;
+ zzp->zz_rtime.value.ui64 = kiop->rtime;
+ zzp->zz_rlentime.value.ui64 = kiop->rlentime;
+ zzp->zz_nwritten.value.ui64 = kiop->nwritten;
+ zzp->zz_writes.value.ui64 = kiop->writes;
+
+ scalehrtime((hrtime_t *)&zzp->zz_rtime.value.ui64);
+ scalehrtime((hrtime_t *)&zzp->zz_rlentime.value.ui64);
+
+ return (0);
+}
+
+static kstat_t *
+zone_zfs_kstat_create(zone_t *zone)
+{
+ kstat_t *ksp;
+ zone_zfs_kstat_t *zzp;
+
+ if ((ksp = kstat_create_zone("zone_zfs", zone->zone_id,
+ zone->zone_name, "zone_zfs", KSTAT_TYPE_NAMED,
+ sizeof (zone_zfs_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+ return (NULL);
+
+ if (zone->zone_id != GLOBAL_ZONEID)
+ kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+ zzp = ksp->ks_data = kmem_zalloc(sizeof (zone_zfs_kstat_t), KM_SLEEP);
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ ksp->ks_lock = &zone->zone_zfs_lock;
+ zone->zone_zfs_stats = zzp;
+
+ /* The kstat "name" field is not large enough for a full zonename */
+ kstat_named_init(&zzp->zz_zonename, "zonename", KSTAT_DATA_STRING);
+ kstat_named_setstr(&zzp->zz_zonename, zone->zone_name);
+ kstat_named_init(&zzp->zz_nread, "nread", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_reads, "reads", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_rtime, "rtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_rlentime, "rlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_nwritten, "nwritten", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_writes, "writes", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_waittime, "waittime", KSTAT_DATA_UINT64);
+
+ ksp->ks_update = zone_zfs_kstat_update;
+ ksp->ks_private = zone;
+
+ kstat_install(ksp);
+ return (ksp);
+}
static int
zone_mcap_kstat_update(kstat_t *ksp, int rw)
@@ -1859,11 +2175,19 @@ zone_mcap_kstat_update(kstat_t *ksp, int rw)
if (rw == KSTAT_WRITE)
return (EACCES);
+ zmp->zm_rss.value.ui64 = zone->zone_phys_mem;
+ zmp->zm_phys_cap.value.ui64 = zone->zone_phys_mem_ctl;
+ zmp->zm_swap.value.ui64 = zone->zone_max_swap;
+ zmp->zm_swap_cap.value.ui64 = zone->zone_max_swap_ctl;
+ zmp->zm_nover.value.ui64 = zone->zone_mcap_nover;
+ zmp->zm_pagedout.value.ui64 = zone->zone_mcap_pagedout;
zmp->zm_pgpgin.value.ui64 = zone->zone_pgpgin;
zmp->zm_anonpgin.value.ui64 = zone->zone_anonpgin;
zmp->zm_execpgin.value.ui64 = zone->zone_execpgin;
zmp->zm_fspgin.value.ui64 = zone->zone_fspgin;
zmp->zm_anon_alloc_fail.value.ui64 = zone->zone_anon_alloc_fail;
+ zmp->zm_pf_throttle.value.ui64 = zone->zone_pf_throttle;
+ zmp->zm_pf_throttle_usec.value.ui64 = zone->zone_pf_throttle_usec;
return (0);
}
@@ -1891,12 +2215,22 @@ zone_mcap_kstat_create(zone_t *zone)
/* The kstat "name" field is not large enough for a full zonename */
kstat_named_init(&zmp->zm_zonename, "zonename", KSTAT_DATA_STRING);
kstat_named_setstr(&zmp->zm_zonename, zone->zone_name);
+ kstat_named_init(&zmp->zm_rss, "rss", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_phys_cap, "physcap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_swap, "swap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_swap_cap, "swapcap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_nover, "nover", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_pagedout, "pagedout", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_pgpgin, "pgpgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_anonpgin, "anonpgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_execpgin, "execpgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_fspgin, "fspgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_anon_alloc_fail, "anon_alloc_fail",
KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_pf_throttle, "n_pf_throttle",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_pf_throttle_usec, "n_pf_throttle_usec",
+ KSTAT_DATA_UINT64);
ksp->ks_update = zone_mcap_kstat_update;
ksp->ks_private = zone;
@@ -1992,13 +2326,25 @@ zone_misc_kstat_create(zone_t *zone)
static void
zone_kstat_create(zone_t *zone)
{
- zone->zone_lockedmem_kstat = zone_kstat_create_common(zone,
+ zone->zone_lockedmem_kstat = zone_rctl_kstat_create_common(zone,
"lockedmem", zone_lockedmem_kstat_update);
- zone->zone_swapresv_kstat = zone_kstat_create_common(zone,
+ zone->zone_swapresv_kstat = zone_rctl_kstat_create_common(zone,
"swapresv", zone_swapresv_kstat_update);
- zone->zone_nprocs_kstat = zone_kstat_create_common(zone,
+ zone->zone_physmem_kstat = zone_rctl_kstat_create_common(zone,
+ "physicalmem", zone_physmem_kstat_update);
+ zone->zone_nprocs_kstat = zone_rctl_kstat_create_common(zone,
"nprocs", zone_nprocs_kstat_update);
+ if ((zone->zone_vfs_ksp = zone_vfs_kstat_create(zone)) == NULL) {
+ zone->zone_vfs_stats = kmem_zalloc(
+ sizeof (zone_vfs_kstat_t), KM_SLEEP);
+ }
+
+ if ((zone->zone_zfs_ksp = zone_zfs_kstat_create(zone)) == NULL) {
+ zone->zone_zfs_stats = kmem_zalloc(
+ sizeof (zone_zfs_kstat_t), KM_SLEEP);
+ }
+
if ((zone->zone_mcap_ksp = zone_mcap_kstat_create(zone)) == NULL) {
zone->zone_mcap_stats = kmem_zalloc(
sizeof (zone_mcap_kstat_t), KM_SLEEP);
@@ -2030,8 +2376,15 @@ zone_kstat_delete(zone_t *zone)
sizeof (zone_kstat_t));
zone_kstat_delete_common(&zone->zone_swapresv_kstat,
sizeof (zone_kstat_t));
+ zone_kstat_delete_common(&zone->zone_physmem_kstat,
+ sizeof (zone_kstat_t));
zone_kstat_delete_common(&zone->zone_nprocs_kstat,
sizeof (zone_kstat_t));
+
+ zone_kstat_delete_common(&zone->zone_vfs_ksp,
+ sizeof (zone_vfs_kstat_t));
+ zone_kstat_delete_common(&zone->zone_zfs_ksp,
+ sizeof (zone_zfs_kstat_t));
zone_kstat_delete_common(&zone->zone_mcap_ksp,
sizeof (zone_mcap_kstat_t));
zone_kstat_delete_common(&zone->zone_misc_ksp,
@@ -2069,6 +2422,8 @@ zone_zsd_init(void)
zone0.zone_locked_mem_ctl = UINT64_MAX;
ASSERT(zone0.zone_max_swap == 0);
zone0.zone_max_swap_ctl = UINT64_MAX;
+ zone0.zone_phys_mem = 0;
+ zone0.zone_phys_mem_ctl = UINT64_MAX;
zone0.zone_max_lofi = 0;
zone0.zone_max_lofi_ctl = UINT64_MAX;
zone0.zone_shmmax = 0;
@@ -2092,8 +2447,9 @@ zone_zsd_init(void)
zone0.zone_initname = initname;
zone0.zone_lockedmem_kstat = NULL;
zone0.zone_swapresv_kstat = NULL;
+ zone0.zone_physmem_kstat = NULL;
zone0.zone_nprocs_kstat = NULL;
-
+ zone0.zone_zfs_io_pri = 1;
zone0.zone_stime = 0;
zone0.zone_utime = 0;
zone0.zone_wtime = 0;
@@ -2204,6 +2560,21 @@ zone_init(void)
RCTL_GLOBAL_INFINITE,
MAXCAP, MAXCAP, &zone_cpu_cap_ops);
+ rc_zone_cpu_baseline = rctl_register("zone.cpu-baseline",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ MAXCAP, MAXCAP, &zone_cpu_base_ops);
+
+ rc_zone_cpu_burst_time = rctl_register("zone.cpu-burst-time",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ INT_MAX, INT_MAX, &zone_cpu_burst_time_ops);
+
+ rc_zone_zfs_io_pri = rctl_register("zone.zfs-io-priority",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ 16384, 16384, &zone_zfs_io_pri_ops);
+
rc_zone_nlwps = rctl_register("zone.max-lwps", RCENTITY_ZONE,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
INT_MAX, INT_MAX, &zone_lwps_ops);
@@ -2245,6 +2616,20 @@ zone_init(void)
rde = rctl_dict_lookup("zone.cpu-shares");
(void) rctl_val_list_insert(&rde->rcd_default_value, dval);
+ /*
+ * Create a rctl_val with PRIVILEGED, NOACTION, value = 1. Then attach
+ * this at the head of the rctl_dict_entry for ``zone.zfs-io-priority'.
+ */
+ dval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
+ bzero(dval, sizeof (rctl_val_t));
+ dval->rcv_value = 1;
+ dval->rcv_privilege = RCPRIV_PRIVILEGED;
+ dval->rcv_flagaction = RCTL_LOCAL_NOACTION;
+ dval->rcv_action_recip_pid = -1;
+
+ rde = rctl_dict_lookup("zone.zfs-io-priority");
+ (void) rctl_val_list_insert(&rde->rcd_default_value, dval);
+
rc_zone_locked_mem = rctl_register("zone.max-locked-memory",
RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES |
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
@@ -2255,6 +2640,11 @@ zone_init(void)
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
&zone_max_swap_ops);
+ rc_zone_phys_mem = rctl_register("zone.max-physical-memory",
+ RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES |
+ RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
+ &zone_phys_mem_ops);
+
rc_zone_max_lofi = rctl_register("zone.max-lofi",
RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT |
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
@@ -2276,6 +2666,8 @@ zone_init(void)
zone0.zone_ntasks = 1;
mutex_exit(&p0.p_lock);
zone0.zone_restart_init = B_TRUE;
+ zone0.zone_reboot_on_init_exit = B_FALSE;
+ zone0.zone_init_status = -1;
zone0.zone_brand = &native_brand;
rctl_prealloc_destroy(gp);
/*
@@ -2355,6 +2747,8 @@ zone_init(void)
static void
zone_free(zone_t *zone)
{
+ zone_dl_t *zdl;
+
ASSERT(zone != global_zone);
ASSERT(zone->zone_ntasks == 0);
ASSERT(zone->zone_nlwps == 0);
@@ -2383,6 +2777,19 @@ zone_free(zone_t *zone)
list_destroy(&zone->zone_ref_list);
zone_free_zsd(zone);
zone_free_datasets(zone);
+
+ /*
+ * While dlmgmtd should have removed all of these, it could have left
+ * something behind or crashed. In which case it's not safe for us to
+ * assume that the list is empty which list_destroy() will ASSERT. We
+ * clean up for our userland comrades which may have crashed, or worse,
+ * been disabled by SMF.
+ */
+ while ((zdl = list_remove_head(&zone->zone_dl_list)) != NULL) {
+ if (zdl->zdl_net != NULL)
+ nvlist_free(zdl->zdl_net);
+ kmem_free(zdl, sizeof (zone_dl_t));
+ }
list_destroy(&zone->zone_dl_list);
if (zone->zone_rootvp != NULL)
@@ -2427,12 +2834,18 @@ zone_free(zone_t *zone)
static void
zone_status_set(zone_t *zone, zone_status_t status)
{
+ timestruc_t now;
+ uint64_t t;
nvlist_t *nvl = NULL;
ASSERT(MUTEX_HELD(&zone_status_lock));
ASSERT(status > ZONE_MIN_STATE && status <= ZONE_MAX_STATE &&
status >= zone_status_get(zone));
+ /* Current time since Jan 1 1970 but consumers expect NS */
+ gethrestime(&now);
+ t = (now.tv_sec * NANOSEC) + now.tv_nsec;
+
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
nvlist_add_string(nvl, ZONE_CB_NAME, zone->zone_name) ||
nvlist_add_string(nvl, ZONE_CB_NEWSTATE,
@@ -2440,7 +2853,7 @@ zone_status_set(zone_t *zone, zone_status_t status)
nvlist_add_string(nvl, ZONE_CB_OLDSTATE,
zone_status_table[zone->zone_status]) ||
nvlist_add_int32(nvl, ZONE_CB_ZONEID, zone->zone_id) ||
- nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, (uint64_t)gethrtime()) ||
+ nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, t) ||
sysevent_evc_publish(zone_event_chan, ZONE_EVENT_STATUS_CLASS,
ZONE_EVENT_STATUS_SUBCLASS, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
#ifdef DEBUG
@@ -2518,9 +2931,14 @@ zone_set_brand(zone_t *zone, const char *brand)
return (EINVAL);
}
- /* set up the brand specific data */
+ /*
+ * Set up the brand specific data.
+ * Note that it's possible that the hook has to drop the
+ * zone_status_lock and reaquire it before returning so we can't
+ * assume the lock has been held the entire time.
+ */
zone->zone_brand = bp;
- ZBROP(zone)->b_init_brand_data(zone);
+ ZBROP(zone)->b_init_brand_data(zone, &zone_status_lock);
mutex_exit(&zone_status_lock);
return (0);
@@ -2566,14 +2984,65 @@ zone_set_initname(zone_t *zone, const char *zone_initname)
return (0);
}
+/*
+ * The zone_set_mcap_nover and zone_set_mcap_pageout functions are used
+ * to provide the physical memory capping kstats. Since physical memory
+ * capping is currently implemented in userland, that code uses the setattr
+ * entry point to increment the kstats. We always simply increment nover
+ * every time that setattr is called and we always add in the input value
+ * to zone_mcap_pagedout every time that is called.
+ */
+/*ARGSUSED*/
static int
-zone_set_phys_mcap(zone_t *zone, const uint64_t *zone_mcap)
+zone_set_mcap_nover(zone_t *zone, const uint64_t *zone_nover)
{
- uint64_t mcap;
- int err = 0;
+ zone->zone_mcap_nover++;
+
+ return (0);
+}
+
+static int
+zone_set_mcap_pageout(zone_t *zone, const uint64_t *zone_pageout)
+{
+ uint64_t pageout;
+ int err;
+
+ if ((err = copyin(zone_pageout, &pageout, sizeof (uint64_t))) == 0)
+ zone->zone_mcap_pagedout += pageout;
+
+ return (err);
+}
+
+/*
+ * The zone_set_page_fault_delay function is used to set the number of usecs
+ * to throttle page faults. This is normally 0 but can be set to a non-0 value
+ * by the user-land memory capping code when the zone is over its physcial
+ * memory cap.
+ */
+static int
+zone_set_page_fault_delay(zone_t *zone, const uint32_t *pfdelay)
+{
+ uint32_t dusec;
+ int err;
+
+ if ((err = copyin(pfdelay, &dusec, sizeof (uint32_t))) == 0)
+ zone->zone_pg_flt_delay = dusec;
+
+ return (err);
+}
+
+/*
+ * The zone_set_rss function is used to set the zone's RSS when we do the
+ * fast, approximate calculation in user-land.
+ */
+static int
+zone_set_rss(zone_t *zone, const uint64_t *prss)
+{
+ uint64_t rss;
+ int err;
- if ((err = copyin(zone_mcap, &mcap, sizeof (uint64_t))) == 0)
- zone->zone_phys_mcap = mcap;
+ if ((err = copyin(prss, &rss, sizeof (uint64_t))) == 0)
+ zone->zone_phys_mem = rss;
return (err);
}
@@ -2985,6 +3454,12 @@ getzoneid(void)
return (curproc->p_zone->zone_id);
}
+zoneid_t
+getzonedid(void)
+{
+ return (curproc->p_zone->zone_did);
+}
+
/*
* Internal versions of zone_find_by_*(). These don't zone_hold() or
* check the validity of a zone's state.
@@ -3728,6 +4203,17 @@ zone_start_init(void)
*/
z->zone_proc_initpid = p->p_pid;
+ if (z->zone_setup_app_contract == B_TRUE) {
+ /*
+ * Normally a process cannot modify its own contract, but we're
+ * just starting the zone's init process and its contract is
+ * always initialized from the sys_process_tmpl template, so
+ * this is the simplest way to setup init's contract to kill
+ * the process if any other process in the contract exits.
+ */
+ p->p_ct_process->conp_ev_fatal |= CT_PR_EV_EXIT;
+ }
+
/*
* We maintain zone_boot_err so that we can return the cause of the
* failure back to the caller of the zone_boot syscall.
@@ -3756,9 +4242,54 @@ zone_start_init(void)
lwp_exit();
}
} else {
+ id_t cid = curthread->t_cid;
+
if (zone_status_get(z) == ZONE_IS_BOOTING)
zone_status_set(z, ZONE_IS_RUNNING);
mutex_exit(&zone_status_lock);
+
+ mutex_enter(&class_lock);
+ ASSERT(cid < loaded_classes);
+ if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+ z->zone_fixed_hipri) {
+ /*
+ * If the zone is using FX then by default all
+ * processes start at the lowest priority and stay
+ * there. We provide a mechanism for the zone to
+ * indicate that it should run at "high priority". In
+ * this case we setup init to run at the highest FX
+ * priority (which is one level higher than the
+ * non-fixed scheduling classes can use).
+ */
+ pcparms_t pcparms;
+
+ pcparms.pc_cid = cid;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+ FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+ FX_DOUPRILIM | FX_DOUPRI;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&curproc->p_lock);
+
+ (void) parmsset(&pcparms, curthread);
+
+ mutex_exit(&curproc->p_lock);
+ mutex_exit(&pidlock);
+ } else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+ /*
+ * zsched always starts the init lwp at priority
+ * minclsyspri - 1. This priority gets set in t_pri and
+ * is invalid for RT, but RT never uses t_pri. However
+ * t_pri is used by procfs, so we always see processes
+ * within an RT zone with an invalid priority value.
+ * We fix that up now.
+ */
+ curthread->t_pri = RTGPPRIO0;
+ }
+ mutex_exit(&class_lock);
+
/* cause the process to return to userland. */
lwp_rtt();
}
@@ -3800,6 +4331,7 @@ zsched(void *arg)
PTOU(pp)->u_argc = 0;
PTOU(pp)->u_argv = NULL;
PTOU(pp)->u_envp = NULL;
+ PTOU(pp)->u_commpagep = NULL;
closeall(P_FINFO(pp));
/*
@@ -4242,8 +4774,9 @@ parse_rctls(caddr_t ubuf, size_t buflen, nvlist_t **nvlp)
error = EINVAL;
name = nvpair_name(nvp);
- if (strncmp(nvpair_name(nvp), "zone.", sizeof ("zone.") - 1)
- != 0 || nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
+ if ((strncmp(name, "zone.", sizeof ("zone.") - 1) != 0 &&
+ strncmp(name, "project.", sizeof ("project.") - 1) != 0) ||
+ nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
goto out;
}
if ((hndl = rctl_hndl_lookup(name)) == -1) {
@@ -4271,7 +4804,8 @@ out:
}
int
-zone_create_error(int er_error, int er_ext, int *er_out) {
+zone_create_error(int er_error, int er_ext, int *er_out)
+{
if (er_out != NULL) {
if (copyout(&er_ext, er_out, sizeof (int))) {
return (set_errno(EFAULT));
@@ -4361,13 +4895,13 @@ zone_create(const char *zone_name, const char *zone_root,
caddr_t rctlbuf, size_t rctlbufsz,
caddr_t zfsbuf, size_t zfsbufsz, int *extended_error,
int match, uint32_t doi, const bslabel_t *label,
- int flags)
+ int flags, zoneid_t zone_did)
{
struct zsched_arg zarg;
nvlist_t *rctls = NULL;
proc_t *pp = curproc;
zone_t *zone, *ztmp;
- zoneid_t zoneid;
+ zoneid_t zoneid, start = GLOBAL_ZONEID;
int error;
int error2 = 0;
char *str;
@@ -4382,8 +4916,59 @@ zone_create(const char *zone_name, const char *zone_root,
return (zone_create_error(ENOTSUP, ZE_CHROOTED,
extended_error));
+ /*
+ * As the first step of zone creation, we want to allocate a zoneid.
+ * This allocation is complicated by the fact that netstacks use the
+ * zoneid to determine their stackid, but netstacks themselves are
+ * freed asynchronously with respect to zone destruction. This means
+ * that a netstack reference leak (or in principle, an extraordinarily
+ * long netstack reference hold) could result in a zoneid being
+ * allocated that in fact corresponds to a stackid from an active
+ * (referenced) netstack -- unleashing all sorts of havoc when that
+ * netstack is actually (re)used. (In the abstract, we might wish a
+ * zoneid to not be deallocated until its last referencing netstack
+ * has been released, but netstacks lack a backpointer into their
+ * referencing zone -- and changing them to have such a pointer would
+ * be substantial, to put it euphemistically.) To avoid this, we
+ * detect this condition on allocation: if we have allocated a zoneid
+ * that corresponds to a netstack that's still in use, we warn about
+ * it (as it is much more likely to be a reference leak than an actual
+ * netstack reference), free it, and allocate another. That these
+ * identifers are allocated out of an ID space assures that we won't
+ * see the identifier we just allocated.
+ */
+ for (;;) {
+ zoneid = id_alloc(zoneid_space);
+
+ if (!netstack_inuse_by_stackid(zoneid_to_netstackid(zoneid)))
+ break;
+
+ id_free(zoneid_space, zoneid);
+
+ if (start == GLOBAL_ZONEID) {
+ start = zoneid;
+ } else if (zoneid == start) {
+ /*
+ * We have managed to iterate over the entire available
+ * zoneid space -- there are no identifiers available,
+ * presumably due to some number of leaked netstack
+ * references. While it's in principle possible for us
+ * to continue to try, it seems wiser to give up at
+ * this point to warn and fail explicitly with a
+ * distinctive error.
+ */
+ cmn_err(CE_WARN, "zone_create() failed: all available "
+ "zone IDs have netstacks still in use");
+ return (set_errno(ENFILE));
+ }
+
+ cmn_err(CE_WARN, "unable to reuse zone ID %d; "
+ "netstack still in use", zoneid);
+ }
+
zone = kmem_zalloc(sizeof (zone_t), KM_SLEEP);
- zoneid = zone->zone_id = id_alloc(zoneid_space);
+ zone->zone_id = zoneid;
+ zone->zone_did = zone_did;
zone->zone_status = ZONE_IS_UNINITIALIZED;
zone->zone_pool = pool_default;
zone->zone_pool_mod = gethrtime();
@@ -4391,6 +4976,8 @@ zone_create(const char *zone_name, const char *zone_root,
zone->zone_ncpus = 0;
zone->zone_ncpus_online = 0;
zone->zone_restart_init = B_TRUE;
+ zone->zone_reboot_on_init_exit = B_FALSE;
+ zone->zone_init_status = -1;
zone->zone_brand = &native_brand;
zone->zone_initname = NULL;
mutex_init(&zone->zone_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -4452,10 +5039,14 @@ zone_create(const char *zone_name, const char *zone_root,
zone->zone_locked_mem_ctl = UINT64_MAX;
zone->zone_max_swap = 0;
zone->zone_max_swap_ctl = UINT64_MAX;
+ zone->zone_phys_mem = 0;
+ zone->zone_phys_mem_ctl = UINT64_MAX;
zone->zone_max_lofi = 0;
zone->zone_max_lofi_ctl = UINT64_MAX;
- zone0.zone_lockedmem_kstat = NULL;
- zone0.zone_swapresv_kstat = NULL;
+ zone->zone_lockedmem_kstat = NULL;
+ zone->zone_swapresv_kstat = NULL;
+ zone->zone_physmem_kstat = NULL;
+ zone->zone_zfs_io_pri = 1;
/*
* Zsched initializes the rctls.
@@ -4610,8 +5201,8 @@ zone_create(const char *zone_name, const char *zone_root,
/*
* The process, task, and project rctls are probably wrong;
* we need an interface to get the default values of all rctls,
- * and initialize zsched appropriately. I'm not sure that that
- * makes much of a difference, though.
+ * and initialize zsched appropriately. However, we allow zoneadmd
+ * to pass down both zone and project rctls for the zone's init.
*/
error = newproc(zsched, (void *)&zarg, syscid, minclsyspri, NULL, 0);
if (error != 0) {
@@ -4750,6 +5341,7 @@ zone_boot(zoneid_t zoneid)
static int
zone_empty(zone_t *zone)
{
+ int cnt = 0;
int waitstatus;
/*
@@ -4760,7 +5352,16 @@ zone_empty(zone_t *zone)
ASSERT(MUTEX_NOT_HELD(&zonehash_lock));
while ((waitstatus = zone_status_timedwait_sig(zone,
ddi_get_lbolt() + hz, ZONE_IS_EMPTY)) == -1) {
- killall(zone->zone_id);
+ boolean_t force = B_FALSE;
+
+ /* Every 30 seconds, try harder */
+ if (cnt++ >= 30) {
+ cmn_err(CE_WARN, "attempt to force kill zone %d\n",
+ zone->zone_id);
+ force = B_TRUE;
+ cnt = 0;
+ }
+ killall(zone->zone_id, force);
}
/*
* return EINTR if we were signaled
@@ -5511,14 +6112,6 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
error = EFAULT;
}
break;
- case ZONE_ATTR_PHYS_MCAP:
- size = sizeof (zone->zone_phys_mcap);
- if (bufsize > size)
- bufsize = size;
- if (buf != NULL &&
- copyout(&zone->zone_phys_mcap, buf, bufsize) != 0)
- error = EFAULT;
- break;
case ZONE_ATTR_SCHED_CLASS:
mutex_enter(&class_lock);
@@ -5573,6 +6166,23 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
}
kmem_free(zbuf, bufsize);
break;
+ case ZONE_ATTR_DID:
+ size = sizeof (zoneid_t);
+ if (bufsize > size)
+ bufsize = size;
+
+ if (buf != NULL && copyout(&zone->zone_did, buf, bufsize) != 0)
+ error = EFAULT;
+ break;
+ case ZONE_ATTR_SCHED_FIXEDHI:
+ size = sizeof (boolean_t);
+ if (bufsize > size)
+ bufsize = size;
+
+ if (buf != NULL && copyout(&zone->zone_fixed_hipri, buf,
+ bufsize) != 0)
+ error = EFAULT;
+ break;
default:
if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) {
size = bufsize;
@@ -5604,10 +6214,11 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
return (set_errno(EPERM));
/*
- * Only the ZONE_ATTR_PHYS_MCAP attribute can be set on the
- * global zone.
+ * Only the ZONE_ATTR_PMCAP_NOVER and ZONE_ATTR_PMCAP_PAGEOUT
+ * attributes can be set on the global zone.
*/
- if (zoneid == GLOBAL_ZONEID && attr != ZONE_ATTR_PHYS_MCAP) {
+ if (zoneid == GLOBAL_ZONEID &&
+ attr != ZONE_ATTR_PMCAP_NOVER && attr != ZONE_ATTR_PMCAP_PAGEOUT) {
return (set_errno(EINVAL));
}
@@ -5624,7 +6235,9 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
* non-global zones.
*/
zone_status = zone_status_get(zone);
- if (attr != ZONE_ATTR_PHYS_MCAP && zone_status > ZONE_IS_READY) {
+ if (attr != ZONE_ATTR_PMCAP_NOVER && attr != ZONE_ATTR_PMCAP_PAGEOUT &&
+ attr != ZONE_ATTR_PG_FLT_DELAY && attr != ZONE_ATTR_RSS &&
+ zone_status > ZONE_IS_READY) {
err = EINVAL;
goto done;
}
@@ -5646,8 +6259,17 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
case ZONE_ATTR_FS_ALLOWED:
err = zone_set_fs_allowed(zone, (const char *)buf);
break;
- case ZONE_ATTR_PHYS_MCAP:
- err = zone_set_phys_mcap(zone, (const uint64_t *)buf);
+ case ZONE_ATTR_PMCAP_NOVER:
+ err = zone_set_mcap_nover(zone, (const uint64_t *)buf);
+ break;
+ case ZONE_ATTR_PMCAP_PAGEOUT:
+ err = zone_set_mcap_pageout(zone, (const uint64_t *)buf);
+ break;
+ case ZONE_ATTR_PG_FLT_DELAY:
+ err = zone_set_page_fault_delay(zone, (const uint32_t *)buf);
+ break;
+ case ZONE_ATTR_RSS:
+ err = zone_set_rss(zone, (const uint64_t *)buf);
break;
case ZONE_ATTR_SCHED_CLASS:
err = zone_set_sched_class(zone, (const char *)buf);
@@ -5676,6 +6298,22 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
err = zone_set_network(zoneid, zbuf);
kmem_free(zbuf, bufsize);
break;
+ case ZONE_ATTR_APP_SVC_CT:
+ if (bufsize != sizeof (boolean_t)) {
+ err = EINVAL;
+ } else {
+ zone->zone_setup_app_contract = (boolean_t)buf;
+ err = 0;
+ }
+ break;
+ case ZONE_ATTR_SCHED_FIXEDHI:
+ if (bufsize != sizeof (boolean_t)) {
+ err = EINVAL;
+ } else {
+ zone->zone_fixed_hipri = (boolean_t)buf;
+ err = 0;
+ }
+ break;
default:
if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone))
err = ZBROP(zone)->b_setattr(zone, attr, buf, bufsize);
@@ -6368,6 +7006,7 @@ zone(int cmd, void *arg1, void *arg2, void *arg3, void *arg4)
zs.doi = zs32.doi;
zs.label = (const bslabel_t *)(uintptr_t)zs32.label;
zs.flags = zs32.flags;
+ zs.zoneid = zs32.zoneid;
#else
panic("get_udatamodel() returned bogus result\n");
#endif
@@ -6378,7 +7017,7 @@ zone(int cmd, void *arg1, void *arg2, void *arg3, void *arg4)
(caddr_t)zs.rctlbuf, zs.rctlbufsz,
(caddr_t)zs.zfsbuf, zs.zfsbufsz,
zs.extended_error, zs.match, zs.doi,
- zs.label, zs.flags));
+ zs.label, zs.flags, zs.zoneid));
case ZONE_BOOT:
return (zone_boot((zoneid_t)(uintptr_t)arg1));
case ZONE_DESTROY:
@@ -6479,6 +7118,7 @@ zone_ki_call_zoneadmd(struct zarg *zargp)
bcopy(zone->zone_name, zone_name, zone_namelen);
zoneid = zone->zone_id;
uniqid = zone->zone_uniqid;
+ arg.status = zone->zone_init_status;
/*
* zoneadmd may be down, but at least we can empty out the zone.
* We can ignore the return value of zone_empty() since we're called
@@ -6656,7 +7296,7 @@ zone_kadmin(int cmd, int fcn, const char *mdep, cred_t *credp)
* zone_ki_call_zoneadmd() will do a more thorough job of this
* later.
*/
- killall(zone->zone_id);
+ killall(zone->zone_id, B_FALSE);
/*
* Now, create the thread to contact zoneadmd and do the rest of the
* work. This thread can't be created in our zone otherwise
@@ -6719,16 +7359,15 @@ zone_shutdown_global(void)
}
/*
- * Returns true if the named dataset is visible in the current zone.
+ * Returns true if the named dataset is visible in the specified zone.
* The 'write' parameter is set to 1 if the dataset is also writable.
*/
int
-zone_dataset_visible(const char *dataset, int *write)
+zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write)
{
static int zfstype = -1;
zone_dataset_t *zd;
size_t len;
- zone_t *zone = curproc->p_zone;
const char *name = NULL;
vfs_t *vfsp = NULL;
@@ -6796,7 +7435,8 @@ zone_dataset_visible(const char *dataset, int *write)
vfs_list_read_lock();
vfsp = zone->zone_vfslist;
do {
- ASSERT(vfsp);
+ if (vfsp == NULL)
+ break;
if (vfsp->vfs_fstype == zfstype) {
name = refstr_value(vfsp->vfs_resource);
@@ -6833,6 +7473,18 @@ zone_dataset_visible(const char *dataset, int *write)
}
/*
+ * Returns true if the named dataset is visible in the current zone.
+ * The 'write' parameter is set to 1 if the dataset is also writable.
+ */
+int
+zone_dataset_visible(const char *dataset, int *write)
+{
+ zone_t *zone = curproc->p_zone;
+
+ return (zone_dataset_visible_inzone(zone, dataset, write));
+}
+
+/*
* zone_find_by_any_path() -
*
* kernel-private routine similar to zone_find_by_path(), but which
diff --git a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas_hash.c b/usr/src/uts/common/refhash/refhash.c
index 8f96c2d9f1..e2de00597e 100644
--- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas_hash.c
+++ b/usr/src/uts/common/refhash/refhash.c
@@ -10,16 +10,18 @@
*/
/*
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
-#include <sys/scsi/adapters/mpt_sas/mptsas_hash.h>
+#include <sys/refhash.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/list.h>
#include <sys/ddi.h>
+#define RHL_F_DEAD 0x01
+
#ifdef lint
extern refhash_link_t *obj_to_link(refhash_t *, void *);
extern void *link_to_obj(refhash_t *, refhash_link_t *);
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index d5dd20bff9..052a28a5e2 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -23,6 +23,7 @@
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2014, Joyent, Inc. All rights reserved.
# Copyright 2013 Garrett D'Amore <garrett@damore.org>
+# Copyright 2015, Joyent, Inc. All rights reserved.
# Copyright 2013 Saso Kiselkov. All rights reserved.
# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
# Copyright 2016 Nexenta Systems, Inc.
@@ -250,6 +251,7 @@ CHKHDRS= \
flock.h \
flock_impl.h \
fork.h \
+ frameio.h \
fss.h \
fsspriocntl.h \
fsid.h \
@@ -275,6 +277,7 @@ CHKHDRS= \
idmap.h \
ieeefp.h \
id_space.h \
+ inotify.h \
instance.h \
int_const.h \
int_fmtio.h \
@@ -343,6 +346,7 @@ CHKHDRS= \
lgrp.h \
lgrp_user.h \
libc_kernel.h \
+ limits.h \
link.h \
list.h \
list_impl.h \
@@ -424,6 +428,9 @@ CHKHDRS= \
ontrap.h \
open.h \
openpromio.h \
+ overlay.h \
+ overlay_common.h \
+ overlay_target.h \
panic.h \
param.h \
pathconf.h \
@@ -646,6 +653,8 @@ CHKHDRS= \
vmem.h \
vmem_impl.h \
vmsystm.h \
+ vnd.h \
+ vnd_errno.h \
vnic.h \
vnic_impl.h \
vnode.h \
@@ -657,12 +666,14 @@ CHKHDRS= \
vuid_queue.h \
vuid_state.h \
vuid_store.h \
+ vxlan.h \
wait.h \
waitq.h \
wanboot_impl.h \
watchpoint.h \
winlockio.h \
zcons.h \
+ zfd.h \
zone.h \
xti_inet.h \
xti_osi.h \
@@ -856,13 +867,14 @@ FSHDRS= \
autofs.h \
decomp.h \
dv_node.h \
- sdev_impl.h \
fifonode.h \
hsfs_isospec.h \
hsfs_node.h \
hsfs_rrip.h \
hsfs_spec.h \
hsfs_susp.h \
+ hyprlofs.h \
+ hyprlofs_info.h \
lofs_info.h \
lofs_node.h \
mntdata.h \
@@ -872,6 +884,8 @@ FSHDRS= \
pc_label.h \
pc_node.h \
pxfs_ki.h \
+ sdev_impl.h \
+ sdev_plugin.h \
snode.h \
swapnode.h \
tmp.h \
@@ -996,6 +1010,7 @@ SATAGENHDRS= \
SYSEVENTHDRS= \
ap_driver.h \
+ datalink.h \
dev.h \
domain.h \
dr.h \
diff --git a/usr/src/uts/common/sys/aggr_impl.h b/usr/src/uts/common/sys/aggr_impl.h
index 547c9cc241..a4c0409304 100644
--- a/usr/src/uts/common/sys/aggr_impl.h
+++ b/usr/src/uts/common/sys/aggr_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 OmniTI Computer Consulting, Inc All rights reserved.
*/
#ifndef _SYS_AGGR_IMPL_H
@@ -308,6 +309,8 @@ extern boolean_t aggr_port_notify_link(aggr_grp_t *, aggr_port_t *);
extern void aggr_port_init_callbacks(aggr_port_t *);
extern void aggr_recv_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t);
+extern void aggr_recv_promisc_cb(void *, mac_resource_handle_t, mblk_t *,
+ boolean_t);
extern void aggr_tx_ring_update(void *, uintptr_t);
extern void aggr_tx_notify_thread(void *);
diff --git a/usr/src/uts/common/sys/auxv.h b/usr/src/uts/common/sys/auxv.h
index 3a2e705850..48b94e2951 100644
--- a/usr/src/uts/common/sys/auxv.h
+++ b/usr/src/uts/common/sys/auxv.h
@@ -29,7 +29,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_AUXV_H
@@ -78,6 +78,9 @@ typedef struct {
#define AT_FLAGS 8 /* processor flags */
#define AT_ENTRY 9 /* a.out entry point */
+/* First introduced on Linux */
+#define AT_RANDOM 25 /* address of 16 random bytes */
+
/*
* These relate to the original PPC ABI document; Linux reused
* the values for other things (see below), so disambiguation of
@@ -90,19 +93,18 @@ typedef struct {
* These are the values from LSB 1.3, the first five are also described
* in the draft amd64 ABI.
*
- * At the time of writing, Solaris doesn't place any of these values into
- * the aux vector, except AT_CLKTCK which is placed on the aux vector for
- * lx branded processes; also, we do similar things via AT_SUN_ values.
+ * At the time of writing, illumos doesn't place any of these values into the
+ * aux vector, except where noted. We do similar things via AT_SUN_ values.
*
* AT_NOTELF 10 program is not ELF?
- * AT_UID 11 real user id
- * AT_EUID 12 effective user id
- * AT_GID 13 real group id
- * AT_EGID 14 effective group id
+ * AT_UID 11 real user id (provided in LX)
+ * AT_EUID 12 effective user id (provided in LX)
+ * AT_GID 13 real group id (provided in LX)
+ * AT_EGID 14 effective group id (provided in LX)
*
* AT_PLATFORM 15
* AT_HWCAP 16
- * AT_CLKTCK 17 c.f. _SC_CLK_TCK
+ * AT_CLKTCK 17 c.f. _SC_CLK_TCK (provided in LX)
* AT_FPUCW 18
*
* AT_DCACHEBSIZE 19 (moved from 10)
@@ -110,6 +112,16 @@ typedef struct {
* AT_UCACHEBSIZE 21 (moved from 12)
*
* AT_IGNOREPPC 22
+ *
+ * On Linux:
+ * AT_* values 18 through 22 are reserved
+ * AT_SECURE 23 secure mode boolean (provided in LX)
+ * AT_BASE_PLATFORM 24 string identifying real platform, may
+ * differ from AT_PLATFORM.
+ * AT_HWCAP2 26 extension of AT_HWCAP
+ * AT_EXECFN 31 filename of program
+ * AT_SYSINFO 32
+ * AT_SYSINFO_EHDR 33 The vDSO location
*/
/*
@@ -186,6 +198,13 @@ extern uint_t getisax(uint32_t *, uint_t);
#define AT_SUN_BRAND_AUX1 2020
#define AT_SUN_BRAND_AUX2 2021
#define AT_SUN_BRAND_AUX3 2022
+#define AT_SUN_BRAND_AUX4 2025
+#define AT_SUN_BRAND_NROOT 2024
+
+/*
+ * Aux vector for comm page
+ */
+#define AT_SUN_COMMPAGE 2026
/*
* Note that 2023 is reserved for the AT_SUN_HWCAP2 word defined above.
diff --git a/usr/src/uts/common/sys/brand.h b/usr/src/uts/common/sys/brand.h
index badc3faff8..231daa5e9d 100644
--- a/usr/src/uts/common/sys/brand.h
+++ b/usr/src/uts/common/sys/brand.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#ifndef _SYS_BRAND_H
@@ -102,29 +103,101 @@ struct brand_mach_ops;
struct intpdata;
struct execa;
+/*
+ * Common structure to define hooks for brand operation.
+ *
+ * Required Fields:
+ * b_init_brand_data - Setup zone brand data during zone_setbrand
+ * b_free_brand_data - Free zone brand data during zone_destroy
+ * b_brandsys - Syscall handler for brandsys
+ * b_setbrand - Initialize process brand data
+ * b_getattr - Get brand-custom zone attribute
+ * b_setattr - Set brand-custom zone attribute
+ * b_copy_procdata - Copy process brand data during fork
+ * b_proc_exit - Perform process brand exit processing
+ * b_exec - Reset branded process state on exec
+ * b_lwp_setrval - Set return code for forked child
+ * b_initlwp - Initialize lwp brand data (cannot drop p->p_lock)
+ * b_forklwp - Copy lwp brand data during fork
+ * b_freelwp - Free lwp brand data
+ * b_lwpexit - Perform lwp-specific brand exit processing
+ * b_elfexec - Load and execute ELF binary
+ * b_sigset_native_to_brand - Convert sigset native->brand
+ * b_sigset_brand_to_native - Convert sigset brand->native
+ * b_nsig - Maxiumum signal number
+ * b_sendsig - Update process state after sendsig
+ *
+ * Optional Fields:
+ * b_lwpdata_alloc - Speculatively allocate data for use in b_initlwp
+ * b_lwpdata_free - Free data from allocated by b_lwpdata_alloc if errors occur
+ * during lwp creation before b_initlwp could be called.
+ * b_initlwp_post - Complete lwp branding (can temporarily drop p->p_lock)
+ * b_exit_with_sig - Instead of sending SIGCLD, exit with custom behavior
+ * b_psig_to_proc - Custom additional behavior during psig
+ * b_wait_filter - Filter processes from being matched by waitid
+ * b_native_exec - Provide interpreter path prefix for executables
+ * b_ptrace_exectrap - Custom behavior for legacy ptrace traps
+ * b_map32limit - Specify alternate limit for MAP_32BIT mappings
+ * b_stop_notify - Hook process stop events
+ * b_waitid_helper - Generate synthetic results for waitid
+ * b_sigcld_repost - Post synthetic SIGCLD signals
+ * b_issig_stop - Alter/suppress signal delivery during issig
+ * b_sig_ignorable - Disallow discarding of signals
+ * b_savecontext - Alter context during savecontext
+ * b_restorecontext - Alter context during restorecontext
+ * b_sendsig_stack - Override stack used for signal delivery
+ * b_setid_clear - Override setid_clear behavior
+ * b_pagefault - Trap pagefault events
+ * b_intp_parse_arg - Controls interpreter argument handling (allow 1 or all)
+ */
struct brand_ops {
- void (*b_init_brand_data)(zone_t *);
+ void (*b_init_brand_data)(zone_t *, kmutex_t *);
void (*b_free_brand_data)(zone_t *);
int (*b_brandsys)(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void (*b_setbrand)(struct proc *);
int (*b_getattr)(zone_t *, int, void *, size_t *);
int (*b_setattr)(zone_t *, int, void *, size_t);
void (*b_copy_procdata)(struct proc *, struct proc *);
- void (*b_proc_exit)(struct proc *, klwp_t *);
+ void (*b_proc_exit)(struct proc *);
void (*b_exec)();
void (*b_lwp_setrval)(klwp_t *, int, int);
- int (*b_initlwp)(klwp_t *);
+ void *(*b_lwpdata_alloc)(struct proc *);
+ void (*b_lwpdata_free)(void *);
+ void (*b_initlwp)(klwp_t *, void *);
+ void (*b_initlwp_post)(klwp_t *);
void (*b_forklwp)(klwp_t *, klwp_t *);
void (*b_freelwp)(klwp_t *);
void (*b_lwpexit)(klwp_t *);
int (*b_elfexec)(struct vnode *vp, struct execa *uap,
struct uarg *args, struct intpdata *idata, int level,
long *execsz, int setid, caddr_t exec_file,
- struct cred *cred, int brand_action);
+ struct cred *cred, int *brand_action);
void (*b_sigset_native_to_brand)(sigset_t *);
void (*b_sigset_brand_to_native)(sigset_t *);
+ void (*b_sigfd_translate)(k_siginfo_t *);
int b_nsig;
+ void (*b_exit_with_sig)(proc_t *, sigqueue_t *);
+ boolean_t (*b_wait_filter)(proc_t *, proc_t *);
+ boolean_t (*b_native_exec)(uint8_t, const char **);
+ uint32_t (*b_map32limit)(proc_t *);
+ void (*b_stop_notify)(proc_t *, klwp_t *, ushort_t, ushort_t);
+ int (*b_waitid_helper)(idtype_t, id_t, k_siginfo_t *, int,
+ boolean_t *, int *);
+ int (*b_sigcld_repost)(proc_t *, sigqueue_t *);
+ int (*b_issig_stop)(proc_t *, klwp_t *);
+ boolean_t (*b_sig_ignorable)(proc_t *, klwp_t *, int);
+ void (*b_savecontext)(ucontext_t *);
+#if defined(_SYSCALL32_IMPL)
+ void (*b_savecontext32)(ucontext32_t *);
+#endif
+ void (*b_restorecontext)(ucontext_t *);
+ caddr_t (*b_sendsig_stack)(int);
+ void (*b_sendsig)(int);
+ int (*b_setid_clear)(vattr_t *vap, cred_t *cr);
+ int (*b_pagefault)(proc_t *, klwp_t *, caddr_t, enum fault_type,
+ enum seg_rw);
+ boolean_t b_intp_parse_arg;
};
/*
@@ -135,6 +208,7 @@ typedef struct brand {
char *b_name;
struct brand_ops *b_ops;
struct brand_mach_ops *b_machops;
+ size_t b_data_size;
} brand_t;
extern brand_t native_brand;
@@ -165,7 +239,7 @@ extern brand_t *brand_register_zone(struct brand_attr *);
extern brand_t *brand_find_name(char *);
extern void brand_unregister_zone(brand_t *);
extern int brand_zone_count(brand_t *);
-extern void brand_setbrand(proc_t *);
+extern int brand_setbrand(proc_t *, boolean_t);
extern void brand_clearbrand(proc_t *, boolean_t);
/*
@@ -178,17 +252,16 @@ extern int brand_solaris_cmd(int, uintptr_t, uintptr_t, uintptr_t,
extern void brand_solaris_copy_procdata(proc_t *, proc_t *,
struct brand *);
extern int brand_solaris_elfexec(vnode_t *, execa_t *, uarg_t *,
- intpdata_t *, int, long *, int, caddr_t, cred_t *, int,
- struct brand *, char *, char *, char *, char *, char *);
+ intpdata_t *, int, long *, int, caddr_t, cred_t *, int *,
+ struct brand *, char *, char *, char *);
extern void brand_solaris_exec(struct brand *);
extern int brand_solaris_fini(char **, struct modlinkage *,
struct brand *);
extern void brand_solaris_forklwp(klwp_t *, klwp_t *, struct brand *);
extern void brand_solaris_freelwp(klwp_t *, struct brand *);
-extern int brand_solaris_initlwp(klwp_t *, struct brand *);
+extern void brand_solaris_initlwp(klwp_t *, struct brand *);
extern void brand_solaris_lwpexit(klwp_t *, struct brand *);
-extern void brand_solaris_proc_exit(struct proc *, klwp_t *,
- struct brand *);
+extern void brand_solaris_proc_exit(struct proc *, struct brand *);
extern void brand_solaris_setbrand(proc_t *, struct brand *);
#if defined(_SYSCALL32)
diff --git a/usr/src/uts/common/sys/buf.h b/usr/src/uts/common/sys/buf.h
index a9191aed7c..cb8a6012fc 100644
--- a/usr/src/uts/common/sys/buf.h
+++ b/usr/src/uts/common/sys/buf.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -186,6 +187,7 @@ struct biostats {
#define B_STARTED 0x2000000 /* io:::start probe called for buf */
#define B_ABRWRITE 0x4000000 /* Application based recovery active */
#define B_PAGE_NOWAIT 0x8000000 /* Skip the page if it is locked */
+#define B_INVALCURONLY 0x10000000 /* invalidate only for curproc */
/*
* There is some confusion over the meaning of B_FREE and B_INVAL and what
@@ -198,6 +200,12 @@ struct biostats {
* between the sole use of these two flags. In both cases, IO will be done
* if the page is not yet committed to storage.
*
+ * The B_INVALCURONLY flag modifies the behavior of the B_INVAL flag and is
+ * intended to be used in conjunction with B_INVAL. B_INVALCURONLY has no
+ * meaning on its own. When both B_INVALCURONLY and B_INVAL are set, then
+ * the mapping for the page is only invalidated for the current process.
+ * In this case, the page is not destroyed unless this was the final mapping.
+ *
* In order to discard pages without writing them back, (B_INVAL | B_TRUNC)
* should be used.
*
diff --git a/usr/src/uts/common/sys/contract/process.h b/usr/src/uts/common/sys/contract/process.h
index 21cf94dcf9..2c70d7c9f1 100644
--- a/usr/src/uts/common/sys/contract/process.h
+++ b/usr/src/uts/common/sys/contract/process.h
@@ -21,13 +21,12 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_CONTRACT_PROCESS_H
#define _SYS_CONTRACT_PROCESS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/contract.h>
#include <sys/time.h>
@@ -55,7 +54,8 @@ typedef struct cont_process cont_process_t;
#define CT_PR_NOORPHAN 0x2 /* kill when contract is abandoned */
#define CT_PR_PGRPONLY 0x4 /* only kill process group on fatal errors */
#define CT_PR_REGENT 0x8 /* automatically detach inherited contracts */
-#define CT_PR_ALLPARAM 0xf
+#define CT_PR_KEEP_EXEC 0x10 /* preserve template accross exec */
+#define CT_PR_ALLPARAM 0x1f
/*
* ctr_ev_* flags
diff --git a/usr/src/uts/common/sys/cpucaps.h b/usr/src/uts/common/sys/cpucaps.h
index 6063ff4380..6bc042108c 100644
--- a/usr/src/uts/common/sys/cpucaps.h
+++ b/usr/src/uts/common/sys/cpucaps.h
@@ -22,6 +22,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011, 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_CPUCAPS_H
@@ -84,12 +85,16 @@ extern void cpucaps_zone_remove(zone_t *);
*/
extern int cpucaps_project_set(kproject_t *, rctl_qty_t);
extern int cpucaps_zone_set(zone_t *, rctl_qty_t);
+extern int cpucaps_zone_set_base(zone_t *, rctl_qty_t);
+extern int cpucaps_zone_set_burst_time(zone_t *, rctl_qty_t);
/*
* Get current CPU usage for a project/zone.
*/
extern rctl_qty_t cpucaps_project_get(kproject_t *);
extern rctl_qty_t cpucaps_zone_get(zone_t *);
+extern rctl_qty_t cpucaps_zone_get_base(zone_t *);
+extern rctl_qty_t cpucaps_zone_get_burst_time(zone_t *);
/*
* Scheduling class hooks into CPU caps framework.
diff --git a/usr/src/uts/common/sys/cpucaps_impl.h b/usr/src/uts/common/sys/cpucaps_impl.h
index 95afd21827..2cd4ed644d 100644
--- a/usr/src/uts/common/sys/cpucaps_impl.h
+++ b/usr/src/uts/common/sys/cpucaps_impl.h
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011, 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_CPUCAPS_IMPL_H
@@ -66,8 +67,12 @@ typedef struct cpucap {
waitq_t cap_waitq; /* waitq for capped threads */
kstat_t *cap_kstat; /* cpucaps specific kstat */
int64_t cap_gen; /* zone cap specific */
+ hrtime_t cap_chk_value; /* effective CPU usage cap */
hrtime_t cap_value; /* scaled CPU usage cap */
hrtime_t cap_usage; /* current CPU usage */
+ hrtime_t cap_base; /* base CPU for burst */
+ u_longlong_t cap_burst_limit; /* max secs (in tics) for a burst */
+ u_longlong_t cap_bursting; /* # of ticks currently bursting */
disp_lock_t cap_usagelock; /* protects cap_usage above */
/*
* Per cap statistics.
@@ -75,6 +80,7 @@ typedef struct cpucap {
hrtime_t cap_maxusage; /* maximum cap usage */
u_longlong_t cap_below; /* # of ticks spend below the cap */
u_longlong_t cap_above; /* # of ticks spend above the cap */
+ u_longlong_t cap_above_base; /* # of ticks spent above the base */
} cpucap_t;
/*
diff --git a/usr/src/uts/common/sys/cred.h b/usr/src/uts/common/sys/cred.h
index 5056f9a511..914f132dc0 100644
--- a/usr/src/uts/common/sys/cred.h
+++ b/usr/src/uts/common/sys/cred.h
@@ -93,6 +93,7 @@ extern gid_t crgetgid(const cred_t *);
extern gid_t crgetrgid(const cred_t *);
extern gid_t crgetsgid(const cred_t *);
extern zoneid_t crgetzoneid(const cred_t *);
+extern zoneid_t crgetzonedid(const cred_t *);
extern projid_t crgetprojid(const cred_t *);
extern cred_t *crgetmapped(const cred_t *);
diff --git a/usr/src/uts/common/sys/ctf_api.h b/usr/src/uts/common/sys/ctf_api.h
index 04d73c3181..bc99f67d3f 100644
--- a/usr/src/uts/common/sys/ctf_api.h
+++ b/usr/src/uts/common/sys/ctf_api.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
/*
@@ -60,6 +60,65 @@ extern "C" {
typedef struct ctf_file ctf_file_t;
typedef long ctf_id_t;
+#define ECTF_BASE 1000 /* base value for libctf errnos */
+
+enum {
+ ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
+ ECTF_ELFVERS, /* ELF version is more recent than libctf */
+ ECTF_CTFVERS, /* CTF version is more recent than libctf */
+ ECTF_ENDIAN, /* data is different endian-ness than lib */
+ ECTF_SYMTAB, /* symbol table uses invalid entry size */
+ ECTF_SYMBAD, /* symbol table data buffer invalid */
+ ECTF_STRBAD, /* string table data buffer invalid */
+ ECTF_CORRUPT, /* file data corruption detected */
+ ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
+ ECTF_NOCTFBUF, /* buffer does not contain CTF data */
+ ECTF_NOSYMTAB, /* symbol table data is not available */
+ ECTF_NOPARENT, /* parent CTF container is not available */
+ ECTF_DMODEL, /* data model mismatch */
+ ECTF_MMAP, /* failed to mmap a data section */
+ ECTF_ZMISSING, /* decompression library not installed */
+ ECTF_ZINIT, /* failed to initialize decompression library */
+ ECTF_ZALLOC, /* failed to allocate decompression buffer */
+ ECTF_DECOMPRESS, /* failed to decompress CTF data */
+ ECTF_STRTAB, /* string table for this string is missing */
+ ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
+ ECTF_BADID, /* invalid type ID number */
+ ECTF_NOTSOU, /* type is not a struct or union */
+ ECTF_NOTENUM, /* type is not an enum */
+ ECTF_NOTSUE, /* type is not a struct, union, or enum */
+ ECTF_NOTINTFP, /* type is not an integer or float */
+ ECTF_NOTARRAY, /* type is not an array */
+ ECTF_NOTREF, /* type does not reference another type */
+ ECTF_NAMELEN, /* buffer is too small to hold type name */
+ ECTF_NOTYPE, /* no type found corresponding to name */
+ ECTF_SYNTAX, /* syntax error in type name */
+ ECTF_NOTFUNC, /* symtab entry does not refer to a function */
+ ECTF_NOFUNCDAT, /* no func info available for function */
+ ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
+ ECTF_NOTYPEDAT, /* no type info available for object */
+ ECTF_NOLABEL, /* no label found corresponding to name */
+ ECTF_NOLABELDATA, /* file does not contain any labels */
+ ECTF_NOTSUP, /* feature not supported */
+ ECTF_NOENUMNAM, /* enum element name not found */
+ ECTF_NOMEMBNAM, /* member name not found */
+ ECTF_RDONLY, /* CTF container is read-only */
+ ECTF_DTFULL, /* CTF type is full (no more members allowed) */
+ ECTF_FULL, /* CTF container is full */
+ ECTF_DUPMEMBER, /* duplicate member name definition */
+ ECTF_CONFLICT, /* conflicting type definition present */
+ ECTF_REFERENCED, /* type has outstanding references */
+ ECTF_NOTDYN, /* type is not a dynamic type */
+ ECTF_ELF, /* elf library failure */
+ ECTF_MCHILD, /* cannot merge child container */
+ ECTF_LABELEXISTS, /* label already exists */
+ ECTF_LCONFLICT, /* merged labels conflict */
+ ECTF_ZLIB, /* zlib library failure */
+ ECTF_CONVBKERR, /* CTF conversion backend error */
+ ECTF_CONVNOCSRC, /* No C source to convert from */
+ ECTF_NOCONVBKEND /* No applicable conversion backend */
+};
+
/*
* If the debugger needs to provide the CTF library with a set of raw buffers
* for use as the CTF data, symbol table, and string table, it can do so by
@@ -143,19 +202,24 @@ typedef struct ctf_lblinfo {
typedef int ctf_visit_f(const char *, ctf_id_t, ulong_t, int, void *);
typedef int ctf_member_f(const char *, ctf_id_t, ulong_t, void *);
typedef int ctf_enum_f(const char *, int, void *);
-typedef int ctf_type_f(ctf_id_t, void *);
+typedef int ctf_type_f(ctf_id_t, boolean_t, void *);
typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *);
+typedef int ctf_function_f(const char *, ulong_t, ctf_funcinfo_t *, void *);
+typedef int ctf_object_f(const char *, ctf_id_t, ulong_t, void *);
+typedef int ctf_string_f(const char *, void *);
extern ctf_file_t *ctf_bufopen(const ctf_sect_t *, const ctf_sect_t *,
const ctf_sect_t *, int *);
extern ctf_file_t *ctf_fdopen(int, int *);
extern ctf_file_t *ctf_open(const char *, int *);
extern ctf_file_t *ctf_create(int *);
+extern ctf_file_t *ctf_fdcreate(int, int *);
extern ctf_file_t *ctf_dup(ctf_file_t *);
extern void ctf_close(ctf_file_t *);
extern ctf_file_t *ctf_parent_file(ctf_file_t *);
extern const char *ctf_parent_name(ctf_file_t *);
+extern const char *ctf_parent_label(ctf_file_t *);
extern int ctf_import(ctf_file_t *, ctf_file_t *);
extern int ctf_setmodel(ctf_file_t *, int);
@@ -165,15 +229,20 @@ extern void ctf_setspecific(ctf_file_t *, void *);
extern void *ctf_getspecific(ctf_file_t *);
extern int ctf_errno(ctf_file_t *);
+extern uint_t ctf_flags(ctf_file_t *);
extern const char *ctf_errmsg(int);
extern int ctf_version(int);
extern int ctf_func_info(ctf_file_t *, ulong_t, ctf_funcinfo_t *);
+extern int ctf_func_info_by_id(ctf_file_t *, ctf_id_t, ctf_funcinfo_t *);
extern int ctf_func_args(ctf_file_t *, ulong_t, uint_t, ctf_id_t *);
+extern int ctf_func_args_by_id(ctf_file_t *, ctf_id_t, uint_t, ctf_id_t *);
extern ctf_id_t ctf_lookup_by_name(ctf_file_t *, const char *);
extern ctf_id_t ctf_lookup_by_symbol(ctf_file_t *, ulong_t);
+extern char *ctf_symbol_name(ctf_file_t *, ulong_t, char *, size_t);
+
extern ctf_id_t ctf_type_resolve(ctf_file_t *, ctf_id_t);
extern ssize_t ctf_type_lname(ctf_file_t *, ctf_id_t, char *, size_t);
extern char *ctf_type_name(ctf_file_t *, ctf_id_t, char *, size_t);
@@ -182,6 +251,7 @@ extern char *ctf_type_qname(ctf_file_t *, ctf_id_t, char *, size_t,
extern ssize_t ctf_type_size(ctf_file_t *, ctf_id_t);
extern ssize_t ctf_type_align(ctf_file_t *, ctf_id_t);
extern int ctf_type_kind(ctf_file_t *, ctf_id_t);
+extern const char *ctf_kind_name(ctf_file_t *, int);
extern ctf_id_t ctf_type_reference(ctf_file_t *, ctf_id_t);
extern ctf_id_t ctf_type_pointer(ctf_file_t *, ctf_id_t);
extern int ctf_type_encoding(ctf_file_t *, ctf_id_t, ctf_encoding_t *);
@@ -201,37 +271,50 @@ extern int ctf_label_info(ctf_file_t *, const char *, ctf_lblinfo_t *);
extern int ctf_member_iter(ctf_file_t *, ctf_id_t, ctf_member_f *, void *);
extern int ctf_enum_iter(ctf_file_t *, ctf_id_t, ctf_enum_f *, void *);
-extern int ctf_type_iter(ctf_file_t *, ctf_type_f *, void *);
+extern int ctf_type_iter(ctf_file_t *, boolean_t, ctf_type_f *, void *);
extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *);
+extern int ctf_function_iter(ctf_file_t *, ctf_function_f *, void *);
+extern int ctf_object_iter(ctf_file_t *, ctf_object_f *, void *);
+extern int ctf_string_iter(ctf_file_t *, ctf_string_f *, void *);
extern ctf_id_t ctf_add_array(ctf_file_t *, uint_t, const ctf_arinfo_t *);
-extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_enum(ctf_file_t *, uint_t, const char *);
extern ctf_id_t ctf_add_float(ctf_file_t *, uint_t,
const char *, const ctf_encoding_t *);
extern ctf_id_t ctf_add_forward(ctf_file_t *, uint_t, const char *, uint_t);
-extern ctf_id_t ctf_add_function(ctf_file_t *, uint_t,
- const ctf_funcinfo_t *, const ctf_id_t *);
+extern ctf_id_t ctf_add_funcptr(ctf_file_t *, uint_t, const ctf_funcinfo_t *,
+ const ctf_id_t *);
extern ctf_id_t ctf_add_integer(ctf_file_t *, uint_t,
const char *, const ctf_encoding_t *);
-extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_type(ctf_file_t *, ctf_file_t *, ctf_id_t);
extern ctf_id_t ctf_add_typedef(ctf_file_t *, uint_t, const char *, ctf_id_t);
-extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern ctf_id_t ctf_add_struct(ctf_file_t *, uint_t, const char *);
extern ctf_id_t ctf_add_union(ctf_file_t *, uint_t, const char *);
-extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, const char *, ctf_id_t);
extern int ctf_add_enumerator(ctf_file_t *, ctf_id_t, const char *, int);
-extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t);
+extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t,
+ ulong_t);
+
+
+extern int ctf_add_function(ctf_file_t *, ulong_t, const ctf_funcinfo_t *,
+ const ctf_id_t *);
+extern int ctf_add_object(ctf_file_t *, ulong_t, ctf_id_t);
+extern int ctf_add_label(ctf_file_t *, const char *, ctf_id_t, uint_t);
extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *);
+extern int ctf_set_root(ctf_file_t *, ctf_id_t, const boolean_t);
+extern int ctf_set_size(ctf_file_t *, ctf_id_t, const ulong_t);
extern int ctf_delete_type(ctf_file_t *, ctf_id_t);
extern int ctf_update(ctf_file_t *);
extern int ctf_discard(ctf_file_t *);
extern int ctf_write(ctf_file_t *, int);
+extern void ctf_dataptr(ctf_file_t *, const void **, size_t *);
#ifdef _KERNEL
diff --git a/usr/src/uts/common/sys/dktp/dadk.h b/usr/src/uts/common/sys/dktp/dadk.h
index f5c990e7c0..2178ad1f0d 100644
--- a/usr/src/uts/common/sys/dktp/dadk.h
+++ b/usr/src/uts/common/sys/dktp/dadk.h
@@ -65,6 +65,8 @@ struct dadk {
kstat_t *dad_errstats; /* error stats */
kmutex_t dad_cmd_mutex;
int dad_cmd_count;
+ uint32_t dad_err_cnt; /* number of recent errors */
+ hrtime_t dad_last_log; /* time of last error log */
};
#define DAD_SECSIZ dad_phyg.g_secsiz
diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h
index fb2a0749d3..4cd93be56e 100644
--- a/usr/src/uts/common/sys/dld.h
+++ b/usr/src/uts/common/sys/dld.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_DLD_H
@@ -191,6 +192,7 @@ typedef struct dld_ioc_rename {
datalink_id_t dir_linkid1;
datalink_id_t dir_linkid2;
char dir_link[MAXLINKNAMELEN];
+ boolean_t dir_zoneinit;
} dld_ioc_rename_t;
/*
@@ -203,6 +205,7 @@ typedef struct dld_ioc_rename {
typedef struct dld_ioc_zid {
zoneid_t diz_zid;
datalink_id_t diz_linkid;
+ boolean_t diz_transient;
} dld_ioc_zid_t;
/*
@@ -350,6 +353,7 @@ typedef struct dld_hwgrpinfo {
*/
typedef int (*dld_capab_func_t)(void *, uint_t, void *, uint_t);
+#define DI_DIRECT_RAW 0x1
/*
* Direct Tx/Rx capability.
*/
@@ -374,6 +378,9 @@ typedef struct dld_capab_direct_s {
/* flow control "can I put on a ring" callback */
uintptr_t di_tx_fctl_df; /* canput-like callback */
void *di_tx_fctl_dh;
+
+ /* flags that control our behavior */
+ uint_t di_flags;
} dld_capab_direct_t;
/*
diff --git a/usr/src/uts/common/sys/dld_impl.h b/usr/src/uts/common/sys/dld_impl.h
index a76a927e59..81708aad38 100644
--- a/usr/src/uts/common/sys/dld_impl.h
+++ b/usr/src/uts/common/sys/dld_impl.h
@@ -53,7 +53,8 @@ typedef enum {
typedef enum {
DLD_UNINITIALIZED,
DLD_PASSIVE,
- DLD_ACTIVE
+ DLD_ACTIVE,
+ DLD_EXCLUSIVE
} dld_passivestate_t;
/*
@@ -256,6 +257,8 @@ extern void dld_str_rx_unitdata(void *, mac_resource_handle_t,
extern void dld_str_notify_ind(dld_str_t *);
extern mac_tx_cookie_t str_mdata_fastpath_put(dld_str_t *, mblk_t *,
uintptr_t, uint16_t);
+extern mac_tx_cookie_t str_mdata_raw_fastpath_put(dld_str_t *, mblk_t *,
+ uintptr_t, uint16_t);
extern int dld_flow_ctl_callb(dld_str_t *, uint64_t,
int (*func)(), void *);
diff --git a/usr/src/uts/common/sys/dld_ioc.h b/usr/src/uts/common/sys/dld_ioc.h
index 2f519a8eda..093a4dc0c3 100644
--- a/usr/src/uts/common/sys/dld_ioc.h
+++ b/usr/src/uts/common/sys/dld_ioc.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_DLD_IOC_H
@@ -59,6 +60,7 @@ extern "C" {
#define IPTUN_IOC 0x454A
#define BRIDGE_IOC 0xB81D
#define IBPART_IOC 0x6171
+#define OVERLAY_IOC 0x2005
/* GLDv3 modules use these macros to generate unique ioctl commands */
#define DLDIOC(cmdid) DLD_IOC_CMD(DLD_IOC, (cmdid))
@@ -68,6 +70,7 @@ extern "C" {
#define IPTUNIOC(cmdid) DLD_IOC_CMD(IPTUN_IOC, (cmdid))
#define BRIDGEIOC(cmdid) DLD_IOC_CMD(BRIDGE_IOC, (cmdid))
#define IBPARTIOC(cmdid) DLD_IOC_CMD(IBPART_IOC, (cmdid))
+#define OVERLAYIOC(cmdid) DLD_IOC_CMD(OVERLAY_IOC, (cmdid))
#ifdef _KERNEL
diff --git a/usr/src/uts/common/sys/dlpi.h b/usr/src/uts/common/sys/dlpi.h
index 5bc2bd41c5..34f1c17236 100644
--- a/usr/src/uts/common/sys/dlpi.h
+++ b/usr/src/uts/common/sys/dlpi.h
@@ -107,6 +107,7 @@ typedef struct dl_ipnetinfo {
#define DL_PASSIVE_REQ 0x114 /* Allow access to aggregated link */
#define DL_INTR_MODE_REQ 0x115 /* Request Rx processing in INTR mode */
#define DL_NOTIFY_CONF 0x116 /* Notification from upstream */
+#define DL_EXCLUSIVE_REQ 0x117 /* Make bind active */
/*
* Primitives used for Connectionless Service
@@ -388,6 +389,8 @@ typedef struct dl_ipnetinfo {
#define DL_PROMISC_PHYS 0x01 /* promiscuous mode at phys level */
#define DL_PROMISC_SAP 0x02 /* promiscuous mode at sap level */
#define DL_PROMISC_MULTI 0x03 /* promiscuous mode for multicast */
+#define DL_PROMISC_RX_ONLY 0x04 /* above only enabled for rx */
+#define DL_PROMISC_FIXUPS 0x05 /* above will be fixed up */
/*
* DLPI notification codes for DL_NOTIFY_REQ primitives.
@@ -1107,6 +1110,13 @@ typedef struct {
} dl_intr_mode_req_t;
/*
+ * DL_EXCLUSIVE_REQ, M_PROTO type
+ */
+typedef struct {
+ t_uscalar_t dl_primitive;
+} dl_exclusive_req_t;
+
+/*
* CONNECTION-ORIENTED SERVICE PRIMITIVES
*/
@@ -1528,6 +1538,7 @@ union DL_primitives {
dl_control_ack_t control_ack;
dl_passive_req_t passive_req;
dl_intr_mode_req_t intr_mode_req;
+ dl_exclusive_req_t exclusive_req;
};
#define DL_INFO_REQ_SIZE sizeof (dl_info_req_t)
@@ -1596,6 +1607,7 @@ union DL_primitives {
#define DL_CONTROL_ACK_SIZE sizeof (dl_control_ack_t)
#define DL_PASSIVE_REQ_SIZE sizeof (dl_passive_req_t)
#define DL_INTR_MODE_REQ_SIZE sizeof (dl_intr_mode_req_t)
+#define DL_EXCLUSIVE_REQ_SIZE sizeof (dl_exclusive_req_t)
#ifdef _KERNEL
/*
diff --git a/usr/src/uts/common/sys/dls.h b/usr/src/uts/common/sys/dls.h
index 6bd2bbe35a..81f9e2abac 100644
--- a/usr/src/uts/common/sys/dls.h
+++ b/usr/src/uts/common/sys/dls.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_DLS_H
@@ -85,6 +86,8 @@ typedef struct dls_link_s dls_link_t;
#define DLS_PROMISC_SAP 0x00000001
#define DLS_PROMISC_MULTI 0x00000002
#define DLS_PROMISC_PHYS 0x00000004
+#define DLS_PROMISC_RX_ONLY 0x00000008
+#define DLS_PROMISC_FIXUPS 0x00000010
extern int dls_open(dls_link_t *, dls_dl_handle_t, dld_str_t *);
extern void dls_close(dld_str_t *);
@@ -106,11 +109,13 @@ extern void str_notify(void *, mac_notify_type_t);
extern int dls_devnet_open(const char *,
dls_dl_handle_t *, dev_t *);
+extern int dls_devnet_open_in_zone(const char *,
+ dls_dl_handle_t *, dev_t *, zoneid_t);
extern void dls_devnet_close(dls_dl_handle_t);
extern boolean_t dls_devnet_rebuild();
extern int dls_devnet_rename(datalink_id_t, datalink_id_t,
- const char *);
+ const char *, boolean_t);
extern int dls_devnet_create(mac_handle_t, datalink_id_t,
zoneid_t);
extern int dls_devnet_destroy(mac_handle_t, datalink_id_t *,
@@ -122,12 +127,13 @@ extern int dls_devnet_hold_by_dev(dev_t, dls_dl_handle_t *);
extern void dls_devnet_rele(dls_dl_handle_t);
extern void dls_devnet_prop_task_wait(dls_dl_handle_t);
+extern const char *dls_devnet_link(dls_dl_handle_t);
extern const char *dls_devnet_mac(dls_dl_handle_t);
extern uint16_t dls_devnet_vid(dls_dl_handle_t);
extern datalink_id_t dls_devnet_linkid(dls_dl_handle_t);
extern int dls_devnet_dev2linkid(dev_t, datalink_id_t *);
extern int dls_devnet_phydev(datalink_id_t, dev_t *);
-extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t);
+extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t, boolean_t);
extern zoneid_t dls_devnet_getzid(dls_dl_handle_t);
extern zoneid_t dls_devnet_getownerzid(dls_dl_handle_t);
extern boolean_t dls_devnet_islinkvisible(datalink_id_t, zoneid_t);
@@ -141,6 +147,8 @@ extern int dls_mgmt_update(const char *, uint32_t, boolean_t,
extern int dls_mgmt_get_linkinfo(datalink_id_t, char *,
datalink_class_t *, uint32_t *, uint32_t *);
extern int dls_mgmt_get_linkid(const char *, datalink_id_t *);
+extern int dls_mgmt_get_linkid_in_zone(const char *,
+ datalink_id_t *, zoneid_t);
extern datalink_id_t dls_mgmt_get_next(datalink_id_t, datalink_class_t,
datalink_media_t, uint32_t);
extern int dls_devnet_macname2linkid(const char *,
diff --git a/usr/src/uts/common/sys/dls_impl.h b/usr/src/uts/common/sys/dls_impl.h
index 60f51c47b5..329f8dd08e 100644
--- a/usr/src/uts/common/sys/dls_impl.h
+++ b/usr/src/uts/common/sys/dls_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_DLS_IMPL_H
@@ -46,11 +47,12 @@ typedef struct dls_multicst_addr_s {
} dls_multicst_addr_t;
struct dls_link_s { /* Protected by */
- char dl_name[MAXNAMELEN]; /* SL */
+ char dl_name[MAXNAMELEN]; /* RO */
uint_t dl_ddi_instance; /* SL */
mac_handle_t dl_mh; /* SL */
mac_client_handle_t dl_mch; /* SL */
mac_unicast_handle_t dl_mah; /* SL */
+ mac_notify_handle_t dl_mnh; /* SL */
const mac_info_t *dl_mip; /* SL */
uint_t dl_ref; /* SL */
mod_hash_t *dl_str_hash; /* SL, modhash lock */
@@ -61,6 +63,7 @@ struct dls_link_s { /* Protected by */
uint_t dl_zone_ref;
link_tagmode_t dl_tagmode; /* atomic */
uint_t dl_nonip_cnt; /* SL */
+ uint_t dl_exclusive; /* SL */
};
typedef struct dls_head_s {
@@ -96,13 +99,16 @@ extern void dls_create_str_kstats(dld_str_t *);
extern int dls_stat_update(kstat_t *, dls_link_t *, int);
extern int dls_stat_create(const char *, int, const char *,
zoneid_t, int (*)(struct kstat *, int), void *,
- kstat_t **);
+ kstat_t **, zoneid_t);
+extern void dls_stat_delete(kstat_t *);
extern int dls_devnet_open_by_dev(dev_t, dls_link_t **,
dls_dl_handle_t *);
extern int dls_devnet_hold_link(datalink_id_t, dls_dl_handle_t *,
dls_link_t **);
extern void dls_devnet_rele_link(dls_dl_handle_t, dls_link_t *);
+extern int dls_devnet_hold_tmp_by_link(dls_link_t *,
+ dls_dl_handle_t *);
extern void dls_init(void);
extern int dls_fini(void);
@@ -126,6 +132,7 @@ extern void dls_mgmt_init(void);
extern void dls_mgmt_fini(void);
extern int dls_mgmt_get_phydev(datalink_id_t, dev_t *);
+extern int dls_exclusive_set(dld_str_t *, boolean_t);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/dls_mgmt.h b/usr/src/uts/common/sys/dls_mgmt.h
index b4032c24d6..214e225ac9 100644
--- a/usr/src/uts/common/sys/dls_mgmt.h
+++ b/usr/src/uts/common/sys/dls_mgmt.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _DLS_MGMT_H
@@ -46,13 +47,15 @@ typedef enum {
DATALINK_CLASS_SIMNET = 0x20,
DATALINK_CLASS_BRIDGE = 0x40,
DATALINK_CLASS_IPTUN = 0x80,
- DATALINK_CLASS_PART = 0x100
+ DATALINK_CLASS_PART = 0x100,
+ DATALINK_CLASS_OVERLAY = 0x200
} datalink_class_t;
#define DATALINK_CLASS_ALL (DATALINK_CLASS_PHYS | \
DATALINK_CLASS_VLAN | DATALINK_CLASS_AGGR | DATALINK_CLASS_VNIC | \
DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET | \
- DATALINK_CLASS_BRIDGE | DATALINK_CLASS_IPTUN | DATALINK_CLASS_PART)
+ DATALINK_CLASS_BRIDGE | DATALINK_CLASS_IPTUN | DATALINK_CLASS_PART | \
+ DATALINK_CLASS_OVERLAY)
/*
* A combination of flags and media.
@@ -165,6 +168,7 @@ typedef struct dlmgmt_door_getname {
typedef struct dlmgmt_door_getlinkid {
int ld_cmd;
char ld_link[MAXLINKNAMELEN];
+ zoneid_t ld_zoneid;
} dlmgmt_door_getlinkid_t;
typedef struct dlmgmt_door_getnext_s {
@@ -225,6 +229,7 @@ typedef struct dlmgmt_getattr_retval_s {
char lr_attrval[MAXLINKATTRVALLEN];
} dlmgmt_getattr_retval_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/elf.h b/usr/src/uts/common/sys/elf.h
index dd1eecc70d..b88d215336 100644
--- a/usr/src/uts/common/sys/elf.h
+++ b/usr/src/uts/common/sys/elf.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
@@ -348,6 +348,11 @@ typedef struct {
#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
+/*
+ * Linux specific program headers not even used by Linux (!!)
+ */
+#define PT_PAX_FLAGS 0x65041580 /* PaX flags (see below) */
+
#define PT_LOSUNW 0x6ffffffa
#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment (unused) */
#define PT_SUNWSTACK 0x6ffffffb /* describes the stack segment */
@@ -363,6 +368,45 @@ typedef struct {
#define PF_W 0x2
#define PF_X 0x1
+/*
+ * PaX is a regrettable series of never-integrated Linux patches for a
+ * facility to provide additional protections on memory pages for purposes of
+ * increasing security, and for allowing binaries to demand (or refuse) those
+ * protections via the PT_PAX_FLAGS program header. (Portents of its
+ * rudderless existence, "PaX" is a term of indefinite origin written by an
+ * unknown group of people.) This facility is unfortunate in any number of
+ * ways, and was largely obviated by the broad adoption of non-executable
+ * stacks at any rate -- but it lives on in binaries that continue to mark
+ * themselves to explicitly refuse the (never-integrated, now-obviated)
+ * facility. One might cringe that PaX overloads the meaning of the p_flags
+ * to specify protections, but that is the least of its transgressions:
+ * instead of using one p_type constant to explicitly enable a series of
+ * protections and another to explicitly disable others, it insists on
+ * conflating both actions into PT_PAX_FLAGS. The resulting doubling of
+ * constant definitions (two constant definitions for every protection instead
+ * of merely one) assures that the values can't even fit in the eight
+ * PF_MASKOS bits putatively defined to provide a modicum of cleanliness for
+ * such filthy functionality. And were all of this not enough, there is one
+ * final nomenclature insult to be added to this semantic injury: the
+ * constants for the p_flags don't even embed "_PAX_" in their name -- despite
+ * the fact that this is their only purpose! We resist the temptation to
+ * right this final wrong here; we grit our teeth and provide exactly the
+ * Linux definitions -- or rather, what would have been the Linux definitions
+ * had this belching jalopy ever been permitted to crash itself into mainline.
+ */
+#define PF_PAGEEXEC 0x00000010 /* PaX: enable PAGEEXEC */
+#define PF_NOPAGEEXEC 0x00000020 /* PaX: disable PAGEEXEC */
+#define PF_SEGMEXEC 0x00000040 /* PaX: enable SEGMEXEC */
+#define PF_NOSEGMEXEC 0x00000080 /* PaX: disable SEGMEXEC */
+#define PF_MPROTECT 0x00000100 /* PaX: enable MPROTECT */
+#define PF_NOMPROTECT 0x00000200 /* PaX: disable MPROTECT */
+#define PF_RANDEXEC 0x00000400 /* PaX: enable RANDEXEC */
+#define PF_NORANDEXEC 0x00000800 /* PaX: disable RANDEXEC */
+#define PF_EMUTRAMP 0x00001000 /* PaX: enable EMUTRAMP */
+#define PF_NOEMUTRAMP 0x00002000 /* PaX: disable EMUTRAMP */
+#define PF_RANDMMAP 0x00004000 /* PaX: enable RANDMMAP */
+#define PF_NORANDMMAP 0x00008000 /* PaX: disable RANDMMAP */
+
#define PF_MASKOS 0x0ff00000 /* OS specific values */
#define PF_MASKPROC 0xf0000000 /* processor specific values */
diff --git a/usr/src/uts/common/sys/exec.h b/usr/src/uts/common/sys/exec.h
index b5e2c58be5..b2db3f2987 100644
--- a/usr/src/uts/common/sys/exec.h
+++ b/usr/src/uts/common/sys/exec.h
@@ -26,6 +26,10 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright 2016, Joyent, Inc.
+ */
+
#ifndef _SYS_EXEC_H
#define _SYS_EXEC_H
@@ -102,10 +106,14 @@ typedef struct uarg {
vnode_t *ex_vp;
char *emulator;
char *brandname;
+ const char *brand_nroot;
char *auxp_auxflags; /* addr of auxflags auxv on the user stack */
char *auxp_brand; /* address of first brand auxv on user stack */
cred_t *pfcred;
boolean_t scrubenv;
+ uintptr_t maxstack;
+ boolean_t stk_prot_override;
+ uintptr_t commpage;
} uarg_t;
/*
@@ -175,7 +183,7 @@ struct execsw {
int (*exec_func)(struct vnode *vp, struct execa *uap,
struct uarg *args, struct intpdata *idata, int level,
long *execsz, int setid, caddr_t exec_file,
- struct cred *cred, int brand_action);
+ struct cred *cred, int *brand_action);
int (*exec_core)(struct vnode *vp, struct proc *p,
struct cred *cred, rlim64_t rlimit, int sig,
core_content_t content);
@@ -213,7 +221,7 @@ extern int exec_common(const char *fname, const char **argp,
const char **envp, int brand_action);
extern int gexec(vnode_t **vp, struct execa *uap, struct uarg *args,
struct intpdata *idata, int level, long *execsz, caddr_t exec_file,
- struct cred *cred, int brand_action);
+ struct cred *cred, int *brand_action);
extern struct execsw *allocate_execsw(char *name, char *magic,
size_t magic_size);
extern struct execsw *findexecsw(char *magic);
@@ -238,16 +246,22 @@ extern void exec_set_sp(size_t);
* when compiling the 32-bit compatability elf code in the elfexec module.
*/
extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- long *, int, caddr_t, cred_t *, int);
+ long *, int, caddr_t, cred_t *, int *);
extern int mapexec_brand(vnode_t *, uarg_t *, Ehdr *, Addr *,
- intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+ intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+ uintptr_t *, uintptr_t *);
+extern int elfreadhdr(vnode_t *, cred_t *, Ehdr *, int *, caddr_t *,
+ ssize_t *);
#endif /* !_ELF32_COMPAT */
#if defined(_LP64)
extern int elf32exec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- long *, int, caddr_t, cred_t *, int);
+ long *, int, caddr_t, cred_t *, int *);
extern int mapexec32_brand(vnode_t *, uarg_t *, Elf32_Ehdr *, Elf32_Addr *,
- intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+ intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+ uintptr_t *, uintptr_t *);
+extern int elf32readhdr(vnode_t *, cred_t *, Elf32_Ehdr *, int *, caddr_t *,
+ ssize_t *);
#endif /* _LP64 */
/*
diff --git a/usr/src/uts/common/sys/frameio.h b/usr/src/uts/common/sys/frameio.h
new file mode 100644
index 0000000000..54e6dbeedf
--- /dev/null
+++ b/usr/src/uts/common/sys/frameio.h
@@ -0,0 +1,107 @@
+/*
+ * 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) 2014, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FRAMEIO_H
+#define _SYS_FRAMEIO_H
+
+/*
+ * Frame I/O definitions
+ */
+
+#include <sys/types.h>
+
+#ifdef _KERNEL
+/* Kernel only headers */
+#include <sys/stream.h>
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * An individual frame vector component. Collections of these are used to make
+ * ioctls.
+ */
+typedef struct framevec {
+ void *fv_buf; /* Buffer with data */
+ size_t fv_buflen; /* Size of the buffer */
+ size_t fv_actlen; /* Amount of buffer consumed, ignore on error */
+} framevec_t;
+
+/*
+ * The base unit used with frameio.
+ */
+typedef struct frameio {
+ uint_t fio_version; /* Should always be FRAMEIO_CURRENT_VERSION */
+ uint_t fio_nvpf; /* How many vectors make up one frame */
+ uint_t fio_nvecs; /* The total number of vectors */
+ framevec_t fio_vecs[]; /* C99 VLA */
+} frameio_t;
+
+
+#define FRAMEIO_VERSION_ONE 1
+#define FRAMEIO_CURRENT_VERSION FRAMEIO_VERSION_ONE
+
+#define FRAMEIO_NVECS_MAX 32
+
+/*
+ * Definitions for kernel modules to include as helpers. These are consolidation
+ * private.
+ */
+#ifdef _KERNEL
+
+/*
+ * 32-bit versions for 64-bit kernels
+ */
+typedef struct framevec32 {
+ caddr32_t fv_buf;
+ size32_t fv_buflen;
+ size32_t fv_actlen;
+} framevec32_t;
+
+typedef struct frameio32 {
+ uint_t fio_version;
+ uint_t fio_vecspframe;
+ uint_t fio_nvecs;
+ framevec32_t fio_vecs[];
+} frameio32_t;
+
+/*
+ * Describe the different ways that vectors should map to frames.
+ */
+typedef enum frameio_write_mblk_map {
+ MAP_BLK_FRAME
+} frameio_write_mblk_map_t;
+
+int frameio_init(void);
+void frameio_fini(void);
+frameio_t *frameio_alloc(int);
+void frameio_free(frameio_t *);
+int frameio_hdr_copyin(frameio_t *, int, const void *, uint_t);
+int frameio_mblk_chain_read(frameio_t *, mblk_t **, int *, int);
+int frameio_mblk_chain_write(frameio_t *, frameio_write_mblk_map_t, mblk_t *,
+ int *, int);
+int frameio_hdr_copyout(frameio_t *, int, void *, uint_t);
+size_t frameio_frame_length(frameio_t *, framevec_t *);
+void frameio_mark_consumed(frameio_t *, int);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FRAMEIO_H */
diff --git a/usr/src/uts/common/sys/fs/hyprlofs.h b/usr/src/uts/common/sys/fs/hyprlofs.h
new file mode 100644
index 0000000000..b8c4149df2
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/hyprlofs.h
@@ -0,0 +1,91 @@
+/*
+ * 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 2012, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FS_HYPRLOFS_H
+#define _SYS_FS_HYPRLOFS_H
+
+#include <sys/param.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * hyprlofs ioctl numbers.
+ */
+#define HYPRLOFS_IOC ('H' << 8)
+
+#define HYPRLOFS_ADD_ENTRIES (HYPRLOFS_IOC | 1)
+#define HYPRLOFS_RM_ENTRIES (HYPRLOFS_IOC | 2)
+#define HYPRLOFS_RM_ALL (HYPRLOFS_IOC | 3)
+#define HYPRLOFS_GET_ENTRIES (HYPRLOFS_IOC | 4)
+
+typedef struct {
+ char *hle_path;
+ uint_t hle_plen;
+ char *hle_name;
+ uint_t hle_nlen;
+} hyprlofs_entry_t;
+
+typedef struct {
+ hyprlofs_entry_t *hle_entries;
+ uint_t hle_len;
+} hyprlofs_entries_t;
+
+typedef struct {
+ char hce_path[MAXPATHLEN];
+ char hce_name[MAXPATHLEN];
+} hyprlofs_curr_entry_t;
+
+typedef struct {
+ hyprlofs_curr_entry_t *hce_entries;
+ uint_t hce_cnt;
+} hyprlofs_curr_entries_t;
+
+#ifdef _KERNEL
+typedef struct {
+ caddr32_t hle_path;
+ uint_t hle_plen;
+ caddr32_t hle_name;
+ uint_t hle_nlen;
+} hyprlofs_entry32_t;
+
+typedef struct {
+ caddr32_t hle_entries;
+ uint_t hle_len;
+} hyprlofs_entries32_t;
+
+typedef struct {
+ caddr32_t hce_entries;
+ uint_t hce_cnt;
+} hyprlofs_curr_entries32_t;
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_HYPRLOFS_H */
diff --git a/usr/src/uts/common/sys/fs/hyprlofs_info.h b/usr/src/uts/common/sys/fs/hyprlofs_info.h
new file mode 100644
index 0000000000..38389f77d9
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/hyprlofs_info.h
@@ -0,0 +1,174 @@
+/*
+ * 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 2012, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FS_HYPRLOFS_INFO_H
+#define _SYS_FS_HYPRLOFS_INFO_H
+
+#include <sys/t_lock.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <sys/vfs_opreg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * hlnode is the file system dependent node for hyprlofs.
+ * It is modeled on the tmpfs tmpnode.
+ *
+ * hln_rwlock protects access of the directory list at hln_dir
+ * as well as syncronizing read/writes to directory hlnodes.
+ * hln_tlock protects updates to hln_mode and hln_nlink.
+ * hln_tlock doesn't require any hlnode locks.
+ */
+typedef struct hlnode {
+ struct hlnode *hln_back; /* linked list of hlnodes */
+ struct hlnode *hln_forw; /* linked list of hlnodes */
+ union {
+ struct {
+ struct hldirent *un_dirlist; /* dirent list */
+ uint_t un_dirents; /* number of dirents */
+ } un_dirstruct;
+ vnode_t *un_realvp; /* real vnode */
+ } un_hlnode;
+ vnode_t *hln_vnode; /* vnode for this hlnode */
+ int hln_gen; /* pseudo gen num for hlfid */
+ int hln_looped; /* flag indicating loopback */
+ vattr_t hln_attr; /* attributes */
+ krwlock_t hln_rwlock; /* rw - serialize mods and */
+ /* directory updates */
+ kmutex_t hln_tlock; /* time, flag, and nlink lock */
+} hlnode_t;
+
+/*
+ * hyprlofs per-mount data structure.
+ * All fields are protected by hlm_contents.
+ */
+typedef struct {
+ vfs_t *hlm_vfsp; /* filesystem's vfs struct */
+ hlnode_t *hlm_rootnode; /* root hlnode */
+ char *hlm_mntpath; /* name of hyprlofs mount point */
+ dev_t hlm_dev; /* unique dev # of mounted `device' */
+ uint_t hlm_gen; /* pseudo generation number for files */
+ kmutex_t hlm_contents; /* lock for hlfsmount structure */
+} hlfsmount_t;
+
+/*
+ * hyprlofs directories are made up of a linked list of hldirent structures
+ * hanging off directory hlnodes. File names are not fixed length,
+ * but are null terminated.
+ */
+typedef struct hldirent {
+ hlnode_t *hld_hlnode; /* hlnode for this file */
+ struct hldirent *hld_next; /* next directory entry */
+ struct hldirent *hld_prev; /* prev directory entry */
+ uint_t hld_offset; /* "offset" of dir entry */
+ uint_t hld_hash; /* a hash of td_name */
+ struct hldirent *hld_link; /* linked via the hash table */
+ hlnode_t *hld_parent; /* parent, dir we are in */
+ char *hld_name; /* must be null terminated */
+ /* max length is MAXNAMELEN */
+} hldirent_t;
+
+/*
+ * hlfid overlays the fid structure (for VFS_VGET)
+ */
+typedef struct {
+ uint16_t hlfid_len;
+ ino32_t hlfid_ino;
+ int32_t hlfid_gen;
+} hlfid_t;
+
+/*
+ * File system independent to hyprlofs conversion macros
+ */
+#define VFSTOHLM(vfsp) ((hlfsmount_t *)(vfsp)->vfs_data)
+#define VTOHLM(vp) ((hlfsmount_t *)(vp)->v_vfsp->vfs_data)
+#define VTOHLN(vp) ((hlnode_t *)(vp)->v_data)
+#define HLNTOV(tp) ((tp)->hln_vnode)
+#define REALVP(vp) ((vnode_t *)VTOHLN(vp)->hln_realvp)
+#define hlnode_hold(tp) VN_HOLD(HLNTOV(tp))
+#define hlnode_rele(tp) VN_RELE(HLNTOV(tp))
+
+#define hln_dir un_hlnode.un_dirstruct.un_dirlist
+#define hln_dirents un_hlnode.un_dirstruct.un_dirents
+#define hln_realvp un_hlnode.un_realvp
+
+/*
+ * Attributes
+ */
+#define hln_mask hln_attr.va_mask
+#define hln_type hln_attr.va_type
+#define hln_mode hln_attr.va_mode
+#define hln_uid hln_attr.va_uid
+#define hln_gid hln_attr.va_gid
+#define hln_fsid hln_attr.va_fsid
+#define hln_nodeid hln_attr.va_nodeid
+#define hln_nlink hln_attr.va_nlink
+#define hln_size hln_attr.va_size
+#define hln_atime hln_attr.va_atime
+#define hln_mtime hln_attr.va_mtime
+#define hln_ctime hln_attr.va_ctime
+#define hln_rdev hln_attr.va_rdev
+#define hln_blksize hln_attr.va_blksize
+#define hln_nblocks hln_attr.va_nblocks
+#define hln_seq hln_attr.va_seq
+
+/*
+ * enums
+ */
+enum de_op { DE_CREATE, DE_MKDIR }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR }; /* dirremove ops */
+
+/*
+ * hyprlofs_minfree is the amount (in pages) of anonymous memory that hyprlofs
+ * leaves free for the rest of the system. The default value for
+ * hyprlofs_minfree is btopr(HYPRLOFSMINFREE) but it can be patched to a
+ * different number of pages. Since hyprlofs doesn't actually use much
+ * memory, its unlikely this ever needs to be patched.
+ */
+#define HYPRLOFSMINFREE 8 * 1024 * 1024 /* 8 Megabytes */
+
+extern size_t hyprlofs_minfree; /* Anonymous memory in pages */
+
+extern void hyprlofs_node_init(hlfsmount_t *, hlnode_t *, vattr_t *,
+ cred_t *);
+extern int hyprlofs_dirlookup(hlnode_t *, char *, hlnode_t **, cred_t *);
+extern int hyprlofs_dirdelete(hlnode_t *, hlnode_t *, char *, enum dr_op,
+ cred_t *);
+extern void hyprlofs_dirinit(hlnode_t *, hlnode_t *);
+extern void hyprlofs_dirtrunc(hlnode_t *);
+extern int hyprlofs_taccess(void *, int, cred_t *);
+extern int hyprlofs_direnter(hlfsmount_t *, hlnode_t *, char *, enum de_op,
+ vnode_t *, vattr_t *, hlnode_t **, cred_t *);
+
+extern struct vnodeops *hyprlofs_vnodeops;
+extern const struct fs_operation_def hyprlofs_vnodeops_template[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_HYPRLOFS_INFO_H */
diff --git a/usr/src/uts/common/sys/fs/sdev_impl.h b/usr/src/uts/common/sys/fs/sdev_impl.h
index 1223c91862..5fbef16470 100644
--- a/usr/src/uts/common/sys/fs/sdev_impl.h
+++ b/usr/src/uts/common/sys/fs/sdev_impl.h
@@ -37,6 +37,7 @@ extern "C" {
#include <sys/vfs_opreg.h>
#include <sys/list.h>
#include <sys/nvpair.h>
+#include <sys/fs/sdev_plugin.h>
#include <sys/sunddi.h>
/*
@@ -129,6 +130,21 @@ typedef struct sdev_local_data {
struct sdev_dprof sdev_lprof; /* profile for multi-inst */
} sdev_local_data_t;
+/* sdev_flags */
+typedef enum sdev_flags {
+ SDEV_BUILD = 0x0001, /* directory cache out-of-date */
+ SDEV_GLOBAL = 0x0002, /* global /dev nodes */
+ SDEV_PERSIST = 0x0004, /* backing store persisted node */
+ SDEV_NO_NCACHE = 0x0008, /* do not include in neg. cache */
+ SDEV_DYNAMIC = 0x0010, /* special-purpose vnode ops */
+ /* (ex: pts) */
+ SDEV_VTOR = 0x0020, /* validate sdev_nodes during search */
+ SDEV_ATTR_INVALID = 0x0040, /* invalid node attributes, */
+ /* need update */
+ SDEV_SUBDIR = 0x0080, /* match all subdirs under here */
+ SDEV_ZONED = 0x0100 /* zoned subdir */
+} sdev_flags_t;
+
/*
* /dev filesystem sdev_node defines
*/
@@ -151,7 +167,7 @@ typedef struct sdev_node {
ino64_t sdev_ino; /* inode */
uint_t sdev_nlink; /* link count */
int sdev_state; /* state of this node */
- int sdev_flags; /* flags bit */
+ sdev_flags_t sdev_flags; /* flags bit */
kmutex_t sdev_lookup_lock; /* node creation synch lock */
kcondvar_t sdev_lookup_cv; /* node creation sync cv */
@@ -162,7 +178,7 @@ typedef struct sdev_node {
struct sdev_global_data sdev_globaldata;
struct sdev_local_data sdev_localdata;
} sdev_instance_data;
-
+ list_node_t sdev_plist; /* link on plugin list */
void *sdev_private;
} sdev_node_t;
@@ -193,29 +209,11 @@ typedef enum {
SDEV_READY
} sdev_node_state_t;
-/* sdev_flags */
-#define SDEV_BUILD 0x0001 /* directory cache out-of-date */
-#define SDEV_GLOBAL 0x0002 /* global /dev nodes */
-#define SDEV_PERSIST 0x0004 /* backing store persisted node */
-#define SDEV_NO_NCACHE 0x0008 /* do not include in neg. cache */
-#define SDEV_DYNAMIC 0x0010 /* special-purpose vnode ops */
- /* (ex: pts) */
-#define SDEV_VTOR 0x0020 /* validate sdev_nodes during search */
-#define SDEV_ATTR_INVALID 0x0040 /* invalid node attributes, */
- /* need update */
-#define SDEV_SUBDIR 0x0080 /* match all subdirs under here */
-#define SDEV_ZONED 0x0100 /* zoned subdir */
-
/* sdev_lookup_flags */
#define SDEV_LOOKUP 0x0001 /* node creation in progress */
#define SDEV_READDIR 0x0002 /* VDIR readdir in progress */
#define SDEV_LGWAITING 0x0004 /* waiting for devfsadm completion */
-#define SDEV_VTOR_INVALID -1
-#define SDEV_VTOR_SKIP 0
-#define SDEV_VTOR_VALID 1
-#define SDEV_VTOR_STALE 2
-
/* convenient macros */
#define SDEV_IS_GLOBAL(dv) \
(dv->sdev_flags & SDEV_GLOBAL)
@@ -367,8 +365,13 @@ extern void sdev_devfsadmd_thread(struct sdev_node *, struct sdev_node *,
extern int devname_profile_update(char *, size_t);
extern struct sdev_data *sdev_find_mntinfo(char *);
void sdev_mntinfo_rele(struct sdev_data *);
+typedef void (*sdev_mnt_walk_f)(struct sdev_node *, void *);
+void sdev_mnt_walk(sdev_mnt_walk_f, void *);
extern struct vnodeops *devpts_getvnodeops(void);
extern struct vnodeops *devvt_getvnodeops(void);
+extern void sdev_plugin_nodeready(struct sdev_node *);
+extern int sdev_plugin_init(void);
+extern int sdev_plugin_fini(void);
/*
* boot states - warning, the ordering here is significant
@@ -514,6 +517,23 @@ extern void sdev_nc_path_exists(sdev_nc_list_t *, char *);
extern void sdev_modctl_dump_files(void);
/*
+ * plugin and legacy vtab stuff
+ */
+/* directory dependent vop table */
+typedef struct sdev_vop_table {
+ char *vt_name; /* subdirectory name */
+ const fs_operation_def_t *vt_service; /* vnodeops table */
+ struct vnodeops **vt_global_vops; /* global container for vop */
+ int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */
+ int vt_flags;
+} sdev_vop_table_t;
+
+extern struct sdev_vop_table vtab[];
+extern struct vnodeops *sdev_get_vop(struct sdev_node *);
+extern void sdev_set_no_negcache(struct sdev_node *);
+extern void *sdev_get_vtor(struct sdev_node *dv);
+
+/*
* globals
*/
extern kmutex_t sdev_lock;
@@ -526,6 +546,7 @@ extern struct vnodeops *devipnet_vnodeops;
extern struct vnodeops *devvt_vnodeops;
extern struct sdev_data *sdev_origins; /* mount info for global /dev instance */
extern struct vnodeops *devzvol_vnodeops;
+extern int sdev_vnodeops_tbl_size;
extern const fs_operation_def_t sdev_vnodeops_tbl[];
extern const fs_operation_def_t devpts_vnodeops_tbl[];
diff --git a/usr/src/uts/common/sys/fs/sdev_plugin.h b/usr/src/uts/common/sys/fs/sdev_plugin.h
new file mode 100644
index 0000000000..8783df58e6
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/sdev_plugin.h
@@ -0,0 +1,106 @@
+/*
+ * 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) 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_SDEV_PLUGIN_H
+#define _SYS_SDEV_PLUGIN_H
+
+/*
+ * Kernel sdev plugin interface
+ */
+
+#ifdef _KERNEL
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+typedef uintptr_t sdev_plugin_hdl_t;
+typedef uintptr_t sdev_ctx_t;
+
+/*
+ * Valid return values for sdev_plugin_validate_t.
+ */
+typedef enum sdev_plugin_validate {
+ SDEV_VTOR_INVALID = -1,
+ SDEV_VTOR_SKIP = 0,
+ SDEV_VTOR_VALID = 1,
+ SDEV_VTOR_STALE = 2
+} sdev_plugin_validate_t;
+
+/*
+ * Valid flags
+ */
+typedef enum sdev_plugin_flags {
+ SDEV_PLUGIN_NO_NCACHE = 0x1,
+ SDEV_PLUGIN_SUBDIR = 0x2
+} sdev_plugin_flags_t;
+
+#define SDEV_PLUGIN_FLAGS_MASK 0x3
+
+/*
+ * Functions a module must implement
+ */
+typedef sdev_plugin_validate_t (*sp_valid_f)(sdev_ctx_t);
+typedef int (*sp_filldir_f)(sdev_ctx_t);
+typedef void (*sp_inactive_f)(sdev_ctx_t);
+
+#define SDEV_PLUGIN_VERSION 1
+
+typedef struct sdev_plugin_ops {
+ int spo_version;
+ sdev_plugin_flags_t spo_flags;
+ sp_valid_f spo_validate;
+ sp_filldir_f spo_filldir;
+ sp_inactive_f spo_inactive;
+} sdev_plugin_ops_t;
+
+extern sdev_plugin_hdl_t sdev_plugin_register(const char *, sdev_plugin_ops_t *,
+ int *);
+extern int sdev_plugin_unregister(sdev_plugin_hdl_t);
+
+typedef enum sdev_ctx_flags {
+ SDEV_CTX_GLOBAL = 0x2 /* node belongs to the GZ */
+} sdev_ctx_flags_t;
+
+/*
+ * Context helper functions
+ */
+extern sdev_ctx_flags_t sdev_ctx_flags(sdev_ctx_t);
+extern const char *sdev_ctx_name(sdev_ctx_t);
+extern const char *sdev_ctx_path(sdev_ctx_t);
+extern enum vtype sdev_ctx_vtype(sdev_ctx_t);
+extern const void *sdev_ctx_vtype_data(sdev_ctx_t);
+
+/*
+ * Callbacks to manipulate nodes
+ */
+extern int sdev_plugin_mkdir(sdev_ctx_t, char *);
+extern int sdev_plugin_mknod(sdev_ctx_t, char *, mode_t, dev_t);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SDEV_PLUGIN_H */
diff --git a/usr/src/uts/common/sys/fs/tmp.h b/usr/src/uts/common/sys/fs/tmp.h
index 68dd67c61e..e4e81cc498 100644
--- a/usr/src/uts/common/sys/fs/tmp.h
+++ b/usr/src/uts/common/sys/fs/tmp.h
@@ -22,12 +22,13 @@
* Copyright 2007 Sun Microsystems, Inc.
* All rights reserved. Use is subject to license terms.
*/
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
#ifndef _SYS_FS_TMP_H
#define _SYS_FS_TMP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -42,8 +43,10 @@ struct tmount {
struct vfs *tm_vfsp; /* filesystem's vfs struct */
struct tmpnode *tm_rootnode; /* root tmpnode */
char *tm_mntpath; /* name of tmpfs mount point */
- ulong_t tm_anonmax; /* file system max anon reservation */
- pgcnt_t tm_anonmem; /* pages of reserved anon memory */
+ size_t tm_anonmax; /* file system max anon reservation */
+ size_t tm_anonmem; /* bytes of reserved anon memory */
+ /* and allocated kmem for the fs */
+ size_t tm_allocmem; /* bytes alloced from tmp_kmem_ funcs */
dev_t tm_dev; /* unique dev # of mounted `device' */
uint_t tm_gen; /* pseudo generation number for files */
kmutex_t tm_contents; /* lock for tmount structure */
@@ -57,6 +60,7 @@ struct tmount {
#define VTOTM(vp) ((struct tmount *)(vp)->v_vfsp->vfs_data)
#define VTOTN(vp) ((struct tmpnode *)(vp)->v_data)
#define TNTOV(tp) ((tp)->tn_vnode)
+#define TNTOTM(tp) (VTOTM(TNTOV(tp)))
#define tmpnode_hold(tp) VN_HOLD(TNTOV(tp))
#define tmpnode_rele(tp) VN_RELE(TNTOV(tp))
@@ -68,50 +72,52 @@ enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
/*
* tmpfs_minfree is the amount (in pages) of anonymous memory that tmpfs
- * leaves free for the rest of the system. E.g. in a system with 32MB of
- * configured swap space, if 16MB were reserved (leaving 16MB free),
- * tmpfs could allocate up to 16MB - tmpfs_minfree. The default value
- * for tmpfs_minfree is btopr(TMPMINFREE) but it can cautiously patched
- * to a different number of pages.
- * NB: If tmpfs allocates too much swap space, other processes will be
- * unable to execute.
+ * leaves free for the rest of the system. In antiquity, this number could be
+ * relevant on a system-wide basis, as physical DRAM was routinely exhausted;
+ * however, in more modern times, the relative growth of DRAM with respect to
+ * application footprint means that this number is only likely to become
+ * factor in a virtualized OS environment (e.g., a zone) -- and even then only
+ * when DRAM and swap have both been capped low to allow for maximum tenancy.
+ * TMPMINFREE -- the value from which tmpfs_minfree is derived -- should
+ * therefore be configured to a value that is roughly the smallest practical
+ * value for memory + swap minus the largest reasonable size for tmpfs in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow tmpfs to consume
+ * no more than seven-eighths of this, yielding a TMPMINFREE of 16MB. Care
+ * should be exercised in changing this: tuning this value too high will
+ * result in spurious ENOSPC errors in tmpfs in small zones (a problem that
+ * can induce cascading failure surprisingly often); tuning this value too low
+ * will result in tmpfs consumption alone to alone induce application-level
+ * memory allocation failure.
*/
-#define TMPMINFREE 2 * 1024 * 1024 /* 2 Megabytes */
+#define TMPMINFREE 16 * 1024 * 1024 /* 16 Megabytes */
extern size_t tmpfs_minfree; /* Anonymous memory in pages */
-/*
- * tmpfs can allocate only a certain percentage of kernel memory,
- * which is used for tmpnodes, directories, file names, etc.
- * This is statically set as TMPMAXFRACKMEM of physical memory.
- * The actual number of allocatable bytes can be patched in tmpfs_maxkmem.
- */
-#define TMPMAXFRACKMEM 25 /* 1/25 of physical memory */
-
-extern size_t tmp_kmemspace;
-extern size_t tmpfs_maxkmem; /* Allocatable kernel memory in bytes */
-
extern void tmpnode_init(struct tmount *, struct tmpnode *,
struct vattr *, struct cred *);
+extern void tmpnode_cleanup(struct tmpnode *tp);
extern int tmpnode_trunc(struct tmount *, struct tmpnode *, ulong_t);
extern void tmpnode_growmap(struct tmpnode *, ulong_t);
extern int tdirlookup(struct tmpnode *, char *, struct tmpnode **,
struct cred *);
extern int tdirdelete(struct tmpnode *, struct tmpnode *, char *,
enum dr_op, struct cred *);
-extern void tdirinit(struct tmpnode *, struct tmpnode *);
+extern int tdirinit(struct tmpnode *, struct tmpnode *);
extern void tdirtrunc(struct tmpnode *);
-extern void *tmp_memalloc(size_t, int);
-extern void tmp_memfree(void *, size_t);
extern int tmp_resv(struct tmount *, struct tmpnode *, size_t, int);
extern int tmp_taccess(void *, int, struct cred *);
extern int tmp_sticky_remove_access(struct tmpnode *, struct tmpnode *,
struct cred *);
-extern int tmp_convnum(char *, pgcnt_t *);
+extern int tmp_convnum(char *, size_t *);
+extern int tmp_convmode(char *, mode_t *);
extern int tdirenter(struct tmount *, struct tmpnode *, char *,
enum de_op, struct tmpnode *, struct tmpnode *, struct vattr *,
struct tmpnode **, struct cred *, caller_context_t *);
+extern void *tmp_kmem_zalloc(struct tmount *, size_t, int);
+extern void tmp_kmem_free(struct tmount *, void *, size_t);
+
#define TMP_MUSTHAVE 0x01
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/fx.h b/usr/src/uts/common/sys/fx.h
index 2d4e1aa7fb..4a48af52a1 100644
--- a/usr/src/uts/common/sys/fx.h
+++ b/usr/src/uts/common/sys/fx.h
@@ -21,13 +21,12 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_FX_H
#define _SYS_FX_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/thread.h>
#include <sys/ddi.h>
@@ -145,7 +144,14 @@ typedef struct fxkparms {
uint_t fx_cflags;
} fxkparms_t;
+/*
+ * control flags (kparms->fx_cflags).
+ */
+#define FX_DOUPRILIM 0x01 /* change user priority limit */
+#define FX_DOUPRI 0x02 /* change user priority */
+#define FX_DOTQ 0x04 /* change FX time quantum */
+#define FXMAXUPRI 60 /* maximum user priority setting */
/*
* Interface for partner private code. This is not a public interface.
diff --git a/usr/src/uts/common/sys/gsqueue.h b/usr/src/uts/common/sys/gsqueue.h
new file mode 100644
index 0000000000..40ef4ce982
--- /dev/null
+++ b/usr/src/uts/common/sys/gsqueue.h
@@ -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 (c) 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_GSQUEUE_H
+#define _SYS_GSQUEUE_H
+
+/*
+ * Standard interfaces to serializaion queues for everyone (except IP).
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+typedef struct gsqueue gsqueue_t;
+typedef struct gsqueue_set gsqueue_set_t;
+
+typedef void (*gsqueue_cb_f)(gsqueue_set_t *, gsqueue_t *, void *, boolean_t);
+typedef void (*gsqueue_proc_f)(void *, mblk_t *, gsqueue_t *, void *);
+
+extern gsqueue_set_t *gsqueue_set_create(uint_t, pri_t);
+extern void gsqueue_set_destroy(gsqueue_set_t *);
+extern gsqueue_t *gsqueue_set_get(gsqueue_set_t *, uint_t);
+
+extern uintptr_t gsqueue_set_cb_add(gsqueue_set_t *, gsqueue_cb_f, void *);
+extern int gsqueue_set_cb_remove(gsqueue_set_t *, uintptr_t);
+
+#define GSQUEUE_FILL 0x0001
+#define GSQUEUE_NODRAIN 0x0002
+#define GSQUEUE_PROCESS 0x0004
+
+extern void gsqueue_enter_one(gsqueue_t *, mblk_t *, gsqueue_proc_f, void *,
+ int, uint8_t);
+
+/*
+ * The default wait is inherited from IP. This determines the amount of time
+ * that must pass after queuing work, before we wake up the worker thread. This
+ * value is in milliseconds.
+ */
+#define GSQUEUE_DEFAULT_WAIT 10
+#define GSQUEUE_DEFAULT_PRIORITY MAXCLSYSPRI
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_GSQUEUE_H */
diff --git a/usr/src/uts/common/sys/id_space.h b/usr/src/uts/common/sys/id_space.h
index d56fcceb5a..46d25f207f 100644
--- a/usr/src/uts/common/sys/id_space.h
+++ b/usr/src/uts/common/sys/id_space.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All Rights reserved.
*/
#ifndef _ID_SPACE_H
@@ -34,8 +35,6 @@ extern "C" {
#include <sys/mutex.h>
#include <sys/vmem.h>
-#ifdef _KERNEL
-
typedef vmem_t id_space_t;
id_space_t *id_space_create(const char *, id_t, id_t);
@@ -48,8 +47,6 @@ id_t id_allocff_nosleep(id_space_t *);
id_t id_alloc_specific_nosleep(id_space_t *, id_t);
void id_free(id_space_t *, id_t);
-#endif /* _KERNEL */
-
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/inotify.h b/usr/src/uts/common/sys/inotify.h
new file mode 100644
index 0000000000..8acc1a7280
--- /dev/null
+++ b/usr/src/uts/common/sys/inotify.h
@@ -0,0 +1,153 @@
+/*
+ * 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) 2014 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Header file to support for the inotify facility. Note that this facility
+ * is designed to be binary compatible with the Linux inotify facility; values
+ * for constants here should therefore exactly match those found in Linux, and
+ * this facility shouldn't be extended independently of Linux.
+ */
+
+#ifndef _SYS_INOTIFY_H
+#define _SYS_INOTIFY_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Events that can be explicitly requested on any inotify watch.
+ */
+#define IN_ACCESS 0x00000001
+#define IN_MODIFY 0x00000002
+#define IN_ATTRIB 0x00000004
+#define IN_CLOSE_WRITE 0x00000008
+#define IN_CLOSE_NOWRITE 0x00000010
+#define IN_OPEN 0x00000020
+#define IN_MOVED_FROM 0x00000040
+#define IN_MOVED_TO 0x00000080
+#define IN_CREATE 0x00000100
+#define IN_DELETE 0x00000200
+#define IN_DELETE_SELF 0x00000400
+#define IN_MOVE_SELF 0x00000800
+
+/*
+ * Events that can be sent to an inotify watch -- requested or not.
+ */
+#define IN_UNMOUNT 0x00002000
+#define IN_Q_OVERFLOW 0x00004000
+#define IN_IGNORED 0x00008000
+
+/*
+ * Flags that can modify an inotify event.
+ */
+#define IN_ONLYDIR 0x01000000
+#define IN_DONT_FOLLOW 0x02000000
+#define IN_EXCL_UNLINK 0x04000000
+#define IN_MASK_ADD 0x20000000
+#define IN_ISDIR 0x40000000
+#define IN_ONESHOT 0x80000000
+
+/*
+ * Helpful constants.
+ */
+#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
+#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
+#define IN_ALL_EVENTS \
+ (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+ IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | \
+ IN_DELETE | IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF)
+
+#define IN_CHILD_EVENTS \
+ (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+ IN_CLOSE_NOWRITE | IN_MODIFY | IN_OPEN)
+
+/*
+ * To assure binary compatibility with Linux, these values are fixed at their
+ * Linux equivalents, not their native ones.
+ */
+#define IN_CLOEXEC 02000000 /* LX_O_CLOEXEC */
+#define IN_NONBLOCK 04000 /* LX_O_NONBLOCK */
+
+struct inotify_event {
+ int32_t wd; /* watch descriptor */
+ uint32_t mask; /* mask of events */
+ uint32_t cookie; /* event association cookie, if any */
+ uint32_t len; /* size of name field */
+ char name[]; /* optional NUL-terminated name */
+};
+
+/*
+ * These ioctl values are specific to the native implementation; applications
+ * shouldn't be using them directly, and they should therefore be safe to
+ * change without breaking apps.
+ */
+#define INOTIFYIOC (('i' << 24) | ('n' << 16) | ('y' << 8))
+#define INOTIFYIOC_ADD_WATCH (INOTIFYIOC | 1) /* add watch */
+#define INOTIFYIOC_RM_WATCH (INOTIFYIOC | 2) /* remove watch */
+#define INOTIFYIOC_ADD_CHILD (INOTIFYIOC | 3) /* add child watch */
+#define INOTIFYIOC_ACTIVATE (INOTIFYIOC | 4) /* activate watch */
+
+#ifndef _LP64
+#ifndef _LITTLE_ENDIAN
+#define INOTIFY_PTR(type, name) uint32_t name##pad; type *name
+#else
+#define INOTIFY_PTR(type, name) type *name; uint32_t name##pad
+#endif
+#else
+#define INOTIFY_PTR(type, name) type *name
+#endif
+
+typedef struct inotify_addwatch {
+ int inaw_fd; /* open fd for object */
+ uint32_t inaw_mask; /* desired mask */
+} inotify_addwatch_t;
+
+typedef struct inotify_addchild {
+ INOTIFY_PTR(char, inac_name); /* pointer to name */
+ int inac_fd; /* open fd for parent */
+} inotify_addchild_t;
+
+#ifndef _KERNEL
+
+extern int inotify_init(void);
+extern int inotify_init1(int);
+extern int inotify_add_watch(int, const char *, uint32_t);
+extern int inotify_rm_watch(int, int);
+
+#else
+
+#define IN_UNMASKABLE \
+ (IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED | IN_ISDIR)
+
+#define IN_MODIFIERS \
+ (IN_EXCL_UNLINK | IN_ONESHOT)
+
+#define IN_FLAGS \
+ (IN_ONLYDIR | IN_DONT_FOLLOW | IN_MASK_ADD)
+
+#define IN_REMOVAL (1ULL << 32)
+#define INOTIFYMNRN_INOTIFY 0
+#define INOTIFYMNRN_CLONE 1
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_INOTIFY_H */
diff --git a/usr/src/uts/common/sys/ipc_impl.h b/usr/src/uts/common/sys/ipc_impl.h
index 0569c3e967..d7dc365c09 100644
--- a/usr/src/uts/common/sys/ipc_impl.h
+++ b/usr/src/uts/common/sys/ipc_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#ifndef _IPC_IMPL_H
@@ -226,6 +227,7 @@ int ipc_commit_begin(ipc_service_t *, key_t, int, kipc_perm_t *);
kmutex_t *ipc_commit_end(ipc_service_t *, kipc_perm_t *);
void ipc_cleanup(ipc_service_t *, kipc_perm_t *);
+void ipc_rmsvc(ipc_service_t *, kipc_perm_t *);
int ipc_rmid(ipc_service_t *, int, cred_t *);
int ipc_ids(ipc_service_t *, int *, uint_t, uint_t *);
diff --git a/usr/src/uts/common/sys/iso/signal_iso.h b/usr/src/uts/common/sys/iso/signal_iso.h
index b1990121b8..0ae64b45d7 100644
--- a/usr/src/uts/common/sys/iso/signal_iso.h
+++ b/usr/src/uts/common/sys/iso/signal_iso.h
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -95,7 +96,7 @@ extern "C" {
/* insert new signals here, and move _SIGRTM* appropriately */
#define _SIGRTMIN 42 /* first (highest-priority) realtime signal */
-#define _SIGRTMAX 73 /* last (lowest-priority) realtime signal */
+#define _SIGRTMAX 74 /* last (lowest-priority) realtime signal */
extern long _sysconf(int); /* System Private interface to sysconf() */
#define SIGRTMIN ((int)_sysconf(_SC_SIGRT_MIN)) /* first realtime signal */
#define SIGRTMAX ((int)_sysconf(_SC_SIGRT_MAX)) /* last realtime signal */
diff --git a/usr/src/uts/common/sys/klwp.h b/usr/src/uts/common/sys/klwp.h
index 41b70f6a6e..0ea1a396b9 100644
--- a/usr/src/uts/common/sys/klwp.h
+++ b/usr/src/uts/common/sys/klwp.h
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_KLWP_H
@@ -191,7 +191,14 @@ typedef struct _klwp {
struct ct_template *lwp_ct_active[CTT_MAXTYPE]; /* active templates */
struct contract *lwp_ct_latest[CTT_MAXTYPE]; /* last created contract */
- void *lwp_brand; /* per-lwp brand data */
+ /*
+ * Branding:
+ * lwp_brand - per-lwp brand data
+ * lwp_brand_syscall - brand syscall interposer
+ */
+ void *lwp_brand;
+ int (*lwp_brand_syscall)(void);
+
struct psinfo *lwp_spymaster; /* if an agent LWP, our spymaster */
} klwp_t;
diff --git a/usr/src/uts/common/sys/kmem_impl.h b/usr/src/uts/common/sys/kmem_impl.h
index 90e0477c45..26ab055dbc 100644
--- a/usr/src/uts/common/sys/kmem_impl.h
+++ b/usr/src/uts/common/sys/kmem_impl.h
@@ -302,7 +302,6 @@ typedef struct kmem_defrag {
uint64_t kmd_later; /* LATER responses */
uint64_t kmd_dont_need; /* DONT_NEED responses */
uint64_t kmd_dont_know; /* DONT_KNOW responses */
- uint64_t kmd_hunt_found; /* DONT_KNOW: # found in mag */
uint64_t kmd_slabs_freed; /* slabs freed by moves */
uint64_t kmd_defrags; /* kmem_cache_defrag() */
uint64_t kmd_scans; /* kmem_cache_scan() */
diff --git a/usr/src/uts/common/sys/ksocket.h b/usr/src/uts/common/sys/ksocket.h
index dfe25eec76..be669cb78d 100644
--- a/usr/src/uts/common/sys/ksocket.h
+++ b/usr/src/uts/common/sys/ksocket.h
@@ -21,6 +21,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_KSOCKET_H_
@@ -121,6 +122,10 @@ extern int ksocket_close(ksocket_t, struct cred *);
extern void ksocket_hold(ksocket_t);
extern void ksocket_rele(ksocket_t);
+typedef boolean_t (*ksocket_krecv_f)(ksocket_t, mblk_t *, size_t, int, void *);
+extern int ksocket_krecv_set(ksocket_t, ksocket_krecv_f, void *);
+extern void ksocket_krecv_unblock(ksocket_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/limits.h b/usr/src/uts/common/sys/limits.h
new file mode 100644
index 0000000000..88625d1829
--- /dev/null
+++ b/usr/src/uts/common/sys/limits.h
@@ -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 2015 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LIMITS_H
+#define _SYS_LIMITS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IOV_MAX 1024
+
+#ifdef _KERNEL
+#define IOV_MAX_STACK 16 /* max. IOV on-stack allocation */
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LIMITS_H */
diff --git a/usr/src/uts/common/sys/mac.h b/usr/src/uts/common/sys/mac.h
index 247c3bd48d..cdbbe4ce62 100644
--- a/usr/src/uts/common/sys/mac.h
+++ b/usr/src/uts/common/sys/mac.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
* Copyright (c) 2015 Garrett D'Amore <garrett@damore.org>
*/
@@ -101,6 +101,14 @@ typedef struct mac_propval_uint32_range_s {
} mac_propval_uint32_range_t;
/*
+ * Defines ranges which are a series of C style strings.
+ */
+typedef struct mac_propval_str_range_s {
+ uint32_t mpur_nextbyte;
+ char mpur_data[1];
+} mac_propval_str_range_t;
+
+/*
* Data type of property values.
*/
typedef enum {
@@ -120,6 +128,7 @@ typedef struct mac_propval_range_s {
mac_propval_type_t mpr_type; /* type of value */
union {
mac_propval_uint32_range_t mpr_uint32[1];
+ mac_propval_str_range_t mpr_str;
} u;
} mac_propval_range_t;
@@ -214,6 +223,7 @@ typedef enum {
MAC_PROP_MAX_RXHWCLNT_AVAIL,
MAC_PROP_MAX_TXHWCLNT_AVAIL,
MAC_PROP_IB_LINKMODE,
+ MAC_PROP_VN_PROMISC_FILTERED,
MAC_PROP_SECONDARY_ADDRS,
MAC_PROP_ADV_40GFDX_CAP,
MAC_PROP_EN_40GFDX_CAP,
diff --git a/usr/src/uts/common/sys/mac_client.h b/usr/src/uts/common/sys/mac_client.h
index 0fc4939503..1f2c732e6d 100644
--- a/usr/src/uts/common/sys/mac_client.h
+++ b/usr/src/uts/common/sys/mac_client.h
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
/*
@@ -115,6 +115,7 @@ typedef enum {
#define MAC_PROMISC_FLAGS_NO_PHYS 0x0002
#define MAC_PROMISC_FLAGS_VLAN_TAG_STRIP 0x0004
#define MAC_PROMISC_FLAGS_NO_COPY 0x0008
+#define MAC_PROMISC_FLAGS_DO_FIXUPS 0x0010
/* flags passed to mac_tx() */
#define MAC_DROP_ON_NO_DESC 0x01 /* freemsg() if no tx descs */
diff --git a/usr/src/uts/common/sys/mac_client_impl.h b/usr/src/uts/common/sys/mac_client_impl.h
index 0904b28645..d2fd145375 100644
--- a/usr/src/uts/common/sys/mac_client_impl.h
+++ b/usr/src/uts/common/sys/mac_client_impl.h
@@ -23,7 +23,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_MAC_CLIENT_IMPL_H
@@ -82,6 +82,7 @@ typedef struct mac_promisc_impl_s { /* Protected by */
boolean_t mpi_no_phys; /* WO */
boolean_t mpi_strip_vlan_tag; /* WO */
boolean_t mpi_no_copy; /* WO */
+ boolean_t mpi_do_fixups; /* WO */
} mac_promisc_impl_t;
typedef union mac_tx_percpu_s {
@@ -330,13 +331,14 @@ extern int mac_tx_percpu_cnt;
/* Mac protection flags */
#define MPT_FLAG_V6_LOCAL_ADDR_SET 0x0001
+#define MPT_FLAG_PROMISC_FILTERED 0x0002
/* in mac_client.c */
extern void mac_promisc_client_dispatch(mac_client_impl_t *, mblk_t *);
extern void mac_client_init(void);
extern void mac_client_fini(void);
extern void mac_promisc_dispatch(mac_impl_t *, mblk_t *,
- mac_client_impl_t *);
+ mac_client_impl_t *, boolean_t);
extern int mac_validate_props(mac_impl_t *, mac_resource_props_t *);
diff --git a/usr/src/uts/common/sys/mac_client_priv.h b/usr/src/uts/common/sys/mac_client_priv.h
index 6b409513a6..a5848625c2 100644
--- a/usr/src/uts/common/sys/mac_client_priv.h
+++ b/usr/src/uts/common/sys/mac_client_priv.h
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -171,6 +171,7 @@ extern void mac_client_set_intr_cpu(void *, mac_client_handle_t, int32_t);
extern void *mac_get_devinfo(mac_handle_t);
extern boolean_t mac_is_vnic(mac_handle_t);
+extern boolean_t mac_is_overlay(mac_handle_t);
extern uint32_t mac_no_notification(mac_handle_t);
extern int mac_set_prop(mac_handle_t, mac_prop_id_t, char *, void *, uint_t);
diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h
index 2286b587e8..46293b1a74 100644
--- a/usr/src/uts/common/sys/mac_impl.h
+++ b/usr/src/uts/common/sys/mac_impl.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_MAC_IMPL_H
@@ -331,7 +331,7 @@ struct mac_group_s {
if ((src_mcip)->mci_state_flags & MCIS_SHARE_BOUND) \
rhandle = (mip)->mi_default_tx_ring; \
if (mip->mi_promisc_list != NULL) \
- mac_promisc_dispatch(mip, mp, src_mcip); \
+ mac_promisc_dispatch(mip, mp, src_mcip, B_TRUE); \
/* \
* Grab the proper transmit pointer and handle. Special \
* optimization: we can test mi_bridge_link itself atomically, \
@@ -643,6 +643,7 @@ struct mac_impl_s {
#define MIS_LEGACY 0x0040
#define MIS_NO_ACTIVE 0x0080
#define MIS_POLL_DISABLE 0x0100
+#define MIS_IS_OVERLAY 0x0200
#define mi_getstat mi_callbacks->mc_getstat
#define mi_start mi_callbacks->mc_start
@@ -894,6 +895,8 @@ extern void mac_protect_fini(mac_client_impl_t *);
extern int mac_set_resources(mac_handle_t, mac_resource_props_t *);
extern void mac_get_resources(mac_handle_t, mac_resource_props_t *);
extern void mac_get_effective_resources(mac_handle_t, mac_resource_props_t *);
+extern void mac_set_promisc_filtered(mac_client_handle_t, boolean_t);
+extern boolean_t mac_get_promisc_filtered(mac_client_handle_t);
extern cpupart_t *mac_pset_find(mac_resource_props_t *, boolean_t *);
extern void mac_set_pool_effective(boolean_t, cpupart_t *,
diff --git a/usr/src/uts/common/sys/mac_provider.h b/usr/src/uts/common/sys/mac_provider.h
index 9f7f2a1a73..5f02451542 100644
--- a/usr/src/uts/common/sys/mac_provider.h
+++ b/usr/src/uts/common/sys/mac_provider.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_MAC_PROVIDER_H
@@ -106,7 +107,8 @@ typedef enum {
MAC_CAPAB_NO_NATIVEVLAN = 0x00080000, /* boolean only, no data */
MAC_CAPAB_NO_ZCOPY = 0x00100000, /* boolean only, no data */
MAC_CAPAB_LEGACY = 0x00200000, /* data is mac_capab_legacy_t */
- MAC_CAPAB_VRRP = 0x00400000 /* data is mac_capab_vrrp_t */
+ MAC_CAPAB_VRRP = 0x00400000, /* data is mac_capab_vrrp_t */
+ MAC_CAPAB_OVERLAY = 0x00800000 /* boolean only, no data */
} mac_capab_t;
/*
diff --git a/usr/src/uts/common/sys/mman.h b/usr/src/uts/common/sys/mman.h
index 0182fa9ce3..6463211a2f 100644
--- a/usr/src/uts/common/sys/mman.h
+++ b/usr/src/uts/common/sys/mman.h
@@ -339,6 +339,7 @@ struct memcntl_mha32 {
#define MS_SYNC 0x4 /* wait for msync */
#define MS_ASYNC 0x1 /* return immediately */
#define MS_INVALIDATE 0x2 /* invalidate caches */
+#define MS_INVALCURPROC 0x8 /* invalidate cache for curproc only */
#if (_POSIX_C_SOURCE <= 2) && !defined(_XPG4_2) || defined(__EXTENSIONS__)
/* functions to mctl */
diff --git a/usr/src/uts/common/sys/mntent.h b/usr/src/uts/common/sys/mntent.h
index 88c98dc5a4..7196f7b3ac 100644
--- a/usr/src/uts/common/sys/mntent.h
+++ b/usr/src/uts/common/sys/mntent.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*
* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
@@ -47,6 +48,7 @@ extern "C" {
#define MNTTYPE_PCFS "pcfs" /* PC (MSDOS) file system */
#define MNTTYPE_PC MNTTYPE_PCFS /* Deprecated name; use MNTTYPE_PCFS */
#define MNTTYPE_LOFS "lofs" /* Loop back file system */
+#define MNTTYPE_HYPRLOFS "hyprlofs" /* Hyperlofs file system */
#define MNTTYPE_LO MNTTYPE_LOFS /* Deprecated name; use MNTTYPE_LOFS */
#define MNTTYPE_HSFS "hsfs" /* High Sierra (9660) file system */
#define MNTTYPE_SWAP "swap" /* Swap file system */
diff --git a/usr/src/uts/common/sys/netconfig.h b/usr/src/uts/common/sys/netconfig.h
index 14b1aa55db..883c329aed 100644
--- a/usr/src/uts/common/sys/netconfig.h
+++ b/usr/src/uts/common/sys/netconfig.h
@@ -28,6 +28,7 @@
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_NETCONFIG_H
@@ -147,6 +148,8 @@ extern int endnetpath(void *);
extern struct netconfig *getnetpath(void *);
extern void nc_perror(const char *);
extern char *nc_sperror(void);
+extern void _nsl_brand_set_hooks(int (*)(void),
+ struct netconfig *(*)(int));
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/neti.h b/usr/src/uts/common/sys/neti.h
index 93b5fc3e01..ea85c78f6b 100644
--- a/usr/src/uts/common/sys/neti.h
+++ b/usr/src/uts/common/sys/neti.h
@@ -44,6 +44,8 @@ extern "C" {
#define NHF_INET "NHF_INET"
#define NHF_INET6 "NHF_INET6"
#define NHF_ARP "NHF_ARP"
+#define NHF_VND_INET "NHF_VND_INET"
+#define NHF_VND_INET6 "NHF_VND_INET6"
/*
* Event identification
diff --git a/usr/src/uts/common/sys/netstack.h b/usr/src/uts/common/sys/netstack.h
index 2c77e1be96..4d8e99934b 100644
--- a/usr/src/uts/common/sys/netstack.h
+++ b/usr/src/uts/common/sys/netstack.h
@@ -23,6 +23,11 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+
+/*
+ * Copyright (c) 2016, Joyent, Inc. All rights reserved.
+ */
+
#ifndef _SYS_NETSTACK_H
#define _SYS_NETSTACK_H
@@ -81,7 +86,8 @@ typedef id_t netstackid_t;
#define NS_IPSECESP 16
#define NS_IPNET 17
#define NS_ILB 18
-#define NS_MAX (NS_ILB+1)
+#define NS_VND 19
+#define NS_MAX (NS_VND+1)
/*
* State maintained for each module which tracks the state of
@@ -233,6 +239,7 @@ extern void netstack_rele(netstack_t *);
extern netstack_t *netstack_find_by_cred(const cred_t *);
extern netstack_t *netstack_find_by_stackid(netstackid_t);
extern netstack_t *netstack_find_by_zoneid(zoneid_t);
+extern boolean_t netstack_inuse_by_stackid(netstackid_t stackid);
extern zoneid_t netstackid_to_zoneid(netstackid_t);
extern zoneid_t netstack_get_zoneid(netstack_t *);
diff --git a/usr/src/uts/common/sys/overlay.h b/usr/src/uts/common/sys/overlay.h
new file mode 100644
index 0000000000..12d0dbca51
--- /dev/null
+++ b/usr/src/uts/common/sys/overlay.h
@@ -0,0 +1,96 @@
+/*
+ * 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 2015, Joyent, Inc.
+ */
+
+#ifndef _SYS_OVERLAY_H
+#define _SYS_OVERLAY_H
+
+/*
+ * Overlay device support
+ */
+
+#include <sys/param.h>
+#include <sys/dld_ioc.h>
+#include <sys/mac.h>
+#include <sys/overlay_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OVERLAY_IOC_CREATE OVERLAYIOC(1)
+#define OVERLAY_IOC_DELETE OVERLAYIOC(2)
+#define OVERLAY_IOC_PROPINFO OVERLAYIOC(3)
+#define OVERLAY_IOC_GETPROP OVERLAYIOC(4)
+#define OVERLAY_IOC_SETPROP OVERLAYIOC(5)
+#define OVERLAY_IOC_NPROPS OVERLAYIOC(6)
+#define OVERLAY_IOC_ACTIVATE OVERLAYIOC(7)
+#define OVERLAY_IOC_STATUS OVERLAYIOC(8)
+
+typedef struct overlay_ioc_create {
+ datalink_id_t oic_linkid;
+ uint32_t oic_filler;
+ uint64_t oic_vnetid;
+ char oic_encap[MAXLINKNAMELEN];
+} overlay_ioc_create_t;
+
+typedef struct overlay_ioc_activate {
+ datalink_id_t oia_linkid;
+} overlay_ioc_activate_t;
+
+typedef struct overlay_ioc_delete {
+ datalink_id_t oid_linkid;
+} overlay_ioc_delete_t;
+
+typedef struct overlay_ioc_nprops {
+ datalink_id_t oipn_linkid;
+ int32_t oipn_nprops;
+} overlay_ioc_nprops_t;
+
+typedef struct overlay_ioc_propinfo {
+ datalink_id_t oipi_linkid;
+ int32_t oipi_id;
+ char oipi_name[OVERLAY_PROP_NAMELEN];
+ uint_t oipi_type;
+ uint_t oipi_prot;
+ uint8_t oipi_default[OVERLAY_PROP_SIZEMAX];
+ uint32_t oipi_defsize;
+ uint32_t oipi_posssize;
+ uint8_t oipi_poss[OVERLAY_PROP_SIZEMAX];
+} overlay_ioc_propinfo_t;
+
+typedef struct overlay_ioc_prop {
+ datalink_id_t oip_linkid;
+ int32_t oip_id;
+ char oip_name[OVERLAY_PROP_NAMELEN];
+ uint8_t oip_value[OVERLAY_PROP_SIZEMAX];
+ uint32_t oip_size;
+} overlay_ioc_prop_t;
+
+typedef enum overlay_status {
+ OVERLAY_I_OK = 0x00,
+ OVERLAY_I_DEGRADED = 0x01
+} overlay_status_t;
+
+typedef struct overlay_ioc_status {
+ datalink_id_t ois_linkid;
+ uint_t ois_status;
+ char ois_message[OVERLAY_STATUS_BUFLEN];
+} overlay_ioc_status_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_OVERLAY_H */
diff --git a/usr/src/uts/common/sys/overlay_common.h b/usr/src/uts/common/sys/overlay_common.h
new file mode 100644
index 0000000000..d638096006
--- /dev/null
+++ b/usr/src/uts/common/sys/overlay_common.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_OVERLAY_COMMON_H
+#define _SYS_OVERLAY_COMMON_H
+
+/*
+ * Common overlay definitions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum overlay_target_mode {
+ OVERLAY_TARGET_NONE = 0x0,
+ OVERLAY_TARGET_POINT,
+ OVERLAY_TARGET_DYNAMIC
+} overlay_target_mode_t;
+
+typedef enum overlay_plugin_dest {
+ OVERLAY_PLUGIN_D_INVALID = 0x0,
+ OVERLAY_PLUGIN_D_ETHERNET = 0x1,
+ OVERLAY_PLUGIN_D_IP = 0x2,
+ OVERLAY_PLUGIN_D_PORT = 0x4,
+ OVERLAY_PLUGIN_D_MASK = 0x7
+} overlay_plugin_dest_t;
+
+typedef enum overlay_prop_type {
+ OVERLAY_PROP_T_INT = 0x1, /* signed int */
+ OVERLAY_PROP_T_UINT, /* unsigned int */
+ OVERLAY_PROP_T_IP, /* sinaddr6 */
+ OVERLAY_PROP_T_STRING /* OVERLAY_PROPS_SIZEMAX */
+} overlay_prop_type_t;
+
+typedef enum overlay_prop_prot {
+ OVERLAY_PROP_PERM_REQ = 0x1,
+ OVERLAY_PROP_PERM_READ = 0x2,
+ OVERLAY_PROP_PERM_WRITE = 0x4,
+ OVERLAY_PROP_PERM_RW = 0x6,
+ OVERLAY_PROP_PERM_RRW = 0x7,
+ OVERLAY_PROP_PERM_MASK = 0x7
+} overlay_prop_prot_t;
+
+#define OVERLAY_PROP_NAMELEN 64
+#define OVERLAY_PROP_SIZEMAX 256
+#define OVERLAY_STATUS_BUFLEN 256
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_OVERLAY_COMMON_H */
diff --git a/usr/src/uts/common/sys/overlay_impl.h b/usr/src/uts/common/sys/overlay_impl.h
new file mode 100644
index 0000000000..7fb8b8da1d
--- /dev/null
+++ b/usr/src/uts/common/sys/overlay_impl.h
@@ -0,0 +1,205 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_OVERLAY_IMPL_H
+#define _SYS_OVERLAY_IMPL_H
+
+/*
+ * Overlay device support
+ */
+
+#include <sys/overlay.h>
+#include <sys/overlay_common.h>
+#include <sys/overlay_plugin.h>
+#include <sys/overlay_target.h>
+#include <sys/ksynch.h>
+#include <sys/list.h>
+#include <sys/avl.h>
+#include <sys/ksocket.h>
+#include <sys/socket.h>
+#include <sys/refhash.h>
+#include <sys/ethernet.h>
+#include <sys/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OVEP_VERSION_ONE 0x1
+
+typedef struct overlay_plugin {
+ kmutex_t ovp_mutex;
+ list_node_t ovp_link; /* overlay_plugin_lock */
+ uint_t ovp_active; /* ovp_mutex */
+ const char *ovp_name; /* RO */
+ const overlay_plugin_ops_t *ovp_ops; /* RO */
+ const char *const *ovp_props; /* RO */
+ uint_t ovp_nprops; /* RO */
+ uint_t ovp_id_size; /* RO */
+ overlay_plugin_flags_t ovp_flags; /* RO */
+ overlay_plugin_dest_t ovp_dest; /* RO */
+} overlay_plugin_t;
+
+typedef struct overlay_mux {
+ list_node_t omux_lnode;
+ ksocket_t omux_ksock; /* RO */
+ overlay_plugin_t *omux_plugin; /* RO: associated encap */
+ int omux_domain; /* RO: socket domain */
+ int omux_family; /* RO: socket family */
+ int omux_protocol; /* RO: socket protocol */
+ struct sockaddr *omux_addr; /* RO: socket address */
+ socklen_t omux_alen; /* RO: sockaddr len */
+ kmutex_t omux_lock; /* Protects everything below */
+ uint_t omux_count; /* Active instances */
+ avl_tree_t omux_devices; /* Tree of devices */
+} overlay_mux_t;
+
+typedef enum overlay_target_flag {
+ OVERLAY_T_TEARDOWN = 0x1
+} overlay_target_flag_t;
+
+typedef struct overlay_target {
+ kmutex_t ott_lock;
+ kcondvar_t ott_cond;
+ overlay_target_mode_t ott_mode; /* RO */
+ overlay_plugin_dest_t ott_dest; /* RO */
+ uint64_t ott_id; /* RO */
+ overlay_target_flag_t ott_flags; /* ott_lock */
+ uint_t ott_ocount; /* ott_lock */
+ union { /* ott_lock */
+ overlay_target_point_t ott_point;
+ struct overlay_target_dyn {
+ refhash_t *ott_dhash;
+ avl_tree_t ott_tree;
+ } ott_dyn;
+ } ott_u;
+} overlay_target_t;
+
+typedef enum overlay_dev_flag {
+ OVERLAY_F_ACTIVATED = 0x01, /* Activate ioctl completed */
+ OVERLAY_F_IN_MUX = 0x02, /* Currently in a mux */
+ OVERLAY_F_IN_TX = 0x04, /* Currently doing tx */
+ OVERLAY_F_IN_RX = 0x08, /* Currently doing rx */
+ OVERLAY_F_IOMASK = 0x0c, /* A mask for rx and tx */
+ OVERLAY_F_MDDROP = 0x10, /* Drop traffic for metadata update */
+ OVERLAY_F_STOPMASK = 0x1e, /* None set when stopping */
+ OVERLAY_F_VARPD = 0x20, /* varpd plugin exists */
+ OVERLAY_F_DEGRADED = 0x40, /* device is degraded */
+ OVERLAY_F_MASK = 0x7f /* mask of everything */
+} overlay_dev_flag_t;
+
+typedef struct overlay_dev {
+ kmutex_t odd_lock;
+ kcondvar_t odd_iowait;
+ list_node_t odd_link; /* overlay_dev_lock */
+ mac_handle_t odd_mh; /* RO */
+ overlay_plugin_t *odd_plugin; /* RO */
+ datalink_id_t odd_linkid; /* RO */
+ void *odd_pvoid; /* RO -- only used by plugin */
+ uint_t odd_ref; /* protected by odd_lock */
+ uint_t odd_mtu; /* protected by odd_lock */
+ overlay_dev_flag_t odd_flags; /* protected by odd_lock */
+ uint_t odd_rxcount; /* protected by odd_lock */
+ uint_t odd_txcount; /* protected by odd_lock */
+ overlay_mux_t *odd_mux; /* protected by odd_lock */
+ uint64_t odd_vid; /* RO if active else odd_lock */
+ avl_node_t odd_muxnode; /* managed by mux */
+ overlay_target_t *odd_target; /* See big theory statement */
+ char odd_fmamsg[OVERLAY_STATUS_BUFLEN]; /* odd_lock */
+} overlay_dev_t;
+
+typedef enum overlay_target_entry_flags {
+ OVERLAY_ENTRY_F_PENDING = 0x01, /* lookup in progress */
+ OVERLAY_ENTRY_F_VALID = 0x02, /* entry is currently valid */
+ OVERLAY_ENTRY_F_DROP = 0x04, /* always drop target */
+ OVERLAY_ENTRY_F_VALID_MASK = 0x06
+} overlay_target_entry_flags_t;
+
+typedef struct overlay_target_entry {
+ kmutex_t ote_lock;
+ refhash_link_t ote_reflink; /* hashtable link */
+ avl_node_t ote_avllink; /* iteration link */
+ list_node_t ote_qlink;
+ overlay_target_entry_flags_t ote_flags; /* RW: state flags */
+ uint8_t ote_addr[ETHERADDRL]; /* RO: mac addr */
+ overlay_target_t *ote_ott; /* RO */
+ overlay_dev_t *ote_odd; /* RO */
+ overlay_target_point_t ote_dest; /* RW: destination */
+ mblk_t *ote_chead; /* RW: blocked mb chain head */
+ mblk_t *ote_ctail; /* RW: blocked mb chain tail */
+ size_t ote_mbsize; /* RW: outstanding mblk size */
+ hrtime_t ote_vtime; /* RW: valid timestamp */
+} overlay_target_entry_t;
+
+
+#define OVERLAY_CTL "overlay"
+
+extern dev_info_t *overlay_dip;
+
+extern mblk_t *overlay_m_tx(void *, mblk_t *);
+
+typedef int (*overlay_dev_iter_f)(overlay_dev_t *, void *);
+extern void overlay_dev_iter(overlay_dev_iter_f, void *);
+
+extern void overlay_plugin_init(void);
+extern overlay_plugin_t *overlay_plugin_lookup(const char *);
+extern void overlay_plugin_rele(overlay_plugin_t *);
+extern void overlay_plugin_fini(void);
+typedef int (*overlay_plugin_walk_f)(overlay_plugin_t *, void *);
+extern void overlay_plugin_walk(overlay_plugin_walk_f, void *);
+
+extern void overlay_io_start(overlay_dev_t *, overlay_dev_flag_t);
+extern void overlay_io_done(overlay_dev_t *, overlay_dev_flag_t);
+
+extern void overlay_mux_init(void);
+extern void overlay_mux_fini(void);
+
+extern overlay_mux_t *overlay_mux_open(overlay_plugin_t *, int, int, int,
+ struct sockaddr *, socklen_t, int *);
+extern void overlay_mux_close(overlay_mux_t *);
+extern void overlay_mux_add_dev(overlay_mux_t *, overlay_dev_t *);
+extern void overlay_mux_remove_dev(overlay_mux_t *, overlay_dev_t *);
+extern int overlay_mux_tx(overlay_mux_t *, struct msghdr *, mblk_t *);
+
+extern void overlay_prop_init(overlay_prop_handle_t);
+
+extern void overlay_target_init(void);
+extern int overlay_target_busy(void);
+extern int overlay_target_open(dev_t *, int, int, cred_t *);
+extern int overlay_target_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+extern int overlay_target_close(dev_t, int, int, cred_t *);
+extern void overlay_target_free(overlay_dev_t *);
+
+#define OVERLAY_TARGET_OK 0
+#define OVERLAY_TARGET_DROP 1
+#define OVERLAY_TARGET_ASYNC 2
+extern int overlay_target_lookup(overlay_dev_t *, mblk_t *, struct sockaddr *,
+ socklen_t *);
+extern void overlay_target_quiesce(overlay_target_t *);
+extern void overlay_target_fini(void);
+
+extern void overlay_fm_init(void);
+extern void overlay_fm_fini(void);
+extern void overlay_fm_degrade(overlay_dev_t *, const char *);
+extern void overlay_fm_restore(overlay_dev_t *);
+
+extern overlay_dev_t *overlay_hold_by_dlid(datalink_id_t);
+extern void overlay_hold_rele(overlay_dev_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_OVERLAY_IMPL_H */
diff --git a/usr/src/uts/common/sys/overlay_plugin.h b/usr/src/uts/common/sys/overlay_plugin.h
new file mode 100644
index 0000000000..07efaa05df
--- /dev/null
+++ b/usr/src/uts/common/sys/overlay_plugin.h
@@ -0,0 +1,324 @@
+/*
+ * 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) 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_OVERLAY_PLUGIN_H
+#define _SYS_OVERLAY_PLUGIN_H
+
+/*
+ * overlay plugin interface for encapsulation/decapsulation modules
+ *
+ * This header file defines how encapsulation and decapsulation plugins
+ * interact within the broader system. At this time, these interfaces are
+ * considered private to illumos and therefore are subject to change. As we gain
+ * more experience with a few of the different encapsulation formats, say nvgre
+ * or geneve, then we can move to make this a more-stable interface.
+ *
+ * A plugin is a general kernel module that uses the miscellaneous mod-linkage.
+ *
+ * In it's _init(9E) routine, it must register itself with the overlay
+ * subsystem. To do this, it allocates an overlay_plugin_register_t via
+ * overlay_plugin_alloc(), that it then * fills out with various required
+ * information and then attempts to register with the system via a call to
+ * overlay_plugin_register(). If that succeeds, it should then call
+ * mod_install(9F). If the mod_install(9F) fails, then it should call
+ * overlay_plugin_unregister(). Regardless of success or failure, it should call
+ * overlay_plugin_free() to ensure that any memory that may be associated with
+ * the registration is freed.
+ *
+ * When the module's _fini(9E) is called, overlay_plugin_unregister() should be
+ * called first. It may return an error, such as EBUSY. In such cases, it should
+ * be returned as the return status of _fini(9E). This is quite necessary, it
+ * ensures that if the module is in use it doesn't get unloaded out from under
+ * us the broader subsystem while it's still in use. A driver can use that to
+ * know that there are no current instances of its private data.
+ *
+ * ------------------
+ * Plugin Definitions
+ * ------------------
+ *
+ * A plugin is required to fill in both an operations vector and a series of
+ * information to the callback routine. Here are the routines and their
+ * purposes. The full signatures are available below.
+ *
+ * overlay_plugin_init_t
+ *
+ * This interface is used to create a new instance of a plugin. An instance
+ * of a plugin will be created for each overlay device that is created. For
+ * example, if a device is created with VXLAN ID 23 and ID 42, then there
+ * will be two different calls to this function.
+ *
+ * This function gives the plugin a chance to create a private data
+ * structure that will be returned on subsequent calls to the system.
+ *
+ * overlay_plugin_fini_t
+ *
+ * This is the opposite of overlay_plugin_init_t. It will be called when it
+ * is safe to remove any private data that is associated with this instance
+ * of the plugin.
+ *
+ * overlay_plugin_propinfo_t
+ *
+ * This is called with the name of a property that is registered when the
+ * plugin is created. This function will be called with the name of the
+ * property that information is being requested about. The plugin is
+ * responsible for filling out information such as setting the name, the
+ * type of property it is, the protection of the property (can a user
+ * update it?), whether the property is required, an optional default value
+ * for the property, and an optional set of values or ranges that are
+ * allowed.
+ *
+ * overlay_plugin_getprop_t
+ *
+ * Return the value of the named property from the current instance of the
+ * plugin.
+ *
+ * overlay_plugin_setprop_t
+ *
+ * Set the value of the named property to the specified value for the
+ * current instance of the plugin. Note, that it is the plugin's
+ * responsibility to ensure that the value of the property is valid and to
+ * update state as appropriate.
+ *
+ * overlay_plugin_socket_t
+ *
+ * Every overlay device has a corresponding socket that it uses to send and
+ * receive traffic. This routine is used to get the parameters that should
+ * be used to define such a socket. The actual socket may be multiplexed
+ * with other uses of it.
+ *
+ * overlay_plugin_sockopt_t
+ *
+ * Allow a plugin to set any necessary socket options that it needs on the
+ * kernel socket that is being used by a mux. This will only be called once
+ * for a given mux, if additional devices are added to a mux, it will not
+ * be called additional times.
+ *
+ * overlay_plugin_encap_t
+ *
+ * In this routine you're given a message block and information about the
+ * packet, such as the identifier and are asked to fill out a message block
+ * that represents the encapsulation header and optionally manipulate the
+ * input message if required.
+ *
+ * overlay_plugin_decap_t
+ *
+ * In this routine, you're given the encapsulated message block. The
+ * requirement is to decapsulate it and determine what is the correct
+ * overlay identifier for this network and to fill in the header size so
+ * the broader system knows how much of this data should be considered
+ * consumed.
+ *
+ * ovpo_callbacks
+ *
+ * This should be set to zero, it's reserved for future use.
+ *
+ * Once these properties are defined, the module should define the following
+ * members in the overlay_plugin_register_t.
+ *
+ * ovep_version
+ *
+ * Should be set to the value of the macro OVEP_VERSION.
+ *
+ * ovep_name
+ *
+ * Should be set to a character string that has the name of the module.
+ * Generally this should match the name of the kernel module; however, this
+ * is the name that users will use to refer to this module when creating
+ * devices.
+ *
+ * overlay_plugin_ops_t
+ *
+ * Should be set to the functions as described above.
+ *
+ * ovep_props
+ *
+ * This is an array of character strings that holds the names of the
+ * properties of the encapsulation plugin.
+ *
+ *
+ * ovep_id_size
+ *
+ * This is the size in bytes of the valid range for the identifier. The
+ * valid identifier range is considered a ovep_id_size byte unsigned
+ * integer, [ 0, 1 << (ovep_id_size * 8) ).
+ *
+ * ovep_flags
+ *
+ * A series of flags that indicate optional features that are supported.
+ * Valid flags include:
+ *
+ * OVEP_F_VLAN_TAG
+ *
+ * The encapsulation format allows for the encapsulated
+ * packet to maintain a VLAN tag.
+ *
+ * ovep_dest
+ *
+ * Describes the kind of destination that the overlay plugin supports for
+ * sending traffic. For example, vxlan uses UDP, therefore it requires both
+ * an IP address and a port; however, nvgre uses the gre header and
+ * therefore only requires an IP address. The following flags may be
+ * combined:
+ *
+ * OVERLAY_PLUGIN_D_ETHERNET
+ *
+ * Indicates that to send a packet to its destination, we
+ * require a link-layer ethernet address.
+ *
+ * OVERLAY_PLUGIN_D_IP
+ *
+ * Indicates that to send a packet to its destination, we
+ * require an IP address. Note, all IP addresses are
+ * transmitted as IPv6 addresses and for an IPv4
+ * destination, using an IPv4-mapped IPv6 address is the
+ * expected way to transmit that.
+ *
+ * OVERLAY_PLUGIN_D_PORT
+ *
+ * Indicates that to send a packet to its destination, a
+ * port is required, this usually indicates that the
+ * protocol uses something like TCP or UDP.
+ *
+ *
+ * -------------------------------------------------
+ * Downcalls, Upcalls, and Synchronization Guarantees
+ * -------------------------------------------------
+ *
+ * Every instance of a given module is independent. The kernel only guarantees
+ * that it will probably perform downcalls into different instances in parallel
+ * at some point. No locking is provided by the framework for synchronization
+ * across instances. If a module finds itself needing that, it will be up to it
+ * to provide it.
+ *
+ * In a given instance, the kernel may call into entry points in parallel. If
+ * the instance has private data, it should likely synchronize it. The one
+ * guarantee that we do make, is that calls to getprop and setprop will be done
+ * synchronized by a caller holding the MAC perimeter.
+ *
+ * While servicing a downcall from the general overlay device framework, a
+ * kernel module should not make any upcalls, excepting those functions that are
+ * defined in this header file, eg. the property related callbacks. Improtantly,
+ * it cannot make any assumptions about what locks may or may not be held by the
+ * broader system. The only thing that it is safe for it to use are its own
+ * locks.
+ *
+ * ----------------
+ * Downcall Context
+ * ----------------
+ *
+ * For all of the downcalls, excepting the overlay_plugin_encap_t and
+ * overlay_plugin_decap_t, the calls will be made either in kernel or user
+ * context, the module should not assume either way.
+ *
+ * overlay_plugin_encap_t and overlay_plugin_decap_t may be called in user,
+ * kernel or interrupt context; however, it is guaranteed that the interrupt
+ * will be below LOCK_LEVEL, and therefore it is safe to grab locks.
+ */
+
+#include <sys/stream.h>
+#include <sys/mac_provider.h>
+#include <sys/ksocket.h>
+#include <sys/overlay_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OVEP_VERSION 0x1
+
+typedef enum overlay_plugin_flags {
+ OVEP_F_VLAN_TAG = 0x01 /* Supports VLAN Tags */
+} overlay_plugin_flags_t;
+
+/*
+ * The ID space could easily be more than a 64-bit number, even
+ * though today it's either a 24-64 bit value. How should we future
+ * proof ourselves here?
+ */
+typedef struct ovep_encap_info {
+ uint64_t ovdi_id;
+ size_t ovdi_hdr_size;
+} ovep_encap_info_t;
+
+typedef struct __overlay_prop_handle *overlay_prop_handle_t;
+typedef struct __overlay_handle *overlay_handle_t;
+
+/*
+ * Plugins are guaranteed that calls to setprop are serialized. However, any
+ * number of other calls can be going on in parallel otherwise.
+ */
+typedef int (*overlay_plugin_encap_t)(void *, mblk_t *,
+ ovep_encap_info_t *, mblk_t **);
+typedef int (*overlay_plugin_decap_t)(void *, mblk_t *,
+ ovep_encap_info_t *);
+typedef int (*overlay_plugin_init_t)(overlay_handle_t, void **);
+typedef void (*overlay_plugin_fini_t)(void *);
+typedef int (*overlay_plugin_socket_t)(void *, int *, int *, int *,
+ struct sockaddr *, socklen_t *);
+typedef int (*overlay_plugin_sockopt_t)(ksocket_t);
+typedef int (*overlay_plugin_getprop_t)(void *, const char *, void *,
+ uint32_t *);
+typedef int (*overlay_plugin_setprop_t)(void *, const char *, const void *,
+ uint32_t);
+typedef int (*overlay_plugin_propinfo_t)(const char *, overlay_prop_handle_t);
+
+typedef struct overlay_plugin_ops {
+ uint_t ovpo_callbacks;
+ overlay_plugin_init_t ovpo_init;
+ overlay_plugin_fini_t ovpo_fini;
+ overlay_plugin_encap_t ovpo_encap;
+ overlay_plugin_decap_t ovpo_decap;
+ overlay_plugin_socket_t ovpo_socket;
+ overlay_plugin_sockopt_t ovpo_sockopt;
+ overlay_plugin_getprop_t ovpo_getprop;
+ overlay_plugin_setprop_t ovpo_setprop;
+ overlay_plugin_propinfo_t ovpo_propinfo;
+} overlay_plugin_ops_t;
+
+typedef struct overlay_plugin_register {
+ uint_t ovep_version;
+ const char *ovep_name;
+ const overlay_plugin_ops_t *ovep_ops;
+ const char **ovep_props;
+ uint_t ovep_id_size;
+ uint_t ovep_flags;
+ uint_t ovep_dest;
+} overlay_plugin_register_t;
+
+/*
+ * Functions that interact with registration
+ */
+extern overlay_plugin_register_t *overlay_plugin_alloc(uint_t);
+extern void overlay_plugin_free(overlay_plugin_register_t *);
+extern int overlay_plugin_register(overlay_plugin_register_t *);
+extern int overlay_plugin_unregister(const char *);
+
+/*
+ * Property information callbacks
+ */
+extern void overlay_prop_set_name(overlay_prop_handle_t, const char *);
+extern void overlay_prop_set_prot(overlay_prop_handle_t, overlay_prop_prot_t);
+extern void overlay_prop_set_type(overlay_prop_handle_t, overlay_prop_type_t);
+extern int overlay_prop_set_default(overlay_prop_handle_t, void *, ssize_t);
+extern void overlay_prop_set_nodefault(overlay_prop_handle_t);
+extern void overlay_prop_set_range_uint32(overlay_prop_handle_t, uint32_t,
+ uint32_t);
+extern void overlay_prop_set_range_str(overlay_prop_handle_t, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_OVERLAY_PLUGIN_H */
diff --git a/usr/src/uts/common/sys/overlay_target.h b/usr/src/uts/common/sys/overlay_target.h
new file mode 100644
index 0000000000..cae193c334
--- /dev/null
+++ b/usr/src/uts/common/sys/overlay_target.h
@@ -0,0 +1,292 @@
+/*
+ * 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) 2015 Joyent, Inc.
+ */
+
+#ifndef _OVERLAY_TARGET_H
+#define _OVERLAY_TARGET_H
+
+/*
+ * Overlay device varpd ioctl interface (/dev/overlay)
+ */
+
+#include <sys/types.h>
+#include <sys/ethernet.h>
+#include <netinet/in.h>
+#include <sys/overlay_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct overlay_target_point {
+ uint8_t otp_mac[ETHERADDRL];
+ struct in6_addr otp_ip;
+ uint16_t otp_port;
+} overlay_target_point_t;
+
+#define OVERLAY_TARG_IOCTL (('o' << 24) | ('v' << 16) | ('t' << 8))
+
+#define OVERLAY_TARG_INFO (OVERLAY_TARG_IOCTL | 0x01)
+
+typedef enum overlay_targ_info_flags {
+ OVERLAY_TARG_INFO_F_ACTIVE = 0x01,
+ OVERLAY_TARG_INFO_F_DEGRADED = 0x02
+} overlay_targ_info_flags_t;
+
+/*
+ * Get target information about an overlay device
+ */
+typedef struct overlay_targ_info {
+ datalink_id_t oti_linkid;
+ uint32_t oti_needs;
+ uint64_t oti_flags;
+ uint64_t oti_vnetid;
+} overlay_targ_info_t;
+
+/*
+ * Declare an association between a given varpd instance and a datalink.
+ */
+#define OVERLAY_TARG_ASSOCIATE (OVERLAY_TARG_IOCTL | 0x02)
+
+typedef struct overlay_targ_associate {
+ datalink_id_t ota_linkid;
+ uint32_t ota_mode;
+ uint64_t ota_id;
+ uint32_t ota_provides;
+ overlay_target_point_t ota_point;
+} overlay_targ_associate_t;
+
+/*
+ * Remove an association from a device. If the device has already been started,
+ * this implies OVERLAY_TARG_DEGRADE.
+ */
+#define OVERLAY_TARG_DISASSOCIATE (OVERLAY_TARG_IOCTL | 0x3)
+
+/*
+ * Tells the kernel that while a varpd instance still exists, it basically isn't
+ * making any forward progress, so the device should consider itself degraded.
+ */
+#define OVERLAY_TARG_DEGRADE (OVERLAY_TARG_IOCTL | 0x4)
+
+typedef struct overlay_targ_degrade {
+ datalink_id_t otd_linkid;
+ uint32_t otd_pad;
+ char otd_buf[OVERLAY_STATUS_BUFLEN];
+} overlay_targ_degrade_t;
+
+/*
+ * Tells the kernel to remove the degraded status that it set on a device.
+ */
+#define OVERLAY_TARG_RESTORE (OVERLAY_TARG_IOCTL | 0x5)
+
+typedef struct overlay_targ_id {
+ datalink_id_t otid_linkid;
+} overlay_targ_id_t;
+
+/*
+ * The following ioctls are all used to support dynamic lookups from userland,
+ * generally serviced by varpd.
+ *
+ * The way this is designed to work is that user land will have threads sitting
+ * in OVERLAY_TARG_LOOKUP ioctls waiting to service requests. A thread will sit
+ * waiting for work for up to approximately one second of time before they will
+ * be sent back out to user land to give user land a chance to clean itself up
+ * or more generally, come back into the kernel for work. Once these threads
+ * return, they will have a request with which more action can be done. The
+ * following ioctls can all be used to answer the request.
+ *
+ * OVERLAY_TARG_RESPOND - overlay_targ_resp_t
+ *
+ * The overlay_targ_resp_t has the appropriate information from
+ * which a reply can be generated. The information is filled into
+ * an overlay_targ_point_t as appropriate based on the
+ * overlay_plugin_dest_t type.
+ *
+ *
+ * OVERLAY_TARG_DROP - overlay_targ_resp_t
+ *
+ * The overlay_targ_resp_t should identify a request for which to
+ * drop a packet.
+ *
+ *
+ * OVERLAY_TARG_INJECT - overlay_targ_pkt_t
+ *
+ * The overlay_targ_pkt_t injects a fully formed packet into the
+ * virtual network. It may either be identified by its data link id
+ * or by the request id. If both are specified, the
+ * datalink id will be used. Note, that an injection is not
+ * considered a reply and if this corresponds to a requeset, then
+ * that individual packet must still be dropped.
+ *
+ *
+ * OVERLAY_TARG_PKT - overlay_targ_pkt_t
+ *
+ * This ioctl can be used to copy data from a given request into a
+ * user buffer. This can be used in combination with
+ * OVERLAY_TARG_INJECT to implemnt services such as a proxy-arp.
+ *
+ *
+ * OVERLAY_TARG_RESEND - overlay_targ_pkt_t
+ *
+ * This ioctl is similar to the OVERLAY_TARG_INJECT, except instead
+ * of receiving it on the local mac handle, it queues it for
+ * retransmission again. This is useful if you have a packet that
+ * was originally destined for some broadcast or multicast address
+ * that you now want to send to a unicast address.
+ */
+#define OVERLAY_TARG_LOOKUP (OVERLAY_TARG_IOCTL | 0x10)
+#define OVERLAY_TARG_RESPOND (OVERLAY_TARG_IOCTL | 0x11)
+#define OVERLAY_TARG_DROP (OVERLAY_TARG_IOCTL | 0x12)
+#define OVERLAY_TARG_INJECT (OVERLAY_TARG_IOCTL | 0x13)
+#define OVERLAY_TARG_PKT (OVERLAY_TARG_IOCTL | 0x14)
+#define OVERLAY_TARG_RESEND (OVERLAY_TARG_IOCTL | 0x15)
+
+typedef struct overlay_targ_lookup {
+ uint64_t otl_dlid;
+ uint64_t otl_reqid;
+ uint64_t otl_varpdid;
+ uint64_t otl_vnetid;
+ uint64_t otl_hdrsize;
+ uint64_t otl_pktsize;
+ uint8_t otl_srcaddr[ETHERADDRL];
+ uint8_t otl_dstaddr[ETHERADDRL];
+ uint32_t otl_dsttype;
+ uint32_t otl_sap;
+ int32_t otl_vlan;
+} overlay_targ_lookup_t;
+
+typedef struct overlay_targ_resp {
+ uint64_t otr_reqid;
+ overlay_target_point_t otr_answer;
+} overlay_targ_resp_t;
+
+typedef struct overlay_targ_pkt {
+ uint64_t otp_linkid;
+ uint64_t otp_reqid;
+ uint64_t otp_size;
+ void *otp_buf;
+} overlay_targ_pkt_t;
+
+#ifdef _KERNEL
+
+typedef struct overlay_targ_pkt32 {
+ uint64_t otp_linkid;
+ uint64_t otp_reqid;
+ uint64_t otp_size;
+ caddr32_t otp_buf;
+} overlay_targ_pkt32_t;
+
+#endif /* _KERNEL */
+
+/*
+ * This provides a way to get a list of active overlay devices independently
+ * from dlmgmtd. At the end of the day the kernel always knows what will exist
+ * and this allows varpd which is an implementation of libdladm not to end up
+ * needing to call back into dlmgmtd via libdladm and create an unfortunate
+ * dependency cycle.
+ */
+
+#define OVERLAY_TARG_LIST (OVERLAY_TARG_IOCTL | 0x20)
+
+typedef struct overlay_targ_list {
+ uint32_t otl_nents;
+ uint32_t otl_ents[];
+} overlay_targ_list_t;
+
+/*
+ * The following family of ioctls all manipulate the target cache of a given
+ * device.
+ *
+ * OVERLAY_TARG_CACHE_GET - overlay_targ_cache_t
+ *
+ * The overlay_targ_cache_t should be have its link identifier and
+ * the desired mac address filled in. On return, it will fill in
+ * the otc_dest member, if the entry exists in the table.
+ *
+ *
+ * OVERLAY_TARG_CACHE_SET - overlay_targ_cache_t
+ *
+ * The cache table entry of the mac address referred to by otc_mac
+ * and otd_linkid will be filled in with the details provided by in
+ * the otc_dest member.
+ *
+ * OVERLAY_TARG_CACHE_REMOVE - overlay_targ_cache_t
+ *
+ * Removes the cache entry identified by otc_mac from the table.
+ * Note that this does not stop any in-flight lookups or deal with
+ * any data that is awaiting a lookup.
+ *
+ *
+ * OVERLAY_TARG_CACHE_FLUSH - overlay_targ_cache_t
+ *
+ * Similar to OVERLAY_TARG_CACHE_REMOVE, but functions on the
+ * entire table identified by otc_linkid. All other parameters are
+ * ignored.
+ *
+ *
+ * OVERLAY_TARG_CACHE_ITER - overlay_targ_cache_iter_t
+ *
+ * Iterates over the contents of a target cache identified by
+ * otci_linkid. Iteration is guaranteed to be exactly once for
+ * items which are in the hashtable at the beginning and end of
+ * iteration. For items which are added or removed after iteration
+ * has begun, only at most once semantics are guaranteed. Consumers
+ * should ensure that otci_marker is zeroed before starting
+ * iteration and should preserve its contents across calls.
+ *
+ * Before calling in, otci_count should be set to the number of
+ * entries that space has been allocated for in otci_ents. The
+ * value will be updated to indicate the total number written out.
+ */
+
+#define OVERLAY_TARG_CACHE_GET (OVERLAY_TARG_IOCTL | 0x30)
+#define OVERLAY_TARG_CACHE_SET (OVERLAY_TARG_IOCTL | 0x31)
+#define OVERLAY_TARG_CACHE_REMOVE (OVERLAY_TARG_IOCTL | 0x32)
+#define OVERLAY_TARG_CACHE_FLUSH (OVERLAY_TARG_IOCTL | 0x33)
+#define OVERLAY_TARG_CACHE_ITER (OVERLAY_TARG_IOCTL | 0x34)
+
+/*
+ * This is a pretty arbitrary number that we're constraining ourselves to
+ * for iteration. Basically the goal is to make sure that we can't have a user
+ * ask us to allocate too much memory on their behalf at any time. A more
+ * dynamic form may be necessary some day.
+ */
+#define OVERLAY_TARGET_ITER_MAX 500
+
+#define OVERLAY_TARGET_CACHE_DROP 0x01
+
+typedef struct overlay_targ_cache_entry {
+ uint8_t otce_mac[ETHERADDRL];
+ uint16_t otce_flags;
+ overlay_target_point_t otce_dest;
+} overlay_targ_cache_entry_t;
+
+typedef struct overlay_targ_cache {
+ datalink_id_t otc_linkid;
+ overlay_targ_cache_entry_t otc_entry;
+} overlay_targ_cache_t;
+
+typedef struct overlay_targ_cache_iter {
+ datalink_id_t otci_linkid;
+ uint32_t otci_pad;
+ uint64_t otci_marker;
+ uint16_t otci_count;
+ overlay_targ_cache_entry_t otci_ents[];
+} overlay_targ_cache_iter_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OVERLAY_TARGET_H */
diff --git a/usr/src/uts/common/sys/param.h b/usr/src/uts/common/sys/param.h
index c3a1b9a97b..ea2fdfd886 100644
--- a/usr/src/uts/common/sys/param.h
+++ b/usr/src/uts/common/sys/param.h
@@ -104,7 +104,7 @@ extern "C" {
#define DEFAULT_MAXPID 999999
#define DEFAULT_JUMPPID 100000
#else
-#define DEFAULT_MAXPID 30000
+#define DEFAULT_MAXPID 99999
#define DEFAULT_JUMPPID 0
#endif
diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h
index 5f5b66d437..5328d02c59 100644
--- a/usr/src/uts/common/sys/policy.h
+++ b/usr/src/uts/common/sys/policy.h
@@ -108,6 +108,7 @@ int secpolicy_ipc_owner(const cred_t *, const struct kipc_perm *);
int secpolicy_kmdb(const cred_t *);
int secpolicy_lock_memory(const cred_t *);
int secpolicy_meminfo(const cred_t *);
+int secpolicy_fs_import(const cred_t *);
int secpolicy_modctl(const cred_t *, int);
int secpolicy_net(const cred_t *, int, boolean_t);
int secpolicy_net_bindmlp(const cred_t *);
@@ -174,6 +175,7 @@ int secpolicy_setid_setsticky_clear(vnode_t *, vattr_t *,
const vattr_t *, cred_t *);
int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, vtype_t);
int secpolicy_xvm_control(const cred_t *);
+int secpolicy_hyprlofs_control(const cred_t *);
int secpolicy_basic_exec(const cred_t *, vnode_t *);
int secpolicy_basic_fork(const cred_t *);
diff --git a/usr/src/uts/common/sys/proc.h b/usr/src/uts/common/sys/proc.h
index 5abf8fd3cd..ff4a1abce4 100644
--- a/usr/src/uts/common/sys/proc.h
+++ b/usr/src/uts/common/sys/proc.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -348,7 +349,9 @@ typedef struct proc {
struct zone *p_zone; /* zone in which process lives */
struct vnode *p_execdir; /* directory that p_exec came from */
struct brand *p_brand; /* process's brand */
- void *p_brand_data; /* per-process brand state */
+
+ /* per-process brand state */
+ void *p_brand_data;
/* additional lock to protect p_sessp (but not its contents) */
kmutex_t p_splock;
@@ -363,7 +366,6 @@ typedef struct proc {
*/
struct user p_user; /* (see sys/user.h) */
} proc_t;
-
#define PROC_T /* headers relying on proc_t are OK */
#ifdef _KERNEL
@@ -629,6 +631,7 @@ extern int signal_is_blocked(kthread_t *, int);
extern int sigcheck(proc_t *, kthread_t *);
extern void sigdefault(proc_t *);
+extern struct pid *pid_find(pid_t pid);
extern void pid_setmin(void);
extern pid_t pid_allocate(proc_t *, pid_t, int);
extern int pid_rele(struct pid *);
@@ -644,6 +647,7 @@ extern int sprtrylock_proc(proc_t *);
extern void sprwaitlock_proc(proc_t *);
extern void sprlock_proc(proc_t *);
extern void sprunlock(proc_t *);
+extern void sprunprlock(proc_t *);
extern void pid_init(void);
extern proc_t *pid_entry(int);
extern int pid_slot(proc_t *);
@@ -718,6 +722,10 @@ extern kthread_t *thread_unpin(void);
extern void thread_init(void);
extern void thread_load(kthread_t *, void (*)(), caddr_t, size_t);
+extern void thread_splitstack(void (*)(void *), void *, size_t);
+extern void thread_splitstack_run(caddr_t, void (*)(void *), void *);
+extern void thread_splitstack_cleanup(void);
+
extern void tsd_create(uint_t *, void (*)(void *));
extern void tsd_destroy(uint_t *);
extern void *tsd_getcreate(uint_t *, void (*)(void *), void *(*)(void));
@@ -759,7 +767,7 @@ extern void pokelwps(proc_t *);
extern void continuelwps(proc_t *);
extern int exitlwps(int);
extern void lwp_ctmpl_copy(klwp_t *, klwp_t *);
-extern void lwp_ctmpl_clear(klwp_t *);
+extern void lwp_ctmpl_clear(klwp_t *, boolean_t);
extern klwp_t *forklwp(klwp_t *, proc_t *, id_t);
extern void lwp_load(klwp_t *, gregset_t, uintptr_t);
extern void lwp_setrval(klwp_t *, int, int);
diff --git a/usr/src/uts/common/sys/procfs.h b/usr/src/uts/common/sys/procfs.h
index f592fd9dcf..501af712ef 100644
--- a/usr/src/uts/common/sys/procfs.h
+++ b/usr/src/uts/common/sys/procfs.h
@@ -25,6 +25,7 @@
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_PROCFS_H
@@ -233,6 +234,7 @@ typedef struct pstatus {
#define PR_FAULTED 6
#define PR_SUSPENDED 7
#define PR_CHECKPOINT 8
+#define PR_BRAND 9
/*
* lwp ps(1) information file. /proc/<pid>/lwp/<lwpid>/lwpsinfo
diff --git a/usr/src/uts/common/sys/ptms.h b/usr/src/uts/common/sys/ptms.h
index 6c79ee266d..ba8b2b1210 100644
--- a/usr/src/uts/common/sys/ptms.h
+++ b/usr/src/uts/common/sys/ptms.h
@@ -126,6 +126,12 @@ extern void ptms_logp(char *, uintptr_t);
#define DDBGP(a, b)
#endif
+typedef struct __ptmptsopencb_arg *ptmptsopencb_arg_t;
+typedef struct ptmptsopencb {
+ boolean_t (*ppocb_func)(ptmptsopencb_arg_t);
+ ptmptsopencb_arg_t ppocb_arg;
+} ptmptsopencb_t;
+
#endif /* _KERNEL */
typedef struct pt_own {
@@ -157,6 +163,19 @@ typedef struct pt_own {
#define ZONEPT (('P'<<8)|4) /* set zone of master/slave pair */
#define OWNERPT (('P'<<8)|5) /* set owner/group for slave device */
+#ifdef _KERNEL
+/*
+ * kernel ioctl commands
+ *
+ * PTMPTSOPENCB: Returns a callback function pointer and opaque argument.
+ * The return value of the callback function when it's invoked
+ * with the opaque argument passed to it will indicate if the
+ * pts slave device is currently open.
+ */
+#define PTMPTSOPENCB (('P'<<8)|6) /* check if the slave is open */
+
+#endif /* _KERNEL */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_hash.h b/usr/src/uts/common/sys/refhash.h
index 2069e6d3f1..b7427a454d 100644
--- a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_hash.h
+++ b/usr/src/uts/common/sys/refhash.h
@@ -10,11 +10,11 @@
*/
/*
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
-#ifndef _SYS_SCSI_ADAPTERS_MPTHASH_H
-#define _SYS_SCSI_ADAPTERS_MPTHASH_H
+#ifndef _SYS_REFHASH_H
+#define _SYS_REFHASH_H
#include <sys/types.h>
#include <sys/list.h>
@@ -58,4 +58,4 @@ extern void *refhash_first(refhash_t *);
extern void *refhash_next(refhash_t *, void *);
extern boolean_t refhash_obj_valid(refhash_t *hp, const void *);
-#endif /* _SYS_SCSI_ADAPTERS_MPTHASH_H */
+#endif /* _SYS_REFHASH_H */
diff --git a/usr/src/uts/common/sys/resource.h b/usr/src/uts/common/sys/resource.h
index 2d3800b946..4b70a77db8 100644
--- a/usr/src/uts/common/sys/resource.h
+++ b/usr/src/uts/common/sys/resource.h
@@ -23,6 +23,7 @@
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -191,6 +192,7 @@ struct rusage {
#define _RUSAGESYS_GETRUSAGE_CHLD 1 /* rusage child process */
#define _RUSAGESYS_GETRUSAGE_LWP 2 /* rusage lwp */
#define _RUSAGESYS_GETVMUSAGE 3 /* getvmusage */
+#define _RUSAGESYS_INVALMAP 4 /* vm_map_inval */
#if defined(_SYSCALL32)
diff --git a/usr/src/uts/common/sys/rt.h b/usr/src/uts/common/sys/rt.h
index ca52f8d995..82cc08d326 100644
--- a/usr/src/uts/common/sys/rt.h
+++ b/usr/src/uts/common/sys/rt.h
@@ -22,6 +22,7 @@
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -31,8 +32,6 @@
#ifndef _SYS_RT_H
#define _SYS_RT_H
-#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.4 */
-
#include <sys/types.h>
#include <sys/thread.h>
@@ -77,6 +76,16 @@ typedef struct rtkparms {
int rt_tqsig; /* real-time time quantum signal */
uint_t rt_cflags; /* real-time control flags */
} rtkparms_t;
+
+#define RTGPPRIO0 100 /* Global priority for RT priority 0 */
+
+/*
+ * control flags (kparms->rt_cflags).
+ */
+#define RT_DOPRI 0x01 /* change priority */
+#define RT_DOTQ 0x02 /* change RT time quantum */
+#define RT_DOSIG 0x04 /* change RT time quantum signal */
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h
index 3983188fce..02116b45c4 100644
--- a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h
+++ b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
* Copyright (c) 2014, Tegile Systems Inc. All rights reserved.
*/
@@ -58,10 +58,10 @@
#include <sys/byteorder.h>
#include <sys/queue.h>
+#include <sys/refhash.h>
#include <sys/isa_defs.h>
#include <sys/sunmdi.h>
#include <sys/mdi_impldefs.h>
-#include <sys/scsi/adapters/mpt_sas/mptsas_hash.h>
#include <sys/scsi/adapters/mpt_sas/mptsas_ioctl.h>
#include <sys/scsi/adapters/mpt_sas/mpi/mpi2_tool.h>
#include <sys/scsi/adapters/mpt_sas/mpi/mpi2_cnfg.h>
diff --git a/usr/src/uts/common/sys/scsi/adapters/smrt/smrt.h b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt.h
new file mode 100644
index 0000000000..e9e381f519
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt.h
@@ -0,0 +1,586 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _SMRT_H
+#define _SMRT_H
+
+#include <sys/types.h>
+#include <sys/pci.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sys/map.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/impl/spc3_types.h>
+#include <sys/devops.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sdt.h>
+#include <sys/policy.h>
+
+#if !defined(_LITTLE_ENDIAN) || !defined(_BIT_FIELDS_LTOH)
+/*
+ * This driver contains a number of multi-byte bit fields and other structs
+ * that are only correct on a system with the same ordering as x86.
+ */
+#error "smrt: driver works only on little endian systems"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Some structures are statically sized based on the expected number of logical
+ * drives and controllers in the system. These definitions are used throughout
+ * other driver-specific header files, and must appear prior to their
+ * inclusion.
+ */
+#define SMRT_MAX_LOGDRV 64 /* Maximum number of logical drives */
+
+#include <sys/scsi/adapters/smrt/smrt_ciss.h>
+#include <sys/scsi/adapters/smrt/smrt_scsi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ddi_device_acc_attr_t smrt_dev_attributes;
+
+typedef enum smrt_init_level {
+ SMRT_INITLEVEL_BASIC = (0x1 << 0),
+ SMRT_INITLEVEL_I2O_MAPPED = (0x1 << 1),
+ SMRT_INITLEVEL_CFGTBL_MAPPED = (0x1 << 2),
+ SMRT_INITLEVEL_PERIODIC = (0x1 << 3),
+ SMRT_INITLEVEL_INT_ALLOC = (0x1 << 4),
+ SMRT_INITLEVEL_INT_ADDED = (0x1 << 5),
+ SMRT_INITLEVEL_INT_ENABLED = (0x1 << 6),
+ SMRT_INITLEVEL_SCSA = (0x1 << 7),
+ SMRT_INITLEVEL_MUTEX = (0x1 << 8),
+} smrt_init_level_t;
+
+/*
+ * Commands issued to the controller carry a (generally 32-bit, though with
+ * two reserved signalling bits) identifying tag number. In order to avoid
+ * having the controller confuse us by double-reporting the completion of a
+ * particular tag, we try to reuse them as infrequently as possible. In
+ * practice, this means looping through a range of values. The minimum and
+ * maximum value are defined below.
+ */
+#define SMRT_MIN_TAG_NUMBER 0x00000100
+#define SMRT_MAX_TAG_NUMBER 0x0fffffff
+
+/*
+ * Definitions to support waiting for the controller to converge on a
+ * particular state: ready or not ready. These are used with
+ * smrt_ctlr_wait_for_state().
+ */
+#define SMRT_WAIT_DELAY_SECONDS 120
+typedef enum smrt_wait_state {
+ SMRT_WAIT_STATE_READY = 1,
+ SMRT_WAIT_STATE_UNREADY
+} smrt_wait_state_t;
+
+typedef enum smrt_ctlr_mode {
+ SMRT_CTLR_MODE_UNKNOWN = 0,
+ SMRT_CTLR_MODE_SIMPLE
+} smrt_ctlr_mode_t;
+
+/*
+ * In addition to Logical Volumes, we also expose the controller at a
+ * pseudo target address on the SCSI bus we are essentially pretending to be.
+ */
+#define SMRT_CONTROLLER_TARGET 128
+
+/*
+ * When waiting for logical volume discovery to complete, we wait for a maximum
+ * duration (in seconds) before giving up.
+ */
+#define SMRT_LOGVOL_DISCOVER_TIMEOUT 30
+
+/*
+ * The maintenance routine which checks for controller lockup and aborts
+ * commands that have passed their timeout runs periodically. The time is
+ * expressed in seconds.
+ */
+#define SMRT_PERIODIC_RATE 5
+
+/*
+ * At times, we need to check if the controller is still responding. To do
+ * that, we send a Nop message to the controller and make sure it completes
+ * successfully. So that we don't wait forever, we set a timeout (in seconds).
+ */
+#define SMRT_PING_CHECK_TIMEOUT 60
+
+
+typedef enum smrt_controller_status {
+ /*
+ * A Logical Volume discovery is currently occuring.
+ */
+ SMRT_CTLR_STATUS_DISCOVERY = (0x1 << 0),
+
+ /*
+ * An attempt is being made to detach the controller instance.
+ */
+ SMRT_CTLR_STATUS_DETACHING = (0x1 << 1),
+
+ /*
+ * The controller is believed to be functioning correctly. The driver
+ * is to allow command submission, process interrupts, and perform
+ * periodic background maintenance.
+ */
+ SMRT_CTLR_STATUS_RUNNING = (0x1 << 2),
+
+ /*
+ * The controller is currently being reset.
+ */
+ SMRT_CTLR_STATUS_RESETTING = (0x1 << 3),
+} smrt_controller_status_t;
+
+typedef struct smrt_stats {
+ uint64_t smrts_tran_aborts;
+ uint64_t smrts_tran_resets;
+ uint64_t smrts_tran_starts;
+ uint64_t smrts_ctlr_resets;
+ unsigned smrts_max_inflight;
+ uint64_t smrts_unclaimed_interrupts;
+ uint64_t smrts_claimed_interrupts;
+ uint64_t smrts_ignored_scsi_cmds;
+} smrt_stats_t;
+
+/*
+ * Per-Controller Structure
+ */
+typedef struct smrt smrt_t;
+struct smrt {
+ dev_info_t *smrt_dip;
+ int smrt_instance;
+ smrt_controller_status_t smrt_status;
+ smrt_stats_t smrt_stats;
+
+ /*
+ * Controller configuration discovered during initialisation.
+ */
+ uint32_t smrt_host_support;
+ uint32_t smrt_bus_support;
+ uint32_t smrt_maxcmds;
+ uint32_t smrt_sg_cnt;
+
+ /*
+ * The transport mode of the controller.
+ */
+ smrt_ctlr_mode_t smrt_ctlr_mode;
+
+ /*
+ * The current initialisation level of the driver. Bits in this field
+ * are set during initialisation and unset during cleanup of the
+ * allocated resources.
+ */
+ smrt_init_level_t smrt_init_level;
+
+ /*
+ * Essentially everything is protected by "smrt_mutex". When the
+ * completion queue is updated, threads sleeping on "smrt_cv_finishq"
+ * are awoken.
+ */
+ kmutex_t smrt_mutex;
+ kcondvar_t smrt_cv_finishq;
+
+ /*
+ * List of enumerated logical volumes (smrt_volume_t).
+ */
+ list_t smrt_volumes;
+
+ /*
+ * List of attached SCSA target drivers (smrt_target_t).
+ */
+ list_t smrt_targets;
+
+ /*
+ * Controller Heartbeat Tracking
+ */
+ uint32_t smrt_last_heartbeat;
+ hrtime_t smrt_last_heartbeat_time;
+
+ hrtime_t smrt_last_interrupt_claimed;
+ hrtime_t smrt_last_interrupt_unclaimed;
+ hrtime_t smrt_last_discovery;
+ hrtime_t smrt_last_reset_start;
+ hrtime_t smrt_last_reset_finish;
+
+ /*
+ * Command object tracking. These lists, and all commands within the
+ * lists, are protected by "smrt_mutex".
+ */
+ uint32_t smrt_next_tag;
+ avl_tree_t smrt_inflight;
+ list_t smrt_commands; /* List of all commands. */
+ list_t smrt_finishq; /* List of completed commands. */
+ list_t smrt_abortq; /* List of commands to abort. */
+
+ /*
+ * Controller interrupt handler registration.
+ */
+ int smrt_interrupt_type;
+ int smrt_interrupt_cap;
+ uint_t smrt_interrupt_pri;
+ ddi_intr_handle_t smrt_interrupts[1];
+ int smrt_ninterrupts;
+
+ ddi_periodic_t smrt_periodic;
+
+ scsi_hba_tran_t *smrt_hba_tran;
+
+ ddi_dma_attr_t smrt_dma_attr;
+
+ /*
+ * Access to the I2O Registers:
+ */
+ unsigned smrt_i2o_bar;
+ caddr_t smrt_i2o_space;
+ ddi_acc_handle_t smrt_i2o_handle;
+
+ /*
+ * Access to the Configuration Table:
+ */
+ unsigned smrt_ct_bar;
+ uint32_t smrt_ct_baseaddr;
+ CfgTable_t *smrt_ct;
+ ddi_acc_handle_t smrt_ct_handle;
+};
+
+/*
+ * Logical Volume Structure
+ */
+typedef enum smrt_volume_flags {
+ SMRT_VOL_FLAG_WWN = (0x1 << 0),
+} smrt_volume_flags_t;
+
+typedef struct smrt_volume {
+ LogDevAddr_t smlv_addr;
+ smrt_volume_flags_t smlv_flags;
+
+ uint8_t smlv_wwn[16];
+
+ smrt_t *smlv_ctlr;
+ list_node_t smlv_link;
+
+ /*
+ * List of SCSA targets currently attached to this Logical Volume:
+ */
+ list_t smlv_targets;
+} smrt_volume_t;
+
+/*
+ * Per-Target Structure
+ */
+typedef struct smrt_target {
+ struct scsi_device *smtg_scsi_dev;
+ boolean_t smtg_controller_target;
+
+ /*
+ * Linkage back to the Logical Volume that this target represents:
+ */
+ smrt_volume_t *smtg_volume;
+ list_node_t smtg_link_volume;
+
+ /*
+ * Linkage back to the controller:
+ */
+ smrt_t *smtg_ctlr;
+ list_node_t smtg_link_ctlr;
+} smrt_target_t;
+
+/*
+ * DMA Resource Tracking Structure
+ */
+typedef enum smrt_dma_level {
+ SMRT_DMALEVEL_HANDLE_ALLOC = (0x1 << 0),
+ SMRT_DMALEVEL_MEMORY_ALLOC = (0x1 << 1),
+ SMRT_DMALEVEL_HANDLE_BOUND = (0x1 << 2),
+} smrt_dma_level_t;
+
+typedef struct smrt_dma {
+ smrt_dma_level_t smdma_level;
+ size_t smdma_real_size;
+ ddi_dma_handle_t smdma_dma_handle;
+ ddi_acc_handle_t smdma_acc_handle;
+ ddi_dma_cookie_t smdma_dma_cookies[1];
+ uint_t smdma_dma_ncookies;
+} smrt_dma_t;
+
+
+typedef struct smrt_command smrt_command_t;
+typedef struct smrt_command_internal smrt_command_internal_t;
+typedef struct smrt_command_scsa smrt_command_scsa_t;
+typedef struct smrt_pkt smrt_pkt_t;
+
+typedef enum smrt_command_status {
+ /*
+ * When a command is submitted to the controller, it is marked USED
+ * to avoid accidental reuse of the command without reinitialising
+ * critical fields. The submitted command is also marked INFLIGHT
+ * to reflect its inclusion in the "smrt_inflight" AVL tree. When
+ * the command is completed by the controller, INFLIGHT is unset.
+ */
+ SMRT_CMD_STATUS_USED = (0x1 << 0),
+ SMRT_CMD_STATUS_INFLIGHT = (0x1 << 1),
+
+ /*
+ * This flag is set during abort queue processing to record that this
+ * command was aborted in response to an expired timeout, and not some
+ * other cancellation. If the controller is able to abort the command,
+ * we use this flag to let the SCSI framework know that the command
+ * timed out.
+ */
+ SMRT_CMD_STATUS_TIMEOUT = (0x1 << 2),
+
+ /*
+ * The controller set the error bit when completing this command.
+ * Details of the particular fault may be read from the error
+ * information written by the controller.
+ */
+ SMRT_CMD_STATUS_ERROR = (0x1 << 3),
+
+ /*
+ * This command has been abandoned by the original submitter. This
+ * could happen if the command did not complete in a timely fashion.
+ * When it reaches the finish queue it will be freed without further
+ * processing.
+ */
+ SMRT_CMD_STATUS_ABANDONED = (0x1 << 4),
+
+ /*
+ * This command has made it through the completion queue and had final
+ * processing performed.
+ */
+ SMRT_CMD_STATUS_COMPLETE = (0x1 << 5),
+
+ /*
+ * A polled message will be ignored by the regular processing of the
+ * completion queue. The blocking function doing the polling is
+ * responsible for watching the command on which it has set the POLLED
+ * flag. Regular completion queue processing (which might happen in
+ * the polling function, or it might happen in the interrupt handler)
+ * will set POLL_COMPLETE once it is out of the finish queue
+ * altogether.
+ */
+ SMRT_CMD_STATUS_POLLED = (0x1 << 6),
+ SMRT_CMD_STATUS_POLL_COMPLETE = (0x1 << 7),
+
+ /*
+ * An abort message has been sent to the controller in an attempt to
+ * cancel this command.
+ */
+ SMRT_CMD_STATUS_ABORT_SENT = (0x1 << 8),
+
+ /*
+ * This command has been passed to our tran_start(9E) handler.
+ */
+ SMRT_CMD_STATUS_TRAN_START = (0x1 << 9),
+
+ /*
+ * This command was for a SCSI command that we are explicitly avoiding
+ * sending to the controller.
+ */
+ SMRT_CMD_STATUS_TRAN_IGNORED = (0x1 << 10),
+
+ /*
+ * This command has been submitted once, and subsequently passed to
+ * smrt_command_reuse().
+ */
+ SMRT_CMD_STATUS_REUSED = (0x1 << 11),
+
+ /*
+ * A controller reset has been issued, so a response for this command
+ * is not expected. If one arrives before the controller reset has
+ * taken effect, it likely cannot be trusted.
+ */
+ SMRT_CMD_STATUS_RESET_SENT = (0x1 << 12),
+} smrt_command_status_t;
+
+typedef enum smrt_command_type {
+ SMRT_CMDTYPE_INTERNAL = 1,
+ SMRT_CMDTYPE_ABORTQ,
+ SMRT_CMDTYPE_SCSA,
+} smrt_command_type_t;
+
+struct smrt_command {
+ uint32_t smcm_tag;
+ smrt_command_type_t smcm_type;
+ smrt_command_status_t smcm_status;
+
+ smrt_t *smcm_ctlr;
+ smrt_target_t *smcm_target;
+
+ list_node_t smcm_link; /* Linkage for allocated list. */
+ list_node_t smcm_link_finish; /* Linkage for completion list. */
+ list_node_t smcm_link_abort; /* Linkage for abort list. */
+ avl_node_t smcm_node; /* Inflight AVL membership. */
+
+ hrtime_t smcm_time_submit;
+ hrtime_t smcm_time_complete;
+
+ hrtime_t smcm_expiry;
+
+ /*
+ * The time at which an abort message was sent to try and terminate
+ * this command, as well as the tag of the abort message itself:
+ */
+ hrtime_t smcm_abort_time;
+ uint32_t smcm_abort_tag;
+
+ /*
+ * Ancillary data objects. Only one of these will be allocated for any
+ * given command, but we nonetheless resist the temptation to use a
+ * union of pointers in order to make incorrect usage obvious.
+ */
+ smrt_command_scsa_t *smcm_scsa;
+ smrt_command_internal_t *smcm_internal;
+
+ /*
+ * Physical allocation tracking for the actual command to send to the
+ * controller.
+ */
+ smrt_dma_t smcm_contig;
+
+ CommandList_t *smcm_va_cmd;
+ uint32_t smcm_pa_cmd;
+
+ ErrorInfo_t *smcm_va_err;
+ uint32_t smcm_pa_err;
+};
+
+/*
+ * Commands issued internally to the driver (as opposed to by the HBA
+ * framework) generally require a buffer in which to assemble the command body,
+ * and for receiving the response from the controller. The following object
+ * tracks this (optional) extra buffer.
+ */
+struct smrt_command_internal {
+ smrt_dma_t smcmi_contig;
+
+ void *smcmi_va;
+ uint32_t smcmi_pa;
+ size_t smcmi_len;
+};
+
+/*
+ * Commands issued via the SCSI framework have a number of additional
+ * properties.
+ */
+struct smrt_command_scsa {
+ struct scsi_pkt *smcms_pkt;
+ smrt_command_t *smcms_command;
+};
+
+
+/*
+ * CISS transport routines.
+ */
+void smrt_periodic(void *);
+void smrt_lockup_check(smrt_t *);
+int smrt_submit(smrt_t *, smrt_command_t *);
+void smrt_submit_simple(smrt_t *, smrt_command_t *);
+int smrt_retrieve(smrt_t *);
+void smrt_retrieve_simple(smrt_t *);
+int smrt_poll_for(smrt_t *, smrt_command_t *);
+
+/*
+ * Interrupt service routines.
+ */
+int smrt_interrupts_setup(smrt_t *);
+int smrt_interrupts_enable(smrt_t *);
+void smrt_interrupts_teardown(smrt_t *);
+uint32_t smrt_isr_hw_simple(caddr_t, caddr_t);
+
+/*
+ * Interrupt enable/disable routines.
+ */
+void smrt_intr_set(smrt_t *, boolean_t);
+
+/*
+ * Controller initialisation routines.
+ */
+int smrt_ctlr_init(smrt_t *);
+void smrt_ctlr_teardown(smrt_t *);
+int smrt_ctlr_reset(smrt_t *);
+int smrt_ctlr_ping(smrt_t *, int);
+int smrt_ctlr_wait_for_state(smrt_t *, smrt_wait_state_t);
+int smrt_ctlr_init_simple(smrt_t *);
+void smrt_ctlr_teardown_simple(smrt_t *);
+int smrt_cfgtbl_flush(smrt_t *);
+int smrt_cfgtbl_transport_has_support(smrt_t *, int);
+void smrt_cfgtbl_transport_set(smrt_t *, int);
+int smrt_cfgtbl_transport_confirm(smrt_t *, int);
+uint32_t smrt_ctlr_get_cmdsoutmax(smrt_t *);
+uint32_t smrt_ctlr_get_maxsgelements(smrt_t *);
+
+/*
+ * Device enumeration routines.
+ */
+int smrt_logvol_discover(smrt_t *, uint16_t);
+void smrt_logvol_teardown(smrt_t *);
+smrt_volume_t *smrt_logvol_lookup_by_id(smrt_t *, unsigned);
+smrt_volume_t *smrt_logvol_lookup_by_addr(smrt_t *, struct scsi_address *);
+
+/*
+ * SCSI framework routines.
+ */
+int smrt_hba_setup(smrt_t *);
+void smrt_hba_teardown(smrt_t *);
+
+void smrt_hba_complete(smrt_command_t *);
+
+void smrt_process_finishq(smrt_t *);
+void smrt_process_abortq(smrt_t *);
+
+/*
+ * Command block management.
+ */
+smrt_command_t *smrt_command_alloc(smrt_t *, smrt_command_type_t,
+ int);
+int smrt_command_attach_internal(smrt_t *, smrt_command_t *, size_t,
+ int);
+void smrt_command_free(smrt_command_t *);
+smrt_command_t *smrt_lookup_inflight(smrt_t *, uint32_t);
+void smrt_command_reuse(smrt_command_t *);
+
+/*
+ * Device message construction routines.
+ */
+void smrt_write_lun_addr_phys(LUNAddr_t *, boolean_t, unsigned, unsigned);
+void smrt_write_message_abort_one(smrt_command_t *, uint32_t);
+void smrt_write_message_abort_all(smrt_command_t *, LogDevAddr_t *);
+void smrt_write_message_nop(smrt_command_t *, int);
+
+/*
+ * Device management routines.
+ */
+int smrt_device_setup(smrt_t *);
+void smrt_device_teardown(smrt_t *);
+uint32_t smrt_get32(smrt_t *, offset_t);
+void smrt_put32(smrt_t *, offset_t, uint32_t);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMRT_H */
diff --git a/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_ciss.h b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_ciss.h
new file mode 100644
index 0000000000..fbc6a1be3b
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_ciss.h
@@ -0,0 +1,339 @@
+/*
+ * 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) 2013 Hewlett-Packard Development Company, L.P.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SMRT_CISS_H
+#define _SMRT_CISS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Maximum number of Scatter/Gather List entries. These entries are statically
+ * allocated for all commands.
+ */
+#define CISS_MAXSGENTRIES 64
+
+/*
+ * If the controller advertises a value of 0 for the maximum S/G list length it
+ * supports, the specification states that we should assume a value of 31.
+ */
+#define CISS_SGCNT_FALLBACK 31
+
+/*
+ * The CDB field in the request block is fixed at 16 bytes in length. (See
+ * "3.2. Request Block" in the CISS specification.)
+ */
+#define CISS_CDBLEN 16
+
+/*
+ * Command Status Values. These are listed in "Table 2 Command Status" in "3.3
+ * Error Info" of the CISS specification.
+ */
+#define CISS_CMD_SUCCESS 0x00
+#define CISS_CMD_TARGET_STATUS 0x01
+#define CISS_CMD_DATA_UNDERRUN 0x02
+#define CISS_CMD_DATA_OVERRUN 0x03
+#define CISS_CMD_INVALID 0x04
+#define CISS_CMD_PROTOCOL_ERR 0x05
+#define CISS_CMD_HARDWARE_ERR 0x06
+#define CISS_CMD_CONNECTION_LOST 0x07
+#define CISS_CMD_ABORTED 0x08
+#define CISS_CMD_ABORT_FAILED 0x09
+#define CISS_CMD_UNSOLICITED_ABORT 0x0a
+#define CISS_CMD_TIMEOUT 0x0b
+#define CISS_CMD_UNABORTABLE 0x0c
+
+/*
+ * Request Transfer Directions, used in "RequestBlock.Type.Direction":
+ */
+#define CISS_XFER_NONE 0x00
+#define CISS_XFER_WRITE 0x01
+#define CISS_XFER_READ 0x02
+#define CISS_XFER_RSVD 0x03
+
+/*
+ * Request Attributes, used in "RequestBlock.Type.Attribute":
+ */
+#define CISS_ATTR_UNTAGGED 0x00
+#define CISS_ATTR_SIMPLE 0x04
+#define CISS_ATTR_HEADOFQUEUE 0x05
+#define CISS_ATTR_ORDERED 0x06
+
+/*
+ * Request Type, used in "RequestBlock.Type.Type":
+ */
+#define CISS_TYPE_CMD 0x00
+#define CISS_TYPE_MSG 0x01
+
+/*
+ * I2O Space Register Offsets
+ *
+ * The name "I2O", and these register offsets, appear to be amongst the last
+ * vestiges of a long-defunct attempt at standardising mainframe-style I/O
+ * channels in the Intel server space: the Intelligent Input/Output (I2O)
+ * Architecture Specification.
+ *
+ * The draft of version 1.5 of this specification, in section "4.2.1.5.1
+ * Extensions for PCI", suggests that the following are memory offsets into
+ * "the memory region specified by the first base address configuration
+ * register indicating memory space (offset 10h, 14h, and so forth)". These
+ * match up with the offsets of the first two BARs in a PCI configuration space
+ * type 0 header.
+ *
+ * The specification also calls out the Inbound Post List FIFO, write-only at
+ * offset 40h; the Outbound Post List FIFO, read-only at offset 44h; the
+ * Interrupt Status Register, at offset 30h; and the Interrupt Mask Register,
+ * at offset 34h.
+ *
+ * This ill-fated attempt to increase the proprietary complexity of (and
+ * presumably, thus, the gross margin on) computer systems is all but extinct.
+ * The transport layer of this storage controller is all that's left of their
+ * religion.
+ */
+#define CISS_I2O_INBOUND_DOORBELL 0x20
+#define CISS_I2O_INTERRUPT_STATUS 0x30
+#define CISS_I2O_INTERRUPT_MASK 0x34
+#define CISS_I2O_INBOUND_POST_Q 0x40
+#define CISS_I2O_OUTBOUND_POST_Q 0x44
+#define CISS_I2O_OUTBOUND_DOORBELL_STATUS 0x9c
+#define CISS_I2O_OUTBOUND_DOORBELL_CLEAR 0xa0
+#define CISS_I2O_SCRATCHPAD 0xb0
+#define CISS_I2O_CFGTBL_CFG_OFFSET 0xb4
+#define CISS_I2O_CFGTBL_MEM_OFFSET 0xb8
+
+/*
+ * Rather than make a lot of small mappings for each part of the address
+ * space we wish to access, we will make one large mapping. If more
+ * offsets are added to the I2O list above, this space should be extended
+ * appropriately.
+ */
+#define CISS_I2O_MAP_BASE 0x20
+#define CISS_I2O_MAP_LIMIT 0x100
+
+/*
+ * The Scratchpad Register (I2O_SCRATCHPAD) is not mentioned in the CISS
+ * specification. It serves at least two known functions:
+ * - Signalling controller readiness
+ * - Exposing a debugging code when the controller firmware locks up
+ */
+#define CISS_SCRATCHPAD_INITIALISED 0xffff0000
+
+/*
+ * Outbound Doorbell Register Values.
+ *
+ * These are read from the Outbound Doorbell Set/Status Register
+ * (CISS_I2O_OUTBOUND_DOORBELL_STATUS), but cleared by writing to the Clear
+ * Register (CISS_I2O_OUTBOUND_DOORBELL_CLEAR).
+ */
+#define CISS_ODR_BIT_INTERRUPT (1UL << 0)
+#define CISS_ODR_BIT_LOCKUP (1UL << 1)
+
+/*
+ * Inbound Doorbell Register Values.
+ *
+ * These are written to and read from the Inbound Doorbell Register
+ * (CISS_I2O_INBOUND_DOORBELL).
+ */
+#define CISS_IDR_BIT_CFGTBL_CHANGE (1UL << 0)
+
+/*
+ * Interrupt Mask Register Values.
+ *
+ * These are written to and read from the Interrupt Mask Register
+ * (CISS_I2O_INTERRUPT_MASK). Note that a 1 bit in this register masks or
+ * disables the interrupt in question; to enable the interrupt the bit must be
+ * set to 0.
+ */
+#define CISS_IMR_BIT_SIMPLE_INTR_DISABLE (1UL << 3)
+
+/*
+ * Interrupt Status Register Values.
+ *
+ * These are read from the Interrupt Status Register
+ * (CISS_I2O_INTERRUPT_STATUS).
+ */
+#define CISS_ISR_BIT_SIMPLE_INTR (1UL << 3)
+
+/*
+ * Transport Methods.
+ *
+ * These bit positions are used in the Configuration Table to detect controller
+ * support for a particular method, via "TransportSupport"; to request that the
+ * controller enable a particular method, via "TransportRequest"; and to detect
+ * whether the controller has acknowledged the request and enabled the desired
+ * method, via "TransportActive".
+ *
+ * See: "9.1 Configuration Table" in the CISS Specification.
+ */
+#define CISS_CFGTBL_READY_FOR_COMMANDS (1UL << 0)
+#define CISS_CFGTBL_XPORT_SIMPLE (1UL << 1)
+#define CISS_CFGTBL_XPORT_PERFORMANT (1UL << 2)
+#define CISS_CFGTBL_XPORT_MEMQ (1UL << 4)
+
+/*
+ * In the Simple Transport Method, when the appropriate interrupt status bit is
+ * set (CISS_ISR_BIT_SIMPLE_INTR), the Outbound Post Queue register is
+ * repeatedly read for notifications of the completion of commands previously
+ * submitted to the controller. These macros help break up the read value into
+ * its component fields: the tag number, and whether or not the command
+ * completed in error.
+ */
+#define CISS_OPQ_READ_TAG(x) ((x) >> 2)
+#define CISS_OPQ_READ_ERROR(x) ((x) & (1UL << 1))
+
+/*
+ * The following packed structures are used to ease the manipulation of
+ * requests and responses from the controller.
+ */
+#pragma pack(1)
+
+typedef struct smrt_tag {
+ uint32_t reserved:1;
+ uint32_t error:1;
+ uint32_t tag_value:30;
+ uint32_t unused;
+} smrt_tag_t;
+
+typedef union SCSI3Addr {
+ struct {
+ uint8_t Bus:6;
+ uint8_t Mode:2;
+ uint8_t Dev;
+ } PeripDev;
+ struct {
+ uint8_t DevMSB:6;
+ uint8_t Mode:2;
+ uint8_t DevLSB;
+ } LogDev;
+ struct {
+ uint8_t Targ:6;
+ uint8_t Mode:2;
+ uint8_t Dev:5;
+ uint8_t Bus:3;
+ } LogUnit;
+} SCSI3Addr_t;
+
+typedef struct PhysDevAddr {
+ uint32_t TargetId:24;
+ uint32_t Bus:6;
+ uint32_t Mode:2;
+ SCSI3Addr_t Target[2];
+} PhysDevAddr_t;
+
+typedef struct LogDevAddr {
+ uint32_t VolId:30;
+ uint32_t Mode:2;
+ uint8_t reserved[4];
+} LogDevAddr_t;
+
+typedef union LUNAddr {
+ uint8_t LunAddrBytes[8];
+ SCSI3Addr_t SCSI3Lun[4];
+ PhysDevAddr_t PhysDev;
+ LogDevAddr_t LogDev;
+} LUNAddr_t;
+
+typedef struct CommandListHeader {
+ uint8_t ReplyQueue;
+ uint8_t SGList;
+ uint16_t SGTotal;
+ smrt_tag_t Tag;
+ LUNAddr_t LUN;
+} CommandListHeader_t;
+
+typedef struct RequestBlock {
+ uint8_t CDBLen;
+ struct {
+ uint8_t Type:3;
+ uint8_t Attribute:3;
+ uint8_t Direction:2;
+ } Type;
+ uint16_t Timeout;
+ uint8_t CDB[CISS_CDBLEN];
+} RequestBlock_t;
+
+typedef struct ErrDescriptor {
+ uint64_t Addr;
+ uint32_t Len;
+} ErrDescriptor_t;
+
+typedef struct SGDescriptor {
+ uint64_t Addr;
+ uint32_t Len;
+ uint32_t Ext;
+} SGDescriptor_t;
+
+typedef struct CommandList {
+ CommandListHeader_t Header;
+ RequestBlock_t Request;
+ ErrDescriptor_t ErrDesc;
+ SGDescriptor_t SG[CISS_MAXSGENTRIES];
+} CommandList_t;
+
+typedef union MoreErrInfo {
+ struct {
+ uint8_t Reserved[3];
+ uint8_t Type;
+ uint32_t ErrorInfo;
+ } Common_Info;
+ struct {
+ uint8_t Reserved[2];
+ uint8_t offense_size;
+ uint8_t offense_num;
+ uint32_t offense_value;
+ } Invalid_Cmd;
+} MoreErrInfo_t;
+
+typedef struct ErrorInfo {
+ uint8_t ScsiStatus;
+ uint8_t SenseLen;
+ uint16_t CommandStatus;
+ uint32_t ResidualCnt;
+ MoreErrInfo_t MoreErrInfo;
+ uint8_t SenseInfo[MAX_SENSE_LENGTH];
+} ErrorInfo_t;
+
+typedef struct CfgTable {
+ uint8_t Signature[4];
+ uint32_t SpecValence;
+ uint32_t TransportSupport;
+ uint32_t TransportActive;
+ uint32_t TransportRequest;
+ uint32_t Upper32Addr;
+ uint32_t CoalIntDelay;
+ uint32_t CoalIntCount;
+ uint32_t CmdsOutMax;
+ uint32_t BusTypes;
+ uint32_t TransportMethodOffset;
+ uint8_t ServerName[16];
+ uint32_t HeartBeat;
+ uint32_t HostDrvrSupport;
+ uint32_t MaxSGElements;
+ uint32_t MaxLunSupport;
+ uint32_t MaxPhyDevSupport;
+ uint32_t MaxPhyDrvPerLun;
+ uint32_t MaxPerfModeCmdsOutMax;
+ uint32_t MaxBlockFetchCount;
+} CfgTable_t;
+
+#pragma pack()
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMRT_CISS_H */
diff --git a/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_scsi.h b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_scsi.h
new file mode 100644
index 0000000000..c6a0665eb1
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/adapters/smrt/smrt_scsi.h
@@ -0,0 +1,105 @@
+/*
+ * 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) 2013 Hewlett-Packard Development Company, L.P.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SMRT_SCSI_H
+#define _SMRT_SCSI_H
+
+#include <sys/types.h>
+
+#include <sys/scsi/adapters/smrt/smrt_ciss.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* CISS LUN Addressing MODEs */
+#define PERIPHERIAL_DEV_ADDR 0x0
+#define LOGICAL_VOL_ADDR 0x1
+#define MASK_PERIPHERIAL_DEV_ADDR 0x3
+#define CISS_PHYS_MODE 0x0
+
+/*
+ * Vendor-specific SCSI Commands
+ *
+ * These command opcodes are for use in the opcode byte of the CDB in a request
+ * of type CISS_TYPE_CMD. They are custom SCSI commands, using the
+ * vendor-specific part of the opcode space; i.e., 0xC0 through 0xFF.
+ */
+#define CISS_SCMD_REPORT_LOGICAL_LUNS 0xC2
+#define CISS_SCMD_REPORT_PHYSICAL_LUNS 0xC3
+
+/*
+ * CISS Messages
+ *
+ * The CISS specification describes several directives that do not behave like
+ * SCSI commands. They are sent in requests of type CISS_TYPE_MSG.
+ *
+ * The Abort, Reset, and Nop, messages are defined in "8. Messages" in the CISS
+ * Specification.
+ */
+#define CISS_MSG_ABORT 0x0
+#define CISS_ABORT_TASK 0x0
+#define CISS_ABORT_TASKSET 0x1
+
+#define CISS_MSG_RESET 0x1
+#define CISS_RESET_CTLR 0x0
+#define CISS_RESET_BUS 0x1
+#define CISS_RESET_TGT 0x3
+#define CISS_RESET_LUN 0x4
+
+#define CISS_MSG_NOP 0x3
+
+/*
+ * The following packed structures are used to ease the manipulation of SCSI
+ * commands sent to, and status information returned from, the controller.
+ */
+#pragma pack(1)
+
+typedef struct smrt_report_logical_lun_ent {
+ LogDevAddr_t smrle_addr;
+} smrt_report_logical_lun_ent_t;
+
+typedef struct smrt_report_logical_lun_extent {
+ LogDevAddr_t smrle_addr;
+ uint8_t smrle_wwn[16];
+} smrt_report_logical_lun_extent_t;
+
+typedef struct smrt_report_logical_lun {
+ uint32_t smrll_datasize; /* Big Endian */
+ uint8_t smrll_extflag;
+ uint8_t smrll_reserved1[3];
+ union {
+ smrt_report_logical_lun_ent_t ents[SMRT_MAX_LOGDRV];
+ smrt_report_logical_lun_extent_t extents[SMRT_MAX_LOGDRV];
+ } smrll_data;
+} smrt_report_logical_lun_t;
+
+typedef struct smrt_report_logical_lun_req {
+ uint8_t smrllr_opcode;
+ uint8_t smrllr_extflag;
+ uint8_t smrllr_reserved1[4];
+ uint32_t smrllr_datasize; /* Big Endian */
+ uint8_t smrllr_reserved2;
+ uint8_t smrllr_control;
+} smrt_report_logical_lun_req_t;
+
+#pragma pack()
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SMRT_SCSI_H */
diff --git a/usr/src/uts/common/sys/shm.h b/usr/src/uts/common/sys/shm.h
index e3bd2a77d3..030379488f 100644
--- a/usr/src/uts/common/sys/shm.h
+++ b/usr/src/uts/common/sys/shm.h
@@ -21,6 +21,7 @@
*/
/*
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -120,6 +121,10 @@ struct shmid_ds {
#define SHM_LOCK 3 /* Lock segment in core */
#define SHM_UNLOCK 4 /* Unlock segment */
+#if defined(_KERNEL)
+#define SHM_RMID 5 /* Private RMID for lx support */
+#endif
+
#if !defined(_KERNEL)
int shmget(key_t, size_t, int);
int shmids(int *, uint_t, uint_t *);
diff --git a/usr/src/uts/common/sys/shm_impl.h b/usr/src/uts/common/sys/shm_impl.h
index 4d8cdcede5..1eae2ca0a4 100644
--- a/usr/src/uts/common/sys/shm_impl.h
+++ b/usr/src/uts/common/sys/shm_impl.h
@@ -21,13 +21,12 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_SHM_IMPL_H
#define _SYS_SHM_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/ipc_impl.h>
#if defined(_KERNEL) || defined(_KMEMUSER)
#include <sys/shm.h>
@@ -70,7 +69,11 @@ typedef struct kshmid {
time_t shm_ctime; /* last change time */
struct sptinfo *shm_sptinfo; /* info about ISM segment */
struct seg *shm_sptseg; /* pointer to ISM segment */
- long shm_sptprot; /* was reserved (still a "long") */
+ ulong_t shm_opts;
+ /*
+ * Composed of: sptprot (uchar_t) and
+ * RM_PENDING flag (1 bit).
+ */
} kshmid_t;
/*
@@ -78,6 +81,14 @@ typedef struct kshmid {
*/
#define SHMSA_ISM 1 /* uses shared page table */
+/*
+ * shm_opts definitions
+ * Low byte in shm_opts is used for sptprot (see PROT_ALL). The upper bits are
+ * used for additional options.
+ */
+#define SHM_PROT_MASK 0xff
+#define SHM_RM_PENDING 0x100
+
typedef struct sptinfo {
struct as *sptas; /* dummy as ptr. for spt segment */
} sptinfo_t;
diff --git a/usr/src/uts/common/sys/signal.h b/usr/src/uts/common/sys/signal.h
index 8f0e1794f4..139784d578 100644
--- a/usr/src/uts/common/sys/signal.h
+++ b/usr/src/uts/common/sys/signal.h
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -158,8 +159,8 @@ struct sigaction32 {
* use of these symbols by applications is injurious
* to binary compatibility
*/
-#define NSIG 74 /* valid signals range from 1 to NSIG-1 */
-#define MAXSIG 73 /* size of u_signal[], NSIG-1 <= MAXSIG */
+#define NSIG 75 /* valid signals range from 1 to NSIG-1 */
+#define MAXSIG 74 /* size of u_signal[], NSIG-1 <= MAXSIG */
#endif /* defined(__EXTENSIONS__) || !defined(_XPG4_2) */
#define MINSIGSTKSZ 2048
diff --git a/usr/src/uts/common/sys/signalfd.h b/usr/src/uts/common/sys/signalfd.h
index 2661d5a05f..89d0647020 100644
--- a/usr/src/uts/common/sys/signalfd.h
+++ b/usr/src/uts/common/sys/signalfd.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -75,13 +75,9 @@ extern int signalfd(int, const sigset_t *, int);
#define SIGNALFDMNRN_SIGNALFD 0
#define SIGNALFDMNRN_CLONE 1
-typedef struct sigfd_wake_list {
- list_node_t sigfd_wl_lst;
- void *sigfd_wl_state;
-} sigfd_wake_list_t;
-
/*
* This holds the proc_t state for a process which is using signalfd.
+ * Its presence and contents are protected by p_lock.
*/
typedef struct sigfd_proc_state {
void (*sigfd_pollwake_cb)(void *, int);
diff --git a/usr/src/uts/common/sys/socket.h b/usr/src/uts/common/sys/socket.h
index da8e3ab351..e55cd165aa 100644
--- a/usr/src/uts/common/sys/socket.h
+++ b/usr/src/uts/common/sys/socket.h
@@ -22,6 +22,7 @@
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -39,6 +40,9 @@
/* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
+/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
#ifndef _SYS_SOCKET_H
#define _SYS_SOCKET_H
@@ -194,6 +198,7 @@ struct so_snd_bufinfo {
#define SO_SRCADDR 0x2001 /* Internal: AF_UNIX source address */
#define SO_FILEP 0x2002 /* Internal: AF_UNIX file pointer */
#define SO_UNIX_CLOSE 0x2003 /* Internal: AF_UNIX peer closed */
+#define SO_REUSEPORT 0x2004 /* allow simultaneous port reuse */
#endif /* _KERNEL */
/*
@@ -293,8 +298,9 @@ struct linger {
#define AF_INET_OFFLOAD 30 /* Sun private; do not use */
#define AF_TRILL 31 /* TRILL interface */
#define AF_PACKET 32 /* PF_PACKET Linux socket interface */
+#define AF_LX_NETLINK 33 /* Linux-compatible netlink */
-#define AF_MAX 32
+#define AF_MAX 33
/*
* Protocol families, same as address families for now.
@@ -334,6 +340,7 @@ struct linger {
#define PF_INET_OFFLOAD AF_INET_OFFLOAD /* Sun private; do not use */
#define PF_TRILL AF_TRILL
#define PF_PACKET AF_PACKET
+#define PF_LX_NETLINK AF_LX_NETLINK
#define PF_MAX AF_MAX
@@ -420,6 +427,7 @@ struct msghdr32 {
#define MSG_NOTIFICATION 0x100 /* Notification, not data */
#define MSG_XPG4_2 0x8000 /* Private: XPG4.2 flag */
+/* Obsolete but kept for compilation compatability. Use IOV_MAX. */
#define MSG_MAXIOVLEN 16
#ifdef _KERNEL
diff --git a/usr/src/uts/common/sys/socketvar.h b/usr/src/uts/common/sys/socketvar.h
index 52fa3a5822..da61975904 100644
--- a/usr/src/uts/common/sys/socketvar.h
+++ b/usr/src/uts/common/sys/socketvar.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -102,6 +103,7 @@ struct sockaddr_ux {
typedef struct sonodeops sonodeops_t;
typedef struct sonode sonode_t;
+typedef boolean_t (*so_krecv_f)(sonode_t *, mblk_t *, size_t, int, void *);
struct sodirect_s;
@@ -244,6 +246,10 @@ struct sonode {
struct sof_instance *so_filter_top; /* top of stack */
struct sof_instance *so_filter_bottom; /* bottom of stack */
clock_t so_filter_defertime; /* time when deferred */
+
+ /* Kernel direct receive callbacks */
+ so_krecv_f so_krecv_cb; /* recv callback */
+ void *so_krecv_arg; /* recv cb arg */
};
#define SO_HAVE_DATA(so) \
@@ -297,15 +303,16 @@ struct sonode {
#define SS_OOBPEND 0x00002000 /* OOB pending or present - poll */
#define SS_HAVEOOBDATA 0x00004000 /* OOB data present */
#define SS_HADOOBDATA 0x00008000 /* OOB data consumed */
-#define SS_CLOSING 0x00010000 /* in process of closing */
+#define SS_CLOSING 0x00010000 /* in process of closing */
#define SS_FIL_DEFER 0x00020000 /* filter deferred notification */
#define SS_FILOP_OK 0x00040000 /* socket can attach filters */
#define SS_FIL_RCV_FLOWCTRL 0x00080000 /* filter asserted rcv flow ctrl */
+
#define SS_FIL_SND_FLOWCTRL 0x00100000 /* filter asserted snd flow ctrl */
#define SS_FIL_STOP 0x00200000 /* no more filter actions */
-
#define SS_SODIRECT 0x00400000 /* transport supports sodirect */
+#define SS_FILOP_UNSF 0x00800000 /* block attaching unsafe filters */
#define SS_SENTLASTREADSIG 0x01000000 /* last rx signal has been sent */
#define SS_SENTLASTWRITESIG 0x02000000 /* last tx signal has been sent */
@@ -321,7 +328,8 @@ struct sonode {
/*
* Sockets that can fall back to TPI must ensure that fall back is not
- * initiated while a thread is using a socket.
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future filter attachment.
*/
#define SO_BLOCK_FALLBACK(so, fn) \
ASSERT(MUTEX_NOT_HELD(&(so)->so_lock)); \
@@ -337,6 +345,24 @@ struct sonode {
} \
}
+/*
+ * Sockets that can fall back to TPI must ensure that fall back is not
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future unsafe filter attachment. Safe filters can still attach after
+ * we execute the function in which this macro is used.
+ */
+#define SO_BLOCK_FALLBACK_SAFE(so, fn) \
+ ASSERT(MUTEX_NOT_HELD(&(so)->so_lock)); \
+ rw_enter(&(so)->so_fallback_rwlock, RW_READER); \
+ if ((so)->so_state & SS_FALLBACK_COMP) { \
+ rw_exit(&(so)->so_fallback_rwlock); \
+ return (fn); \
+ } else if (((so)->so_state & SS_FILOP_UNSF) == 0) { \
+ mutex_enter(&(so)->so_lock); \
+ (so)->so_state |= SS_FILOP_UNSF; \
+ mutex_exit(&(so)->so_lock); \
+ }
+
#define SO_UNBLOCK_FALLBACK(so) { \
rw_exit(&(so)->so_fallback_rwlock); \
}
@@ -368,6 +394,7 @@ struct sonode {
/* The modes below are only for non-streams sockets */
#define SM_ACCEPTSUPP 0x400 /* can handle accept() */
#define SM_SENDFILESUPP 0x800 /* Private: proto supp sendfile */
+#define SM_DEFERERR 0x1000 /* Private: defer so_error delivery */
/*
* Socket versions. Used by the socket library when calling _so_socket().
@@ -946,6 +973,15 @@ extern struct sonode *socreate(struct sockparams *, int, int, int, int,
extern int so_copyin(const void *, void *, size_t, int);
extern int so_copyout(const void *, void *, size_t, int);
+/*
+ * Functions to manipulate the use of direct receive callbacks. This should not
+ * be used outside of sockfs and ksocket. These are generally considered a use
+ * once interface for a socket and will cause all outstanding data on the socket
+ * to be flushed.
+ */
+extern int so_krecv_set(sonode_t *, so_krecv_f, void *);
+extern void so_krecv_unblock(sonode_t *);
+
#endif
/*
diff --git a/usr/src/uts/common/sys/sockfilter.h b/usr/src/uts/common/sys/sockfilter.h
index 9f6d8b499b..c4dd6539de 100644
--- a/usr/src/uts/common/sys/sockfilter.h
+++ b/usr/src/uts/common/sys/sockfilter.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_SOCKFILTER_H
@@ -129,6 +130,15 @@ typedef struct sof_ops {
#define SOF_VERSION 1
+/*
+ * Flag indicating that the filter module is safe to attach after bind,
+ * getsockname, getsockopt or setsockopt calls. By default filters are unsafe
+ * so may not be attached after any socket operation. However, a safe filter
+ * can still be attached after one of the above calls. This makes attaching
+ * the filter less dependent on the initial socket setup order.
+ */
+#define SOF_ATT_SAFE 0x1
+
extern int sof_register(int, const char *, const sof_ops_t *, int);
extern int sof_unregister(const char *);
diff --git a/usr/src/uts/common/sys/squeue.h b/usr/src/uts/common/sys/squeue.h
index f1bd429815..35e1cf64c7 100644
--- a/usr/src/uts/common/sys/squeue.h
+++ b/usr/src/uts/common/sys/squeue.h
@@ -29,6 +29,17 @@
extern "C" {
#endif
+/*
+ * Originally in illumos, we had an IP-centric view of the serialization queue
+ * abstraction. While that has useful properties, the implementation of squeues
+ * hardcodes various parts of the implementation of IP into it which makes it
+ * unsuitable for other consumers. To enable them, we created another interface,
+ * but opted not to port all of the functionality that IP uses in the form of
+ * ip_squeue.c As other consumers need the functionality that IP has in squeues,
+ * then we'll come up with more genericized methods and add that functionality
+ * to <sys/gsqueue.h>. Please do not continue to use this header.
+ */
+
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/stream.h>
@@ -76,12 +87,13 @@ typedef enum {
struct ip_recv_attr_s;
extern void squeue_init(void);
-extern squeue_t *squeue_create(clock_t, pri_t);
+extern squeue_t *squeue_create(clock_t, pri_t, boolean_t);
extern void squeue_bind(squeue_t *, processorid_t);
extern void squeue_unbind(squeue_t *);
extern void squeue_enter(squeue_t *, mblk_t *, mblk_t *,
uint32_t, struct ip_recv_attr_s *, int, uint8_t);
extern uintptr_t *squeue_getprivate(squeue_t *, sqprivate_t);
+extern void squeue_destroy(squeue_t *);
struct conn_s;
extern int squeue_synch_enter(struct conn_s *, mblk_t *);
diff --git a/usr/src/uts/common/sys/squeue_impl.h b/usr/src/uts/common/sys/squeue_impl.h
index 22550886eb..d2418bbc15 100644
--- a/usr/src/uts/common/sys/squeue_impl.h
+++ b/usr/src/uts/common/sys/squeue_impl.h
@@ -117,6 +117,7 @@ struct squeue_s {
squeue_set_t *sq_set; /* managed by squeue creator */
pri_t sq_priority; /* squeue thread priority */
+ boolean_t sq_isip; /* use IP-centric features */
/* Keep the debug-only fields at the end of the structure */
#ifdef DEBUG
@@ -165,6 +166,7 @@ struct squeue_s {
#define SQS_POLL_RESTART_DONE 0x01000000
#define SQS_POLL_THR_QUIESCE 0x02000000
#define SQS_PAUSE 0x04000000 /* The squeue has been paused */
+#define SQS_EXIT 0x08000000 /* squeue is being torn down */
#define SQS_WORKER_THR_CONTROL \
(SQS_POLL_QUIESCE | SQS_POLL_RESTART | SQS_POLL_CLEANUP)
diff --git a/usr/src/uts/common/sys/stream.h b/usr/src/uts/common/sys/stream.h
index a04019a9ce..28289649dd 100644
--- a/usr/src/uts/common/sys/stream.h
+++ b/usr/src/uts/common/sys/stream.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -628,16 +629,11 @@ struct stroptions {
/*
* Structure for rw (read/write) procedure calls. A pointer
* to a struiod_t is passed as a parameter to the rwnext() call.
- *
- * Note: DEF_IOV_MAX is defined and used as it is in "fs/vncalls.c"
- * as there isn't a formal definition of IOV_MAX ???
*/
-#define DEF_IOV_MAX 16
-
typedef struct struiod {
mblk_t *d_mp; /* pointer to mblk (chain) */
uio_t d_uio; /* uio info */
- iovec_t d_iov[DEF_IOV_MAX]; /* iov referenced by uio */
+ iovec_t *d_iov; /* iov referenced by uio */
} struiod_t;
/*
diff --git a/usr/src/uts/common/sys/sysevent.h b/usr/src/uts/common/sys/sysevent.h
index 46a800e62b..255e98b871 100644
--- a/usr/src/uts/common/sys/sysevent.h
+++ b/usr/src/uts/common/sys/sysevent.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_SYSEVENT_H
@@ -67,10 +68,12 @@ extern "C" {
#define SE_KERN_PID 0
#define SUNW_VENDOR "SUNW"
+#define ILLUMOS_VENDOR "ILLUMOS"
#define SE_USR_PUB "usr:"
#define SE_KERN_PUB "kern:"
#define SUNW_KERN_PUB SUNW_VENDOR":"SE_KERN_PUB
#define SUNW_USR_PUB SUNW_VENDOR":"SE_USR_PUB
+#define ILLUMOS_KERN_PUB ILLUMOS_VENDOR":"SE_KERN_PUB
/*
* Event header and attribute value limits
diff --git a/usr/src/uts/common/sys/sysevent/datalink.h b/usr/src/uts/common/sys/sysevent/datalink.h
new file mode 100644
index 0000000000..592ef5bdde
--- /dev/null
+++ b/usr/src/uts/common/sys/sysevent/datalink.h
@@ -0,0 +1,54 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_SYSEVENT_DATALINK_H
+#define _SYS_SYSEVENT_DATALINK_H
+
+/*
+ * Datalink System Event payloads
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Event schema for EC_DATALINK_LINK_STATE
+ *
+ * Event Class - EC_DATALINK
+ * Event Sub-Class - EC_DATALINK_LINK_STATE
+ *
+ * Attribute Name - DATALINK_EV_LINK_NAME
+ * Attribute Type - SE_DATA_TYPE_STRING
+ * Attribute Value - [Name of the datalink]
+ *
+ * Attribute Name - DATALINK_EV_LINK_ID
+ * Attribute Type - SE_DATA_TYPE_INT32
+ * Attribute Value - [datalink_id_t for the device]
+ *
+ * Attribute Name - DATALINK_EV_ZONE_ID
+ * Attribute Type - SE_DATA_TYPE_INT32
+ * Attribute Value - [zoneid_t of the zone the datalink is in]
+ */
+
+#define DATALINK_EV_LINK_NAME "link"
+#define DATALINK_EV_LINK_ID "linkid"
+#define DATALINK_EV_ZONE_ID "zone"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SYSEVENT_DATALINK_H */
diff --git a/usr/src/uts/common/sys/sysevent/eventdefs.h b/usr/src/uts/common/sys/sysevent/eventdefs.h
index 25401cec53..0a78d0310b 100644
--- a/usr/src/uts/common/sys/sysevent/eventdefs.h
+++ b/usr/src/uts/common/sys/sysevent/eventdefs.h
@@ -267,9 +267,11 @@ extern "C" {
#define ESC_ZFS_POOL_REGUID "ESC_ZFS_pool_reguid"
/*
- * datalink subclass definitions.
+ * datalink subclass definitions. Supporting attributes for datalink state found
+ * in sys/sysevent/datalink.h.
*/
#define ESC_DATALINK_PHYS_ADD "ESC_datalink_phys_add" /* new physical link */
+#define ESC_DATALINK_LINK_STATE "ESC_datalink_link_state" /* link state */
/*
* VRRP subclass definitions. Supporting attributes (name/value paris) are
diff --git a/usr/src/uts/common/sys/systrace.h b/usr/src/uts/common/sys/systrace.h
index d43974451e..17e509d4d8 100644
--- a/usr/src/uts/common/sys/systrace.h
+++ b/usr/src/uts/common/sys/systrace.h
@@ -22,13 +22,12 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_SYSTRACE_H
#define _SYS_SYSTRACE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dtrace.h>
#ifdef __cplusplus
@@ -47,16 +46,18 @@ extern systrace_sysent_t *systrace_sysent;
extern systrace_sysent_t *systrace_sysent32;
extern void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern void systrace_stub(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern int64_t dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7);
#ifdef _SYSCALL32_IMPL
extern int64_t dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7);
#endif
#endif
diff --git a/usr/src/uts/common/sys/termios.h b/usr/src/uts/common/sys/termios.h
index 09be20858d..889a7096cd 100644
--- a/usr/src/uts/common/sys/termios.h
+++ b/usr/src/uts/common/sys/termios.h
@@ -361,6 +361,24 @@ extern pid_t tcgetsid(int);
#define TCSETSF (_TIOC|16)
/*
+ * linux terminal ioctls we need to be aware of
+ */
+#define TIOCSETLD (_TIOC|123) /* set line discipline parms */
+#define TIOCGETLD (_TIOC|124) /* get line discipline parms */
+
+/*
+ * The VMIN and VTIME and solaris overlap with VEOF and VEOL - This is
+ * perfectly legal except, linux expects them to be separate. So we keep
+ * them separately.
+ */
+struct lx_cc {
+ unsigned char veof; /* veof value */
+ unsigned char veol; /* veol value */
+ unsigned char vmin; /* vmin value */
+ unsigned char vtime; /* vtime value */
+};
+
+/*
* NTP PPS ioctls
*/
#define TIOCGPPS (_TIOC|125)
diff --git a/usr/src/uts/common/sys/thread.h b/usr/src/uts/common/sys/thread.h
index d917944edf..9a75c5282f 100644
--- a/usr/src/uts/common/sys/thread.h
+++ b/usr/src/uts/common/sys/thread.h
@@ -24,6 +24,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#ifndef _SYS_THREAD_H
#define _SYS_THREAD_H
@@ -68,6 +72,8 @@ typedef struct ctxop {
void (*free_op)(void *, int); /* function which frees the context */
void *arg; /* argument to above functions, ctx pointer */
struct ctxop *next; /* next context ops */
+ hrtime_t save_ts; /* timestamp of last save */
+ hrtime_t restore_ts; /* timestamp of last restore */
} ctxop_t;
/*
@@ -366,7 +372,7 @@ typedef struct _kthread {
#define T_WOULDBLOCK 0x0020 /* for lockfs */
#define T_DONTBLOCK 0x0040 /* for lockfs */
#define T_DONTPEND 0x0080 /* for lockfs */
-#define T_SYS_PROF 0x0100 /* profiling on for duration of system call */
+#define T_SPLITSTK 0x0100 /* kernel stack is currently split */
#define T_WAITCVSEM 0x0200 /* waiting for a lwp_cv or lwp_sema on sleepq */
#define T_WATCHPT 0x0400 /* thread undergoing a watchpoint emulation */
#define T_PANIC 0x0800 /* thread initiated a system panic */
@@ -414,8 +420,9 @@ typedef struct _kthread {
#define TS_RESUME 0x1000 /* setrun() by CPR resume process */
#define TS_CREATE 0x2000 /* setrun() by syslwp_create() */
#define TS_RUNQMATCH 0x4000 /* exact run queue balancing by setbackdq() */
+#define TS_BSTART 0x8000 /* setrun() by brand */
#define TS_ALLSTART \
- (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE)
+ (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE|TS_BSTART)
#define TS_ANYWAITQ (TS_PROJWAITQ|TS_ZONEWAITQ)
/*
@@ -443,6 +450,10 @@ typedef struct _kthread {
#define ISTOPPED(t) ((t)->t_state == TS_STOPPED && \
!((t)->t_schedflag & TS_PSTART))
+/* True if thread is stopped for a brand-specific reason */
+#define BSTOPPED(t) ((t)->t_state == TS_STOPPED && \
+ !((t)->t_schedflag & TS_BSTART))
+
/* True if thread is asleep and wakeable */
#define ISWAKEABLE(t) (((t)->t_state == TS_SLEEP && \
((t)->t_flag & T_WAKEABLE)))
@@ -590,6 +601,7 @@ extern disp_lock_t stop_lock; /* lock protecting stopped threads */
caddr_t thread_stk_init(caddr_t); /* init thread stack */
extern int default_binding_mode;
+extern int default_stksize;
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/sys/time.h b/usr/src/uts/common/sys/time.h
index 05cc855a4b..ee64131403 100644
--- a/usr/src/uts/common/sys/time.h
+++ b/usr/src/uts/common/sys/time.h
@@ -15,6 +15,7 @@
* Use is subject to license terms.
*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -261,6 +262,14 @@ typedef longlong_t hrtime_t;
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
+/*
+ * Unsigned counterpart to hrtime_t
+ */
+typedef u_longlong_t uhrtime_t;
+
+#define HRTIME_MAX LLONG_MAX
+#define UHRTIME_MAX ULLONG_MAX
+
#include <sys/time_impl.h>
#include <sys/mutex.h>
diff --git a/usr/src/uts/common/sys/uadmin.h b/usr/src/uts/common/sys/uadmin.h
index d5168c9b2c..c14a3bf11e 100644
--- a/usr/src/uts/common/sys/uadmin.h
+++ b/usr/src/uts/common/sys/uadmin.h
@@ -23,6 +23,7 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -159,7 +160,7 @@ extern kmutex_t ualock;
extern void mdboot(int, int, char *, boolean_t);
extern void mdpreboot(int, int, char *);
extern int kadmin(int, int, void *, cred_t *);
-extern void killall(zoneid_t);
+extern void killall(zoneid_t, boolean_t);
#endif
extern int uadmin(int, int, uintptr_t);
diff --git a/usr/src/uts/common/sys/uio.h b/usr/src/uts/common/sys/uio.h
index e803efeb45..5663929bfb 100644
--- a/usr/src/uts/common/sys/uio.h
+++ b/usr/src/uts/common/sys/uio.h
@@ -145,7 +145,8 @@ typedef struct uioa_s {
*/
typedef enum xuio_type {
UIOTYPE_ASYNCIO,
- UIOTYPE_ZEROCOPY
+ UIOTYPE_ZEROCOPY,
+ UIOTYPE_PEEKSIZE
} xuio_type_t;
typedef struct xuio {
@@ -175,6 +176,15 @@ typedef struct xuio {
int xu_zc_rw; /* read or write buffer */
void *xu_zc_priv; /* fs specific */
} xu_zc;
+
+ /*
+ * Peek Size Support -- facilitate peeking at the size of a
+ * waiting message on a socket.
+ */
+ struct {
+ ssize_t xu_ps_size; /* size of waiting msg */
+ boolean_t xu_ps_set; /* was size calculated? */
+ } xu_ps;
} xu_ext;
} xuio_t;
diff --git a/usr/src/uts/common/sys/user.h b/usr/src/uts/common/sys/user.h
index a7bff8dd52..66250a3f2b 100644
--- a/usr/src/uts/common/sys/user.h
+++ b/usr/src/uts/common/sys/user.h
@@ -26,7 +26,7 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
- * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
@@ -185,9 +185,9 @@ typedef struct { /* kernel syscall set type */
* This value should not be changed in a patch.
*/
#if defined(__sparc)
-#define __KERN_NAUXV_IMPL 20
+#define __KERN_NAUXV_IMPL 24
#elif defined(__i386) || defined(__amd64)
-#define __KERN_NAUXV_IMPL 22
+#define __KERN_NAUXV_IMPL 26
#endif
struct execsw;
@@ -211,6 +211,7 @@ typedef struct user {
int u_argc; /* value of argc passed to main() */
uintptr_t u_argv; /* value of argv passed to main() */
uintptr_t u_envp; /* value of envp passed to main() */
+ uintptr_t u_commpagep; /* address of mapped comm page */
/*
* These fields are protected by p_lock:
diff --git a/usr/src/uts/common/sys/vm_usage.h b/usr/src/uts/common/sys/vm_usage.h
index 1aa4a8ee6d..c2954cbc29 100644
--- a/usr/src/uts/common/sys/vm_usage.h
+++ b/usr/src/uts/common/sys/vm_usage.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_VM_USAGE_H
@@ -79,8 +80,9 @@ extern "C" {
/* zoneid */
#define VMUSAGE_COL_EUSERS 0x2000 /* same as VMUSAGE_COL_RUSERS, but by */
/* euser */
+#define VMUSAGE_A_ZONE 0x4000 /* rss/swap for a specified zone */
-#define VMUSAGE_MASK 0x3fff /* all valid flags for getvmusage() */
+#define VMUSAGE_MASK 0x7fff /* all valid flags for getvmusage() */
typedef struct vmusage {
id_t vmu_zoneid; /* zoneid, or ALL_ZONES for */
@@ -108,6 +110,7 @@ extern int getvmusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres);
int vm_getusage(uint_t, time_t, vmusage_t *, size_t *, int);
void vm_usage_init();
+int vm_map_inval(pid_t, caddr_t, size_t);
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/sys/vmsystm.h b/usr/src/uts/common/sys/vmsystm.h
index 6122b6cd2f..c7b41730b6 100644
--- a/usr/src/uts/common/sys/vmsystm.h
+++ b/usr/src/uts/common/sys/vmsystm.h
@@ -19,6 +19,9 @@
* CDDL HEADER END
*/
/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
+/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -159,6 +162,8 @@ extern void *boot_virt_alloc(void *addr, size_t size);
extern size_t exec_get_spslew(void);
+extern caddr_t map_userlimit(proc_t *pp, struct as *as, int flags);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/vnd.h b/usr/src/uts/common/sys/vnd.h
new file mode 100644
index 0000000000..bc7c9c3122
--- /dev/null
+++ b/usr/src/uts/common/sys/vnd.h
@@ -0,0 +1,141 @@
+/*
+ * 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) 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_VND_H
+#define _SYS_VND_H
+
+#include <sys/types.h>
+#include <sys/vnd_errno.h>
+#include <sys/frameio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * We distinguish between normal ioctls and private ioctls we issues to out
+ * streams version. Streams ioctls have the upper bit set in the lowest byte.
+ * Note that there are no STREAMs ioctls for userland and all definitions
+ * related to them are not present in this file.
+ */
+#define VND_IOC (('v' << 24) | ('n' << 16) | ('d' << 8))
+
+/*
+ * Attach the current minor instance to a given dlpi datalink identified by a
+ * vnd_ioc_name_t argument. This fails if it's already been attached. Note that
+ * unlike the other ioctls, this is passed directly as opposed to every other
+ * function which is passed as a pointer to the value.
+ */
+#define VND_IOC_ATTACH (VND_IOC | 0x1)
+
+#define VND_NAMELEN 32
+
+typedef struct vnd_ioc_attach {
+ char via_name[VND_NAMELEN];
+ zoneid_t via_zoneid;
+ uint32_t via_errno;
+} vnd_ioc_attach_t;
+
+/*
+ * Link the current minor instance into the /devices name space.
+ *
+ * This ioctl adds entries into /devices with a name of the form z%d:%s vil_zid,
+ * vil_name. The device will be namespaced to the zone. The global zone will be
+ * able to see all minor nodes. In the zone, only the /dev entries will exist.
+ * At this time, a given device can only have one link at a time. Note that a
+ * user cannot specify the zone to pass in, rather it is the zone that the
+ * device was attached in.
+ */
+#define VND_IOC_LINK (VND_IOC | 0x2)
+
+typedef struct vnd_ioc_link {
+ char vil_name[VND_NAMELEN];
+ uint32_t vil_errno;
+} vnd_ioc_link_t;
+
+/*
+ * Unlink the opened minor instance from the /devices name space. A zone may use
+ * this to unlink an extent entry in /dev; however, they will not be able to
+ * link it in again.
+ */
+#define VND_IOC_UNLINK (VND_IOC | 0x3)
+typedef struct vnd_ioc_unlink {
+ uint32_t viu_errno;
+} vnd_ioc_unlink_t;
+
+/*
+ * Controls to get and set the current buffer recieve buffer size.
+ */
+typedef struct vnd_ioc_buf {
+ uint64_t vib_size;
+ uint32_t vib_filler;
+ uint32_t vib_errno;
+} vnd_ioc_buf_t;
+
+#define VND_IOC_GETRXBUF (VND_IOC | 0x04)
+#define VND_IOC_SETRXBUF (VND_IOC | 0x05)
+#define VND_IOC_GETMAXBUF (VND_IOC | 0x06)
+#define VND_IOC_GETTXBUF (VND_IOC | 0x07)
+#define VND_IOC_SETTXBUF (VND_IOC | 0x08)
+#define VND_IOC_GETMINTU (VND_IOC | 0x09)
+#define VND_IOC_GETMAXTU (VND_IOC | 0x0a)
+
+/*
+ * Information and listing ioctls
+ *
+ * This gets information about all of the active vnd instances. vl_actents is
+ * always updated to the number around and vl_nents is the number of
+ * vnd_ioc_info_t elements are allocated in vl_ents.
+ */
+typedef struct vnd_ioc_info {
+ uint32_t vii_version;
+ zoneid_t vii_zone;
+ char vii_name[VND_NAMELEN];
+ char vii_datalink[VND_NAMELEN];
+} vnd_ioc_info_t;
+
+typedef struct vnd_ioc_list {
+ uint_t vl_nents;
+ uint_t vl_actents;
+ vnd_ioc_info_t *vl_ents;
+} vnd_ioc_list_t;
+
+#ifdef _KERNEL
+
+typedef struct vnd_ioc_list32 {
+ uint_t vl_nents;
+ uint_t vl_actents;
+ caddr32_t vl_ents;
+} vnd_ioc_list32_t;
+
+#endif /* _KERNEL */
+
+#define VND_IOC_LIST (VND_IOC | 0x20)
+
+/*
+ * Framed I/O ioctls
+ *
+ * Users should use the standard frameio_t as opposed to a vnd specific type.
+ * This is a consolidation private ioctl pending futher stability in the form of
+ * specific system work.
+ */
+#define VND_IOC_FRAMEIO_READ (VND_IOC | 0x30)
+#define VND_IOC_FRAMEIO_WRITE (VND_IOC | 0x31)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VND_H */
diff --git a/usr/src/uts/common/sys/vnd_errno.h b/usr/src/uts/common/sys/vnd_errno.h
new file mode 100644
index 0000000000..89e5fc2543
--- /dev/null
+++ b/usr/src/uts/common/sys/vnd_errno.h
@@ -0,0 +1,72 @@
+/*
+ * 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) 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_VND_ERRNO_H
+#define _SYS_VND_ERRNO_H
+
+/*
+ * This header contains all of the available vnd errors.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum vnd_errno {
+ VND_E_SUCCESS = 0, /* no error */
+ VND_E_NOMEM, /* no memory */
+ VND_E_NODATALINK, /* no such datalink */
+ VND_E_NOTETHER, /* not DL_ETHER */
+ VND_E_DLPIINVAL, /* Unknown DLPI failures */
+ VND_E_ATTACHFAIL, /* DL_ATTACH_REQ failed */
+ VND_E_BINDFAIL, /* DL_BIND_REQ failed */
+ VND_E_PROMISCFAIL, /* DL_PROMISCON_REQ failed */
+ VND_E_DIRECTFAIL, /* DLD_CAPAB_DIRECT enable failed */
+ VND_E_CAPACKINVAL, /* bad dl_capability_ack_t */
+ VND_E_SUBCAPINVAL, /* bad dl_capability_sub_t */
+ VND_E_DLDBADVERS, /* bad dld version */
+ VND_E_KSTATCREATE, /* failed to create kstats */
+ VND_E_NODEV, /* no such vnd link */
+ VND_E_NONETSTACK, /* netstack doesn't exist */
+ VND_E_ASSOCIATED, /* device already associated */
+ VND_E_ATTACHED, /* device already attached */
+ VND_E_LINKED, /* device already linked */
+ VND_E_BADNAME, /* invalid name */
+ VND_E_PERM, /* can't touch this */
+ VND_E_NOZONE, /* no such zone */
+ VND_E_STRINIT, /* failed to initialize vnd stream module */
+ VND_E_NOTATTACHED, /* device not attached */
+ VND_E_NOTLINKED, /* device not linked */
+ VND_E_LINKEXISTS, /* another device has the same link name */
+ VND_E_MINORNODE, /* failed to create minor node */
+ VND_E_BUFTOOBIG, /* requested buffer size is too large */
+ VND_E_BUFTOOSMALL, /* requested buffer size is too small */
+ VND_E_DLEXCL, /* unable to get dlpi excl access */
+ VND_E_DIRECTNOTSUP,
+ /* DLD direct capability not suported over data link */
+ VND_E_BADPROPSIZE, /* invalid property size */
+ VND_E_BADPROP, /* invalid property */
+ VND_E_PROPRDONLY, /* property is read only */
+ VND_E_SYS, /* unexpected system error */
+ VND_E_CAPABPASS,
+ /* capabilities invalid, pass-through module detected */
+ VND_E_UNKNOWN /* unknown error */
+} vnd_errno_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VND_ERRNO_H */
diff --git a/usr/src/uts/common/sys/vnic_impl.h b/usr/src/uts/common/sys/vnic_impl.h
index 7e50091347..1a91158da6 100644
--- a/usr/src/uts/common/sys/vnic_impl.h
+++ b/usr/src/uts/common/sys/vnic_impl.h
@@ -21,7 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_VNIC_IMPL_H
@@ -65,6 +65,7 @@ typedef struct vnic_s {
uint32_t vn_hcksum_txflags;
uint32_t vn_mtu;
+ link_state_t vn_ls;
} vnic_t;
#define vn_mch vn_mc_handles[0]
diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h
index e4d43cea7f..c779cc1ff6 100644
--- a/usr/src/uts/common/sys/vnode.h
+++ b/usr/src/uts/common/sys/vnode.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -221,6 +221,59 @@ struct vsd_node {
* In particular, file systems should not access other fields; they may
* change or even be removed. The functionality which was once provided
* by these fields is available through vn_* functions.
+ *
+ * VNODE PATH THEORY:
+ * In each vnode, the v_path field holds a cached version of the canonical
+ * filesystem path which that node represents. Because vnodes lack contextual
+ * information about their own name or position in the VFS hierarchy, this path
+ * must be calculated when the vnode is instantiated by operations such as
+ * fop_create, fop_lookup, or fop_mkdir. During said operations, both the
+ * parent vnode (and its cached v_path) and future name are known, so the
+ * v_path of the resulting object can easily be set.
+ *
+ * The caching nature of v_path is complicated in the face of directory
+ * renames. Filesystem drivers are responsible for calling vn_renamepath when
+ * a fop_rename operation succeeds. While the v_path on the renamed vnode will
+ * be updated, existing children of the directory (direct, or at deeper levels)
+ * will now possess v_path caches which are stale.
+ *
+ * It is expensive (and for non-directories, impossible) to recalculate stale
+ * v_path entries during operations such as vnodetopath. The best time during
+ * which to correct such wrongs is the same as when v_path is first
+ * initialized: during fop_create/fop_lookup/fop_mkdir/etc, where adequate
+ * context is available to generate the current path.
+ *
+ * In order to quickly detect stale v_path entries (without full lookup
+ * verification) to trigger a v_path update, the v_path_stamp field has been
+ * added to vnode_t. As part of successful fop_create/fop_lookup/fop_mkdir
+ * operations, where the name and parent vnode are available, the following
+ * rules are used to determine updates to the child:
+ *
+ * 1. If the parent lacks a v_path, clear any existing v_path and v_path_stamp
+ * on the child. Until the parent v_path is refreshed to a valid state, the
+ * child v_path must be considered invalid too.
+ *
+ * 2. If the child lacks a v_path (implying v_path_stamp == 0), it inherits the
+ * v_path_stamp value from its parent and its v_path is updated.
+ *
+ * 3. If the child v_path_stamp is less than v_path_stamp in the parent, it is
+ * an indication that the child v_path is stale. The v_path is updated and
+ * v_path_stamp in the child is set to the current hrtime().
+ *
+ * It does _not_ inherit the parent v_path_stamp in order to propagate the
+ * the time of v_path invalidation through the directory structure. This
+ * prevents concurrent invalidations (operating with a now-incorrect v_path)
+ * at deeper levels in the tree from persisting.
+ *
+ * 4. If the child v_path_stamp is greater or equal to the parent, no action
+ * needs to be taken.
+ *
+ * Note that fop_rename operations do not follow this ruleset. They perform an
+ * explicit update of v_path and v_path_stamp (setting it to the current time)
+ *
+ * With these constraints in place, v_path invalidations and updates should
+ * proceed in a timely manner as vnodes are accessed. While there still are
+ * limited cases where vnodetopath operations will fail, the risk is minimized.
*/
struct fem_head; /* from fem.h */
@@ -247,6 +300,7 @@ typedef struct vnode {
void *v_locality; /* hook for locality info */
struct fem_head *v_femhead; /* fs monitoring */
char *v_path; /* cached path */
+ hrtime_t v_path_stamp; /* timestamp for cached path */
uint_t v_rdcnt; /* open for read count (VREG only) */
uint_t v_wrcnt; /* open for write count (VREG only) */
u_longlong_t v_mmap_read; /* mmap read count */
@@ -738,12 +792,14 @@ typedef enum vnevent {
VE_RMDIR = 4, /* Remove of directory vnode's name */
VE_CREATE = 5, /* Create with vnode's name which exists */
VE_LINK = 6, /* Link with vnode's name as source */
- VE_RENAME_DEST_DIR = 7, /* Rename with vnode as target dir */
+ VE_RENAME_DEST_DIR = 7, /* Rename with vnode as target dir */
VE_MOUNTEDOVER = 8, /* File or Filesystem got mounted over vnode */
VE_TRUNCATE = 9, /* Truncate */
VE_PRE_RENAME_SRC = 10, /* Pre-rename, with vnode as source */
VE_PRE_RENAME_DEST = 11, /* Pre-rename, with vnode as target/dest. */
- VE_PRE_RENAME_DEST_DIR = 12 /* Pre-rename with vnode as target dir */
+ VE_PRE_RENAME_DEST_DIR = 12, /* Pre-rename with vnode as target dir */
+ VE_RENAME_SRC_DIR = 13, /* Rename with vnode as source dir */
+ VE_RESIZE = 14 /* Resize/truncate to non-zero offset */
} vnevent_t;
/*
@@ -1291,6 +1347,11 @@ void vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp,
const char *path, size_t plen);
void vn_renamepath(vnode_t *dvp, vnode_t *vp, const char *nm, size_t len);
+/* Private vnode manipulation functions */
+void vn_clearpath(vnode_t *, hrtime_t);
+void vn_updatepath(vnode_t *, vnode_t *, const char *);
+
+
/* Vnode event notification */
void vnevent_rename_src(vnode_t *, vnode_t *, char *, caller_context_t *);
void vnevent_rename_dest(vnode_t *, vnode_t *, char *, caller_context_t *);
@@ -1298,7 +1359,8 @@ void vnevent_remove(vnode_t *, vnode_t *, char *, caller_context_t *);
void vnevent_rmdir(vnode_t *, vnode_t *, char *, caller_context_t *);
void vnevent_create(vnode_t *, caller_context_t *);
void vnevent_link(vnode_t *, caller_context_t *);
-void vnevent_rename_dest_dir(vnode_t *, caller_context_t *ct);
+void vnevent_rename_dest_dir(vnode_t *, vnode_t *, char *,
+ caller_context_t *ct);
void vnevent_mountedover(vnode_t *, caller_context_t *);
void vnevent_truncate(vnode_t *, caller_context_t *);
int vnevent_support(vnode_t *, caller_context_t *);
@@ -1308,6 +1370,7 @@ void vnevent_pre_rename_dest(vnode_t *, vnode_t *, char *,
caller_context_t *);
void vnevent_pre_rename_dest_dir(vnode_t *, vnode_t *, char *,
caller_context_t *);
+void vnevent_resize(vnode_t *, caller_context_t *);
/* Vnode specific data */
void vsd_create(uint_t *, void (*)(void *));
@@ -1337,6 +1400,9 @@ u_longlong_t fs_new_caller_id();
int vn_vmpss_usepageio(vnode_t *);
+/* Empty v_path placeholder */
+extern char *vn_vpath_empty;
+
/*
* Needed for use of IS_VMODSORT() in kernel.
*/
diff --git a/usr/src/uts/common/sys/vxlan.h b/usr/src/uts/common/sys/vxlan.h
new file mode 100644
index 0000000000..d87786b507
--- /dev/null
+++ b/usr/src/uts/common/sys/vxlan.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_VXLAN_H
+#define _SYS_VXLAN_H
+
+/*
+ * Common VXLAN information
+ */
+
+#include <sys/inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Sizes in bytes */
+#define VXLAN_HDR_LEN 8
+#define VXLAN_ID_LEN 3
+
+#define VXLAN_F_VDI 0x08000000
+#define VXLAN_ID_SHIFT 8
+
+#pragma pack(1)
+typedef struct vxlan_hdr {
+ uint32_t vxlan_flags;
+ uint32_t vxlan_id;
+} vxlan_hdr_t;
+#pragma pack()
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VXLAN_H */
diff --git a/usr/src/uts/common/sys/zfd.h b/usr/src/uts/common/sys/zfd.h
new file mode 100644
index 0000000000..e08d75ecba
--- /dev/null
+++ b/usr/src/uts/common/sys/zfd.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_ZFD_H
+#define _SYS_ZFD_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Minor node name of the global zone side (often called the "master" side)
+ * of the zfd dev.
+ */
+#define ZFD_MASTER_NAME "master"
+
+/*
+ * Minor node name of the non-global zone side (often called the "slave"
+ * side) of the zfd dev.
+ */
+#define ZFD_SLAVE_NAME "slave"
+
+#define ZFD_NAME_LEN 16
+
+/*
+ * ZFD_IOC forms the base for all zfd ioctls.
+ */
+#define ZFD_IOC (('Z' << 24) | ('f' << 16) | ('d' << 8))
+
+/*
+ * This ioctl tells the slave side it should push the TTY stream modules
+ * so that the fd looks like a tty.
+ */
+#define ZFD_MAKETTY (ZFD_IOC | 0)
+
+/*
+ * This ioctl puts a hangup into the stream so that the slave side sees EOF.
+ */
+#define ZFD_EOF (ZFD_IOC | 1)
+
+/*
+ * This ioctl succeeds if the slave side is open.
+ */
+#define ZFD_HAS_SLAVE (ZFD_IOC | 2)
+
+/*
+ * This ioctl links two streams into a multiplexer configuration for in-zone
+ * logging.
+ */
+#define ZFD_MUX (ZFD_IOC | 3)
+
+/*
+ * This ioctl controls the flow control setting for the log multiplexer stream
+ * (1 = true, 0 = false). The default is false which implies teeing into the
+ * log stream is "best-effort" but data will be discarded if the stream
+ * becomes full. If set and the log stream begins to fill up, the primary
+ * stream will stop flowing.
+ */
+#define ZFD_MUX_FLOWCON (ZFD_IOC | 4)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ZFD_H */
diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h
index 2e69b0d1c7..754f8e3978 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -20,9 +20,9 @@
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright 2014 Igor Kozhukhov <ikozhukhov@gmail.com>.
+ * Copyright 2016, Joyent, Inc.
*/
#ifndef _SYS_ZONE_H
@@ -97,13 +97,19 @@ extern "C" {
#define ZONE_ATTR_INITNAME 9
#define ZONE_ATTR_BOOTARGS 10
#define ZONE_ATTR_BRAND 11
-#define ZONE_ATTR_PHYS_MCAP 12
+#define ZONE_ATTR_PMCAP_NOVER 12
#define ZONE_ATTR_SCHED_CLASS 13
#define ZONE_ATTR_FLAGS 14
#define ZONE_ATTR_HOSTID 15
#define ZONE_ATTR_FS_ALLOWED 16
#define ZONE_ATTR_NETWORK 17
+#define ZONE_ATTR_DID 18
+#define ZONE_ATTR_PMCAP_PAGEOUT 19
#define ZONE_ATTR_INITNORESTART 20
+#define ZONE_ATTR_PG_FLT_DELAY 21
+#define ZONE_ATTR_RSS 22
+#define ZONE_ATTR_APP_SVC_CT 23
+#define ZONE_ATTR_SCHED_FIXEDHI 24
/* Start of the brand-specific attribute namespace */
#define ZONE_ATTR_BRAND_ATTRS 32768
@@ -184,6 +190,7 @@ typedef struct {
uint32_t doi; /* DOI for label */
caddr32_t label; /* label associated with zone */
int flags;
+ zoneid_t zoneid; /* requested zoneid */
} zone_def32;
#endif
typedef struct {
@@ -200,6 +207,7 @@ typedef struct {
uint32_t doi; /* DOI for label */
const bslabel_t *label; /* label associated with zone */
int flags;
+ zoneid_t zoneid; /* requested zoneid */
} zone_def;
/* extended error information */
@@ -244,9 +252,12 @@ typedef enum zone_cmd {
typedef struct zone_cmd_arg {
uint64_t uniqid; /* unique "generation number" */
zone_cmd_t cmd; /* requested action */
- uint32_t _pad; /* need consistent 32/64 bit alignmt */
+ int status; /* init status on shutdown */
+ uint32_t debug; /* enable brand hook debug */
char locale[MAXPATHLEN]; /* locale in which to render messages */
char bootbuf[BOOTARGS_MAX]; /* arguments passed to zone_boot() */
+ /* Needed for 32/64 zoneadm -> zoneadmd door arg size check. */
+ int pad;
} zone_cmd_arg_t;
/*
@@ -372,7 +383,7 @@ typedef struct zone_dataset {
} zone_dataset_t;
/*
- * structure for zone kstats
+ * structure for rctl zone kstats
*/
typedef struct zone_kstat {
kstat_named_t zk_zonename;
@@ -383,12 +394,57 @@ typedef struct zone_kstat {
struct cpucap;
typedef struct {
+ hrtime_t cycle_start;
+ uint_t cycle_cnt;
+ hrtime_t zone_avg_cnt;
+} sys_zio_cntr_t;
+
+typedef struct {
+ kstat_named_t zv_zonename;
+ kstat_named_t zv_nread;
+ kstat_named_t zv_reads;
+ kstat_named_t zv_rtime;
+ kstat_named_t zv_rlentime;
+ kstat_named_t zv_rcnt;
+ kstat_named_t zv_nwritten;
+ kstat_named_t zv_writes;
+ kstat_named_t zv_wtime;
+ kstat_named_t zv_wlentime;
+ kstat_named_t zv_wcnt;
+ kstat_named_t zv_10ms_ops;
+ kstat_named_t zv_100ms_ops;
+ kstat_named_t zv_1s_ops;
+ kstat_named_t zv_10s_ops;
+ kstat_named_t zv_delay_cnt;
+ kstat_named_t zv_delay_time;
+} zone_vfs_kstat_t;
+
+typedef struct {
+ kstat_named_t zz_zonename;
+ kstat_named_t zz_nread;
+ kstat_named_t zz_reads;
+ kstat_named_t zz_rtime;
+ kstat_named_t zz_rlentime;
+ kstat_named_t zz_nwritten;
+ kstat_named_t zz_writes;
+ kstat_named_t zz_waittime;
+} zone_zfs_kstat_t;
+
+typedef struct {
kstat_named_t zm_zonename;
+ kstat_named_t zm_rss;
+ kstat_named_t zm_phys_cap;
+ kstat_named_t zm_swap;
+ kstat_named_t zm_swap_cap;
+ kstat_named_t zm_nover;
+ kstat_named_t zm_pagedout;
kstat_named_t zm_pgpgin;
kstat_named_t zm_anonpgin;
kstat_named_t zm_execpgin;
kstat_named_t zm_fspgin;
kstat_named_t zm_anon_alloc_fail;
+ kstat_named_t zm_pf_throttle;
+ kstat_named_t zm_pf_throttle_usec;
} zone_mcap_kstat_t;
typedef struct {
@@ -447,6 +503,7 @@ typedef struct zone {
*/
list_node_t zone_linkage;
zoneid_t zone_id; /* ID of zone */
+ zoneid_t zone_did; /* persistent debug ID of zone */
uint_t zone_ref; /* count of zone_hold()s on zone */
uint_t zone_cred_ref; /* count of zone_hold_cred()s on zone */
/*
@@ -499,10 +556,11 @@ typedef struct zone {
kcondvar_t zone_cv; /* used to signal state changes */
struct proc *zone_zsched; /* Dummy kernel "zsched" process */
pid_t zone_proc_initpid; /* pid of "init" for this zone */
- char *zone_initname; /* fs path to 'init' */
+ char *zone_initname; /* fs path to 'init' */
+ int zone_init_status; /* init's exit status */
int zone_boot_err; /* for zone_boot() if boot fails */
char *zone_bootargs; /* arguments passed via zone_boot() */
- uint64_t zone_phys_mcap; /* physical memory cap */
+ rctl_qty_t zone_phys_mem_ctl; /* current phys. memory limit */
/*
* zone_kthreads is protected by zone_status_lock.
*/
@@ -540,9 +598,12 @@ typedef struct zone {
tsol_mlp_list_t zone_mlps; /* MLPs on zone-private addresses */
boolean_t zone_restart_init; /* Restart init if it dies? */
+ boolean_t zone_reboot_on_init_exit; /* Reboot if init dies? */
+ boolean_t zone_setup_app_contract; /* setup contract? */
struct brand *zone_brand; /* zone's brand */
void *zone_brand_data; /* store brand specific data */
id_t zone_defaultcid; /* dflt scheduling class id */
+ boolean_t zone_fixed_hipri; /* fixed sched. hi prio */
kstat_t *zone_swapresv_kstat;
kstat_t *zone_lockedmem_kstat;
/*
@@ -551,6 +612,37 @@ typedef struct zone {
list_t zone_dl_list;
netstack_t *zone_netstack;
struct cpucap *zone_cpucap; /* CPU caps data */
+
+ /*
+ * Data and counters used for ZFS fair-share disk IO.
+ */
+ rctl_qty_t zone_zfs_io_pri; /* ZFS IO priority */
+ uint_t zone_zfs_queued[2]; /* sync I/O enqueued count */
+ uint64_t zone_zfs_weight; /* used to prevent starvation */
+ uint64_t zone_io_util; /* IO utilization metric */
+ boolean_t zone_io_util_above_avg; /* IO util percent > avg. */
+ uint16_t zone_io_delay; /* IO delay on logical r/w */
+ kmutex_t zone_stg_io_lock; /* protects IO window data */
+ sys_zio_cntr_t zone_rd_ops; /* Counters for ZFS reads, */
+ sys_zio_cntr_t zone_wr_ops; /* writes and */
+ sys_zio_cntr_t zone_lwr_ops; /* logical writes. */
+
+ /*
+ * kstats and counters for VFS ops and bytes.
+ */
+ kmutex_t zone_vfs_lock; /* protects VFS statistics */
+ kstat_t *zone_vfs_ksp;
+ kstat_io_t zone_vfs_rwstats;
+ zone_vfs_kstat_t *zone_vfs_stats;
+
+ /*
+ * kstats for ZFS I/O ops and bytes.
+ */
+ kmutex_t zone_zfs_lock; /* protects ZFS statistics */
+ kstat_t *zone_zfs_ksp;
+ kstat_io_t zone_zfs_rwstats;
+ zone_zfs_kstat_t *zone_zfs_stats;
+
/*
* Solaris Auditing per-zone audit context
*/
@@ -569,6 +661,13 @@ typedef struct zone {
/* zone_rctls->rcs_lock */
kstat_t *zone_nprocs_kstat;
+ /*
+ * kstats and counters for physical memory capping.
+ */
+ rctl_qty_t zone_phys_mem; /* current bytes of phys. mem. (RSS) */
+ kstat_t *zone_physmem_kstat;
+ uint64_t zone_mcap_nover; /* # of times over phys. cap */
+ uint64_t zone_mcap_pagedout; /* bytes of mem. paged out */
kmutex_t zone_mcap_lock; /* protects mcap statistics */
kstat_t *zone_mcap_ksp;
zone_mcap_kstat_t *zone_mcap_stats;
@@ -577,6 +676,11 @@ typedef struct zone {
uint64_t zone_execpgin; /* exec pages paged in */
uint64_t zone_fspgin; /* fs pages paged in */
uint64_t zone_anon_alloc_fail; /* cnt of anon alloc fails */
+ uint64_t zone_pf_throttle; /* cnt of page flt throttles */
+ uint64_t zone_pf_throttle_usec; /* time of page flt throttles */
+
+ /* Num usecs to throttle page fault when zone is over phys. mem cap */
+ uint32_t zone_pg_flt_delay;
/*
* Misc. kstats and counters for zone cpu-usage aggregation.
@@ -658,6 +762,7 @@ extern zone_t *zone_find_by_name(char *);
extern zone_t *zone_find_by_any_path(const char *, boolean_t);
extern zone_t *zone_find_by_path(const char *);
extern zoneid_t getzoneid(void);
+extern zoneid_t getzonedid(void);
extern zone_t *zone_find_by_id_nolock(zoneid_t);
extern int zone_datalink_walk(zoneid_t, int (*)(datalink_id_t, void *), void *);
extern int zone_check_datalink(zoneid_t *, datalink_id_t);
@@ -838,6 +943,7 @@ extern int zone_ncpus_online_get(zone_t *);
* Returns true if the named pool/dataset is visible in the current zone.
*/
extern int zone_dataset_visible(const char *, int *);
+extern int zone_dataset_visible_inzone(zone_t *, const char *, int *);
/*
* zone version of kadmin()
@@ -852,6 +958,7 @@ extern int zone_walk(int (*)(zone_t *, void *), void *);
extern rctl_hndl_t rc_zone_locked_mem;
extern rctl_hndl_t rc_zone_max_swap;
+extern rctl_hndl_t rc_zone_phys_mem;
extern rctl_hndl_t rc_zone_max_lofi;
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/syscall/brandsys.c b/usr/src/uts/common/syscall/brandsys.c
index 9b4bd38baa..245ef9f14f 100644
--- a/usr/src/uts/common/syscall/brandsys.c
+++ b/usr/src/uts/common/syscall/brandsys.c
@@ -23,7 +23,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
#include <sys/brand.h>
#include <sys/systm.h>
@@ -35,7 +37,7 @@
*/
int64_t
brandsys(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
- uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg4)
{
struct proc *p = curthread->t_procp;
int64_t rval = 0;
@@ -49,7 +51,7 @@ brandsys(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
return (set_errno(ENOSYS));
if ((err = ZBROP(p->p_zone)->b_brandsys(cmd, &rval, arg1, arg2, arg3,
- arg4, arg5, arg6)) != 0)
+ arg4)) != 0)
return (set_errno(err));
return (rval);
diff --git a/usr/src/uts/common/syscall/fcntl.c b/usr/src/uts/common/syscall/fcntl.c
index 371bc83c29..14e3b7018e 100644
--- a/usr/src/uts/common/syscall/fcntl.c
+++ b/usr/src/uts/common/syscall/fcntl.c
@@ -54,7 +54,8 @@
#include <sys/cmn_err.h>
-static int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+/* This is global so that it can be used by brand emulation. */
+int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
static int flock_get_start(vnode_t *, flock64_t *, offset_t, u_offset_t *);
static void fd_too_big(proc_t *);
@@ -365,7 +366,8 @@ fcntl(int fdes, int cmd, intptr_t arg)
}
}
- if (cmd == F_OFD_SETLK || cmd == F_OFD_SETLKW) {
+ if (cmd == F_OFD_GETLK || cmd == F_OFD_SETLK ||
+ cmd == F_OFD_SETLKW) {
/*
* TBD OFD-style locking is currently limited to
* covering the entire file.
diff --git a/usr/src/uts/common/syscall/memcntl.c b/usr/src/uts/common/syscall/memcntl.c
index 1ee4b6a395..721f884a7e 100644
--- a/usr/src/uts/common/syscall/memcntl.c
+++ b/usr/src/uts/common/syscall/memcntl.c
@@ -115,13 +115,17 @@ memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, int attr, int mask)
* MS_SYNC used to be defined to be zero but is now non-zero.
* For binary compatibility we still accept zero
* (the absence of MS_ASYNC) to mean the same thing.
+ * Binary compatibility is not an issue for MS_INVALCURPROC.
*/
iarg = (uintptr_t)arg;
if ((iarg & ~MS_INVALIDATE) == 0)
iarg |= MS_SYNC;
- if (((iarg & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) != 0) ||
- ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC))) {
+ if (((iarg &
+ ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC)) != 0) ||
+ ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC)) ||
+ ((iarg & (MS_INVALIDATE|MS_INVALCURPROC)) ==
+ (MS_INVALIDATE|MS_INVALCURPROC))) {
error = set_errno(EINVAL);
} else {
error = as_ctl(as, addr, len, cmd, attr, iarg, NULL, 0);
diff --git a/usr/src/uts/common/syscall/open.c b/usr/src/uts/common/syscall/open.c
index edb04c824b..874e31869c 100644
--- a/usr/src/uts/common/syscall/open.c
+++ b/usr/src/uts/common/syscall/open.c
@@ -74,12 +74,12 @@ copen(int startfd, char *fname, int filemode, int createmode)
if (filemode & (FSEARCH|FEXEC)) {
/*
- * Must be one or the other and neither FREAD nor FWRITE
+ * Must be one or the other.
* Must not be any of FAPPEND FCREAT FTRUNC FXATTR FXATTRDIROPEN
- * XXX: Should these just be silently ignored?
+ * XXX: Should these just be silently ignored like we
+ * silently ignore FREAD|FWRITE?
*/
- if ((filemode & (FREAD|FWRITE)) ||
- (filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
+ if ((filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
(filemode & (FAPPEND|FCREAT|FTRUNC|FXATTR|FXATTRDIROPEN)))
return (set_errno(EINVAL));
}
diff --git a/usr/src/uts/common/syscall/poll.c b/usr/src/uts/common/syscall/poll.c
index cc125f127a..7b159ddc37 100644
--- a/usr/src/uts/common/syscall/poll.c
+++ b/usr/src/uts/common/syscall/poll.c
@@ -29,7 +29,7 @@
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2016, Joyent, Inc.
*/
/*
@@ -317,20 +317,57 @@ polllock(pollhead_t *php, kmutex_t *lp)
return (0);
}
-static int
-poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+int
+poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds)
+{
+ pollfd_t *pollfdp;
+ nfds_t old_nfds;
+
+ /*
+ * NOTE: for performance, buffers are saved across poll() calls.
+ * The theory is that if a process polls heavily, it tends to poll
+ * on the same set of descriptors. Therefore, we only reallocate
+ * buffers when nfds changes. There is no hysteresis control,
+ * because there is no data to suggest that this is necessary;
+ * the penalty of reallocating is not *that* great in any event.
+ */
+ old_nfds = ps->ps_nfds;
+ if (nfds != old_nfds) {
+ kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+ pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+ ps->ps_pollfd = pollfdp;
+ ps->ps_nfds = nfds;
+ }
+
+ pollfdp = ps->ps_pollfd;
+ if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
+ return (EFAULT);
+ }
+
+ if (fds == NULL) {
+ /*
+ * If the process has page 0 mapped, then the copyin() above
+ * will succeed even if fds is NULL. However, our cached
+ * poll lists are keyed by the address of the passed-in fds
+ * structure, and we use the value NULL to indicate an unused
+ * poll cache list entry. As such, we elect not to support
+ * NULL as a valid (user) memory address and fail the poll()
+ * call.
+ */
+ return (EFAULT);
+ }
+ return (0);
+}
+
+int
+poll_common(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, timespec_t *tsp,
+ int *fdcnt)
{
kthread_t *t = curthread;
- klwp_t *lwp = ttolwp(t);
- proc_t *p = ttoproc(t);
- int fdcnt = 0;
- int i;
hrtime_t deadline; /* hrtime value when we want to return */
pollfd_t *pollfdp;
- pollstate_t *ps;
pollcache_t *pcp;
int error = 0;
- nfds_t old_nfds;
int cacheindex = 0; /* which cache set is used */
/*
@@ -340,40 +377,42 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
deadline = -1;
} else if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) {
deadline = 0;
+ } else if (tsp->tv_sec >= HRTIME_MAX/NANOSEC) {
+ /* Use an indefinite timeout if tv_sec would cause overflow */
+ deadline = -1;
} else {
+ /*
+ * The above check, when combined with the protections offered
+ * by itimerspecfix (ensuring that neither field is negative
+ * and that tv_nsec represents less than a whole second), will
+ * prevent overflow during the conversion from timespec_t to
+ * uhrtime_t.
+ */
+ uhrtime_t utime = tsp->tv_sec * NANOSEC;
+ utime += tsp->tv_nsec;
+
/* They must wait at least a tick. */
- deadline = ((hrtime_t)tsp->tv_sec * NANOSEC) + tsp->tv_nsec;
- deadline = MAX(deadline, nsec_per_tick);
- deadline += gethrtime();
- }
+ utime = MAX(utime, nsec_per_tick);
- /*
- * Reset our signal mask, if requested.
- */
- if (ksetp != NULL) {
- mutex_enter(&p->p_lock);
- schedctl_finish_sigblock(t);
- lwp->lwp_sigoldmask = t->t_hold;
- t->t_hold = *ksetp;
- t->t_flag |= T_TOMASK;
/*
- * Call cv_reltimedwait_sig() just to check for signals.
- * We will return immediately with either 0 or -1.
+ * Since utime has an upper bound of HRTIME_MAX, adding the
+ * gethrtime() result cannot incur an overflow as the unsigned
+ * type has an adequate bound.
*/
- if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
- TR_CLOCK_TICK)) {
- mutex_exit(&p->p_lock);
- error = EINTR;
- goto pollout;
+ utime += (uhrtime_t)gethrtime();
+ if (utime > HRTIME_MAX) {
+ deadline = -1;
+ } else {
+ deadline = (hrtime_t)utime;
}
- mutex_exit(&p->p_lock);
}
/*
- * Check to see if this guy just wants to use poll() as a timeout.
+ * Check to see if the caller just wants to use poll() as a timeout.
* If yes then bypass all the other stuff and make him sleep.
*/
if (nfds == 0) {
+ *fdcnt = 0;
/*
* Sleep until we have passed the requested future
* time or until interrupted by a signal.
@@ -385,66 +424,14 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
&t->t_delay_lock, deadline)) > 0)
continue;
mutex_exit(&t->t_delay_lock);
- error = (error == 0) ? EINTR : 0;
+ return ((error == 0) ? EINTR : 0);
}
- goto pollout;
- }
-
- if (nfds > p->p_fno_ctl) {
- mutex_enter(&p->p_lock);
- (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
- p->p_rctls, p, RCA_SAFE);
- mutex_exit(&p->p_lock);
- error = EINVAL;
- goto pollout;
- }
-
- /*
- * Need to allocate memory for pollstate before anything because
- * the mutex and cv are created in this space
- */
- ps = pollstate_create();
-
- if (ps->ps_pcache == NULL)
- ps->ps_pcache = pcache_alloc();
- pcp = ps->ps_pcache;
-
- /*
- * NOTE: for performance, buffers are saved across poll() calls.
- * The theory is that if a process polls heavily, it tends to poll
- * on the same set of descriptors. Therefore, we only reallocate
- * buffers when nfds changes. There is no hysteresis control,
- * because there is no data to suggest that this is necessary;
- * the penalty of reallocating is not *that* great in any event.
- */
- old_nfds = ps->ps_nfds;
- if (nfds != old_nfds) {
-
- kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
- pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
- ps->ps_pollfd = pollfdp;
- ps->ps_nfds = nfds;
+ return (0);
}
+ VERIFY(ps != NULL);
pollfdp = ps->ps_pollfd;
- if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
- error = EFAULT;
- goto pollout;
- }
-
- if (fds == NULL) {
- /*
- * If the process has page 0 mapped, then the copyin() above
- * will succeed even if fds is NULL. However, our cached
- * poll lists are keyed by the address of the passed-in fds
- * structure, and we use the value NULL to indicate an unused
- * poll cache list entry. As such, we elect not to support
- * NULL as a valid (user) memory address and fail the poll()
- * call.
- */
- error = EINVAL;
- goto pollout;
- }
+ VERIFY(pollfdp != NULL);
/*
* If this thread polls for the first time, allocate ALL poll
@@ -460,10 +447,10 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
/*
* poll and cache this poll fd list in ps_pcacheset[0].
*/
- error = pcacheset_cache_list(ps, fds, &fdcnt, cacheindex);
- if (fdcnt || error) {
+ error = pcacheset_cache_list(ps, fds, fdcnt, cacheindex);
+ if (error || *fdcnt) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
} else {
pollcacheset_t *pcset = ps->ps_pcacheset;
@@ -488,11 +475,11 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
* the callee will guarantee the consistency
* of cached poll list and cache content.
*/
- error = pcacheset_resolve(ps, nfds, &fdcnt,
+ error = pcacheset_resolve(ps, nfds, fdcnt,
cacheindex);
if (error) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
break;
}
@@ -509,11 +496,11 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
* found an unused entry. Use it to cache
* this poll list.
*/
- error = pcacheset_cache_list(ps, fds, &fdcnt,
+ error = pcacheset_cache_list(ps, fds, fdcnt,
cacheindex);
- if (fdcnt || error) {
+ if (error || *fdcnt) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
break;
}
@@ -527,10 +514,10 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
cacheindex = pcacheset_replace(ps);
ASSERT(cacheindex < ps->ps_nsets);
pcset[cacheindex].pcs_usradr = (uintptr_t)fds;
- error = pcacheset_resolve(ps, nfds, &fdcnt, cacheindex);
+ error = pcacheset_resolve(ps, nfds, fdcnt, cacheindex);
if (error) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
}
}
@@ -548,8 +535,8 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
mutex_enter(&pcp->pc_lock);
for (;;) {
pcp->pc_flag = 0;
- error = pcache_poll(pollfdp, ps, nfds, &fdcnt, cacheindex);
- if (fdcnt || error) {
+ error = pcache_poll(pollfdp, ps, nfds, fdcnt, cacheindex);
+ if (error || *fdcnt) {
mutex_exit(&pcp->pc_lock);
mutex_exit(&ps->ps_lock);
break;
@@ -595,13 +582,116 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
mutex_enter(&pcp->pc_lock);
}
+ return (error);
+}
+
+/*
+ * This is the system call trap that poll(),
+ * select() and pselect() are built upon.
+ * It is a private interface between libc and the kernel.
+ */
+int
+pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ timespec_t ts;
+ timespec_t *tsp;
+ k_sigset_t kset;
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL;
+ int error = 0, fdcnt = 0;
+
+ /*
+ * Copy in timeout
+ */
+ if (timeoutp == NULL) {
+ tsp = NULL;
+ } else {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+
+ /*
+ * Copy in and reset signal mask, if requested.
+ */
+ if (setp != NULL) {
+ sigset_t set;
+
+ if (copyin(setp, &set, sizeof (set)))
+ return (set_errno(EFAULT));
+ sigutok(&set, &kset);
+
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = kset;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto pollout;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ * If nfds == 0, we will skip all of the copying and check steps and
+ * proceed directly into poll_common to process the supplied timeout.
+ */
+ if (nfds != 0) {
+ if (nfds > p->p_fno_ctl) {
+ mutex_enter(&p->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+ p->p_rctls, p, RCA_SAFE);
+ mutex_exit(&p->p_lock);
+ error = EINVAL;
+ goto pollout;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ if ((error = poll_copyin(ps, fds, nfds)) != 0)
+ goto pollout;
+ pollfdp = ps->ps_pollfd;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
pollout:
/*
- * If we changed the signal mask but we received
- * no signal then restore the signal mask.
- * Otherwise psig() will deal with the signal mask.
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
*/
- if (ksetp != NULL) {
+ if (setp != NULL) {
mutex_enter(&p->p_lock);
if (lwp->lwp_cursig == 0) {
t->t_hold = lwp->lwp_sigoldmask;
@@ -612,12 +702,10 @@ pollout:
if (error)
return (set_errno(error));
-
/*
* Copy out the events and return the fdcnt to the user.
*/
- if (nfds != 0 &&
- copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
+ if (nfds != 0 && copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
return (set_errno(EFAULT));
#ifdef DEBUG
@@ -625,7 +713,7 @@ pollout:
* Another sanity check:
*/
if (fdcnt) {
- int reventcnt = 0;
+ int i, reventcnt = 0;
for (i = 0; i < nfds; i++) {
if (pollfdp[i].fd < 0) {
@@ -638,6 +726,8 @@ pollout:
}
ASSERT(fdcnt == reventcnt);
} else {
+ int i;
+
for (i = 0; i < nfds; i++) {
ASSERT(pollfdp[i].revents == 0);
}
@@ -648,52 +738,6 @@ pollout:
}
/*
- * This is the system call trap that poll(),
- * select() and pselect() are built upon.
- * It is a private interface between libc and the kernel.
- */
-int
-pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
-{
- timespec_t ts;
- timespec_t *tsp;
- sigset_t set;
- k_sigset_t kset;
- k_sigset_t *ksetp;
- model_t datamodel = get_udatamodel();
-
- if (timeoutp == NULL)
- tsp = NULL;
- else {
- if (datamodel == DATAMODEL_NATIVE) {
- if (copyin(timeoutp, &ts, sizeof (ts)))
- return (set_errno(EFAULT));
- } else {
- timespec32_t ts32;
-
- if (copyin(timeoutp, &ts32, sizeof (ts32)))
- return (set_errno(EFAULT));
- TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
- }
-
- if (itimerspecfix(&ts))
- return (set_errno(EINVAL));
- tsp = &ts;
- }
-
- if (setp == NULL)
- ksetp = NULL;
- else {
- if (copyin(setp, &set, sizeof (set)))
- return (set_errno(EFAULT));
- sigutok(&set, &kset);
- ksetp = &kset;
- }
-
- return (poll_common(fds, nfds, tsp, ksetp));
-}
-
-/*
* Clean up any state left around by poll(2). Called when a thread exits.
*/
void
diff --git a/usr/src/uts/common/syscall/rusagesys.c b/usr/src/uts/common/syscall/rusagesys.c
index 3e0e63f4c0..417c629168 100644
--- a/usr/src/uts/common/syscall/rusagesys.c
+++ b/usr/src/uts/common/syscall/rusagesys.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
/*
@@ -257,6 +258,19 @@ rusagesys(int code, void *arg1, void *arg2, void *arg3, void *arg4)
case _RUSAGESYS_GETVMUSAGE:
return (vm_getusage((uint_t)(uintptr_t)arg1, (time_t)arg2,
(vmusage_t *)arg3, (size_t *)arg4, 0));
+ case _RUSAGESYS_INVALMAP:
+ /*
+ * SPARC sfmmu hat does not support HAT_CURPROC_PGUNLOAD
+ * handling so callers on SPARC should get simple sync
+ * handling with invalidation to all processes.
+ */
+#if defined(__sparc)
+ return (memcntl((caddr_t)arg2, (size_t)arg3, MC_SYNC,
+ (caddr_t)(MS_ASYNC | MS_INVALIDATE), 0, 0));
+#else
+ return (vm_map_inval((pid_t)(uintptr_t)arg1, (caddr_t)arg2,
+ (size_t)arg3));
+#endif
default:
return (set_errno(EINVAL));
}
diff --git a/usr/src/uts/common/syscall/rw.c b/usr/src/uts/common/syscall/rw.c
index a28894b2c9..943b7d244e 100644
--- a/usr/src/uts/common/syscall/rw.c
+++ b/usr/src/uts/common/syscall/rw.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -50,6 +50,7 @@
#include <sys/debug.h>
#include <sys/rctl.h>
#include <sys/nbmlock.h>
+#include <sys/limits.h>
#define COPYOUT_MAX_CACHE (1<<17) /* 128K */
@@ -607,19 +608,12 @@ out:
return (bcount);
}
-/*
- * XXX -- The SVID refers to IOV_MAX, but doesn't define it. Grrrr....
- * XXX -- However, SVVS expects readv() and writev() to fail if
- * XXX -- iovcnt > 16 (yes, it's hard-coded in the SVVS source),
- * XXX -- so I guess that's the "interface".
- */
-#define DEF_IOV_MAX 16
-
ssize_t
readv(int fdes, struct iovec *iovp, int iovcnt)
{
struct uio auio;
- struct iovec aiov[DEF_IOV_MAX];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
file_t *fp;
register vnode_t *vp;
struct cpu *cp;
@@ -630,9 +624,14 @@ readv(int fdes, struct iovec *iovp, int iovcnt)
u_offset_t fileoff;
int in_crit = 0;
- if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+ if (iovcnt <= 0 || iovcnt > IOV_MAX)
return (set_errno(EINVAL));
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded,
@@ -640,36 +639,63 @@ readv(int fdes, struct iovec *iovp, int iovcnt)
* of data in a single call.
*/
if (get_udatamodel() == DATAMODEL_ILP32) {
- struct iovec32 aiov32[DEF_IOV_MAX];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len;
ssize32_t count32;
- if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ if (aiovlen != 0)
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+ if (copyin(iovp, aiov32, aiov32len)) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
- if (iovlen32 < 0 || count32 < 0)
+ if (iovlen32 < 0 || count32 < 0) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EINVAL));
+ }
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+
+ if (aiovlen != 0)
+ kmem_free(aiov32, aiov32len);
} else
#endif
- if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+ if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EFAULT));
+ }
count = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
count += iovlen;
- if (iovlen < 0 || count < 0)
+ if (iovlen < 0 || count < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
+ }
}
- if ((fp = getf(fdes)) == NULL)
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EBADF));
+ }
if (((fflag = fp->f_flag) & FREAD) == 0) {
error = EBADF;
goto out;
@@ -768,6 +794,8 @@ out:
if (in_crit)
nbl_end_crit(vp);
releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
if (error)
return (set_errno(error));
return (count);
@@ -777,7 +805,8 @@ ssize_t
writev(int fdes, struct iovec *iovp, int iovcnt)
{
struct uio auio;
- struct iovec aiov[DEF_IOV_MAX];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
file_t *fp;
register vnode_t *vp;
struct cpu *cp;
@@ -788,9 +817,14 @@ writev(int fdes, struct iovec *iovp, int iovcnt)
u_offset_t fileoff;
int in_crit = 0;
- if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+ if (iovcnt <= 0 || iovcnt > IOV_MAX)
return (set_errno(EINVAL));
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded,
@@ -798,36 +832,62 @@ writev(int fdes, struct iovec *iovp, int iovcnt)
* of data in a single call.
*/
if (get_udatamodel() == DATAMODEL_ILP32) {
- struct iovec32 aiov32[DEF_IOV_MAX];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len;
ssize32_t count32;
- if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ if (aiovlen != 0)
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+ if (copyin(iovp, aiov32, aiov32len)) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen = aiov32[i].iov_len;
count32 += iovlen;
- if (iovlen < 0 || count32 < 0)
+ if (iovlen < 0 || count32 < 0) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EINVAL));
+ }
aiov[i].iov_len = iovlen;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+ if (aiovlen != 0)
+ kmem_free(aiov32, aiov32len);
} else
#endif
- if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+ if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EFAULT));
+ }
count = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
count += iovlen;
- if (iovlen < 0 || count < 0)
+ if (iovlen < 0 || count < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
+ }
}
- if ((fp = getf(fdes)) == NULL)
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EBADF));
+ }
if (((fflag = fp->f_flag) & FWRITE) == 0) {
error = EBADF;
goto out;
@@ -917,6 +977,8 @@ out:
if (in_crit)
nbl_end_crit(vp);
releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
if (error)
return (set_errno(error));
return (count);
@@ -927,7 +989,8 @@ preadv(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
off_t extended_offset)
{
struct uio auio;
- struct iovec aiov[DEF_IOV_MAX];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
file_t *fp;
register vnode_t *vp;
struct cpu *cp;
@@ -952,9 +1015,14 @@ preadv(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
int in_crit = 0;
- if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+ if (iovcnt <= 0 || iovcnt > IOV_MAX)
return (set_errno(EINVAL));
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded,
@@ -962,39 +1030,68 @@ preadv(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
* of data in a single call.
*/
if (get_udatamodel() == DATAMODEL_ILP32) {
- struct iovec32 aiov32[DEF_IOV_MAX];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len;
ssize32_t count32;
- if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ if (aiovlen != 0)
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+ if (copyin(iovp, aiov32, aiov32len)) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
- if (iovlen32 < 0 || count32 < 0)
+ if (iovlen32 < 0 || count32 < 0) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EINVAL));
+ }
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+ if (aiovlen != 0)
+ kmem_free(aiov32, aiov32len);
} else
#endif /* _SYSCALL32_IMPL */
- if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+ if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EFAULT));
+ }
count = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
count += iovlen;
- if (iovlen < 0 || count < 0)
+ if (iovlen < 0 || count < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
+ }
}
- if ((bcount = (ssize_t)count) < 0)
+ if ((bcount = (ssize_t)count) < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
- if ((fp = getf(fdes)) == NULL)
+ }
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EBADF));
+ }
if (((fflag = fp->f_flag) & FREAD) == 0) {
error = EBADF;
goto out;
@@ -1099,6 +1196,8 @@ out:
if (in_crit)
nbl_end_crit(vp);
releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
if (error)
return (set_errno(error));
return (count);
@@ -1109,7 +1208,8 @@ pwritev(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
off_t extended_offset)
{
struct uio auio;
- struct iovec aiov[DEF_IOV_MAX];
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
file_t *fp;
register vnode_t *vp;
struct cpu *cp;
@@ -1134,9 +1234,14 @@ pwritev(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
int in_crit = 0;
- if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+ if (iovcnt <= 0 || iovcnt > IOV_MAX)
return (set_errno(EINVAL));
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+
#ifdef _SYSCALL32_IMPL
/*
* 32-bit callers need to have their iovec expanded,
@@ -1144,39 +1249,68 @@ pwritev(int fdes, struct iovec *iovp, int iovcnt, off_t offset,
* of data in a single call.
*/
if (get_udatamodel() == DATAMODEL_ILP32) {
- struct iovec32 aiov32[DEF_IOV_MAX];
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len;
ssize32_t count32;
- if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ if (aiovlen != 0)
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+ if (copyin(iovp, aiov32, aiov32len)) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EFAULT));
+ }
count32 = 0;
for (i = 0; i < iovcnt; i++) {
ssize32_t iovlen32 = aiov32[i].iov_len;
count32 += iovlen32;
- if (iovlen32 < 0 || count32 < 0)
+ if (iovlen32 < 0 || count32 < 0) {
+ if (aiovlen != 0) {
+ kmem_free(aiov32, aiov32len);
+ kmem_free(aiov, aiovlen);
+ }
return (set_errno(EINVAL));
+ }
aiov[i].iov_len = iovlen32;
aiov[i].iov_base =
(caddr_t)(uintptr_t)aiov32[i].iov_base;
}
+ if (aiovlen != 0)
+ kmem_free(aiov32, aiov32len);
} else
#endif /* _SYSCALL32_IMPL */
- if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+ if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EFAULT));
+ }
count = 0;
for (i = 0; i < iovcnt; i++) {
ssize_t iovlen = aiov[i].iov_len;
count += iovlen;
- if (iovlen < 0 || count < 0)
+ if (iovlen < 0 || count < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
+ }
}
- if ((bcount = (ssize_t)count) < 0)
+ if ((bcount = (ssize_t)count) < 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EINVAL));
- if ((fp = getf(fdes)) == NULL)
+ }
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
return (set_errno(EBADF));
+ }
if (((fflag = fp->f_flag) & FWRITE) == 0) {
error = EBADF;
goto out;
@@ -1308,6 +1442,8 @@ out:
if (in_crit)
nbl_end_crit(vp);
releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
if (error)
return (set_errno(error));
return (count);
diff --git a/usr/src/uts/common/syscall/sendfile.c b/usr/src/uts/common/syscall/sendfile.c
index cb8246f584..ccceca7c6d 100644
--- a/usr/src/uts/common/syscall/sendfile.c
+++ b/usr/src/uts/common/syscall/sendfile.c
@@ -82,7 +82,7 @@ extern sotpi_info_t *sotpi_sototpi(struct sonode *);
* 64 bit kernel or 32 bit kernel. For 32 bit apps, we can't transfer
* more than 2GB of data.
*/
-int
+static int
sendvec_chunk64(file_t *fp, u_offset_t *fileoff, struct ksendfilevec64 *sfv,
int copy_cnt, ssize32_t *count)
{
@@ -343,7 +343,7 @@ sendvec_chunk64(file_t *fp, u_offset_t *fileoff, struct ksendfilevec64 *sfv,
return (0);
}
-ssize32_t
+static ssize32_t
sendvec64(file_t *fp, const struct ksendfilevec64 *vec, int sfvcnt,
size32_t *xferred, int fildes)
{
@@ -390,7 +390,7 @@ sendvec64(file_t *fp, const struct ksendfilevec64 *vec, int sfvcnt,
}
#endif
-int
+static int
sendvec_small_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv,
int copy_cnt, ssize_t total_size, int maxblk, ssize_t *count)
{
@@ -680,7 +680,7 @@ sendvec_small_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv,
}
-int
+static int
sendvec_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv,
int copy_cnt, ssize_t *count)
{
@@ -1160,6 +1160,17 @@ sendfilev(int opcode, int fildes, const struct sendfilevec *vec, int sfvcnt,
} else {
maxblk = (int)vp->v_stream->sd_maxblk;
}
+
+ /*
+ * We need to make sure that the socket that we're sending on
+ * supports sendfile behavior. sockfs doesn't know that the APIs
+ * we want to use are coming from sendfile, so we can't rely on
+ * it to check for us.
+ */
+ if ((so->so_mode & SM_SENDFILESUPP) == 0) {
+ error = EOPNOTSUPP;
+ goto err;
+ }
break;
case VREG:
break;
diff --git a/usr/src/uts/common/syscall/stat.c b/usr/src/uts/common/syscall/stat.c
index 4085104cc7..93f26121bc 100644
--- a/usr/src/uts/common/syscall/stat.c
+++ b/usr/src/uts/common/syscall/stat.c
@@ -61,7 +61,7 @@
* to VOP_GETATTR
*/
-static int
+int
cstatat_getvp(int fd, char *name, int follow, vnode_t **vp, cred_t **cred)
{
vnode_t *startvp;
diff --git a/usr/src/uts/common/syscall/sysconfig.c b/usr/src/uts/common/syscall/sysconfig.c
index 03f2fabe13..26ea859224 100644
--- a/usr/src/uts/common/syscall/sysconfig.c
+++ b/usr/src/uts/common/syscall/sysconfig.c
@@ -22,6 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -170,8 +171,8 @@ sysconfig(int which)
* even though rcapd can be used on the global zone too.
*/
if (!INGLOBALZONE(curproc) &&
- curproc->p_zone->zone_phys_mcap != 0)
- return (MIN(btop(curproc->p_zone->zone_phys_mcap),
+ curproc->p_zone->zone_phys_mem_ctl != UINT64_MAX)
+ return (MIN(btop(curproc->p_zone->zone_phys_mem_ctl),
physinstalled));
return (physinstalled);
@@ -179,26 +180,23 @@ sysconfig(int which)
case _CONFIG_AVPHYS_PAGES:
/*
* If the non-global zone has a phys. memory cap, use
- * the phys. memory cap - zone's current rss. We always
+ * the phys. memory cap - zone's rss. We always
* report the system-wide value for the global zone, even
- * though rcapd can be used on the global zone too.
+ * though memory capping can be used on the global zone too.
+ * We use the cached value for the RSS since vm_getusage()
+ * is so expensive and we don't need this value to be exact.
*/
if (!INGLOBALZONE(curproc) &&
- curproc->p_zone->zone_phys_mcap != 0) {
+ curproc->p_zone->zone_phys_mem_ctl != UINT64_MAX) {
pgcnt_t cap, rss, free;
- vmusage_t in_use;
- size_t cnt = 1;
- cap = btop(curproc->p_zone->zone_phys_mcap);
+ cap = btop(curproc->p_zone->zone_phys_mem_ctl);
if (cap > physinstalled)
return (freemem);
- if (vm_getusage(VMUSAGE_ZONE, 1, &in_use, &cnt,
- FKIOCTL) != 0)
- in_use.vmu_rss_all = 0;
- rss = btop(in_use.vmu_rss_all);
+ rss = btop(curproc->p_zone->zone_phys_mem);
/*
- * Because rcapd implements a soft cap, it is possible
+ * Because this is a soft cap, it is possible
* for rss to be temporarily over the cap.
*/
if (cap > rss)
diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c
index 2dda4001bf..68aa1a95f5 100644
--- a/usr/src/uts/common/syscall/uadmin.c
+++ b/usr/src/uts/common/syscall/uadmin.c
@@ -78,7 +78,7 @@ volatile int fastreboot_dryrun = 0;
* system with many zones.
*/
void
-killall(zoneid_t zoneid)
+killall(zoneid_t zoneid, boolean_t force)
{
proc_t *p;
@@ -108,7 +108,7 @@ killall(zoneid_t zoneid)
p->p_stat != SIDL &&
p->p_stat != SZOMB) {
mutex_enter(&p->p_lock);
- if (sigismember(&p->p_sig, SIGKILL)) {
+ if (!force && sigismember(&p->p_sig, SIGKILL)) {
mutex_exit(&p->p_lock);
p = p->p_next;
} else {
@@ -245,7 +245,7 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
*/
zone_shutdown_global();
- killall(ALL_ZONES);
+ killall(ALL_ZONES, B_FALSE);
/*
* If we are calling kadmin() from a kernel context then we
* do not release these resources.
diff --git a/usr/src/uts/common/vm/hat.h b/usr/src/uts/common/vm/hat.h
index 1d91475e38..c908a9e16c 100644
--- a/usr/src/uts/common/vm/hat.h
+++ b/usr/src/uts/common/vm/hat.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -269,7 +270,12 @@ void hat_kpm_walk(void (*)(void *, void *, size_t), void *);
* call.
*
* int hat_pageunload(pp, forceflag)
- * unload all translations attached to pp.
+ * Unload all translations attached to pp. On x86 the bulk of the work is
+ * done by hat_page_inval.
+ *
+ * void hat_page_inval(pp, pgsz, curhat)
+ * Unload translations attached to pp. If curhat is provided, only the
+ * translation for that process is unloaded, otherwise all are unloaded.
*
* uint_t hat_pagesync(pp, flags)
* get hw stats from hardware into page struct and reset hw stats
@@ -291,6 +297,7 @@ void hat_page_setattr(struct page *, uint_t);
void hat_page_clrattr(struct page *, uint_t);
uint_t hat_page_getattr(struct page *, uint_t);
int hat_pageunload(struct page *, uint_t);
+void hat_page_inval(struct page *, uint_t, struct hat *);
uint_t hat_pagesync(struct page *, uint_t);
ulong_t hat_page_getshare(struct page *);
int hat_page_checkshare(struct page *, ulong_t);
@@ -460,6 +467,7 @@ void hat_setstat(struct as *, caddr_t, size_t, uint_t);
*/
#define HAT_ADV_PGUNLOAD 0x00
#define HAT_FORCE_PGUNLOAD 0x01
+#define HAT_CURPROC_PGUNLOAD 0x02
/*
* Attributes for hat_page_*attr, hat_setstats and
diff --git a/usr/src/uts/common/vm/seg_kmem.c b/usr/src/uts/common/vm/seg_kmem.c
index 90e1b73b70..439c859d96 100644
--- a/usr/src/uts/common/vm/seg_kmem.c
+++ b/usr/src/uts/common/vm/seg_kmem.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -773,7 +774,7 @@ segkmem_capable(struct seg *seg, segcapability_t capability)
return (0);
}
-static struct seg_ops segkmem_ops = {
+struct seg_ops segkmem_ops = {
SEGKMEM_BADOP(int), /* dup */
SEGKMEM_BADOP(int), /* unmap */
SEGKMEM_BADOP(void), /* free */
diff --git a/usr/src/uts/common/vm/seg_kmem.h b/usr/src/uts/common/vm/seg_kmem.h
index 2a4ed3b2aa..3ad4202e91 100644
--- a/usr/src/uts/common/vm/seg_kmem.h
+++ b/usr/src/uts/common/vm/seg_kmem.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _VM_SEG_KMEM_H
@@ -136,6 +137,8 @@ extern size_t segkmem_kmemlp_max;
#define IS_KMEM_VA_LARGEPAGE(vaddr) \
(((vaddr) >= heap_lp_base) && ((vaddr) < heap_lp_end))
+extern struct seg_ops segkmem_ops;
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/vm/seg_umap.c b/usr/src/uts/common/vm/seg_umap.c
new file mode 100644
index 0000000000..3b4bb04f69
--- /dev/null
+++ b/usr/src/uts/common/vm/seg_umap.c
@@ -0,0 +1,453 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * VM - Kernel-to-user mapping segment
+ *
+ * The umap segment driver was primarily designed to facilitate the comm page:
+ * a portion of kernel memory shared with userspace so that certain (namely
+ * clock-related) actions could operate without making an expensive trip into
+ * the kernel.
+ *
+ * Since the initial requirements for the comm page are slim, advanced features
+ * of the segment driver such as per-page protection have been left
+ * unimplemented at this time.
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/kmem.h>
+#include <sys/lgrp.h>
+#include <sys/mman.h>
+
+#include <vm/hat.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_kmem.h>
+#include <vm/seg_umap.h>
+
+
+static boolean_t segumap_verify_safe(caddr_t, size_t);
+static int segumap_dup(struct seg *, struct seg *);
+static int segumap_unmap(struct seg *, caddr_t, size_t);
+static void segumap_free(struct seg *);
+static faultcode_t segumap_fault(struct hat *, struct seg *, caddr_t, size_t,
+ enum fault_type, enum seg_rw);
+static faultcode_t segumap_faulta(struct seg *, caddr_t);
+static int segumap_setprot(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_checkprot(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_sync(struct seg *, caddr_t, size_t, int, uint_t);
+static size_t segumap_incore(struct seg *, caddr_t, size_t, char *);
+static int segumap_lockop(struct seg *, caddr_t, size_t, int, int, ulong_t *,
+ size_t);
+static int segumap_getprot(struct seg *, caddr_t, size_t, uint_t *);
+static u_offset_t segumap_getoffset(struct seg *, caddr_t);
+static int segumap_gettype(struct seg *, caddr_t);
+static int segumap_getvp(struct seg *, caddr_t, struct vnode **);
+static int segumap_advise(struct seg *, caddr_t, size_t, uint_t);
+static void segumap_dump(struct seg *);
+static int segumap_pagelock(struct seg *, caddr_t, size_t, struct page ***,
+ enum lock_type, enum seg_rw);
+static int segumap_setpagesize(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_getmemid(struct seg *, caddr_t, memid_t *);
+static int segumap_capable(struct seg *, segcapability_t);
+
+static struct seg_ops segumap_ops = {
+ segumap_dup,
+ segumap_unmap,
+ segumap_free,
+ segumap_fault,
+ segumap_faulta,
+ segumap_setprot,
+ segumap_checkprot,
+ NULL, /* kluster: disabled */
+ NULL, /* swapout: disabled */
+ segumap_sync,
+ segumap_incore,
+ segumap_lockop,
+ segumap_getprot,
+ segumap_getoffset,
+ segumap_gettype,
+ segumap_getvp,
+ segumap_advise,
+ segumap_dump,
+ segumap_pagelock,
+ segumap_setpagesize,
+ segumap_getmemid,
+ NULL, /* getpolicy: disabled */
+ segumap_capable,
+ seg_inherit_notsup
+};
+
+
+/*
+ * Create a kernel/user-mapped segment.
+ */
+int
+segumap_create(struct seg *seg, void *argsp)
+{
+ segumap_crargs_t *a = (struct segumap_crargs *)argsp;
+ segumap_data_t *data;
+
+ ASSERT((uintptr_t)a->kaddr > _userlimit);
+
+ /*
+ * Check several aspects of the mapping request to ensure validity:
+ * - kernel pages must reside entirely in kernel space
+ * - target protection must be user-accessible
+ * - kernel address must be page-aligned
+ * - kernel address must reside inside a "safe" segment
+ */
+ if ((uintptr_t)a->kaddr <= _userlimit ||
+ ((uintptr_t)a->kaddr + seg->s_size) < (uintptr_t)a->kaddr ||
+ (a->prot & PROT_USER) == 0 ||
+ ((uintptr_t)a->kaddr & PAGEOFFSET) != 0 ||
+ !segumap_verify_safe(a->kaddr, seg->s_size)) {
+ return (EINVAL);
+ }
+
+ data = kmem_zalloc(sizeof (*data), KM_SLEEP);
+ rw_init(&data->sud_lock, NULL, RW_DEFAULT, NULL);
+ data->sud_kaddr = a->kaddr;
+ data->sud_prot = a->prot;
+
+ seg->s_ops = &segumap_ops;
+ seg->s_data = data;
+ return (0);
+}
+
+static boolean_t
+segumap_verify_safe(caddr_t kaddr, size_t len)
+{
+ struct seg *seg;
+
+ /*
+ * Presently, only pages which are backed by segkmem are allowed to be
+ * shared with userspace. This prevents nasty paging behavior with
+ * other drivers such as seg_kp. Furthermore, the backing kernel
+ * segment must completely contain the region to be mapped.
+ *
+ * Failing these checks is fatal for now since such mappings are done
+ * in a very limited context from the kernel.
+ */
+ AS_LOCK_ENTER(&kas, RW_READER);
+ seg = as_segat(&kas, kaddr);
+ VERIFY(seg != NULL);
+ VERIFY(seg->s_base + seg->s_size >= kaddr + len);
+ VERIFY(seg->s_ops == &segkmem_ops);
+ AS_LOCK_EXIT(&kas);
+
+ return (B_TRUE);
+}
+
+static int
+segumap_dup(struct seg *seg, struct seg *newseg)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+ segumap_data_t *newsud;
+
+ ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
+
+ newsud = kmem_zalloc(sizeof (segumap_data_t), KM_SLEEP);
+ rw_init(&newsud->sud_lock, NULL, RW_DEFAULT, NULL);
+ newsud->sud_kaddr = sud->sud_kaddr;
+ newsud->sud_prot = sud->sud_prot;
+
+ newseg->s_ops = seg->s_ops;
+ newseg->s_data = newsud;
+ return (0);
+}
+
+static int
+segumap_unmap(struct seg *seg, caddr_t addr, size_t len)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+ ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
+
+ /* Only allow unmap of entire segment */
+ if (addr != seg->s_base || len != seg->s_size) {
+ return (EINVAL);
+ }
+ if (sud->sud_softlockcnt != 0) {
+ return (EAGAIN);
+ }
+
+ /*
+ * Unconditionally unload the entire segment range.
+ */
+ hat_unload(seg->s_as->a_hat, addr, len, HAT_UNLOAD_UNMAP);
+
+ seg_free(seg);
+ return (0);
+}
+
+static void
+segumap_free(struct seg *seg)
+{
+ segumap_data_t *data = (segumap_data_t *)seg->s_data;
+
+ ASSERT(data != NULL);
+
+ rw_destroy(&data->sud_lock);
+ VERIFY(data->sud_softlockcnt == 0);
+ kmem_free(data, sizeof (*data));
+ seg->s_data = NULL;
+}
+
+/* ARGSUSED */
+static faultcode_t
+segumap_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
+ enum fault_type type, enum seg_rw tw)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ if (type == F_PROT) {
+ /*
+ * Since protection on the segment is fixed, there is nothing
+ * to do but report an error for protection faults.
+ */
+ return (FC_PROT);
+ } else if (type == F_SOFTUNLOCK) {
+ size_t plen = btop(len);
+
+ rw_enter(&sud->sud_lock, RW_WRITER);
+ VERIFY(sud->sud_softlockcnt >= plen);
+ sud->sud_softlockcnt -= plen;
+ rw_exit(&sud->sud_lock);
+ return (0);
+ }
+
+ ASSERT(type == F_INVAL || type == F_SOFTLOCK);
+ rw_enter(&sud->sud_lock, RW_WRITER);
+
+ if (type == F_INVAL ||
+ (type == F_SOFTLOCK && sud->sud_softlockcnt == 0)) {
+ /*
+ * Load the (entire) segment into the HAT.
+ *
+ * It's possible that threads racing into as_fault will cause
+ * seg_umap to load the same range multiple times in quick
+ * succession. Redundant hat_devload operations are safe.
+ */
+ for (uintptr_t i = 0; i < seg->s_size; i += PAGESIZE) {
+ pfn_t pfn;
+
+ pfn = hat_getpfnum(kas.a_hat, sud->sud_kaddr + i);
+ VERIFY(pfn != PFN_INVALID);
+ hat_devload(seg->s_as->a_hat, seg->s_base + i,
+ PAGESIZE, pfn, sud->sud_prot, HAT_LOAD);
+ }
+ }
+ if (type == F_SOFTLOCK) {
+ size_t nval = sud->sud_softlockcnt + btop(len);
+
+ if (sud->sud_softlockcnt >= nval) {
+ rw_exit(&sud->sud_lock);
+ return (FC_MAKE_ERR(EOVERFLOW));
+ }
+ sud->sud_softlockcnt = nval;
+ }
+
+ rw_exit(&sud->sud_lock);
+ return (0);
+}
+
+/* ARGSUSED */
+static faultcode_t
+segumap_faulta(struct seg *seg, caddr_t addr)
+{
+ /* Do nothing since asynch pagefault should not load translation. */
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
+{
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ /*
+ * The seg_umap driver does not yet allow protection to be changed.
+ */
+ return (EACCES);
+}
+
+/* ARGSUSED */
+static int
+segumap_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+ int error = 0;
+
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ rw_enter(&sud->sud_lock, RW_READER);
+ if ((sud->sud_prot & prot) != prot) {
+ error = EACCES;
+ }
+ rw_exit(&sud->sud_lock);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+segumap_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
+{
+ /* Always succeed since there are no backing store to sync */
+ return (0);
+}
+
+/* ARGSUSED */
+static size_t
+segumap_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
+{
+ size_t sz = 0;
+
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ len = (len + PAGEOFFSET) & PAGEMASK;
+ while (len > 0) {
+ *vec = 1;
+ sz += PAGESIZE;
+ vec++;
+ len -= PAGESIZE;
+ }
+ return (sz);
+}
+
+/* ARGSUSED */
+static int
+segumap_lockop(struct seg *seg, caddr_t addr, size_t len, int attr, int op,
+ ulong_t *lockmap, size_t pos)
+{
+ /* Report success since kernel pages are always in memory. */
+ return (0);
+}
+
+static int
+segumap_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+ size_t pgno;
+ uint_t prot;
+
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ rw_enter(&sud->sud_lock, RW_READER);
+ prot = sud->sud_prot;
+ rw_exit(&sud->sud_lock);
+
+ /*
+ * Reporting protection is simple since it is not tracked per-page.
+ */
+ pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
+ while (pgno > 0) {
+ protv[--pgno] = prot;
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static u_offset_t
+segumap_getoffset(struct seg *seg, caddr_t addr)
+{
+ /*
+ * To avoid leaking information about the layout of the kernel address
+ * space, always report '0' as the offset.
+ */
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_gettype(struct seg *seg, caddr_t addr)
+{
+ /*
+ * Since already-existing kernel pages are being mapped into userspace,
+ * always report the segment type as shared.
+ */
+ return (MAP_SHARED);
+}
+
+/* ARGSUSED */
+static int
+segumap_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp)
+{
+ ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+ *vpp = NULL;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
+{
+ if (behav == MADV_PURGE) {
+ /* Purge does not make sense for this mapping */
+ return (EINVAL);
+ }
+ /* Indicate success for everything else. */
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+segumap_dump(struct seg *seg)
+{
+ /*
+ * Since this is a mapping to share kernel data with userspace, nothing
+ * additional should be dumped.
+ */
+}
+
+/* ARGSUSED */
+static int
+segumap_pagelock(struct seg *seg, caddr_t addr, size_t len, struct page ***ppp,
+ enum lock_type type, enum seg_rw rw)
+{
+ return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static int
+segumap_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc)
+{
+ return (ENOTSUP);
+}
+
+static int
+segumap_getmemid(struct seg *seg, caddr_t addr, memid_t *memidp)
+{
+ segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+ memidp->val[0] = (uintptr_t)sud->sud_kaddr;
+ memidp->val[1] = (uintptr_t)(addr - seg->s_base);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_capable(struct seg *seg, segcapability_t capability)
+{
+ /* no special capablities */
+ return (0);
+}
diff --git a/usr/src/uts/common/vm/seg_umap.h b/usr/src/uts/common/vm/seg_umap.h
new file mode 100644
index 0000000000..8db23723ed
--- /dev/null
+++ b/usr/src/uts/common/vm/seg_umap.h
@@ -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 2016 Joyent, Inc.
+ */
+
+#ifndef _VM_SEG_UMAP_H
+#define _VM_SEG_UMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct segumap_crargs {
+ caddr_t kaddr;
+ uchar_t prot; /* protection */
+ uchar_t maxprot; /* maximum protection */
+} segumap_crargs_t;
+
+typedef struct segumap_data {
+ krwlock_t sud_lock;
+ caddr_t sud_kaddr;
+ uchar_t sud_prot;
+ size_t sud_softlockcnt;
+} segumap_data_t;
+
+extern int segumap_create(struct seg *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VM_SEG_UMAP_H */
diff --git a/usr/src/uts/common/vm/seg_vn.c b/usr/src/uts/common/vm/seg_vn.c
index 875dec7fe9..f143c1e464 100644
--- a/usr/src/uts/common/vm/seg_vn.c
+++ b/usr/src/uts/common/vm/seg_vn.c
@@ -7308,7 +7308,8 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
vpp = svd->vpage;
offset = svd->offset + (uintptr_t)(addr - seg->s_base);
bflags = ((flags & MS_ASYNC) ? B_ASYNC : 0) |
- ((flags & MS_INVALIDATE) ? B_INVAL : 0);
+ ((flags & MS_INVALIDATE) ? B_INVAL : 0) |
+ ((flags & MS_INVALCURPROC) ? (B_INVALCURONLY | B_INVAL) : 0);
if (attr) {
pageprot = attr & ~(SHARED|PRIVATE);
@@ -7333,11 +7334,11 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
vpp = &svd->vpage[seg_page(seg, addr)];
} else if (svd->vp && svd->amp == NULL &&
- (flags & MS_INVALIDATE) == 0) {
+ (flags & (MS_INVALIDATE | MS_INVALCURPROC)) == 0) {
/*
- * No attributes, no anonymous pages and MS_INVALIDATE flag
- * is not on, just use one big request.
+ * No attributes, no anonymous pages and MS_INVAL* flags
+ * are not on, just use one big request.
*/
err = VOP_PUTPAGE(svd->vp, (offset_t)offset, len,
bflags, svd->cred, NULL);
@@ -7389,7 +7390,7 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
* might race in and lock the page after we unlock and before
* we do the PUTPAGE, then PUTPAGE simply does nothing.
*/
- if (flags & MS_INVALIDATE) {
+ if (flags & (MS_INVALIDATE | MS_INVALCURPROC)) {
if ((pp = page_lookup(vp, off, SE_SHARED)) != NULL) {
if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
page_unlock(pp);
diff --git a/usr/src/uts/common/vm/vm_as.c b/usr/src/uts/common/vm/vm_as.c
index ea28b0814d..e6389bf694 100644
--- a/usr/src/uts/common/vm/vm_as.c
+++ b/usr/src/uts/common/vm/vm_as.c
@@ -58,6 +58,7 @@
#include <sys/debug.h>
#include <sys/tnf_probe.h>
#include <sys/vtrace.h>
+#include <sys/ddi.h>
#include <vm/hat.h>
#include <vm/as.h>
@@ -850,8 +851,7 @@ as_fault(struct hat *hat, struct as *as, caddr_t addr, size_t size,
struct seg *segsav;
int as_lock_held;
klwp_t *lwp = ttolwp(curthread);
-
-
+ zone_t *zonep = curzone;
retry:
/*
@@ -887,6 +887,22 @@ retry:
if (as == &kas)
CPU_STATS_ADDQ(CPU, vm, kernel_asflt, 1);
CPU_STATS_EXIT_K();
+ if (zonep->zone_pg_flt_delay != 0) {
+ /*
+ * The zone in which this process is running is
+ * currently over it's physical memory cap. Throttle
+ * page faults to help the user-land memory capper
+ * catch up. Note that drv_usectohz() rounds up.
+ */
+ atomic_add_64(&zonep->zone_pf_throttle, 1);
+ atomic_add_64(&zonep->zone_pf_throttle_usec,
+ zonep->zone_pg_flt_delay);
+ if (zonep->zone_pg_flt_delay < TICK_TO_USEC(1)) {
+ drv_usecwait(zonep->zone_pg_flt_delay);
+ } else {
+ delay(drv_usectohz(zonep->zone_pg_flt_delay));
+ }
+ }
break;
}
diff --git a/usr/src/uts/common/vm/vm_pvn.c b/usr/src/uts/common/vm/vm_pvn.c
index 1b8d12eb8d..a206320a30 100644
--- a/usr/src/uts/common/vm/vm_pvn.c
+++ b/usr/src/uts/common/vm/vm_pvn.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -432,7 +433,14 @@ pvn_write_done(page_t *plist, int flags)
page_io_unlock(pp);
page_unlock(pp);
}
- } else if (flags & B_INVAL) {
+ } else if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
+ /*
+ * If B_INVALCURONLY is set, then we handle that case
+ * in the next conditional if hat_page_is_mapped()
+ * indicates that there are no additional mappings
+ * to the page.
+ */
+
/*
* XXX - Failed writes with B_INVAL set are
* not handled appropriately.
@@ -573,8 +581,9 @@ pvn_write_done(page_t *plist, int flags)
}
/*
- * Flags are composed of {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED, B_DELWRI,
- * B_TRUNC, B_FORCE}. B_DELWRI indicates that this page is part of a kluster
+ * Flags are composed of {B_ASYNC, B_INVAL, B_INVALCURONLY, B_FREE,
+ * B_DONTNEED, B_DELWRI, B_TRUNC, B_FORCE}.
+ * B_DELWRI indicates that this page is part of a kluster
* operation and is only to be considered if it doesn't involve any
* waiting here. B_TRUNC indicates that the file is being truncated
* and so no i/o needs to be done. B_FORCE indicates that the page
@@ -628,13 +637,17 @@ pvn_getdirty(page_t *pp, int flags)
* If we want to free or invalidate the page then
* we need to unload it so that anyone who wants
* it will have to take a minor fault to get it.
+ * If we are only invalidating the page for the
+ * current process, then pass in a different flag.
* Otherwise, we're just writing the page back so we
* need to sync up the hardwre and software mod bit to
* detect any future modifications. We clear the
* software mod bit when we put the page on the dirty
* list.
*/
- if (flags & (B_INVAL | B_FREE)) {
+ if (flags & B_INVALCURONLY) {
+ (void) hat_pageunload(pp, HAT_CURPROC_PGUNLOAD);
+ } else if (flags & (B_INVAL | B_FREE)) {
(void) hat_pageunload(pp, HAT_FORCE_PGUNLOAD);
} else {
(void) hat_pagesync(pp, HAT_SYNC_ZERORM);
@@ -646,7 +659,7 @@ pvn_getdirty(page_t *pp, int flags)
* list after all.
*/
page_io_unlock(pp);
- if (flags & B_INVAL) {
+ if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
/*LINTED: constant in conditional context*/
VN_DISPOSE(pp, B_INVAL, 0, kcred);
} else if (flags & B_FREE) {
@@ -658,6 +671,9 @@ pvn_getdirty(page_t *pp, int flags)
* of VOP_PUTPAGE() who prefer freeing the
* page _only_ if no one else is accessing it.
* E.g. segmap_release()
+ * We also take this path for B_INVALCURONLY and
+ * let page_release call VN_DISPOSE if no one else is
+ * using the page.
*
* The above hat_ismod() check is useless because:
* (1) we may not be holding SE_EXCL lock;
@@ -682,7 +698,7 @@ pvn_getdirty(page_t *pp, int flags)
* We'll detect the fact that they used it when the
* i/o is done and avoid freeing the page.
*/
- if (flags & B_FREE)
+ if (flags & (B_FREE | B_INVALCURONLY))
page_downgrade(pp);
diff --git a/usr/src/uts/common/vm/vm_swap.c b/usr/src/uts/common/vm/vm_swap.c
index 1a28c04357..2a008e114b 100644
--- a/usr/src/uts/common/vm/vm_swap.c
+++ b/usr/src/uts/common/vm/vm_swap.c
@@ -18,6 +18,11 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
/*
* Copyright (c) 1987, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -625,7 +630,18 @@ swapctl(int sc_cmd, void *sc_arg, int *rv)
return (0);
}
beginning:
+ mutex_enter(&swapinfo_lock);
tmp_nswapfiles = nswapfiles;
+ mutex_exit(&swapinfo_lock);
+
+ /*
+ * Return early if there are no swap entries to report:
+ */
+ if (tmp_nswapfiles < 1) {
+ *rv = 0;
+ return (0);
+ }
+
/* Return an error if not enough space for the whole table. */
if (length < tmp_nswapfiles)
return (ENOMEM);
@@ -920,7 +936,18 @@ swapctl32(int sc_cmd, void *sc_arg, int *rv)
return (0);
}
beginning:
+ mutex_enter(&swapinfo_lock);
tmp_nswapfiles = nswapfiles;
+ mutex_exit(&swapinfo_lock);
+
+ /*
+ * Return early if there are no swap entries to report:
+ */
+ if (tmp_nswapfiles < 1) {
+ *rv = 0;
+ return (0);
+ }
+
/* Return an error if not enough space for the whole table. */
if (length < tmp_nswapfiles)
return (ENOMEM);
diff --git a/usr/src/uts/common/vm/vm_usage.c b/usr/src/uts/common/vm/vm_usage.c
index 57166b4e63..8b9fd0d7a3 100644
--- a/usr/src/uts/common/vm/vm_usage.c
+++ b/usr/src/uts/common/vm/vm_usage.c
@@ -25,6 +25,10 @@
*/
/*
+ * Copyright 2016, Joyent, Inc.
+ */
+
+/*
* vm_usage
*
* This file implements the getvmusage() private system call.
@@ -114,7 +118,7 @@
* For accurate counting of map-shared and COW-shared pages.
*
* - visited private anons (refcnt > 1) for each collective.
- * (entity->vme_anon_hash)
+ * (entity->vme_anon)
* For accurate counting of COW-shared pages.
*
* The common accounting structure is the vmu_entity_t, which represents
@@ -152,6 +156,7 @@
#include <sys/vm_usage.h>
#include <sys/zone.h>
#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
#include <sys/avl.h>
#include <vm/anon.h>
#include <vm/as.h>
@@ -199,6 +204,14 @@ typedef struct vmu_object {
} vmu_object_t;
/*
+ * Node for tree of visited COW anons.
+ */
+typedef struct vmu_anon {
+ avl_node_t vma_node;
+ uintptr_t vma_addr;
+} vmu_anon_t;
+
+/*
* Entity by which to count results.
*
* The entity structure keeps the current rss/swap counts for each entity
@@ -221,7 +234,7 @@ typedef struct vmu_entity {
struct vmu_entity *vme_next_calc;
mod_hash_t *vme_vnode_hash; /* vnodes visited for entity */
mod_hash_t *vme_amp_hash; /* shared amps visited for entity */
- mod_hash_t *vme_anon_hash; /* COW anons visited for entity */
+ avl_tree_t vme_anon; /* COW anons visited for entity */
vmusage_t vme_result; /* identifies entity and results */
} vmu_entity_t;
@@ -324,6 +337,23 @@ bounds_cmp(const void *bnd1, const void *bnd2)
}
/*
+ * Comparison routine for our AVL tree of anon structures.
+ */
+static int
+vmu_anon_cmp(const void *lhs, const void *rhs)
+{
+ const vmu_anon_t *l = lhs, *r = rhs;
+
+ if (l->vma_addr == r->vma_addr)
+ return (0);
+
+ if (l->vma_addr < r->vma_addr)
+ return (-1);
+
+ return (1);
+}
+
+/*
* Save a bound on the free list.
*/
static void
@@ -363,13 +393,18 @@ static void
vmu_free_entity(mod_hash_val_t val)
{
vmu_entity_t *entity = (vmu_entity_t *)val;
+ vmu_anon_t *anon;
+ void *cookie = NULL;
if (entity->vme_vnode_hash != NULL)
i_mod_hash_clear_nosync(entity->vme_vnode_hash);
if (entity->vme_amp_hash != NULL)
i_mod_hash_clear_nosync(entity->vme_amp_hash);
- if (entity->vme_anon_hash != NULL)
- i_mod_hash_clear_nosync(entity->vme_anon_hash);
+
+ while ((anon = avl_destroy_nodes(&entity->vme_anon, &cookie)) != NULL)
+ kmem_free(anon, sizeof (vmu_anon_t));
+
+ avl_destroy(&entity->vme_anon);
entity->vme_next = vmu_data.vmu_free_entities;
vmu_data.vmu_free_entities = entity;
@@ -485,10 +520,10 @@ vmu_alloc_entity(id_t id, int type, id_t zoneid)
"vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object,
sizeof (struct anon_map));
- if (entity->vme_anon_hash == NULL)
- entity->vme_anon_hash = mod_hash_create_ptrhash(
- "vmusage anon hash", VMUSAGE_HASH_SIZE,
- mod_hash_null_valdtor, sizeof (struct anon));
+ VERIFY(avl_first(&entity->vme_anon) == NULL);
+
+ avl_create(&entity->vme_anon, vmu_anon_cmp, sizeof (struct vmu_anon),
+ offsetof(struct vmu_anon, vma_node));
entity->vme_next = vmu_data.vmu_entities;
vmu_data.vmu_entities = entity;
@@ -518,7 +553,8 @@ vmu_alloc_zone(id_t id)
zone->vmz_id = id;
- if ((vmu_data.vmu_calc_flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) != 0)
+ if ((vmu_data.vmu_calc_flags &
+ (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE)) != 0)
zone->vmz_zone = vmu_alloc_entity(id, VMUSAGE_ZONE, id);
if ((vmu_data.vmu_calc_flags & (VMUSAGE_PROJECTS |
@@ -613,21 +649,19 @@ vmu_find_insert_object(mod_hash_t *hash, caddr_t key, uint_t type)
}
static int
-vmu_find_insert_anon(mod_hash_t *hash, caddr_t key)
+vmu_find_insert_anon(vmu_entity_t *entity, void *key)
{
- int ret;
- caddr_t val;
+ vmu_anon_t anon, *ap;
- ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key,
- (mod_hash_val_t *)&val);
+ anon.vma_addr = (uintptr_t)key;
- if (ret == 0)
+ if (avl_find(&entity->vme_anon, &anon, NULL) != NULL)
return (0);
- ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key,
- (mod_hash_val_t)key, (mod_hash_hndl_t)0);
+ ap = kmem_alloc(sizeof (vmu_anon_t), KM_SLEEP);
+ ap->vma_addr = (uintptr_t)key;
- ASSERT(ret == 0);
+ avl_add(&entity->vme_anon, ap);
return (1);
}
@@ -918,6 +952,8 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
next = AVL_NEXT(tree, next);
continue;
}
+
+ ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
bound_type = next->vmb_type;
index = next->vmb_start;
while (index <= next->vmb_end) {
@@ -937,7 +973,10 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
if (ap != NULL && vn != NULL && vn->v_pages != NULL &&
(page = page_exists(vn, off)) != NULL) {
- page_type = VMUSAGE_BOUND_INCORE;
+ if (PP_ISFREE(page))
+ page_type = VMUSAGE_BOUND_NOT_INCORE;
+ else
+ page_type = VMUSAGE_BOUND_INCORE;
if (page->p_szc > 0) {
pgcnt = page_get_pagecnt(page->p_szc);
pgshft = page_get_shift(page->p_szc);
@@ -947,8 +986,10 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
} else {
page_type = VMUSAGE_BOUND_NOT_INCORE;
}
+
if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
next->vmb_type = page_type;
+ bound_type = page_type;
} else if (next->vmb_type != page_type) {
/*
* If current bound type does not match page
@@ -1009,6 +1050,7 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
continue;
}
+ ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
bound_type = next->vmb_type;
index = next->vmb_start;
while (index <= next->vmb_end) {
@@ -1024,7 +1066,10 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
if (vnode->v_pages != NULL &&
(page = page_exists(vnode, ptob(index))) != NULL) {
- page_type = VMUSAGE_BOUND_INCORE;
+ if (PP_ISFREE(page))
+ page_type = VMUSAGE_BOUND_NOT_INCORE;
+ else
+ page_type = VMUSAGE_BOUND_INCORE;
if (page->p_szc > 0) {
pgcnt = page_get_pagecnt(page->p_szc);
pgshft = page_get_shift(page->p_szc);
@@ -1034,8 +1079,10 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
} else {
page_type = VMUSAGE_BOUND_NOT_INCORE;
}
+
if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
next->vmb_type = page_type;
+ bound_type = page_type;
} else if (next->vmb_type != page_type) {
/*
* If current bound type does not match page
@@ -1304,6 +1351,12 @@ vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg)
}
/*
+ * Pages on the free list aren't counted for the rss.
+ */
+ if (PP_ISFREE(page))
+ continue;
+
+ /*
* Assume anon structs with a refcnt
* of 1 are not COW shared, so there
* is no reason to track them per entity.
@@ -1320,8 +1373,7 @@ vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg)
* Track COW anons per entity so
* they are not double counted.
*/
- if (vmu_find_insert_anon(entity->vme_anon_hash,
- (caddr_t)ap) == 0)
+ if (vmu_find_insert_anon(entity, ap) == 0)
continue;
result->vmu_rss_all += (pgcnt << PAGESHIFT);
@@ -1461,8 +1513,9 @@ vmu_calculate_proc(proc_t *p)
entities = tmp;
}
if (vmu_data.vmu_calc_flags &
- (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS |
- VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
+ (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE |
+ VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
+ VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS |
VMUSAGE_ALL_EUSERS)) {
ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash,
@@ -1594,8 +1647,7 @@ vmu_free_extra()
mod_hash_destroy_hash(te->vme_vnode_hash);
if (te->vme_amp_hash != NULL)
mod_hash_destroy_hash(te->vme_amp_hash);
- if (te->vme_anon_hash != NULL)
- mod_hash_destroy_hash(te->vme_anon_hash);
+ VERIFY(avl_first(&te->vme_anon) == NULL);
kmem_free(te, sizeof (vmu_entity_t));
}
while (vmu_data.vmu_free_zones != NULL) {
@@ -1739,12 +1791,34 @@ vmu_cache_rele(vmu_cache_t *cache)
}
/*
+ * When new data is calculated, update the phys_mem rctl usage value in the
+ * zones.
+ */
+static void
+vmu_update_zone_rctls(vmu_cache_t *cache)
+{
+ vmusage_t *rp;
+ size_t i = 0;
+ zone_t *zp;
+
+ for (rp = cache->vmc_results; i < cache->vmc_nresults; rp++, i++) {
+ if (rp->vmu_type == VMUSAGE_ZONE &&
+ rp->vmu_zoneid != ALL_ZONES) {
+ if ((zp = zone_find_by_id(rp->vmu_zoneid)) != NULL) {
+ zp->zone_phys_mem = rp->vmu_rss_all;
+ zone_rele(zp);
+ }
+ }
+ }
+}
+
+/*
* Copy out the cached results to a caller. Inspect the callers flags
* and zone to determine which cached results should be copied.
*/
static int
vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
- uint_t flags, int cpflg)
+ uint_t flags, id_t req_zone_id, int cpflg)
{
vmusage_t *result, *out_result;
vmusage_t dummy;
@@ -1763,7 +1837,7 @@ vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
/* figure out what results the caller is interested in. */
if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone)
types |= VMUSAGE_SYSTEM;
- if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES))
+ if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE))
types |= VMUSAGE_ZONE;
if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
VMUSAGE_COL_PROJECTS))
@@ -1826,26 +1900,33 @@ vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
continue;
}
- /* Skip "other zone" results if not requested */
- if (result->vmu_zoneid != curproc->p_zone->zone_id) {
- if (result->vmu_type == VMUSAGE_ZONE &&
- (flags & VMUSAGE_ALL_ZONES) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_PROJECTS &&
- (flags & (VMUSAGE_ALL_PROJECTS |
- VMUSAGE_COL_PROJECTS)) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_TASKS &&
- (flags & VMUSAGE_ALL_TASKS) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_RUSERS &&
- (flags & (VMUSAGE_ALL_RUSERS |
- VMUSAGE_COL_RUSERS)) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_EUSERS &&
- (flags & (VMUSAGE_ALL_EUSERS |
- VMUSAGE_COL_EUSERS)) == 0)
+ if (result->vmu_type == VMUSAGE_ZONE &&
+ flags & VMUSAGE_A_ZONE) {
+ /* Skip non-requested zone results */
+ if (result->vmu_zoneid != req_zone_id)
continue;
+ } else {
+ /* Skip "other zone" results if not requested */
+ if (result->vmu_zoneid != curproc->p_zone->zone_id) {
+ if (result->vmu_type == VMUSAGE_ZONE &&
+ (flags & VMUSAGE_ALL_ZONES) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_PROJECTS &&
+ (flags & (VMUSAGE_ALL_PROJECTS |
+ VMUSAGE_COL_PROJECTS)) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_TASKS &&
+ (flags & VMUSAGE_ALL_TASKS) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_RUSERS &&
+ (flags & (VMUSAGE_ALL_RUSERS |
+ VMUSAGE_COL_RUSERS)) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_EUSERS &&
+ (flags & (VMUSAGE_ALL_EUSERS |
+ VMUSAGE_COL_EUSERS)) == 0)
+ continue;
+ }
}
count++;
if (out_result != NULL) {
@@ -1901,10 +1982,12 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
int cacherecent = 0;
hrtime_t now;
uint_t flags_orig;
+ id_t req_zone_id;
/*
* Non-global zones cannot request system wide and/or collated
- * results, or the system result, so munge the flags accordingly.
+ * results, or the system result, or usage of another zone, so munge
+ * the flags accordingly.
*/
flags_orig = flags;
if (curproc->p_zone != global_zone) {
@@ -1924,6 +2007,10 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
flags &= ~VMUSAGE_SYSTEM;
flags |= VMUSAGE_ZONE;
}
+ if (flags & VMUSAGE_A_ZONE) {
+ flags &= ~VMUSAGE_A_ZONE;
+ flags |= VMUSAGE_ZONE;
+ }
}
/* Check for unknown flags */
@@ -1934,6 +2021,21 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
if ((flags & VMUSAGE_MASK) == 0)
return (set_errno(EINVAL));
+ /* If requesting results for a specific zone, get the zone ID */
+ if (flags & VMUSAGE_A_ZONE) {
+ size_t bufsize;
+ vmusage_t zreq;
+
+ if (ddi_copyin((caddr_t)nres, &bufsize, sizeof (size_t), cpflg))
+ return (set_errno(EFAULT));
+ /* Requested zone ID is passed in buf, so 0 len not allowed */
+ if (bufsize == 0)
+ return (set_errno(EINVAL));
+ if (ddi_copyin((caddr_t)buf, &zreq, sizeof (vmusage_t), cpflg))
+ return (set_errno(EFAULT));
+ req_zone_id = zreq.vmu_id;
+ }
+
mutex_enter(&vmu_data.vmu_lock);
now = gethrtime();
@@ -1953,7 +2055,7 @@ start:
mutex_exit(&vmu_data.vmu_lock);
ret = vmu_copyout_results(cache, buf, nres, flags_orig,
- cpflg);
+ req_zone_id, cpflg);
mutex_enter(&vmu_data.vmu_lock);
vmu_cache_rele(cache);
if (vmu_data.vmu_pending_waiters > 0)
@@ -2009,8 +2111,11 @@ start:
mutex_exit(&vmu_data.vmu_lock);
+ /* update zone's phys. mem. rctl usage */
+ vmu_update_zone_rctls(cache);
/* copy cache */
- ret = vmu_copyout_results(cache, buf, nres, flags_orig, cpflg);
+ ret = vmu_copyout_results(cache, buf, nres, flags_orig,
+ req_zone_id, cpflg);
mutex_enter(&vmu_data.vmu_lock);
vmu_cache_rele(cache);
mutex_exit(&vmu_data.vmu_lock);
@@ -2030,3 +2135,185 @@ start:
vmu_data.vmu_pending_waiters--;
goto start;
}
+
+#if defined(__x86)
+/*
+ * Attempt to invalidate all of the pages in the mapping for the given process.
+ */
+static void
+map_inval(proc_t *p, struct seg *seg, caddr_t addr, size_t size)
+{
+ page_t *pp;
+ size_t psize;
+ u_offset_t off;
+ caddr_t eaddr;
+ struct vnode *vp;
+ struct segvn_data *svd;
+ struct hat *victim_hat;
+
+ ASSERT((addr + size) <= (seg->s_base + seg->s_size));
+
+ victim_hat = p->p_as->a_hat;
+ svd = (struct segvn_data *)seg->s_data;
+ vp = svd->vp;
+ psize = page_get_pagesize(seg->s_szc);
+
+ off = svd->offset + (uintptr_t)(addr - seg->s_base);
+
+ for (eaddr = addr + size; addr < eaddr; addr += psize, off += psize) {
+ pp = page_lookup_nowait(vp, off, SE_SHARED);
+
+ if (pp != NULL) {
+ /* following logic based on pvn_getdirty() */
+
+ if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
+ page_unlock(pp);
+ continue;
+ }
+
+ page_io_lock(pp);
+ hat_page_inval(pp, 0, victim_hat);
+ page_io_unlock(pp);
+
+ /*
+ * For B_INVALCURONLY-style handling we let
+ * page_release call VN_DISPOSE if no one else is using
+ * the page.
+ *
+ * A hat_ismod() check would be useless because:
+ * (1) we are not be holding SE_EXCL lock
+ * (2) we've not unloaded _all_ translations
+ *
+ * Let page_release() do the heavy-lifting.
+ */
+ (void) page_release(pp, 1);
+ }
+ }
+}
+
+/*
+ * vm_map_inval()
+ *
+ * Invalidate as many pages as possible within the given mapping for the given
+ * process. addr is expected to be the base address of the mapping and size is
+ * the length of the mapping. In some cases a mapping will encompass an
+ * entire segment, but at least for anon or stack mappings, these will be
+ * regions within a single large segment. Thus, the invalidation is oriented
+ * around a single mapping and not an entire segment.
+ *
+ * SPARC sfmmu hat does not support HAT_CURPROC_PGUNLOAD-style handling so
+ * this code is only applicable to x86.
+ */
+int
+vm_map_inval(pid_t pid, caddr_t addr, size_t size)
+{
+ int ret;
+ int error = 0;
+ proc_t *p; /* target proc */
+ struct as *as; /* target proc's address space */
+ struct seg *seg; /* working segment */
+
+ if (curproc->p_zone != global_zone || crgetruid(curproc->p_cred) != 0)
+ return (set_errno(EPERM));
+
+ /* If not a valid mapping address, return an error */
+ if ((caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK) != addr)
+ return (set_errno(EINVAL));
+
+again:
+ mutex_enter(&pidlock);
+ p = prfind(pid);
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ return (set_errno(ESRCH));
+ }
+
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if (panicstr != NULL) {
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ as = p->p_as;
+
+ /*
+ * Try to set P_PR_LOCK - prevents process "changing shape"
+ * - blocks fork
+ * - blocks sigkill
+ * - cannot be a system proc
+ * - must be fully created proc
+ */
+ ret = sprtrylock_proc(p);
+ if (ret == -1) {
+ /* Process in invalid state */
+ mutex_exit(&p->p_lock);
+ return (set_errno(ESRCH));
+ }
+
+ if (ret == 1) {
+ /*
+ * P_PR_LOCK is already set. Wait and try again. This also
+ * drops p_lock so p may no longer be valid since the proc may
+ * have exited.
+ */
+ sprwaitlock_proc(p);
+ goto again;
+ }
+
+ /* P_PR_LOCK is now set */
+ mutex_exit(&p->p_lock);
+
+ AS_LOCK_ENTER(as, RW_READER);
+ if ((seg = as_segat(as, addr)) == NULL) {
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+ return (set_errno(ENOMEM));
+ }
+
+ /*
+ * The invalidation behavior only makes sense for vnode-backed segments.
+ */
+ if (seg->s_ops != &segvn_ops) {
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+ return (0);
+ }
+
+ /*
+ * If the mapping is out of bounds of the segement return an error.
+ */
+ if ((addr + size) > (seg->s_base + seg->s_size)) {
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Don't use MS_INVALCURPROC flag here since that would eventually
+ * initiate hat invalidation based on curthread. Since we're doing this
+ * on behalf of a different process, that would erroneously invalidate
+ * our own process mappings.
+ */
+ error = SEGOP_SYNC(seg, addr, size, 0, (uint_t)MS_ASYNC);
+ if (error == 0) {
+ /*
+ * Since we didn't invalidate during the sync above, we now
+ * try to invalidate all of the pages in the mapping.
+ */
+ map_inval(p, seg, addr, size);
+ }
+ AS_LOCK_EXIT(as);
+
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+
+ if (error)
+ (void) set_errno(error);
+ return (error);
+}
+#endif
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index 9829939b16..f118c9e331 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -23,6 +23,7 @@
# Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
#
# Copyright (c) 2010, Intel Corporation.
+# Copyright 2016 Joyent, Inc.
#
# This Makefile defines file modules in the directory uts/i86pc
# and its children. These are the source files which are i86pc
@@ -40,6 +41,7 @@ CORE_OBJS += \
cmi.o \
cmi_hw.o \
cms.o \
+ comm_page.o \
confunix.o \
cpu_idle.o \
cpuid.o \
diff --git a/usr/src/uts/i86pc/acpi_drv/Makefile b/usr/src/uts/i86pc/acpi_drv/Makefile
index b1aafef9b2..ce5eb13317 100644
--- a/usr/src/uts/i86pc/acpi_drv/Makefile
+++ b/usr/src/uts/i86pc/acpi_drv/Makefile
@@ -23,7 +23,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the acpi_drv
@@ -63,6 +63,8 @@ $(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS)
CPPFLAGS += -DSUNDDI
+CERRWARN += -_gcc=-Wno-unused-function
+
LDFLAGS += -dy -N misc/acpica
#
diff --git a/usr/src/uts/i86pc/acpidev/Makefile b/usr/src/uts/i86pc/acpidev/Makefile
index ab9cbde6ee..b59e9501b1 100644
--- a/usr/src/uts/i86pc/acpidev/Makefile
+++ b/usr/src/uts/i86pc/acpidev/Makefile
@@ -24,6 +24,8 @@
# Copyright (c) 2009, Intel Corporation.
# All rights reserved.
#
+# Copyright 2016, Joyent, Inc.
+#
# This makefile drives the production of the ACPI device configuration
# kernel module.
#
@@ -61,6 +63,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
LDFLAGS += -dy -N misc/acpica
CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
CERRWARN += -_gcc=-Wno-type-limits
#
diff --git a/usr/src/uts/i86pc/acpinex/Makefile b/usr/src/uts/i86pc/acpinex/Makefile
index 0b51c74a45..5269dc4115 100644
--- a/usr/src/uts/i86pc/acpinex/Makefile
+++ b/usr/src/uts/i86pc/acpinex/Makefile
@@ -24,6 +24,8 @@
# Copyright (c) 2009, Intel Corporation.
# All rights reserved.
#
+# Copyright 2016, Joyent, Inc.
+#
# This makefile drives the production of the ACPI virtual nexus
# kernel module.
#
@@ -60,6 +62,8 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
LDFLAGS += -dy -N misc/acpica -N misc/acpidev
+CERRWARN += -_gcc=-Wno-unused-function
+
#
# Default build targets.
#
diff --git a/usr/src/uts/i86pc/acpippm/Makefile b/usr/src/uts/i86pc/acpippm/Makefile
index 65a42fdfa8..0f08acc855 100644
--- a/usr/src/uts/i86pc/acpippm/Makefile
+++ b/usr/src/uts/i86pc/acpippm/Makefile
@@ -24,6 +24,7 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2011 Bayard G. Bell. All rights reserved.
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the power managment
# module for the ACPI subsystem
@@ -58,6 +59,9 @@ ALL_TARGET = $(BINARY) $(SRC_CONFILE)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+$(OBJS_DIR)/acpippm.o := CERRWARN += -_gcc=-Wno-unused-function
+$(OBJS_DIR)/acpisleep.o := CERRWARN += -_gcc=-Wno-unused-function
+
#
# lint pass one enforcement
#
diff --git a/usr/src/uts/i86pc/amd_iommu/Makefile b/usr/src/uts/i86pc/amd_iommu/Makefile
index a90023cde9..b676a83fc2 100644
--- a/usr/src/uts/i86pc/amd_iommu/Makefile
+++ b/usr/src/uts/i86pc/amd_iommu/Makefile
@@ -21,6 +21,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This Makefile drives production of the amd_iommu driver kernel module.
#
#
@@ -58,6 +60,7 @@ LDFLAGS += -dy -Nmisc/iommulib -Nmisc/acpica
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/amd_opteron/Makefile b/usr/src/uts/i86pc/amd_opteron/Makefile
index c074a57d61..911d5ebd71 100644
--- a/usr/src/uts/i86pc/amd_opteron/Makefile
+++ b/usr/src/uts/i86pc/amd_opteron/Makefile
@@ -20,7 +20,7 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2016 Joyent, Inc.
#
#
@@ -63,6 +63,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
# Overrides and additions
#
+$(OBJS_DIR)/ao_mca.o := CERRWARN += -_gcc=-Wno-unused-function
CLEANFILES += $(AO_MCA_DISP_C)
CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR)
ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR)
diff --git a/usr/src/uts/i86pc/apix/Makefile b/usr/src/uts/i86pc/apix/Makefile
index e65d4cf1ee..18100b7ced 100644
--- a/usr/src/uts/i86pc/apix/Makefile
+++ b/usr/src/uts/i86pc/apix/Makefile
@@ -22,6 +22,7 @@
# uts/i86pc/apix/Makefile
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2016, Joyent, Inc.
#
# This makefile drives the production of the pcplusmp "mach"
# kernel module.
@@ -63,6 +64,7 @@ $(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS)
LDFLAGS += -dy -N misc/acpica
CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/cpr/Makefile b/usr/src/uts/i86pc/cpr/Makefile
index c8c403adac..a1dd7e68ee 100644
--- a/usr/src/uts/i86pc/cpr/Makefile
+++ b/usr/src/uts/i86pc/cpr/Makefile
@@ -23,6 +23,7 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2011 Bayard G. Bell. All rights reserved.
+# Copyright 2016, Joyent, Inc.
#
# This makefile drives the production of the cpr misc kernel module.
#
@@ -76,6 +77,7 @@ CERRWARN += -_gcc=-Wno-unused-variable
CERRWARN += -_gcc=-Wno-unused-label
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+$(OBJS_DIR)/cpr_impl.o := CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/cpudrv/Makefile b/usr/src/uts/i86pc/cpudrv/Makefile
index 8a59334d72..73f67a9971 100644
--- a/usr/src/uts/i86pc/cpudrv/Makefile
+++ b/usr/src/uts/i86pc/cpudrv/Makefile
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2016, Joyent, Inc.
#
# This makefile drives the production of the cpu driver
# kernel module.
@@ -57,6 +58,7 @@ CFLAGS += $(CCVERBOSE)
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Link to acpica for ACPI services
diff --git a/usr/src/uts/i86pc/dboot/dboot_startkern.c b/usr/src/uts/i86pc/dboot/dboot_startkern.c
index 7fc43e27bd..6abb7c6349 100644
--- a/usr/src/uts/i86pc/dboot/dboot_startkern.c
+++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c
@@ -63,6 +63,15 @@ extern int have_cpuid(void);
#define SHA1_ASCII_LENGTH (SHA1_DIGEST_LENGTH * 2)
/*
+ * Region of memory that may be corrupted by external actors. This can go away
+ * once the firmware bug RICHMOND-16 is fixed and all systems with the bug are
+ * upgraded.
+ */
+#define CORRUPT_REGION_START 0xc700000
+#define CORRUPT_REGION_SIZE 0x100000
+#define CORRUPT_REGION_END (CORRUPT_REGION_START + CORRUPT_REGION_SIZE)
+
+/*
* This file contains code that runs to transition us from either a multiboot
* compliant loader (32 bit non-paging) or a XPV domain loader to
* regular kernel execution. Its task is to setup the kernel memory image
@@ -1155,6 +1164,38 @@ init_mem_alloc(void)
case 1:
if (end > max_mem)
max_mem = end;
+
+ /*
+ * Well, this is sad. One some systems, there
+ * is a region of memory that can be corrupted
+ * until some number of seconds after we have
+ * booted. And the BIOS doesn't tell us that
+ * this memory is unsafe to use. And we don't
+ * know how long it's dangerous. So we'll
+ * chop out this range from any memory list
+ * that would otherwise be usable. Note that
+ * any system of this type will give us the
+ * new-style (0x40) memlist, so we need not
+ * fix up the other path below.
+ */
+ if (start < CORRUPT_REGION_START &&
+ end > CORRUPT_REGION_START) {
+ memlists[memlists_used].addr = start;
+ memlists[memlists_used].size =
+ CORRUPT_REGION_START - start;
+ ++memlists_used;
+ if (end > CORRUPT_REGION_END)
+ start = CORRUPT_REGION_END;
+ else
+ continue;
+ }
+ if (start >= CORRUPT_REGION_START &&
+ start < CORRUPT_REGION_END) {
+ if (end <= CORRUPT_REGION_END)
+ continue;
+ start = CORRUPT_REGION_END;
+ }
+
memlists[memlists_used].addr = start;
memlists[memlists_used].size = end - start;
++memlists_used;
diff --git a/usr/src/uts/i86pc/drmach_acpi/Makefile b/usr/src/uts/i86pc/drmach_acpi/Makefile
index c803d07f77..23e40f018e 100644
--- a/usr/src/uts/i86pc/drmach_acpi/Makefile
+++ b/usr/src/uts/i86pc/drmach_acpi/Makefile
@@ -25,6 +25,8 @@
# Copyright (c) 2010, Intel Corporation.
# All rights reserved.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the drmach_acpi loadable module.
#
# i86pc architecture dependent
@@ -61,6 +63,8 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
DEF_BUILDS = $(DEF_BUILDS64)
ALL_BUILDS = $(ALL_BUILDS64)
+$(OBJS_DIR)/drmach_acpi.o := CERRWARN += -_gcc=-Wno-unused-function
+
#
# lint pass one enforcement
#
diff --git a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c
index e9664c7614..ef05a80280 100644
--- a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c
+++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2016, Joyent, Inc.
*/
/*
* Copyright (c) 2009-2010, Intel Corporation.
@@ -431,7 +433,7 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
uint_t high;
ASSERT(addrp != NULL && rhdl != NULL);
- if (addrp->AddressLength == 0) {
+ if (addrp->Address.AddressLength == 0) {
return (AE_OK);
}
@@ -471,10 +473,11 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
acpidev_regspec_t reg;
reg.phys_hi = high;
- reg.phys_mid = addrp->Minimum >> 32;
- reg.phys_low = addrp->Minimum & 0xFFFFFFFF;
- reg.size_hi = addrp->AddressLength >> 32;
- reg.size_low = addrp->AddressLength & 0xFFFFFFFF;
+ reg.phys_mid = addrp->Address.Minimum >> 32;
+ reg.phys_low = addrp->Address.Minimum & 0xFFFFFFFF;
+ reg.size_hi = addrp->Address.AddressLength >> 32;
+ reg.size_low = addrp->Address.AddressLength &
+ 0xFFFFFFFF;
rc = acpidev_resource_insert_reg(rhdl, &reg);
if (ACPI_FAILURE(rc)) {
ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
@@ -487,19 +490,21 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
acpidev_ranges_t range;
range.child_hi = high;
- range.child_mid = addrp->Minimum >> 32;
- range.child_low = addrp->Minimum & 0xFFFFFFFF;
+ range.child_mid = addrp->Address.Minimum >> 32;
+ range.child_low = addrp->Address.Minimum & 0xFFFFFFFF;
/* It's IO on parent side if Translation is true. */
if (addrp->Info.Mem.Translation) {
range.parent_hi = ACPIDEV_REG_TYPE_IO;
} else {
range.parent_hi = high;
}
- paddr = addrp->Minimum + addrp->TranslationOffset;
+ paddr = addrp->Address.Minimum +
+ addrp->Address.TranslationOffset;
range.parent_mid = paddr >> 32;
range.parent_low = paddr & 0xFFFFFFFF;
- range.size_hi = addrp->AddressLength >> 32;
- range.size_low = addrp->AddressLength & 0xFFFFFFFF;
+ range.size_hi = addrp->Address.AddressLength >> 32;
+ range.size_low = addrp->Address.AddressLength &
+ 0xFFFFFFFF;
rc = acpidev_resource_insert_range(rhdl, &range);
if (ACPI_FAILURE(rc)) {
ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
@@ -539,10 +544,11 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
acpidev_regspec_t reg;
reg.phys_hi = high;
- reg.phys_mid = addrp->Minimum >> 32;
- reg.phys_low = addrp->Minimum & 0xFFFFFFFF;
- reg.size_hi = addrp->AddressLength >> 32;
- reg.size_low = addrp->AddressLength & 0xFFFFFFFF;
+ reg.phys_mid = addrp->Address.Minimum >> 32;
+ reg.phys_low = addrp->Address.Minimum & 0xFFFFFFFF;
+ reg.size_hi = addrp->Address.AddressLength >> 32;
+ reg.size_low = addrp->Address.AddressLength &
+ 0xFFFFFFFF;
rc = acpidev_resource_insert_reg(rhdl, &reg);
if (ACPI_FAILURE(rc)) {
ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
@@ -555,19 +561,21 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
acpidev_ranges_t range;
range.child_hi = high;
- range.child_mid = addrp->Minimum >> 32;
- range.child_low = addrp->Minimum & 0xFFFFFFFF;
+ range.child_mid = addrp->Address.Minimum >> 32;
+ range.child_low = addrp->Address.Minimum & 0xFFFFFFFF;
/* It's Memory on parent side if Translation is true. */
if (addrp->Info.Io.Translation) {
range.parent_hi = ACPIDEV_REG_TYPE_MEMORY;
} else {
range.parent_hi = high;
}
- paddr = addrp->Minimum + addrp->TranslationOffset;
+ paddr = addrp->Address.Minimum +
+ addrp->Address.TranslationOffset;
range.parent_mid = paddr >> 32;
range.parent_low = paddr & 0xFFFFFFFF;
- range.size_hi = addrp->AddressLength >> 32;
- range.size_low = addrp->AddressLength & 0xFFFFFFFF;
+ range.size_hi = addrp->Address.AddressLength >> 32;
+ range.size_low = addrp->Address.AddressLength &
+ 0xFFFFFFFF;
rc = acpidev_resource_insert_range(rhdl, &range);
if (ACPI_FAILURE(rc)) {
ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
@@ -583,14 +591,15 @@ acpidev_resource_address64(acpidev_resource_handle_t rhdl,
uint64_t end;
acpidev_bus_range_t bus;
- end = addrp->Minimum + addrp->AddressLength;
- if (end < addrp->Minimum || end > UINT_MAX) {
+ end = addrp->Address.Minimum +
+ addrp->Address.AddressLength;
+ if (end < addrp->Address.Minimum || end > UINT_MAX) {
ACPIDEV_DEBUG(CE_WARN, "!acpidev: bus range "
"in ADDRESS64 is invalid.");
rc = AE_ERROR;
break;
}
- bus.bus_start = addrp->Minimum & 0xFFFFFFFF;
+ bus.bus_start = addrp->Address.Minimum & 0xFFFFFFFF;
bus.bus_end = end & 0xFFFFFFFF;
ASSERT(bus.bus_start <= bus.bus_end);
rc = acpidev_resource_insert_bus(rhdl, &bus);
@@ -696,12 +705,16 @@ acpidev_resource_walk_producer(ACPI_RESOURCE *rscp, void *ctxp)
}
*(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
- addr64.Granularity = rscp->Data.ExtAddress64.Granularity;
- addr64.Minimum = rscp->Data.ExtAddress64.Minimum;
- addr64.Maximum = rscp->Data.ExtAddress64.Maximum;
- addr64.TranslationOffset =
- rscp->Data.ExtAddress64.TranslationOffset;
- addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength;
+ addr64.Address.Granularity =
+ rscp->Data.ExtAddress64.Address.Granularity;
+ addr64.Address.Minimum =
+ rscp->Data.ExtAddress64.Address.Minimum;
+ addr64.Address.Maximum =
+ rscp->Data.ExtAddress64.Address.Maximum;
+ addr64.Address.TranslationOffset =
+ rscp->Data.ExtAddress64.Address.TranslationOffset;
+ addr64.Address.AddressLength =
+ rscp->Data.ExtAddress64.Address.AddressLength;
if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
&addr64))) {
ACPIDEV_DEBUG(CE_WARN,
@@ -911,12 +924,16 @@ acpidev_resource_walk_consumer(ACPI_RESOURCE *rscp, void *ctxp)
}
*(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
- addr64.Granularity = rscp->Data.ExtAddress64.Granularity;
- addr64.Minimum = rscp->Data.ExtAddress64.Minimum;
- addr64.Maximum = rscp->Data.ExtAddress64.Maximum;
- addr64.TranslationOffset =
- rscp->Data.ExtAddress64.TranslationOffset;
- addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength;
+ addr64.Address.Granularity =
+ rscp->Data.ExtAddress64.Address.Granularity;
+ addr64.Address.Minimum =
+ rscp->Data.ExtAddress64.Address.Minimum;
+ addr64.Address.Maximum =
+ rscp->Data.ExtAddress64.Address.Maximum;
+ addr64.Address.TranslationOffset =
+ rscp->Data.ExtAddress64.Address.TranslationOffset;
+ addr64.Address.AddressLength =
+ rscp->Data.ExtAddress64.Address.AddressLength;
if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
&addr64))) {
ACPIDEV_DEBUG(CE_WARN,
diff --git a/usr/src/uts/i86pc/io/ppm/acpisleep.c b/usr/src/uts/i86pc/io/ppm/acpisleep.c
index 78328170e6..f2eb080f48 100644
--- a/usr/src/uts/i86pc/io/ppm/acpisleep.c
+++ b/usr/src/uts/i86pc/io/ppm/acpisleep.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016, Joyent, Inc.
*/
#include <sys/types.h>
@@ -86,7 +87,7 @@ acpi_enter_sleepstate(s3a_t *s3ap)
PT(PT_SWV);
/* Set waking vector */
- if (AcpiSetFirmwareWakingVector(wakephys) != AE_OK) {
+ if (AcpiSetFirmwareWakingVector(wakephys, NULL) != AE_OK) {
PT(PT_SWV_FAIL);
PMD(PMD_SX, ("Can't SetFirmwareWakingVector(%lx)\n",
(long)wakephys))
@@ -163,6 +164,11 @@ acpi_exit_sleepstate(s3a_t *s3ap)
PMD(PMD_SX, ("!We woke up!\n"))
PT(PT_LSS);
+ if (AcpiLeaveSleepStatePrep(Sx) != AE_OK) {
+ PT(PT_LSS_FAIL);
+ PMD(PMD_SX, ("Problem with LeaveSleepState!\n"))
+ }
+
if (AcpiLeaveSleepState(Sx) != AE_OK) {
PT(PT_LSS_FAIL);
PMD(PMD_SX, ("Problem with LeaveSleepState!\n"))
diff --git a/usr/src/uts/i86pc/io/psm/psm_common.c b/usr/src/uts/i86pc/io/psm/psm_common.c
index 7a3dd8a733..623c6e5617 100644
--- a/usr/src/uts/i86pc/io/psm/psm_common.c
+++ b/usr/src/uts/i86pc/io/psm/psm_common.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#include <sys/types.h>
@@ -166,7 +167,7 @@ acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, int ipin,
*/
static int
acpi_eval_lnk(dev_info_t *dip, char *lnkname, int *pci_irqp,
-iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
+ iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
{
ACPI_HANDLE tmpobj;
ACPI_HANDLE lnkobj;
diff --git a/usr/src/uts/i86pc/io/xsvc/xsvc.c b/usr/src/uts/i86pc/io/xsvc/xsvc.c
index 1d0b9741b0..de3b727544 100644
--- a/usr/src/uts/i86pc/io/xsvc/xsvc.c
+++ b/usr/src/uts/i86pc/io/xsvc/xsvc.c
@@ -22,6 +22,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/errno.h>
@@ -807,7 +809,7 @@ xsvc_mnode_key_compare(const void *q, const void *e)
/*ARGSUSED*/
static int
xsvc_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
- size_t *maplen, uint_t model)
+ size_t *maplen, uint_t model)
{
ddi_umem_cookie_t cookie;
xsvc_state_t *state;
@@ -874,6 +876,19 @@ xsvc_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
kvai = kva;
for (i = 0; i < npages; i++) {
+ page_t *pp = page_numtopp_nolock(pfn);
+
+ /*
+ * Preemptively check for panic conditions from
+ * hat_devload and error out instead.
+ */
+ if (pp != NULL && (PP_ISFREE(pp) ||
+ (!PAGE_LOCKED(pp) && !PP_ISNORELOC(pp)))) {
+ err = DDI_FAILURE;
+ npages = i;
+ goto devmapfail_cookie_alloc;
+ }
+
hat_devload(kas.a_hat, kvai, PAGESIZE, pfn,
PROT_READ | PROT_WRITE, HAT_LOAD_LOCK);
pfn++;
diff --git a/usr/src/uts/i86pc/isa/Makefile b/usr/src/uts/i86pc/isa/Makefile
index 73e7e3c617..9cfd72f370 100644
--- a/usr/src/uts/i86pc/isa/Makefile
+++ b/usr/src/uts/i86pc/isa/Makefile
@@ -23,6 +23,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the ISA nexus driver
#
# i86pc implementation architecture dependent
@@ -54,6 +56,8 @@ ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+$(OBJS_DIR)/isa.o := CERRWARN += -_gcc=-Wno-unused-function
+
#
# lint pass one enforcement
#
diff --git a/usr/src/uts/i86pc/ml/comm_page.s b/usr/src/uts/i86pc/ml/comm_page.s
new file mode 100644
index 0000000000..49d39397bf
--- /dev/null
+++ b/usr/src/uts/i86pc/ml/comm_page.s
@@ -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 2016 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/asm_misc.h>
+#include <sys/param.h>
+#include <sys/comm_page.h>
+#include <sys/tsc.h>
+
+#if defined(_GENCTF) || defined(__lint)
+
+hrtime_t tsc_last;
+hrtime_t tsc_resume_cap;
+hrtime_t tsc_hrtime_base;
+uint32_t tsc_max_delta;
+volatile uint32_t hres_lock;
+uint32_t tsc_type;
+uint32_t nsec_scale;
+int64_t hrestime_adj;
+hrtime_t hres_last_tick;
+uint32_t tsc_ncpu;
+volatile timestruc_t hrestime;
+hrtime_t tsc_sync_tick_delta[NCPU];
+
+comm_page_t comm_page;
+
+#else /* defined(_GENCTF) || defined(__lint) */
+
+#include "assym.h"
+
+/*
+ * x86 Comm Page
+ *
+ * This is the definition for the comm page on x86. The purpose of this struct
+ * is to consolidate certain pieces of kernel state into one contiguous section
+ * of memory in order for it to be exposed (read-only) to userspace. The
+ * struct contents are defined by hand so that member variables will maintain
+ * their original symbols for use throughout the rest of the kernel. This
+ * layout must exactly match the C definition of comm_page_t.
+ * See: "uts/i86pc/sys/comm_page.h"
+ */
+
+ .data
+ DGDEF3(comm_page, COMM_PAGE_S_SIZE, 4096)
+ DGDEF2(tsc_last, 8)
+ .fill 1, 8, 0
+ DGDEF2(tsc_hrtime_base, 8)
+ .fill 1, 8, 0
+ DGDEF2(tsc_resume_cap, 8)
+ .fill 1, 8, 0
+ DGDEF2(tsc_type, 4);
+ .fill 1, 4, _CONST(TSC_RDTSC_CPUID)
+ DGDEF2(tsc_max_delta, 4);
+ .fill 1, 4, 0
+ DGDEF2(hres_lock, 4);
+ .fill 1, 4, 0
+ DGDEF2(nsec_scale, 4);
+ .fill 1, 4, 0
+ DGDEF2(hrestime_adj, 8)
+ .fill 1, 8, 0
+ DGDEF2(hres_last_tick, 8)
+ .fill 1, 8, 0
+ DGDEF2(tsc_ncpu, 4)
+ .fill 1, 4, 0
+ /* _cp_pad */
+ .fill 1, 4, 0
+ DGDEF2(hrestime, _MUL(2, 8))
+ .fill 2, 8, 0
+ DGDEF2(tsc_sync_tick_delta, _MUL(NCPU, 8))
+ .fill _CONST(NCPU), 8, 0
+
+ /* pad out the rest of the page from the struct end */
+ .fill _CONST(COMM_PAGE_SIZE - COMM_PAGE_S_SIZE), 1, 0
+
+#endif /* defined(_GENCTF) || defined(__lint) */
diff --git a/usr/src/uts/i86pc/ml/offsets.in b/usr/src/uts/i86pc/ml/offsets.in
index 721d32fa3a..406d389000 100644
--- a/usr/src/uts/i86pc/ml/offsets.in
+++ b/usr/src/uts/i86pc/ml/offsets.in
@@ -1,6 +1,7 @@
\
\ Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
\ Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
+\ Copyright 2016 Joyent, Inc.
\
\ CDDL HEADER START
\
@@ -61,6 +62,7 @@
#include <sys/brand.h>
#include <sys/fastboot.h>
#include <sys/cpr_wakecode.h>
+#include <sys/comm_page.h>
proc PROCSIZE
p_link
@@ -150,6 +152,7 @@ _klwp
lwp_thread
lwp_procp
lwp_brand
+ lwp_brand_syscall
lwp_eosys
lwp_regs
lwp_arg
@@ -467,3 +470,5 @@ wc_cpu WC_CPU_SIZE
wc_wakecode
wc_cpu
+
+comm_page_s COMM_PAGE_S_SIZE
diff --git a/usr/src/uts/i86pc/ml/syscall_asm.s b/usr/src/uts/i86pc/ml/syscall_asm.s
index 61ef4ac6c3..68181be28a 100644
--- a/usr/src/uts/i86pc/ml/syscall_asm.s
+++ b/usr/src/uts/i86pc/ml/syscall_asm.s
@@ -631,6 +631,36 @@ _sysenter_done:
sysexit
SET_SIZE(sys_sysenter)
SET_SIZE(brand_sys_sysenter)
+#endif /* __lint */
+
+#if defined(__lint)
+/*
+ * System call via an int80. This entry point is only used by the Linux
+ * application environment. Unlike the sysenter path, there is no default
+ * action to take if no callback is registered for this process.
+ */
+void
+sys_int80()
+{}
+
+#else /* __lint */
+
+ ENTRY_NP(brand_sys_int80)
+ BRAND_CALLBACK(BRAND_CB_INT80)
+
+ ALTENTRY(sys_int80)
+ /*
+ * We hit an int80, but this process isn't of a brand with an int80
+ * handler. Bad process! Make it look as if the INT failed.
+ * Modify %eip to point before the INT, push the expected error
+ * code and fake a GP fault.
+ *
+ */
+ subl $2, (%esp) /* int insn 2-bytes */
+ pushl $_CONST(_MUL(T_INT80, GATE_DESC_SIZE) + 2)
+ jmp gptrap / GP fault
+ SET_SIZE(sys_int80)
+ SET_SIZE(brand_sys_int80)
/*
* Declare a uintptr_t which covers the entire pc range of syscall
diff --git a/usr/src/uts/i86pc/ml/syscall_asm_amd64.s b/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
index f26468c8cc..a4dd2722b6 100644
--- a/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
+++ b/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/asm_linkage.h>
@@ -503,6 +503,7 @@ noprod_sys_syscall:
movq T_LWP(%r15), %r14
ASSERT_NO_RUPDATE_PENDING(%r14)
+
ENABLE_INTR_FLAGS
MSTATE_TRANSITION(LMS_USER, LMS_SYSTEM)
@@ -516,6 +517,28 @@ noprod_sys_syscall:
incq %gs:CPU_STATS_SYS_SYSCALL
+ /*
+ * If our LWP has an alternate system call handler, run that instead of
+ * the regular system call path.
+ */
+ movq LWP_BRAND_SYSCALL(%r14), %rdi
+ testq %rdi, %rdi
+ jz _syscall_no_brand
+
+ pushq %rax
+ subq $8, %rsp /* align stack for call to C */
+ call *%rdi
+ addq $8, %rsp
+
+ /*
+ * If the alternate handler returns 0, we skip straight to the return to
+ * usermode. Otherwise, we resume regular system call processing.
+ */
+ testl %eax, %eax
+ popq %rax
+ jz _syscall_after_brand
+
+_syscall_no_brand:
movw %ax, T_SYSNUM(%r15)
movzbl T_PRE_SYS(%r15), %ebx
ORL_SYSCALLTRACE(%ebx)
@@ -550,6 +573,8 @@ _syscall_invoke:
shrq $32, %r13 /* upper 32-bits into %edx */
movl %r12d, %r12d /* lower 32-bits into %eax */
5:
+
+_syscall_after_brand:
/*
* Optimistically assume that there's no post-syscall
* work to do. (This is to avoid having to call syscall_mstate()
@@ -795,11 +820,37 @@ _syscall32_save:
incq %gs:CPU_STATS_SYS_SYSCALL
/*
+ * If our lwp has an alternate system call handler, run that instead
+ * of the regular system call path.
+ */
+ movq LWP_BRAND_SYSCALL(%r14), %rax
+ testq %rax, %rax
+ jz _syscall32_no_brand
+
+ movb $LWP_SYS, LWP_STATE(%r14)
+ call *%rax
+
+ /*
+ * If the alternate handler returns 0, we skip straight to the return
+ * to usermode. Otherwise, we resume regular system call processing.
+ */
+ testl %eax, %eax
+ jz _syscall32_after_brand
+
+_syscall32_no_brand:
+ /*
* Make some space for MAXSYSARGS (currently 8) 32-bit args placed
* into 64-bit (long) arg slots, maintaining 16 byte alignment. Or
* more succinctly:
*
* SA(MAXSYSARGS * sizeof (long)) == 64
+ *
+ * Note, this space is used both to copy in the arguments from user
+ * land, but also to as part of the old UNIX style syscall_ap() method.
+ * syscall_entry expects that we do not change the values of this space
+ * that we give it. However, this means that when we end up in the more
+ * recent model of passing the arguments based on the calling
+ * conventions, we'll need to save an additional 16 bytes of stack.
*/
#define SYS_DROP 64 /* drop for args */
subq $SYS_DROP, %rsp
@@ -827,12 +878,16 @@ _syscall32_save:
*/
movq %rax, %rbx
- movl 0(%rsp), %edi
- movl 8(%rsp), %esi
- movl 0x10(%rsp), %edx
- movl 0x18(%rsp), %ecx
- movl 0x20(%rsp), %r8d
- movl 0x28(%rsp), %r9d
+ movl 0x0(%rsp), %edi /* arg0 */
+ movl 0x8(%rsp), %esi /* arg1 */
+ movl 0x10(%rsp), %edx /* arg2 */
+ movl 0x38(%rsp), %eax /* arg7 load */
+ movl 0x18(%rsp), %ecx /* arg3 */
+ pushq %rax /* arg7 saved to stack */
+ movl 0x28(%rsp), %r8d /* arg4 */
+ movl 0x38(%rsp), %eax /* arg6 load */
+ movl 0x30(%rsp), %r9d /* arg5 */
+ pushq %rax /* arg6 saved to stack */
call *SY_CALLC(%rbx)
@@ -850,6 +905,8 @@ _syscall32_save:
shrq $32, %r13 /* upper 32-bits into %edx */
movl %eax, %r12d /* lower 32-bits into %eax */
+_syscall32_after_brand:
+
/*
* Optimistically assume that there's no post-syscall
* work to do. (This is to avoid having to call syscall_mstate()
@@ -1079,15 +1136,20 @@ sys_sysenter()
/*
* Fetch the arguments copied onto the kernel stack and put
* them in the right registers to invoke a C-style syscall handler.
- * %rax contains the handler address.
+ * %rax contains the handler address. For the last two arguments, we
+ * push them onto the stack -- we can't clobber the old arguments.
*/
movq %rax, %rbx
- movl 0(%rsp), %edi
- movl 8(%rsp), %esi
- movl 0x10(%rsp), %edx
- movl 0x18(%rsp), %ecx
- movl 0x20(%rsp), %r8d
- movl 0x28(%rsp), %r9d
+ movl 0x0(%rsp), %edi /* arg0 */
+ movl 0x8(%rsp), %esi /* arg1 */
+ movl 0x10(%rsp), %edx /* arg2 */
+ movl 0x38(%rsp), %eax /* arg7 load */
+ movl 0x18(%rsp), %ecx /* arg3 */
+ pushq %rax /* arg7 saved to stack */
+ movl 0x28(%rsp), %r8d /* arg4 */
+ movl 0x38(%rsp), %eax /* arg6 load */
+ movl 0x30(%rsp), %r9d /* arg5 */
+ pushq %rax /* arg6 saved to stack */
call *SY_CALLC(%rbx)
@@ -1159,6 +1221,74 @@ sys_sysenter()
SET_SIZE(brand_sys_sysenter)
#endif /* __lint */
+
+#if defined(__lint)
+/*
+ * System call via an int80. This entry point is only used by the Linux
+ * application environment. Unlike the other entry points, there is no
+ * default action to take if no callback is registered for this process.
+ */
+void
+sys_int80()
+{}
+
+#else /* __lint */
+
+ ENTRY_NP(brand_sys_int80)
+ SWAPGS /* kernel gsbase */
+ XPV_TRAP_POP
+ call smap_enable
+
+ /*
+ * We first attempt to call the "b_int80" handler from the "struct
+ * brand_mach_ops" for this brand. If no handler function is installed
+ * for this brand, the BRAND_CALLBACK() macro returns here and we
+ * check the lwp for a "lwp_brand_syscall" handler.
+ */
+ BRAND_CALLBACK(BRAND_CB_INT80, BRAND_URET_FROM_INTR_STACK())
+
+ /*
+ * Check to see if this lwp provides "lwp_brand_syscall". If so, we
+ * will route this int80 through the regular system call handling path.
+ */
+ movq %r15, %gs:CPU_RTMP_R15
+ movq %gs:CPU_THREAD, %r15
+ movq T_LWP(%r15), %r15
+ movq LWP_BRAND_SYSCALL(%r15), %r15
+ testq %r15, %r15
+ movq %gs:CPU_RTMP_R15, %r15
+ jnz nopop_syscall_int
+
+ /*
+ * The brand provided neither a "b_int80", nor a "lwp_brand_syscall"
+ * function, and has thus opted out of handling this trap.
+ */
+ SWAPGS /* user gsbase */
+ jmp nopop_int80
+
+ ENTRY_NP(sys_int80)
+ /*
+ * We hit an int80, but this process isn't of a brand with an int80
+ * handler. Bad process! Make it look as if the INT failed.
+ * Modify %rip to point before the INT, push the expected error
+ * code and fake a GP fault. Note on 64-bit hypervisor we need
+ * to undo the XPV_TRAP_POP and push rcx and r11 back on the stack
+ * because gptrap will pop them again with its own XPV_TRAP_POP.
+ */
+ XPV_TRAP_POP
+ call smap_enable
+nopop_int80:
+ subq $2, (%rsp) /* int insn 2-bytes */
+ pushq $_CONST(_MUL(T_INT80, GATE_DESC_SIZE) + 2)
+#if defined(__xpv)
+ push %r11
+ push %rcx
+#endif
+ jmp gptrap / GP fault
+ SET_SIZE(sys_int80)
+ SET_SIZE(brand_sys_int80)
+#endif /* __lint */
+
/*
* This is the destination of the "int $T_SYSCALLINT" interrupt gate, used by
diff --git a/usr/src/uts/i86pc/npe/Makefile b/usr/src/uts/i86pc/npe/Makefile
index 93d7c4a2bc..c431ce7d75 100644
--- a/usr/src/uts/i86pc/npe/Makefile
+++ b/usr/src/uts/i86pc/npe/Makefile
@@ -24,6 +24,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the PCI-E nexus driver
#
# i86pc implementation architecture dependent
@@ -80,6 +82,7 @@ LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/os/cpr_impl.c b/usr/src/uts/i86pc/os/cpr_impl.c
index 91fb583a01..f173a1dc57 100644
--- a/usr/src/uts/i86pc/os/cpr_impl.c
+++ b/usr/src/uts/i86pc/os/cpr_impl.c
@@ -753,6 +753,20 @@ i_cpr_is_supported(int sleeptype)
if (sleeptype != CPR_TORAM)
return (0);
+ /*
+ * Unfortunately, the x86 resume code was never implemented for GAS.
+ * The only obvious problem is that a trick necessary to appease Sun
+ * Studio does the wrong thing for GAS. Doubley unfortunate is that
+ * the condition used to detect GAS is incorrect, so we do in fact
+ * compile the Studio path, it just immediately fails in resume.
+ *
+ * Given that, if we were built using GCC, never allow CPR to be
+ * attempted.
+ */
+#ifdef __GNUC__
+ return (0);
+#endif
+
/*
* The next statement tests if a specific platform has turned off
* cpr support.
diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c
index dfb72dacf4..027ed29c3d 100644
--- a/usr/src/uts/i86pc/os/cpuid.c
+++ b/usr/src/uts/i86pc/os/cpuid.c
@@ -32,7 +32,7 @@
* Portions Copyright 2009 Advanced Micro Devices, Inc.
*/
/*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
* Various routines to handle identification
@@ -57,6 +57,8 @@
#include <sys/auxv_386.h>
#include <sys/memnode.h>
#include <sys/pci_cfgspace.h>
+#include <sys/comm_page.h>
+#include <sys/tsc.h>
#ifdef __xpv
#include <sys/hypervisor.h>
@@ -2826,6 +2828,13 @@ cpuid_pass4(cpu_t *cpu, uint_t *hwcap_out)
}
+ /* Detect systems with a potential CPUID limit */
+ if (cpi->cpi_vendor == X86_VENDOR_Intel && cpi->cpi_maxeax < 4) {
+ cmn_err(CE_NOTE, "CPUID limit detected, "
+ "see the CPUID(7D) man page for details\n");
+ }
+
+
if (cpi->cpi_xmaxeax < 0x80000001)
goto pass4_done;
@@ -4607,27 +4616,30 @@ patch_tsc_read(int flag)
size_t cnt;
switch (flag) {
- case X86_NO_TSC:
+ case TSC_NONE:
cnt = &_no_rdtsc_end - &_no_rdtsc_start;
(void) memcpy((void *)tsc_read, (void *)&_no_rdtsc_start, cnt);
break;
- case X86_HAVE_TSCP:
- cnt = &_tscp_end - &_tscp_start;
- (void) memcpy((void *)tsc_read, (void *)&_tscp_start, cnt);
- break;
- case X86_TSC_MFENCE:
+ case TSC_RDTSC_MFENCE:
cnt = &_tsc_mfence_end - &_tsc_mfence_start;
(void) memcpy((void *)tsc_read,
(void *)&_tsc_mfence_start, cnt);
break;
- case X86_TSC_LFENCE:
+ case TSC_RDTSC_LFENCE:
cnt = &_tsc_lfence_end - &_tsc_lfence_start;
(void) memcpy((void *)tsc_read,
(void *)&_tsc_lfence_start, cnt);
break;
+ case TSC_TSCP:
+ cnt = &_tscp_end - &_tscp_start;
+ (void) memcpy((void *)tsc_read, (void *)&_tscp_start, cnt);
+ break;
default:
+ /* Bail for unexpected TSC types. (TSC_NONE covers 0) */
+ cmn_err(CE_PANIC, "Unrecogized TSC type: %d", flag);
break;
}
+ tsc_type = flag;
}
int
diff --git a/usr/src/uts/i86pc/os/ibft.c b/usr/src/uts/i86pc/os/ibft.c
index d9ed882705..fab1324787 100644
--- a/usr/src/uts/i86pc/os/ibft.c
+++ b/usr/src/uts/i86pc/os/ibft.c
@@ -39,6 +39,7 @@
#include <sys/kmem.h>
#include <sys/psm.h>
#include <sys/bootconf.h>
+#include <sys/reboot.h>
typedef enum ibft_structure_type {
Reserved = 0,
@@ -206,6 +207,7 @@ static ibft_status_t iscsi_parse_ibft_NIC(iscsi_ibft_nic_t *nicp);
static ibft_status_t iscsi_parse_ibft_target(char *begin_of_ibft,
iscsi_ibft_tgt_t *tgtp);
+extern int boothowto;
/*
* Return value:
@@ -759,7 +761,9 @@ ld_ib_prop()
* 1) pass "-B ibft-noprobe=1" on kernel command line
* 2) add line "set ibft_noprobe=1" in /etc/system
*/
- cmn_err(CE_NOTE, IBFT_NOPROBE_MSG);
+ if (boothowto & RB_VERBOSE) {
+ cmn_err(CE_NOTE, IBFT_NOPROBE_MSG);
+ }
return;
}
diff --git a/usr/src/uts/i86pc/os/lgrpplat.c b/usr/src/uts/i86pc/os/lgrpplat.c
index b46bbf849d..981398970e 100644
--- a/usr/src/uts/i86pc/os/lgrpplat.c
+++ b/usr/src/uts/i86pc/os/lgrpplat.c
@@ -2799,7 +2799,11 @@ lgrp_plat_process_sli(uint32_t domain_id, uchar_t *sli_info,
/*
* Read ACPI System Resource Affinity Table (SRAT) to determine which CPUs
* and memory are local to each other in the same NUMA node and return number
- * of nodes
+ * of nodes.
+ *
+ * The SRAT table pointer is populated during bootup by
+ * build_firmware_properties() in fakebop.c. Several motherboard and BIOS
+ * manufacturers are guilty of not having a SRAT table.
*/
static int
lgrp_plat_process_srat(ACPI_TABLE_SRAT *tp, ACPI_TABLE_MSCT *mp,
@@ -2816,9 +2820,15 @@ lgrp_plat_process_srat(ACPI_TABLE_SRAT *tp, ACPI_TABLE_MSCT *mp,
/*
* Nothing to do when no SRAT or disabled
*/
- if (tp == NULL || !lgrp_plat_srat_enable)
+ if (!lgrp_plat_srat_enable)
return (-1);
+ if (tp == NULL) {
+ cmn_err(CE_WARN, "Couldn't read ACPI SRAT table from BIOS. "
+ "lgrp support will be limited to one group.\n");
+ return (-1);
+ }
+
/*
* Try to get domain information from MSCT table.
* ACPI4.0: OSPM will use information provided by the MSCT only
diff --git a/usr/src/uts/i86pc/os/mlsetup.c b/usr/src/uts/i86pc/os/mlsetup.c
index 045adbcb7b..438f83b6e9 100644
--- a/usr/src/uts/i86pc/os/mlsetup.c
+++ b/usr/src/uts/i86pc/os/mlsetup.c
@@ -23,6 +23,7 @@
*
* Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
* Copyright (c) 2010, Intel Corporation.
@@ -61,6 +62,7 @@
#include <sys/promif.h>
#include <sys/pci_cfgspace.h>
#include <sys/bootvfs.h>
+#include <sys/tsc.h>
#ifdef __xpv
#include <sys/hypervisor.h>
#else
@@ -227,15 +229,15 @@ mlsetup(struct regs *rp)
*/
if ((get_hwenv() & HW_XEN_HVM) == 0 &&
is_x86_feature(x86_featureset, X86FSET_TSCP))
- patch_tsc_read(X86_HAVE_TSCP);
+ patch_tsc_read(TSC_TSCP);
else if (cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
cpuid_getfamily(CPU) <= 0xf &&
is_x86_feature(x86_featureset, X86FSET_SSE2))
- patch_tsc_read(X86_TSC_MFENCE);
+ patch_tsc_read(TSC_RDTSC_MFENCE);
else if (cpuid_getvendor(CPU) == X86_VENDOR_Intel &&
cpuid_getfamily(CPU) <= 6 &&
is_x86_feature(x86_featureset, X86FSET_SSE2))
- patch_tsc_read(X86_TSC_LFENCE);
+ patch_tsc_read(TSC_RDTSC_LFENCE);
#endif /* !__xpv */
@@ -246,7 +248,7 @@ mlsetup(struct regs *rp)
* return 0.
*/
if (!is_x86_feature(x86_featureset, X86FSET_TSC))
- patch_tsc_read(X86_NO_TSC);
+ patch_tsc_read(TSC_NONE);
#endif /* __i386 && !__xpv */
#if defined(__amd64) && !defined(__xpv)
diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c
index 3c7b453949..829c631096 100644
--- a/usr/src/uts/i86pc/os/mp_startup.c
+++ b/usr/src/uts/i86pc/os/mp_startup.c
@@ -27,7 +27,7 @@
* All rights reserved.
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
@@ -166,6 +166,8 @@ init_cpu_info(struct cpu *cp)
void
init_cpu_syscall(struct cpu *cp)
{
+ uint64_t flags;
+
kpreempt_disable();
#if defined(__amd64)
@@ -247,6 +249,24 @@ init_cpu_syscall(struct cpu *cp)
kpreempt_enable();
}
+#if !defined(__xpv)
+/*
+ * Configure per-cpu ID GDT
+ */
+static void
+init_cpu_id_gdt(struct cpu *cp)
+{
+ /* Write cpu_id into limit field of GDT for usermode retrieval */
+#if defined(__amd64)
+ set_usegd(&cp->cpu_gdt[GDT_CPUID], SDP_SHORT, NULL, cp->cpu_id,
+ SDT_MEMRODA, SEL_UPL, SDP_BYTES, SDP_OP32);
+#elif defined(__i386)
+ set_usegd(&cp->cpu_gdt[GDT_CPUID], NULL, cp->cpu_id, SDT_MEMRODA,
+ SEL_UPL, SDP_BYTES, SDP_OP32);
+#endif
+}
+#endif /* !defined(__xpv) */
+
/*
* Multiprocessor initialization.
*
@@ -430,6 +450,10 @@ mp_cpu_configure_common(int cpun, boolean_t boot)
init_cpu_info(cp);
+#if !defined(__xpv)
+ init_cpu_id_gdt(cp);
+#endif
+
/*
* alloc space for ucode_info
*/
@@ -1486,6 +1510,10 @@ start_other_cpus(int cprboot)
*/
init_cpu_info(CPU);
+#if !defined(__xpv)
+ init_cpu_id_gdt(CPU);
+#endif
+
cmn_err(CE_CONT, "?cpu%d: %s\n", CPU->cpu_id, CPU->cpu_idstr);
cmn_err(CE_CONT, "?cpu%d: %s\n", CPU->cpu_id, CPU->cpu_brandstr);
diff --git a/usr/src/uts/i86pc/os/timestamp.c b/usr/src/uts/i86pc/os/timestamp.c
index c40159018c..7344e1a492 100644
--- a/usr/src/uts/i86pc/os/timestamp.c
+++ b/usr/src/uts/i86pc/os/timestamp.c
@@ -25,6 +25,7 @@
*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -47,6 +48,7 @@
#include <sys/panic.h>
#include <sys/cpu.h>
#include <sys/sdt.h>
+#include <sys/comm_page.h>
/*
* Using the Pentium's TSC register for gethrtime()
@@ -99,7 +101,6 @@
#define NSEC_SHIFT 5
-static uint_t nsec_scale;
static uint_t nsec_unscale;
/*
@@ -140,18 +141,12 @@ static volatile int tsc_sync_go;
int tsc_master_slave_sync_needed = 1;
-static int tsc_max_delta;
-static hrtime_t tsc_sync_tick_delta[NCPU];
typedef struct tsc_sync {
volatile hrtime_t master_tsc, slave_tsc;
} tsc_sync_t;
static tsc_sync_t *tscp;
-static hrtime_t largest_tsc_delta = 0;
-static ulong_t shortest_write_time = ~0UL;
-static hrtime_t tsc_last = 0;
static hrtime_t tsc_last_jumped = 0;
-static hrtime_t tsc_hrtime_base = 0;
static int tsc_jumped = 0;
static uint32_t tsc_wayback = 0;
/*
@@ -159,7 +154,6 @@ static uint32_t tsc_wayback = 0;
* tsc_tick() function runs which means that when gethrtime() is called it
* should never be more than 1 second since tsc_last was updated.
*/
-static hrtime_t tsc_resume_cap;
static hrtime_t tsc_resume_cap_ns = NANOSEC; /* 1s */
static hrtime_t shadow_tsc_hrtime_base;
@@ -451,25 +445,27 @@ tsc_gethrtimeunscaled_delta(void)
}
/*
- * Called by the master in the TSC sync operation (usually the boot CPU).
- * If the slave is discovered to have a skew, gethrtimef will be changed to
- * point to tsc_gethrtime_delta(). Calculating skews is precise only when
- * the master and slave TSCs are read simultaneously; however, there is no
- * algorithm that can read both CPUs in perfect simultaneity. The proposed
- * algorithm is an approximate method based on the behaviour of cache
- * management. The slave CPU continuously reads TSC and then reads a global
- * variable which the master CPU updates. The moment the master's update reaches
- * the slave's visibility (being forced by an mfence operation) we use the TSC
- * reading taken on the slave. A corresponding TSC read will be taken on the
- * master as soon as possible after finishing the mfence operation. But the
- * delay between causing the slave to notice the invalid cache line and the
- * competion of mfence is not repeatable. This error is heuristically assumed
- * to be 1/4th of the total write time as being measured by the two TSC reads
- * on the master sandwiching the mfence. Furthermore, due to the nature of
- * bus arbitration, contention on memory bus, etc., the time taken for the write
- * to reflect globally can vary a lot. So instead of taking a single reading,
- * a set of readings are taken and the one with least write time is chosen
- * to calculate the final skew.
+ * TSC Sync Master
+ *
+ * Typically called on the boot CPU, this attempts to quantify TSC skew between
+ * different CPUs. If an appreciable difference is found, gethrtimef will be
+ * changed to point to tsc_gethrtime_delta().
+ *
+ * Calculating skews is precise only when the master and slave TSCs are read
+ * simultaneously; however, there is no algorithm that can read both CPUs in
+ * perfect simultaneity. The proposed algorithm is an approximate method based
+ * on the behaviour of cache management. The slave CPU continuously polls the
+ * TSC while reading a global variable updated by the master CPU. The latest
+ * TSC reading is saved when the master's update (forced via mfence) reaches
+ * visibility on the slave. The master will also take a TSC reading
+ * immediately following the mfence.
+ *
+ * While the delay between cache line invalidation on the slave and mfence
+ * completion on the master is not repeatable, the error is heuristically
+ * assumed to be 1/4th of the write time recorded by the master. Multiple
+ * samples are taken to control for the variance caused by external factors
+ * such as bus contention. Each sample set is independent per-CPU to control
+ * for differing memory latency on NUMA systems.
*
* TSC sync is disabled in the context of virtualization because the CPUs
* assigned to the guest are virtual CPUs which means the real CPUs on which
@@ -482,7 +478,7 @@ void
tsc_sync_master(processorid_t slave)
{
ulong_t flags, source, min_write_time = ~0UL;
- hrtime_t write_time, x, mtsc_after, tdelta;
+ hrtime_t write_time, mtsc_after, last_delta = 0;
tsc_sync_t *tsc = tscp;
int cnt;
int hwtype;
@@ -505,57 +501,53 @@ tsc_sync_master(processorid_t slave)
SMT_PAUSE();
write_time = mtsc_after - tsc->master_tsc;
if (write_time <= min_write_time) {
- min_write_time = write_time;
+ hrtime_t tdelta;
+
+ tdelta = tsc->slave_tsc - mtsc_after;
+ if (tdelta < 0)
+ tdelta = -tdelta;
/*
- * Apply heuristic adjustment only if the calculated
- * delta is > 1/4th of the write time.
+ * If the margin exists, subtract 1/4th of the measured
+ * write time from the master's TSC value. This is an
+ * estimate of how late the mfence completion came
+ * after the slave noticed the cache line change.
*/
- x = tsc->slave_tsc - mtsc_after;
- if (x < 0)
- x = -x;
- if (x > (min_write_time/4))
- /*
- * Subtract 1/4th of the measured write time
- * from the master's TSC value, as an estimate
- * of how late the mfence completion came
- * after the slave noticed the cache line
- * change.
- */
+ if (tdelta > (write_time/4)) {
tdelta = tsc->slave_tsc -
- (mtsc_after - (min_write_time/4));
- else
+ (mtsc_after - (write_time/4));
+ } else {
tdelta = tsc->slave_tsc - mtsc_after;
- tsc_sync_tick_delta[slave] =
- tsc_sync_tick_delta[source] - tdelta;
+ }
+ last_delta = tsc_sync_tick_delta[source] - tdelta;
+ tsc_sync_tick_delta[slave] = last_delta;
+ min_write_time = write_time;
}
tsc->master_tsc = tsc->slave_tsc = write_time = 0;
membar_enter();
tsc_sync_go = TSC_SYNC_STOP;
}
- if (tdelta < 0)
- tdelta = -tdelta;
- if (tdelta > largest_tsc_delta)
- largest_tsc_delta = tdelta;
- if (min_write_time < shortest_write_time)
- shortest_write_time = min_write_time;
+
/*
- * Enable delta variants of tsc functions if the largest of all chosen
- * deltas is > smallest of the write time.
+ * Only enable the delta variants of the TSC functions if the measured
+ * skew is greater than the fastest write time.
*/
- if (largest_tsc_delta > shortest_write_time) {
+ last_delta = (last_delta < 0) ? -last_delta : last_delta;
+ if (last_delta > min_write_time) {
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+ tsc_ncpu = NCPU;
}
restore_int_flag(flags);
}
/*
+ * TSC Sync Slave
+ *
* Called by a CPU which has just been onlined. It is expected that the CPU
* performing the online operation will call tsc_sync_master().
*
- * TSC sync is disabled in the context of virtualization. See comments
- * above tsc_sync_master.
+ * Like tsc_sync_master, this logic is skipped on virtualized platforms.
*/
void
tsc_sync_slave(void)
@@ -579,11 +571,9 @@ tsc_sync_slave(void)
tsc_sync_go = TSC_SYNC_GO;
do {
/*
- * Do not put an SMT_PAUSE here. For instance,
- * if the master and slave are really the same
- * hyper-threaded CPU, then you want the master
- * to yield to the slave as quickly as possible here,
- * but not the other way.
+ * Do not put an SMT_PAUSE here. If the master and
+ * slave are the same hyper-threaded CPU, we want the
+ * master to yield as quickly as possible to the slave.
*/
s1 = tsc_read();
} while (tsc->master_tsc == 0);
@@ -688,6 +678,12 @@ tsc_hrtimeinit(uint64_t cpu_freq_hz)
hrtime_tick = tsc_tick;
gethrtime_hires = 1;
/*
+ * Being part of the comm page, tsc_ncpu communicates the published
+ * length of the tsc_sync_tick_delta array. This is kept zeroed to
+ * ignore the absent delta data while the TSCs are synced.
+ */
+ tsc_ncpu = 0;
+ /*
* Allocate memory for the structure used in the tsc sync logic.
* This structure should be aligned on a multiple of cache line size.
*/
@@ -708,12 +704,10 @@ get_tsc_ready()
}
/*
- * Adjust all the deltas by adding the passed value to the array.
- * Then use the "delt" versions of the the gethrtime functions.
- * Note that 'tdelta' _could_ be a negative number, which should
- * reduce the values in the array (used, for example, if the Solaris
- * instance was moved by a virtual manager to a machine with a higher
- * value of tsc).
+ * Adjust all the deltas by adding the passed value to the array and activate
+ * the "delta" versions of the gethrtime functions. It is possible that the
+ * adjustment could be negative. Such may occur if the SunOS instance was
+ * moved by a virtual manager to a machine with a higher value of TSC.
*/
void
tsc_adjust_delta(hrtime_t tdelta)
@@ -726,19 +720,16 @@ tsc_adjust_delta(hrtime_t tdelta)
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+ tsc_ncpu = NCPU;
}
/*
* Functions to manage TSC and high-res time on suspend and resume.
*/
-/*
- * declarations needed for time adjustment
- */
-extern void rtcsync(void);
+/* tod_ops from "uts/i86pc/io/todpc_subr.c" */
extern tod_ops_t *tod_ops;
-/* There must be a better way than exposing nsec_scale! */
-extern uint_t nsec_scale;
+
static uint64_t tsc_saved_tsc = 0; /* 1 in 2^64 chance this'll screw up! */
static timestruc_t tsc_saved_ts;
static int tsc_needs_resume = 0; /* We only want to do this once. */
@@ -748,23 +739,20 @@ int tsc_suspend_count = 0;
int tsc_resume_in_cyclic = 0;
/*
- * Let timestamp.c know that we are suspending. It needs to take
- * snapshots of the current time, and do any pre-suspend work.
+ * Take snapshots of the current time and do any other pre-suspend work.
*/
void
tsc_suspend(void)
{
-/*
- * What we need to do here, is to get the time we suspended, so that we
- * know how much we should add to the resume.
- * This routine is called by each CPU, so we need to handle reentry.
- */
+ /*
+ * We need to collect the time at which we suspended here so we know
+ * now much should be added during the resume. This is called by each
+ * CPU, so reentry must be properly handled.
+ */
if (tsc_gethrtime_enable) {
/*
- * We put the tsc_read() inside the lock as it
- * as no locking constraints, and it puts the
- * aquired value closer to the time stamp (in
- * case we delay getting the lock).
+ * Perform the tsc_read after acquiring the lock to make it as
+ * accurate as possible in the face of contention.
*/
mutex_enter(&tod_lock);
tsc_saved_tsc = tsc_read();
@@ -786,8 +774,7 @@ tsc_suspend(void)
}
/*
- * Restore all timestamp state based on the snapshots taken at
- * suspend time.
+ * Restore all timestamp state based on the snapshots taken at suspend time.
*/
void
tsc_resume(void)
diff --git a/usr/src/uts/i86pc/os/trap.c b/usr/src/uts/i86pc/os/trap.c
index 82be4f7e8a..c88fec6fbe 100644
--- a/usr/src/uts/i86pc/os/trap.c
+++ b/usr/src/uts/i86pc/os/trap.c
@@ -32,7 +32,7 @@
/* */
/*
- * Copyright 2012 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/types.h>
@@ -101,6 +101,7 @@
#include <sys/hypervisor.h>
#endif
#include <sys/contract/process_impl.h>
+#include <sys/brand.h>
#define USER 0x10000 /* user-mode flag added to trap type */
@@ -862,6 +863,17 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid)
fault_type = F_INVAL;
}
+ /*
+ * Allow the brand to interpose on invalid memory accesses
+ * prior to running the native pagefault handler. If this
+ * brand hook returns zero, it was able to handle the fault
+ * completely. Otherwise, drive on and call pagefault().
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_pagefault != NULL &&
+ BROP(p)->b_pagefault(p, lwp, addr, fault_type, rw) == 0) {
+ goto out;
+ }
+
res = pagefault(addr, fault_type, rw, 0);
/*
diff --git a/usr/src/uts/i86pc/pci-ide/Makefile b/usr/src/uts/i86pc/pci-ide/Makefile
index 2a50977aac..399ccc95e5 100644
--- a/usr/src/uts/i86pc/pci-ide/Makefile
+++ b/usr/src/uts/i86pc/pci-ide/Makefile
@@ -25,6 +25,8 @@
# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the pci-ide "drv"
@@ -68,6 +70,7 @@ DEBUG_DEFS += $(DEBUG_FLGS)
INC_PATH += -I$(UTSBASE)/common/io/pci-ide
CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/pci/Makefile b/usr/src/uts/i86pc/pci/Makefile
index 5a4e6e891a..f2f9952670 100644
--- a/usr/src/uts/i86pc/pci/Makefile
+++ b/usr/src/uts/i86pc/pci/Makefile
@@ -23,6 +23,8 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the PCI nexus driver
@@ -77,6 +79,7 @@ LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86pc/pcplusmp/Makefile b/usr/src/uts/i86pc/pcplusmp/Makefile
index 6590ef9f28..92c455017d 100644
--- a/usr/src/uts/i86pc/pcplusmp/Makefile
+++ b/usr/src/uts/i86pc/pcplusmp/Makefile
@@ -24,6 +24,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016, Joyent, Inc.
+#
#
# This makefile drives the production of the pcplusmp "mach"
@@ -65,6 +67,8 @@ $(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS)
#
LDFLAGS += -dy -N misc/acpica
+CERRWARN += -_gcc=-Wno-unused-function
+
#
# For now, disable these lint checks; maintainers should endeavor
# to investigate and remove these for maximum lint coverage.
diff --git a/usr/src/uts/i86pc/sys/acpidev.h b/usr/src/uts/i86pc/sys/acpidev.h
index 6d11277aaf..a3bd54d4e3 100644
--- a/usr/src/uts/i86pc/sys/acpidev.h
+++ b/usr/src/uts/i86pc/sys/acpidev.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009-2010, Intel Corporation.
* All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_ACPIDEV_H
@@ -128,7 +129,7 @@ typedef enum acpidev_class_id {
#ifdef _KERNEL
/* Common ACPI object names. */
-#define ACPIDEV_OBJECT_NAME_SB ACPI_NS_SYSTEM_BUS
+#define ACPIDEV_OBJECT_NAME_SB METHOD_NAME__SB_
#define ACPIDEV_OBJECT_NAME_PR "_PR_"
/* Common ACPI method names. */
diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h
index 8a87760456..11ae48340a 100644
--- a/usr/src/uts/i86pc/sys/apic.h
+++ b/usr/src/uts/i86pc/sys/apic.h
@@ -382,7 +382,7 @@ struct apic_io_intr {
/* special or reserve vectors */
#define APIC_CHECK_RESERVE_VECTORS(v) \
(((v) == T_FASTTRAP) || ((v) == APIC_SPUR_INTR) || \
- ((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET))
+ ((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET) || ((v) == 0x80))
/* cmos shutdown code for BIOS */
#define BIOS_SHUTDOWN 0x0a
diff --git a/usr/src/uts/i86pc/sys/comm_page.h b/usr/src/uts/i86pc/sys/comm_page.h
new file mode 100644
index 0000000000..ea19c856a8
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/comm_page.h
@@ -0,0 +1,100 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _COMM_PAGE_H
+#define _COMM_PAGE_H
+
+#ifndef _ASM
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#endif /* _ASM */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define COMM_PAGE_SIZE PAGESIZE
+#define COMM_PAGE_ALIGN 0x4000
+
+#ifndef _ASM
+
+/*
+ * x86 comm page
+ *
+ * This struct defines the data format for the "comm page": kernel data made
+ * directly available to userspace for read-only operations. This enables
+ * facilities such as clock_gettime to operate entirely in userspace without
+ * the need for a trap or fasttrap.
+ *
+ * A note about 32-bit/64-bit compatibility:
+ * The current format of the comm page is designed to be consistent for both
+ * 32-bit and 64-bit programs running in a 64-bit kernel. On 32-bit kernels,
+ * the comm page is not exposed to userspace due to the difference in
+ * timespec_t sizing.
+ *
+ * This struct is instantiated "by hand" in assembly to preserve the global
+ * symbols it contains. That layout must be kept in sync with the structure
+ * defined here.
+ * See: "uts/i86pc/ml/comm_page.s"
+ */
+typedef struct comm_page_s {
+ hrtime_t cp_tsc_last;
+ hrtime_t cp_tsc_hrtime_base;
+ hrtime_t cp_tsc_resume_cap;
+ uint32_t cp_tsc_type;
+ uint32_t cp_tsc_max_delta;
+
+ volatile uint32_t cp_hres_lock; /* must be 8-byte aligned */
+ uint32_t cp_nsec_scale;
+ int64_t cp_hrestime_adj;
+ hrtime_t cp_hres_last_tick;
+ uint32_t cp_tsc_ncpu;
+ uint32_t _cp_pad;
+ volatile int64_t cp_hrestime[2];
+#if defined(_MACHDEP)
+ hrtime_t cp_tsc_sync_tick_delta[NCPU];
+#else
+ /* length resides in cp_ncpu */
+ hrtime_t cp_tsc_sync_tick_delta[];
+#endif /* defined(_MACHDEP) */
+} comm_page_t;
+
+#if defined(_KERNEL)
+extern comm_page_t comm_page;
+
+#if defined(_MACHDEP)
+extern hrtime_t tsc_last;
+extern hrtime_t tsc_hrtime_base;
+extern hrtime_t tsc_resume_cap;
+extern uint32_t tsc_type;
+extern uint32_t tsc_max_delta;
+extern volatile uint32_t hres_lock;
+extern uint32_t nsec_scale;
+extern int64_t hrestime_adj;
+extern hrtime_t hres_last_tick;
+extern uint32_t tsc_ncpu;
+extern volatile timestruc_t hrestime;
+extern hrtime_t tsc_sync_tick_delta[NCPU];
+#endif /* defined(_MACHDEP) */
+#endif /* defined(_KERNEL) */
+
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMM_PAGE_H */
diff --git a/usr/src/uts/i86pc/sys/machparam.h b/usr/src/uts/i86pc/sys/machparam.h
index 99ae0d4d3b..fc34522307 100644
--- a/usr/src/uts/i86pc/sys/machparam.h
+++ b/usr/src/uts/i86pc/sys/machparam.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -54,6 +55,10 @@ extern "C" {
*/
#if defined(__amd64)
+/*
+ * If NCPU grows beyond 256, sizing for the x86 comm page will require
+ * adjustment.
+ */
#define NCPU 256
#define NCPU_LOG2 8
#elif defined(__i386)
diff --git a/usr/src/uts/i86pc/sys/tsc.h b/usr/src/uts/i86pc/sys/tsc.h
new file mode 100644
index 0000000000..d4090381c4
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/tsc.h
@@ -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 2016 Joyent, Inc.
+ */
+
+#ifndef _TSC_H
+#define _TSC_H
+
+/*
+ * flags to patch tsc_read routine.
+ */
+#define TSC_NONE 0x0
+#define TSC_RDTSC_CPUID 0x1
+#define TSC_RDTSC_MFENCE 0x2
+#define TSC_RDTSC_LFENCE 0x3
+#define TSC_TSCP 0x4
+
+#endif /* _TSC_H */
diff --git a/usr/src/uts/i86pc/tzmon/Makefile b/usr/src/uts/i86pc/tzmon/Makefile
index 4410c18c88..b8f15a547b 100644
--- a/usr/src/uts/i86pc/tzmon/Makefile
+++ b/usr/src/uts/i86pc/tzmon/Makefile
@@ -23,6 +23,8 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the tzmon
# ThermalZone Monitor driver kernel module.
@@ -60,6 +62,7 @@ DEBUG_FLGS =
$(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS)
CPPFLAGS += -DSUNDDI
+CERRWARN += -_gcc=-Wno-unused-function
LDFLAGS += -dy -N misc/acpica
diff --git a/usr/src/uts/i86pc/unix/Makefile b/usr/src/uts/i86pc/unix/Makefile
index 27389eea17..2b3eeb8c8b 100644
--- a/usr/src/uts/i86pc/unix/Makefile
+++ b/usr/src/uts/i86pc/unix/Makefile
@@ -22,6 +22,7 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of unix (and unix.o).
#
@@ -64,7 +65,7 @@ GENUNIX_DIR = ../../intel/$(GENUNIX)
LIBOPTS = -L $(GENUNIX_DIR)/$(OBJS_DIR) -l $(GENUNIX)
-CTFEXTRAOBJS = $(OBJS_DIR)/vers.o
+CTFEXTRAOBJS = $(OBJS_DIR)/vers.o $(OBJS_DIR)/comm_page_ctf.o
DBOOT_OBJS_DIR = dboot/$(OBJS_DIR)
DBOOT_OBJECTS = $(DBOOT_OBJS:%=$(DBOOT_OBJS_DIR)/%)
@@ -97,9 +98,10 @@ UNIX_DIR = .
#
CLEANFILES += \
$(UNIX_O) $(MODSTUBS_O) \
- $(OBJS_DIR)/vers.c $(OBJS_DIR)/vers.o \
+ $(OBJS_DIR)/vers.c \
$(OBJS_DIR)/dtracestubs.s \
- $(DTRACESTUBS_O) $(DTRACESTUBS)
+ $(DTRACESTUBS_O) $(DTRACESTUBS) \
+ $(CTFEXTRAOBJS)
CLEANFILES += \
$(DBOOT_O) $(DBOOT_S) \
@@ -188,7 +190,7 @@ $(UNIX_BIN): $(UNIX_O) $(MODSTUBS_O) $(MAPFILE_NAME) \
$(CTFMERGE_UNIQUIFY_AGAINST_GENUNIX)
$(POST_PROCESS)
-$(UNIX_O): $(OBJECTS) $(OBJS_DIR)/vers.o
+$(UNIX_O): $(OBJECTS) $(CTFEXTRAOBJS)
$(LD) -r -o $@ $(OBJECTS) $(OBJS_DIR)/vers.o
$(DBOOT_BIN): $(DBOOT_OBJS_DIR) $(DBOOT_OBJECTS) dboot/Mapfile.dboot
@@ -205,6 +207,10 @@ $(DBOOT_O): $(DBOOT_BIN)
$(DBOOT_OBJS_DIR):
-@mkdir -p $@ 2> /dev/null
+$(OBJS_DIR)/comm_page_ctf.o: $(UTSBASE)/i86pc/ml/comm_page.s
+ $(COMPILE.c) -_gcc=-xc -D_GENCTF -o $@ $(UTSBASE)/i86pc/ml/comm_page.s
+ $(CTFCONVERT_O)
+
#
# dboot is built as an intermediate target in dboot.o, so just make
# dboot.o the dependency here.
diff --git a/usr/src/uts/i86pc/uppc/Makefile b/usr/src/uts/i86pc/uppc/Makefile
index cd34be9e4f..5e3dc71b4c 100644
--- a/usr/src/uts/i86pc/uppc/Makefile
+++ b/usr/src/uts/i86pc/uppc/Makefile
@@ -23,7 +23,7 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the uppc mach
# kernel module.
@@ -59,6 +59,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
# Overrides.
#
+CERRWARN += -_gcc=-Wno-unused-function
DEBUG_FLGS =
DEBUG_DEFS += $(DEBUG_FLGS)
diff --git a/usr/src/uts/i86pc/vm/hat_i86.c b/usr/src/uts/i86pc/vm/hat_i86.c
index ea2a83b2bd..a8b4e6edfc 100644
--- a/usr/src/uts/i86pc/vm/hat_i86.c
+++ b/usr/src/uts/i86pc/vm/hat_i86.c
@@ -27,6 +27,7 @@
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
* Copyright (c) 2014, 2015 by Delphix. All rights reserved.
*/
@@ -3323,7 +3324,7 @@ hat_page_getattr(struct page *pp, uint_t flag)
/*
- * common code used by hat_pageunload() and hment_steal()
+ * common code used by hat_page_inval() and hment_steal()
*/
hment_t *
hati_page_unmap(page_t *pp, htable_t *ht, uint_t entry)
@@ -3379,15 +3380,13 @@ hati_page_unmap(page_t *pp, htable_t *ht, uint_t entry)
extern int vpm_enable;
/*
- * Unload all translations to a page. If the page is a subpage of a large
+ * Unload translations to a page. If the page is a subpage of a large
* page, the large page mappings are also removed.
- *
- * The forceflags are unused.
+ * If curhat is not NULL, then we only unload the translation
+ * for the given process, otherwise all translations are unloaded.
*/
-
-/*ARGSUSED*/
-static int
-hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
+void
+hat_page_inval(struct page *pp, uint_t pg_szcd, struct hat *curhat)
{
page_t *cur_pp = pp;
hment_t *hm;
@@ -3395,15 +3394,10 @@ hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
htable_t *ht;
uint_t entry;
level_t level;
+ ulong_t cnt;
XPV_DISALLOW_MIGRATE();
- /*
- * prevent recursion due to kmem_free()
- */
- ++curthread->t_hatdepth;
- ASSERT(curthread->t_hatdepth < 16);
-
#if defined(__amd64)
/*
* clear the vpm ref.
@@ -3416,6 +3410,8 @@ hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
* The loop with next_size handles pages with multiple pagesize mappings
*/
next_size:
+ if (curhat != NULL)
+ cnt = hat_page_getshare(cur_pp);
for (;;) {
/*
@@ -3427,14 +3423,13 @@ next_size:
if (hm == NULL) {
x86_hm_exit(cur_pp);
+curproc_done:
/*
* If not part of a larger page, we're done.
*/
if (cur_pp->p_szc <= pg_szcd) {
- ASSERT(curthread->t_hatdepth > 0);
- --curthread->t_hatdepth;
XPV_ALLOW_MIGRATE();
- return (0);
+ return;
}
/*
@@ -3453,8 +3448,20 @@ next_size:
* If this mapping size matches, remove it.
*/
level = ht->ht_level;
- if (level == pg_szcd)
- break;
+ if (level == pg_szcd) {
+ if (curhat == NULL || ht->ht_hat == curhat)
+ break;
+ /*
+ * Unloading only the given process but it's
+ * not the hat for the current process. Leave
+ * entry in place. Also do a safety check to
+ * ensure we don't get in an infinite loop
+ */
+ if (cnt-- == 0) {
+ x86_hm_exit(cur_pp);
+ goto curproc_done;
+ }
+ }
}
/*
@@ -3464,14 +3471,44 @@ next_size:
hm = hati_page_unmap(cur_pp, ht, entry);
if (hm != NULL)
hment_free(hm);
+
+ /* Perform check above for being part of a larger page. */
+ if (curhat != NULL)
+ goto curproc_done;
}
}
+/*
+ * Unload translations to a page. If unloadflag is HAT_CURPROC_PGUNLOAD, then
+ * we only unload the translation for the current process, otherwise all
+ * translations are unloaded.
+ */
+static int
+hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t unloadflag)
+{
+ struct hat *curhat = NULL;
+
+ /*
+ * prevent recursion due to kmem_free()
+ */
+ ++curthread->t_hatdepth;
+ ASSERT(curthread->t_hatdepth < 16);
+
+ if (unloadflag == HAT_CURPROC_PGUNLOAD)
+ curhat = curthread->t_procp->p_as->a_hat;
+
+ hat_page_inval(pp, pg_szcd, curhat);
+
+ ASSERT(curthread->t_hatdepth > 0);
+ --curthread->t_hatdepth;
+ return (0);
+}
+
int
-hat_pageunload(struct page *pp, uint_t forceflag)
+hat_pageunload(struct page *pp, uint_t unloadflag)
{
ASSERT(PAGE_EXCL(pp));
- return (hati_pageunload(pp, 0, forceflag));
+ return (hati_pageunload(pp, 0, unloadflag));
}
/*
diff --git a/usr/src/uts/i86pc/vm/vm_machdep.c b/usr/src/uts/i86pc/vm/vm_machdep.c
index 2212202a01..1c2bd3e0ec 100644
--- a/usr/src/uts/i86pc/vm/vm_machdep.c
+++ b/usr/src/uts/i86pc/vm/vm_machdep.c
@@ -24,6 +24,7 @@
/*
* Copyright (c) 2010, Intel Corporation.
* All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -623,10 +624,8 @@ void
map_addr(caddr_t *addrp, size_t len, offset_t off, int vacalign, uint_t flags)
{
struct proc *p = curproc;
- caddr_t userlimit = (flags & _MAP_LOW32) ?
- (caddr_t)_userlimit32 : p->p_as->a_userlimit;
-
- map_addr_proc(addrp, len, off, vacalign, userlimit, curproc, flags);
+ map_addr_proc(addrp, len, off, vacalign,
+ map_userlimit(p, p->p_as, flags), curproc, flags);
}
/*ARGSUSED*/
diff --git a/usr/src/uts/i86xpv/amd_opteron/Makefile b/usr/src/uts/i86xpv/amd_opteron/Makefile
index 23e03c33e3..49f2519a8b 100644
--- a/usr/src/uts/i86xpv/amd_opteron/Makefile
+++ b/usr/src/uts/i86xpv/amd_opteron/Makefile
@@ -21,6 +21,7 @@
# Use is subject to license terms.
#
# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2016 Joyent, Inc.
#
#
@@ -64,6 +65,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
# Overrides and additions
#
+$(OBJS_DIR)/ao_mca.o := CERRWARN += -_gcc=-Wno-unused-function
CLEANFILES += $(AO_MCA_DISP_C)
CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR)
ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR)
diff --git a/usr/src/uts/i86xpv/isa/Makefile b/usr/src/uts/i86xpv/isa/Makefile
index c6a37b99b4..04df9350d3 100644
--- a/usr/src/uts/i86xpv/isa/Makefile
+++ b/usr/src/uts/i86xpv/isa/Makefile
@@ -23,6 +23,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the ISA nexus driver
#
# i86xpv implementation architecture dependent
@@ -54,6 +56,8 @@ ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+$(OBJS_DIR)/isa.o := CERRWARN += -_gcc=-Wno-unused-function
+
#
# lint pass one enforcement
#
diff --git a/usr/src/uts/i86xpv/npe/Makefile b/usr/src/uts/i86xpv/npe/Makefile
index f460ef6d41..0a3ed67eb1 100644
--- a/usr/src/uts/i86xpv/npe/Makefile
+++ b/usr/src/uts/i86xpv/npe/Makefile
@@ -23,6 +23,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the PCI-E nexus driver
#
# i86xpv implementation architecture dependent
@@ -74,6 +76,7 @@ LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86xpv/os/xpv_timestamp.c b/usr/src/uts/i86xpv/os/xpv_timestamp.c
index d7d13f984b..8b7d226305 100644
--- a/usr/src/uts/i86xpv/os/xpv_timestamp.c
+++ b/usr/src/uts/i86xpv/os/xpv_timestamp.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -71,6 +72,11 @@ static volatile hrtime_t hrtime_last;
static hrtime_t hrtime_suspend_time;
static hrtime_t hrtime_addend;
+volatile uint32_t hres_lock;
+hrtime_t hres_last_tick;
+int64_t hrestime_adj;
+volatile timestruc_t hrestime;
+
/*
* These functions are used in DTrace probe context, and must be removed from
* fbt consideration. Currently fbt ignores all weak symbols, so this will
diff --git a/usr/src/uts/i86xpv/pci-ide/Makefile b/usr/src/uts/i86xpv/pci-ide/Makefile
index 449973b7a6..496f59c4a6 100644
--- a/usr/src/uts/i86xpv/pci-ide/Makefile
+++ b/usr/src/uts/i86xpv/pci-ide/Makefile
@@ -23,6 +23,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the pci-ide "drv"
@@ -66,6 +68,7 @@ DEBUG_DEFS += $(DEBUG_FLGS)
INC_PATH += -I$(UTSBASE)/common/io/pci-ide
CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86xpv/pci/Makefile b/usr/src/uts/i86xpv/pci/Makefile
index a57bbefc42..e032aa6018 100644
--- a/usr/src/uts/i86xpv/pci/Makefile
+++ b/usr/src/uts/i86xpv/pci/Makefile
@@ -23,6 +23,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the PCI nexus driver
@@ -72,6 +74,7 @@ LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86xpv/rootnex/Makefile b/usr/src/uts/i86xpv/rootnex/Makefile
index af3f6354f4..125f0c25f6 100644
--- a/usr/src/uts/i86xpv/rootnex/Makefile
+++ b/usr/src/uts/i86xpv/rootnex/Makefile
@@ -23,6 +23,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the rootnex driver
@@ -66,6 +68,7 @@ LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-unused-label
CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/i86xpv/unix/Makefile b/usr/src/uts/i86xpv/unix/Makefile
index 989e79399d..e9ce16af05 100644
--- a/usr/src/uts/i86xpv/unix/Makefile
+++ b/usr/src/uts/i86xpv/unix/Makefile
@@ -23,6 +23,8 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of unix (and unix.o).
#
# i86xpv implementation architecture dependent
diff --git a/usr/src/uts/i86xpv/xpv_psm/Makefile b/usr/src/uts/i86xpv/xpv_psm/Makefile
index 009ccabac1..23d1b62ba4 100644
--- a/usr/src/uts/i86xpv/xpv_psm/Makefile
+++ b/usr/src/uts/i86xpv/xpv_psm/Makefile
@@ -23,6 +23,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the xpv_psm mach
@@ -77,6 +79,7 @@ CERRWARN += -_gcc=-Wno-type-limits
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
CERRWARN += -_gcc=-Wno-empty-body
+CERRWARN += -_gcc=-Wno-unused-function
# Default build targets.
#
diff --git a/usr/src/uts/i86xpv/xpv_uppc/Makefile b/usr/src/uts/i86xpv/xpv_uppc/Makefile
index d66402e579..81537582ac 100644
--- a/usr/src/uts/i86xpv/xpv_uppc/Makefile
+++ b/usr/src/uts/i86xpv/xpv_uppc/Makefile
@@ -23,7 +23,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the xpv_uppc mach
# kernel module.
@@ -62,6 +62,8 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
DEBUG_FLGS =
DEBUG_DEFS += $(DEBUG_FLGS)
+CERRWARN += -_gcc=-Wno-unused-function
+
#
#
# Depends on ACPI CA interpreter
diff --git a/usr/src/uts/intel/Makefile b/usr/src/uts/intel/Makefile
index af65cc095e..a30bb132f0 100644
--- a/usr/src/uts/intel/Makefile
+++ b/usr/src/uts/intel/Makefile
@@ -65,7 +65,7 @@ install_h.prereq := TARGET= install_h
.PARALLEL: $(PARALLEL_KMODS) $(XMODS) config $(LINT_DEPS)
-def all install clean clobber modlist: $(KMODS) $(XMODS) config
+def all install clean clobber modlist: genassym $(KMODS) $(XMODS) config
clobber: clobber.targ
@@ -107,7 +107,7 @@ CLOBBERFILES += $(PRIVS_C)
# intel/dtrace depends on i86pc/genassym, so we need to build both
# i86pc/genassym and intel/genassym.
#
-all.prereq install.prereq def.prereq: genunix FRC
+all.prereq install.prereq def.prereq: genassym genunix FRC
@cd ../i86pc/genassym; pwd; $(MAKE) $(@:%.prereq=%)
#
@@ -125,7 +125,7 @@ genunix: $(PRIVS_C)
modlintlib clean.lint: $(LINT_KMODS) $(XMODS)
-$(KMODS) $(SUBDIRS) config: FRC
+genassym $(KMODS) $(SUBDIRS) config: FRC
@cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET)
$(XMODS): FRC
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 9f964d9636..aeddaa9203 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -21,7 +21,7 @@
#
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright 2016, Joyent, Inc.
# Copyright 2016 Nexenta Systems, Inc.
#
@@ -36,6 +36,7 @@
#
CORE_OBJS += \
arch_kdi.o \
+ comm_page_util.o \
copy.o \
copy_subr.o \
cpc_subr.o \
@@ -100,6 +101,30 @@ GENUNIX_OBJS += \
#
CORE_OBJS += \
prmachdep.o
+
+LX_CGROUP_OBJS += \
+ cgrps_node.o \
+ cgrps_vfsops.o \
+ cgrps_vnops.o
+
+LX_DEVFS_OBJS += \
+ lxd_attrdb.o \
+ lxd_node.o \
+ lxd_vfsops.o \
+ lxd_vnops.o
+
+LX_PROC_OBJS += \
+ lx_prsubr.o \
+ lx_prvfsops.o \
+ lx_prvnops.o
+
+LX_SYS_OBJS += \
+ lx_syssubr.o \
+ lx_sysvfsops.o \
+ lx_sysvnops.o
+
+LX_AUTOFS_OBJS += \
+ lx_autofs.o
#
# ZFS file system module
@@ -182,38 +207,54 @@ KRTLD_OBJS += \
#
# misc. modules
#
-ACPICA_OBJS += dbcmds.o dbdisply.o \
- dbexec.o dbfileio.o dbhistry.o dbinput.o dbstats.o \
- dbutils.o dbxface.o evevent.o evgpe.o evgpeblk.o \
- evmisc.o evregion.o evrgnini.o evsci.o evxface.o \
- evxfevnt.o evxfregn.o hwacpi.o hwgpe.o hwregs.o \
- hwsleep.o hwtimer.o dsfield.o dsinit.o dsmethod.o \
- dsmthdat.o dsobject.o dsopcode.o dsutils.o dswexec.o \
- dswload.o dswscope.o dswstate.o exconfig.o exconvrt.o \
- excreate.o exdump.o exfield.o exfldio.o exmisc.o \
- exmutex.o exnames.o exoparg1.o exoparg2.o exoparg3.o \
- exoparg6.o exprep.o exregion.o exresnte.o exresolv.o \
- exresop.o exstore.o exstoren.o exstorob.o exsystem.o \
- exutils.o psargs.o psopcode.o psparse.o psscope.o \
- pstree.o psutils.o pswalk.o psxface.o nsaccess.o \
- nsalloc.o nsdump.o nsdumpdv.o nseval.o nsinit.o \
- nsload.o nsnames.o nsobject.o nsparse.o nssearch.o \
- nsutils.o nswalk.o nsxfeval.o nsxfname.o nsxfobj.o \
- rsaddr.o rscalc.o rscreate.o rsdump.o \
+ACPICA_OBJS += \
+ dmbuffer.o dmcstyle.o dmdeferred.o dmnames.o dmopcode.o \
+ dmresrc.o dmresrcl.o dmresrcl2.o dmresrcs.o dmutils.o \
+ dmwalk.o \
+ \
+ dsargs.o dscontrol.o dsdebug.o dsfield.o dsinit.o \
+ dsmethod.o dsmthdat.o dsobject.o dsopcode.o dsutils.o \
+ dswexec.o dswload.o dswload2.o dswscope.o dswstate.o \
+ \
+ evevent.o evglock.o evgpe.o evgpeblk.o evgpeinit.o \
+ evgpeutil.o evhandler.o evmisc.o evregion.o evrgnini.o \
+ evsci.o evxface.o evxfevnt.o evxfgpe.o evxfregn.o \
+ \
+ exconcat.o exconfig.o exconvrt.o excreate.o exdebug.o \
+ exdump.o exfield.o exfldio.o exmisc.o exmutex.o exnames.o \
+ exoparg1.o exoparg2.o exoparg3.o exoparg6.o exprep.o \
+ exregion.o exresnte.o exresolv.o exresop.o exstore.o \
+ exstoren.o exstorob.o exsystem.o extrace.o exutils.o \
+ \
+ hwacpi.o hwesleep.o hwgpe.o hwpci.o hwregs.o hwsleep.o \
+ hwtimer.o hwvalid.o hwxface.o hwxfsleep.o \
+ \
+ psargs.o psloop.o psobject.o psopcode.o psopinfo.o \
+ psparse.o psscope.o pstree.o psutils.o pswalk.o psxface.o \
+ \
+ nsaccess.o nsalloc.o nsarguments.o nsconvert.o nsdump.o \
+ nsdumpdv.o nseval.o nsinit.o nsload.o nsnames.o nsobject.o \
+ nsparse.o nspredef.o nsprepkg.o nsrepair.o nsrepair2.o \
+ nssearch.o nsutils.o nswalk.o nsxfeval.o nsxfname.o \
+ nsxfobj.o \
+ \
+ rsaddr.o rscalc.o rscreate.o rsdump.o rsdumpinfo.o \
rsinfo.o rsio.o rsirq.o rslist.o rsmemory.o rsmisc.o \
- rsutils.o rsxface.o tbfadt.o tbfind.o tbinstal.o \
- tbutils.o tbxface.o tbxfroot.o \
- utalloc.o utclib.o utcopy.o utdebug.o utdelete.o \
- uteval.o utglobal.o utinit.o utmath.o utmisc.o \
- utobject.o utresrc.o utxface.o acpica.o acpi_enum.o \
- master_ops.o osl.o osl_ml.o acpica_ec.o utcache.o \
- utmutex.o utstate.o dmbuffer.o dmnames.o dmobject.o \
- dmopcode.o dmresrc.o dmresrcl.o dmresrcs.o dmutils.o \
- dmwalk.o psloop.o nspredef.o hwxface.o hwvalid.o \
- utlock.o utids.o nsrepair.o nsrepair2.o \
- dbmethod.o dbnames.o dsargs.o dscontrol.o dswload2.o \
- evglock.o evgpeinit.o evgpeutil.o evxfgpe.o exdebug.o \
- hwpci.o utdecode.o utosi.o utxferror.o
+ rsserial.o rsutils.o rsxface.o \
+ \
+ tbdata.o tbfadt.o tbfind.o tbinstal.o tbprint.o tbutils.o \
+ tbxface.o tbxfload.o tbxfroot.o \
+ \
+ utaddress.o utalloc.o utascii.o utbuffer.o utcache.o \
+ utclib.o utcopy.o utdebug.o utdecode.o utdelete.o \
+ uterror.o uteval.o utexcep.o utglobal.o uthex.o utids.o \
+ utinit.o utlock.o utmath.o utmisc.o utmutex.o utnonansi.o \
+ utobject.o utosi.o utownerid.o utpredef.o utprint.o \
+ utresrc.o utstate.o utstring.o uttrack.o utuuid.o utxface.o \
+ utxferror.o utxfinit.o utxfmutex.o \
+ \
+ acpi_enum.o acpica_ec.o acpica.o ahids.o master_ops.o \
+ osl_ml.o osl.o
AGP_OBJS += agpmaster.o
@@ -264,6 +305,65 @@ IOMMULIB_OBJS = iommulib.o
#
SN1_BRAND_OBJS = sn1_brand.o sn1_brand_asm.o
S10_BRAND_OBJS = s10_brand.o s10_brand_asm.o
+LX_BRAND_OBJS = \
+ lx_access.o \
+ lx_aio.o \
+ lx_archdep.o \
+ lx_auxv.o \
+ lx_brand.o \
+ lx_brk.o \
+ lx_chmod.o \
+ lx_chown.o \
+ lx_clone.o \
+ lx_close.o \
+ lx_cpu.o \
+ lx_dup.o \
+ lx_errno.o \
+ lx_epoll.o \
+ lx_fadvise.o \
+ lx_fallocate.o \
+ lx_fcntl.o \
+ lx_futex.o \
+ lx_getcwd.o \
+ lx_getdents.o \
+ lx_getpid.o \
+ lx_getrandom.o \
+ lx_id.o \
+ lx_ioctl.o \
+ lx_ioprio.o \
+ lx_kill.o \
+ lx_link.o \
+ lx_lseek.o \
+ lx_misc.o \
+ lx_miscsys.o \
+ lx_mkdir.o \
+ lx_modify_ldt.o \
+ lx_mount.o \
+ lx_open.o \
+ lx_personality.o \
+ lx_pgrp.o \
+ lx_pid.o \
+ lx_pipe.o \
+ lx_poll.o \
+ lx_prctl.o \
+ lx_ptrace.o \
+ lx_rename.o \
+ lx_rlimit.o \
+ lx_rw.o \
+ lx_sched.o \
+ lx_signal.o \
+ lx_signum.o \
+ lx_socket.o \
+ lx_stat.o \
+ lx_sync.o \
+ lx_syscall.o \
+ lx_sysinfo.o \
+ lx_thread_area.o \
+ lx_timer.o \
+ lx_umask.o \
+ lx_uname.o \
+ lx_wait.o \
+ lx_xattr.o
#
# special files
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index a1cabf9407..a71a50d516 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -46,6 +46,7 @@ PLATFORM = i86pc
#
UNIX_DIR = $(UTSBASE)/i86pc/unix
GENLIB_DIR = $(UTSBASE)/intel/genunix
+GENASSYM_DIR = $(UTSBASE)/intel/genassym
IPDRV_DIR = $(UTSBASE)/intel/ip
MODSTUBS_DIR = $(UNIX_DIR)
DSF_DIR = $(UTSBASE)/$(PLATFORM)/genassym
@@ -139,6 +140,7 @@ ASFLAGS_XARCH_64 = $(amd64_ASFLAGS)
ASFLAGS_XARCH = $(ASFLAGS_XARCH_$(CLASS))
ASFLAGS += $(ASFLAGS_XARCH)
+AS_INC_PATH += -I$(GENASSYM_DIR)/$(OBJS_DIR)
#
# Define the base directory for installation.
@@ -240,6 +242,7 @@ DRV_KMODS += devinfo
DRV_KMODS += dld
DRV_KMODS += dlpistub
DRV_KMODS_32 += dnet
+DRV_KMODS += dr_sas
DRV_KMODS += dump
DRV_KMODS += ecpp
DRV_KMODS += emlxs
@@ -253,6 +256,7 @@ DRV_KMODS += i8042
DRV_KMODS += i915
DRV_KMODS += icmp
DRV_KMODS += icmp6
+DRV_KMODS += inotify
DRV_KMODS += intel_nb5000
DRV_KMODS += intel_nhm
DRV_KMODS += ip
@@ -287,6 +291,7 @@ DRV_KMODS += mpt_sas
DRV_KMODS += mr_sas
DRV_KMODS += mwl
DRV_KMODS += nca
+DRV_KMODS += nfp
DRV_KMODS += nsmb
DRV_KMODS += nulldriver
DRV_KMODS += nv_sata
@@ -356,6 +361,8 @@ DRV_KMODS += uath
DRV_KMODS += urtw
DRV_KMODS += vgatext
DRV_KMODS += heci
+DRV_KMODS += vmxnet
+DRV_KMODS += vnd
DRV_KMODS += vnic
DRV_KMODS += vscan
DRV_KMODS += wc
@@ -364,8 +371,10 @@ DRV_KMODS += wpi
DRV_KMODS += xge
DRV_KMODS += yge
DRV_KMODS += zcons
+DRV_KMODS += zfd
DRV_KMODS += zyd
DRV_KMODS += simnet
+DRV_KMODS += smrt
DRV_KMODS += stmf
DRV_KMODS += stmf_sbd
DRV_KMODS += fct
@@ -493,6 +502,15 @@ DRV_KMODS += usbftdi
DRV_KMODS += usbecm
#
+# USBGEM modules
+#
+DRV_KMODS += usbgem
+DRV_KMODS += axf
+DRV_KMODS += udmf
+DRV_KMODS += upf
+DRV_KMODS += urf
+
+#
# 1394 modules
#
MISC_KMODS += s1394 sbp2
@@ -516,7 +534,8 @@ MISC_KMODS += md_sp
#
# Brand modules
#
-BRAND_KMODS += sn1_brand s10_brand
+BRAND_KMODS += sn1_brand s10_brand lx_brand
+DRV_KMODS += lx_systrace lx_ptm lx_netlink
#
# Exec Class Modules (/kernel/exec):
@@ -531,10 +550,10 @@ SCHED_KMODS += IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL SDC
#
# File System Modules (/kernel/fs):
#
-FS_KMODS += autofs ctfs dcfs dev devfs fdfs fifofs hsfs lofs
-FS_KMODS += mntfs namefs nfs objfs zfs zut
-FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs
-FS_KMODS += smbfs bootfs
+FS_KMODS += autofs ctfs dcfs dev devfs fdfs fifofs hsfs hyprlofs
+FS_KMODS += lofs lxautofs lx_proc lxprocfs mntfs namefs nfs objfs zfs zut
+FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs lx_sysfs
+FS_KMODS += smbfs bootfs lx_cgroup lx_devfs
#
# Streams Modules (/kernel/strmod):
@@ -594,6 +613,7 @@ MISC_KMODS += drm
MISC_KMODS += fssnap_if
MISC_KMODS += gda
MISC_KMODS += gld
+MISC_KMODS += gsqueue
MISC_KMODS += hidparser
MISC_KMODS += hook
MISC_KMODS += hpcsvc
@@ -710,6 +730,12 @@ MAC_KMODS += mac_wifi
MAC_KMODS += mac_ib
#
+# Overlay related modules (/kernel/overlay)
+#
+DRV_KMODS += overlay
+OVERLAY_KMODS += vxlan
+
+#
# socketmod (kernel/socketmod)
#
SOCKET_KMODS += sockpfp
@@ -717,6 +743,7 @@ SOCKET_KMODS += socksctp
SOCKET_KMODS += socksdp
SOCKET_KMODS += sockrds
SOCKET_KMODS += ksslf
+SOCKET_KMODS += datafilt
#
# kiconv modules (/kernel/kiconv):
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index 2b21ba76c7..2c00dd3c1d 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -178,6 +178,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/intel_nb5000/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/vmxnet/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/intel_nhm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -466,6 +470,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/scsi/targets/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/vgatext/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/vmxnet/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/io/heci/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/acpica/Makefile b/usr/src/uts/intel/acpica/Makefile
index c901c5d2ba..a72d55bacb 100644
--- a/usr/src/uts/intel/acpica/Makefile
+++ b/usr/src/uts/intel/acpica/Makefile
@@ -2,6 +2,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the ACPI CA services
# kernel module.
@@ -60,6 +62,7 @@ LINTFLAGS += -errwarn=%none
CERRWARN += -_gcc=-Wno-unused-variable
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/intel/axf/Makefile b/usr/src/uts/intel/axf/Makefile
new file mode 100644
index 0000000000..e4665c0186
--- /dev/null
+++ b/usr/src/uts/intel/axf/Makefile
@@ -0,0 +1,73 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = axf
+OBJECTS = $(AXF_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(AXF_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CPPFLAGS += -I$(UTSBASE)/common/io/usbgem
+CPPFLAGS += -DVERSION=\"2.0.2\"
+CPPFLAGS += -DUSBGEM_CONFIG_GLDv3
+LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem
+
+CERRWARN += -_gcc=-Wno-unused-function
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/bpf/Makefile b/usr/src/uts/intel/bpf/Makefile
index b7136e61db..8986871c95 100644
--- a/usr/src/uts/intel/bpf/Makefile
+++ b/usr/src/uts/intel/bpf/Makefile
@@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
+LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
INC_PATH += -I$(UTSBASE)/common/io/bpf
#
diff --git a/usr/src/uts/intel/brand/common/brand_asm.h b/usr/src/uts/intel/brand/common/brand_asm.h
index 1d540db2a9..c820d8e187 100644
--- a/usr/src/uts/intel/brand/common/brand_asm.h
+++ b/usr/src/uts/intel/brand/common/brand_asm.h
@@ -161,7 +161,7 @@ extern "C" {
#define GET_P_BRAND_DATA(sp, pcnt, reg) \
GET_PROCP(sp, pcnt, reg); \
- mov P_BRAND_DATA(reg), reg /* get p_brand_data */
+ mov __P_BRAND_DATA(reg), reg /* get p_brand_data */
/*
* Each of the following macros returns to the standard syscall codepath if
diff --git a/usr/src/uts/intel/brand/lx/lx_archdep.c b/usr/src/uts/intel/brand/lx/lx_archdep.c
new file mode 100644
index 0000000000..5d2c33ab9f
--- /dev/null
+++ b/usr/src/uts/intel/brand/lx/lx_archdep.c
@@ -0,0 +1,1720 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * LX brand Intel-specific routines.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/privregs.h>
+#include <sys/pcb.h>
+#include <sys/archsystm.h>
+#include <sys/stack.h>
+#include <sys/sdt.h>
+#include <sys/sysmacros.h>
+#include <sys/psw.h>
+#include <lx_errno.h>
+
+/*
+ * Argument constants for fix_segreg.
+ * See usr/src/uts/intel/ia32/os/archdep.c for the originals.
+ */
+#define IS_CS 1
+#define IS_NOT_CS 0
+
+extern greg_t fix_segreg(greg_t, int, model_t);
+
+
+#define LX_REG(ucp, r) ((ucp)->uc_mcontext.gregs[(r)])
+
+#define PSLMERGE(oldval, newval) \
+ (((oldval) & ~PSL_USERMASK) | ((newval) & PSL_USERMASK))
+
+#ifdef __amd64
+/* 64-bit native user_regs_struct */
+typedef struct lx_user_regs64 {
+ int64_t lxur_r15;
+ int64_t lxur_r14;
+ int64_t lxur_r13;
+ int64_t lxur_r12;
+ int64_t lxur_rbp;
+ int64_t lxur_rbx;
+ int64_t lxur_r11;
+ int64_t lxur_r10;
+ int64_t lxur_r9;
+ int64_t lxur_r8;
+ int64_t lxur_rax;
+ int64_t lxur_rcx;
+ int64_t lxur_rdx;
+ int64_t lxur_rsi;
+ int64_t lxur_rdi;
+ int64_t lxur_orig_rax;
+ int64_t lxur_rip;
+ int64_t lxur_xcs;
+ int64_t lxur_rflags;
+ int64_t lxur_rsp;
+ int64_t lxur_xss;
+ int64_t lxur_xfs_base;
+ int64_t lxur_xgs_base;
+ int64_t lxur_xds;
+ int64_t lxur_xes;
+ int64_t lxur_xfs;
+ int64_t lxur_xgs;
+} lx_user_regs64_t;
+
+/* 64-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs64 {
+ uint16_t lxufp_cwd;
+ uint16_t lxufp_swd;
+ uint16_t lxufp_ftw;
+ uint16_t lxufp_fop;
+ uint64_t lxufp_rip;
+ uint64_t lxufp_rdp;
+ uint32_t lxufp_mxcsr;
+ uint32_t lxufp_mxcr_mask;
+ /* 8*16 bytes for each FP-reg = 128 bytes */
+ uint32_t lxufp_st_space[32];
+ /* 16*16 bytes for each XMM-reg = 256 bytes */
+ uint32_t lxufp_xmm_space[64];
+ uint32_t lxufp_padding[24];
+} lx_user_fpregs64_t;
+
+/* 64-bit native user_struct */
+typedef struct lx_user64 {
+ lx_user_regs64_t lxu_regs;
+ int32_t lxu_fpvalid;
+ int32_t lxu_pad0;
+ lx_user_fpregs64_t lxu_i387;
+ uint64_t lxu_tsize;
+ uint64_t lxu_dsize;
+ uint64_t lxu_ssize;
+ uint64_t lxu_start_code;
+ uint64_t lxu_start_stack;
+ int64_t lxu_signal;
+ int32_t lxu_reserved;
+ int32_t lxu_pad1;
+ /* help gdb to locate user_regs structure */
+ caddr_t lxu_ar0;
+ /* help gdb to locate user_fpregs structure */
+ caddr_t lxu_fpstate;
+ uint64_t lxu_magic;
+ char lxu_comm[32];
+ uint64_t lxu_debugreg[8];
+ uint64_t lxu_error_code;
+ uint64_t lxu_fault_address;
+} lx_user64_t;
+
+#endif /* __amd64 */
+
+/* 32-bit native user_regs_struct */
+typedef struct lx_user_regs32 {
+ int32_t lxur_ebx;
+ int32_t lxur_ecx;
+ int32_t lxur_edx;
+ int32_t lxur_esi;
+ int32_t lxur_edi;
+ int32_t lxur_ebp;
+ int32_t lxur_eax;
+ int32_t lxur_xds;
+ int32_t lxur_xes;
+ int32_t lxur_xfs;
+ int32_t lxur_xgs;
+ int32_t lxur_orig_eax;
+ int32_t lxur_eip;
+ int32_t lxur_xcs;
+ int32_t lxur_eflags;
+ int32_t lxur_esp;
+ int32_t lxur_xss;
+} lx_user_regs32_t;
+
+/* 32-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs32 {
+ int32_t lxufp_cwd;
+ int32_t lxufp_swd;
+ int32_t lxufp_twd;
+ int32_t lxufp_fip;
+ int32_t lxufp_fcs;
+ int32_t lxufp_foo;
+ int32_t lxufp_fos;
+ int32_t lxufp_st_space[20];
+} lx_user_fpregs32_t;
+
+/* 32-bit native user_fpxregs_struct */
+typedef struct lx_user_fpxregs32 {
+ uint16_t lxufpx_cwd;
+ uint16_t lxufpx_swd;
+ uint16_t lxufpx_twd;
+ uint16_t lxufpx_fop;
+ int32_t lxufpx_fip;
+ int32_t lxufpx_fcs;
+ int32_t lxufpx_foo;
+ int32_t lxufpx_fos;
+ int32_t lxufpx_mxcsr;
+ int32_t lxufpx_reserved;
+ /* 8*16 bytes for each FP-reg = 128 bytes */
+ int32_t lxufpx_st_space[32];
+ /* 8*16 bytes for each XMM-reg = 128 bytes */
+ int32_t lxufpx_xmm_space[32];
+ int32_t lxufpx_padding[56];
+} lx_user_fpxregs32_t;
+
+/* 32-bit native user_struct */
+typedef struct lx_user32 {
+ lx_user_regs32_t lxu_regs;
+ int32_t lxu_fpvalid;
+ lx_user_fpregs32_t lxu_i387;
+ uint32_t lxu_tsize;
+ uint32_t lxu_dsize;
+ uint32_t lxu_ssize;
+ uint32_t lxu_start_code;
+ uint32_t lxu_start_stack;
+ int32_t lxu_signal;
+ int32_t lxu_reserved;
+ caddr32_t lxu_ar0;
+ caddr32_t lxu_fpstate;
+ uint32_t lxu_magic;
+ char lxu_comm[32];
+ int32_t lxu_debugreg[8];
+} lx_user32_t;
+
+/*
+ * Certain version of strace (on centos6 for example) use the %cs value to
+ * determine what kind of process is being traced. Here is a sample comment:
+ * Check CS register value. On x86-64 linux it is:
+ * 0x33 for long mode (64 bit and x32))
+ * 0x23 for compatibility mode (32 bit)
+ * %ds = 0x2b for x32 mode (x86-64 in 32 bit)
+ * We can't change the %cs value in the ucp (see setgregs and _sys_rtt) so we
+ * emulate the expected value for ptrace use.
+ */
+#define LX_CS_64BIT 0x33
+#define LX_CS_32BIT 0x23
+
+extern int getsetcontext(int, void *);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+#endif
+
+static int
+lx_rw_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz, boolean_t writing)
+{
+ int error = 0;
+ size_t rem = ucsz;
+ off_t pos = 0;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Grab P_PR_LOCK so that we can drop p_lock while doing I/O.
+ */
+ sprlock_proc(p);
+
+ /*
+ * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+ while (rem != 0) {
+ uintptr_t addr = (uintptr_t)ucp + pos;
+ size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+ if (writing) {
+ error = uwrite(p, (caddr_t)kucp + pos, len, addr);
+ } else {
+ error = uread(p, (caddr_t)kucp + pos, len, addr);
+ }
+
+ if (error != 0) {
+ break;
+ }
+
+ rem -= len;
+ pos += len;
+ }
+ mutex_enter(&p->p_lock);
+
+ sprunlock(p);
+ mutex_enter(&p->p_lock);
+
+ return (error);
+}
+
+/*
+ * Read a ucontext_t from the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_read_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+ return (lx_rw_uc(p, ucp, kucp, ucsz, B_FALSE));
+}
+
+/*
+ * Write a ucontext_t to the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_write_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+ return (lx_rw_uc(p, ucp, kucp, ucsz, B_TRUE));
+}
+
+static void
+lx_getfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+ fpregset32_t fp;
+ getfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+ fpregset_t fp;
+ getfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+
+ /*
+ * The fpchip_state.state field should correspond to all 27 fields in
+ * the 32-bit structure.
+ */
+ bcopy(&fp.fp_reg_set.fpchip_state.state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+ fpregset32_t fp;
+#else /* __i386 */
+ fpregset_t fp;
+#endif /* __amd64 */
+
+ /*
+ * The fpchip_state field should correspond to all 27 fields in the
+ * native 32-bit structure.
+ */
+ bcopy(lfp, &fp.fp_reg_set.fpchip_state.state, sizeof (*lfp));
+
+#ifdef __amd64
+ setfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+ setfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+}
+
+static int
+lx_get_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_ebx = LX_REG(&uc, EBX);
+ lxrp->lxur_ecx = LX_REG(&uc, ECX);
+ lxrp->lxur_edx = LX_REG(&uc, EDX);
+ lxrp->lxur_esi = LX_REG(&uc, ESI);
+ lxrp->lxur_edi = LX_REG(&uc, EDI);
+ lxrp->lxur_ebp = LX_REG(&uc, EBP);
+ lxrp->lxur_eax = LX_REG(&uc, EAX);
+ lxrp->lxur_orig_eax = 0;
+
+ lxrp->lxur_eip = LX_REG(&uc, EIP);
+ lxrp->lxur_eflags = LX_REG(&uc, EFL);
+ lxrp->lxur_esp = LX_REG(&uc, UESP);
+ lxrp->lxur_xss = LX_REG(&uc, SS);
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ lxrp->lxur_xds = LX_REG(&uc, DS);
+ lxrp->lxur_xes = LX_REG(&uc, ES);
+ lxrp->lxur_xfs = LX_REG(&uc, FS);
+ lxrp->lxur_xgs = LX_REG(&uc, GS);
+ return (0);
+}
+
+static int
+lx_get_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ void *ucp;
+#ifdef __amd64
+ struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+ VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+ switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_get_user_regs32_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+#ifdef __amd64
+ lxrp->lxur_ebx = (int32_t)rp->r_rbx;
+ lxrp->lxur_ecx = (int32_t)rp->r_rcx;
+ lxrp->lxur_edx = (int32_t)rp->r_rdx;
+ lxrp->lxur_esi = (int32_t)rp->r_rsi;
+ lxrp->lxur_edi = (int32_t)rp->r_rdi;
+ lxrp->lxur_ebp = (int32_t)rp->r_rbp;
+ lxrp->lxur_eax = (int32_t)rp->r_rax;
+ lxrp->lxur_orig_eax = 0;
+ lxrp->lxur_eip = (int32_t)rp->r_rip;
+ lxrp->lxur_eflags = (int32_t)rp->r_rfl;
+ lxrp->lxur_esp = (int32_t)rp->r_rsp;
+ lxrp->lxur_xss = (int32_t)rp->r_ss;
+
+ kpreempt_disable();
+ if (pcb->pcb_rupdate == 1) {
+ lxrp->lxur_xds = pcb->pcb_ds;
+ lxrp->lxur_xes = pcb->pcb_es;
+ lxrp->lxur_xfs = pcb->pcb_fs;
+ lxrp->lxur_xgs = pcb->pcb_gs;
+ } else {
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+ }
+ kpreempt_enable();
+#else /* __i386 */
+ lxrp->lxur_ebx = rp->r_ebx;
+ lxrp->lxur_ecx = rp->r_ecx;
+ lxrp->lxur_edx = rp->r_edx;
+ lxrp->lxur_esi = rp->r_esi;
+ lxrp->lxur_edi = rp->r_edi;
+ lxrp->lxur_ebp = rp->r_ebp;
+ lxrp->lxur_eax = rp->r_eax;
+ lxrp->lxur_orig_eax = 0;
+ lxrp->lxur_eip = rp->r_eip;
+ lxrp->lxur_eflags = rp->r_efl;
+ lxrp->lxur_esp = rp->r_esp;
+ lxrp->lxur_xss = rp->r_ss;
+
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+#endif /* __amd64 */
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+ lxrp->lxur_eax = (int32_t)-lx_errno(ENOTSUP, EINVAL);
+ lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+ } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+ }
+
+ return (0);
+}
+
+static int
+lx_set_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, EBP) = lxrp->lxur_ebp;
+ LX_REG(&uc, EBX) = lxrp->lxur_ebx;
+ LX_REG(&uc, EAX) = lxrp->lxur_eax;
+ LX_REG(&uc, ECX) = lxrp->lxur_ecx;
+ LX_REG(&uc, EDX) = lxrp->lxur_edx;
+ LX_REG(&uc, ESI) = lxrp->lxur_esi;
+ LX_REG(&uc, EDI) = lxrp->lxur_edi;
+ LX_REG(&uc, EIP) = lxrp->lxur_eip;
+ LX_REG(&uc, EFL) = PSLMERGE(LX_REG(&uc, EFL), lxrp->lxur_eflags);
+ LX_REG(&uc, UESP) = lxrp->lxur_esp;
+ LX_REG(&uc, SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lx_set_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ void *ucp;
+#ifdef __amd64
+ struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+ VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+ switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_set_user_regs32_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+#ifdef __amd64
+ rp->r_rbx = (int32_t)lxrp->lxur_ebx;
+ rp->r_rcx = (int32_t)lxrp->lxur_ecx;
+ rp->r_rdx = (int32_t)lxrp->lxur_edx;
+ rp->r_rsi = (int32_t)lxrp->lxur_esi;
+ rp->r_rdi = (int32_t)lxrp->lxur_edi;
+ rp->r_rbp = (int32_t)lxrp->lxur_ebp;
+ rp->r_rax = (int32_t)lxrp->lxur_eax;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+ rp->r_rip = (int32_t)lxrp->lxur_eip;
+ rp->r_rfl = (int32_t)PSLMERGE(rp->r_rfl, lxrp->lxur_eflags);
+ rp->r_rsp = (int32_t)lxrp->lxur_esp;
+ rp->r_ss = (int32_t)fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ kpreempt_disable();
+ pcb->pcb_rupdate = 1;
+ pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+ kpreempt_enable();
+#else /* __i386 */
+ rp->r_ebx = lxrp->lxur_ebx;
+ rp->r_ecx = lxrp->lxur_ecx;
+ rp->r_edx = lxrp->lxur_edx;
+ rp->r_esi = lxrp->lxur_esi;
+ rp->r_edi = lxrp->lxur_edi;
+ rp->r_ebp = lxrp->lxur_ebp;
+ rp->r_eax = lxrp->lxur_eax;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+ rp->r_eip = lxrp->lxur_eip;
+ rp->r_efl = PSLMERGE(rp->r_efl, lxrp->lxur_eflags);
+ rp->r_esp = lxrp->lxur_esp;
+ rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_ILP32);
+
+ rp->r_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+#endif /* __amd64 */
+
+ return (0);
+}
+
+#ifdef __amd64
+
+static void
+lx_getfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+ fpregset_t fp;
+
+ getfpregs(lwpd->br_lwp, &fp);
+ /* Drop the extra illumos status/xstatus fields when copying state */
+ bcopy(&fp.fp_reg_set.fpchip_state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+ fpregset_t fp;
+
+ /*
+ * Since the Linux fpregs structure does not contain the same
+ * additional status register which illumos contains, we simply
+ * preserve the existing values when setting fp state.
+ */
+ getfpregs(lwpd->br_lwp, &fp);
+
+ /* Copy the identically formatted state */
+ bcopy(lfp, &fp.fp_reg_set.fpchip_state, sizeof (*lfp));
+
+ setfpregs(lwpd->br_lwp, &fp);
+}
+
+static int
+lx_get_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64: {
+ ucontext_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_r15 = LX_REG(&uc, REG_R15);
+ lxrp->lxur_r14 = LX_REG(&uc, REG_R14);
+ lxrp->lxur_r13 = LX_REG(&uc, REG_R13);
+ lxrp->lxur_r12 = LX_REG(&uc, REG_R12);
+ lxrp->lxur_rbp = LX_REG(&uc, REG_RBP);
+ lxrp->lxur_rbx = LX_REG(&uc, REG_RBX);
+ lxrp->lxur_r11 = LX_REG(&uc, REG_R11);
+ lxrp->lxur_r10 = LX_REG(&uc, REG_R10);
+ lxrp->lxur_r9 = LX_REG(&uc, REG_R9);
+ lxrp->lxur_r8 = LX_REG(&uc, REG_R8);
+ lxrp->lxur_rax = LX_REG(&uc, REG_RAX);
+ lxrp->lxur_rcx = LX_REG(&uc, REG_RCX);
+ lxrp->lxur_rdx = LX_REG(&uc, REG_RDX);
+ lxrp->lxur_rsi = LX_REG(&uc, REG_RSI);
+ lxrp->lxur_rdi = LX_REG(&uc, REG_RDI);
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rip = LX_REG(&uc, REG_RIP);
+ lxrp->lxur_rflags = LX_REG(&uc, REG_RFL);
+ lxrp->lxur_rsp = LX_REG(&uc, REG_RSP);
+ lxrp->lxur_xss = LX_REG(&uc, REG_SS);
+ lxrp->lxur_xfs_base = LX_REG(&uc, REG_FSBASE);
+ lxrp->lxur_xgs_base = LX_REG(&uc, REG_GSBASE);
+
+ lxrp->lxur_xds = LX_REG(&uc, REG_DS);
+ lxrp->lxur_xes = LX_REG(&uc, REG_ES);
+ lxrp->lxur_xfs = LX_REG(&uc, REG_FS);
+ lxrp->lxur_xgs = LX_REG(&uc, REG_GS);
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_64BIT;
+ return (0);
+ }
+
+ case DATAMODEL_ILP32: {
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_r15 = 0;
+ lxrp->lxur_r14 = 0;
+ lxrp->lxur_r13 = 0;
+ lxrp->lxur_r12 = 0;
+ lxrp->lxur_r11 = 0;
+ lxrp->lxur_r10 = 0;
+ lxrp->lxur_r9 = 0;
+ lxrp->lxur_r8 = 0;
+ lxrp->lxur_rbp = LX_REG(&uc, EBP);
+ lxrp->lxur_rbx = LX_REG(&uc, EBX);
+ lxrp->lxur_rax = LX_REG(&uc, EAX);
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rcx = LX_REG(&uc, ECX);
+ lxrp->lxur_rdx = LX_REG(&uc, EDX);
+ lxrp->lxur_rsi = LX_REG(&uc, ESI);
+ lxrp->lxur_rdi = LX_REG(&uc, EDI);
+ lxrp->lxur_rip = LX_REG(&uc, EIP);
+
+ lxrp->lxur_rflags = LX_REG(&uc, EFL);
+ lxrp->lxur_rsp = LX_REG(&uc, UESP);
+ lxrp->lxur_xss = LX_REG(&uc, SS);
+ lxrp->lxur_xfs_base = 0;
+ lxrp->lxur_xgs_base = 0;
+
+ lxrp->lxur_xds = LX_REG(&uc, DS);
+ lxrp->lxur_xes = LX_REG(&uc, ES);
+ lxrp->lxur_xfs = LX_REG(&uc, FS);
+ lxrp->lxur_xgs = LX_REG(&uc, GS);
+
+ /* See comment above re: %cs register */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+static int
+lx_get_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ struct pcb *pcb = &lwp->lwp_pcb;
+ void *ucp;
+
+ switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_get_user_regs64_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ lxrp->lxur_r15 = rp->r_r15;
+ lxrp->lxur_r14 = rp->r_r14;
+ lxrp->lxur_r13 = rp->r_r13;
+ lxrp->lxur_r12 = rp->r_r12;
+ lxrp->lxur_rbp = rp->r_rbp;
+ lxrp->lxur_rbx = rp->r_rbx;
+ lxrp->lxur_r11 = rp->r_r11;
+ lxrp->lxur_r10 = rp->r_r10;
+ lxrp->lxur_r9 = rp->r_r9;
+ lxrp->lxur_r8 = rp->r_r8;
+ lxrp->lxur_rax = rp->r_rax;
+ lxrp->lxur_rcx = rp->r_rcx;
+ lxrp->lxur_rdx = rp->r_rdx;
+ lxrp->lxur_rsi = rp->r_rsi;
+ lxrp->lxur_rdi = rp->r_rdi;
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rip = rp->r_rip;
+
+ lxrp->lxur_rflags = rp->r_rfl;
+ lxrp->lxur_rsp = rp->r_rsp;
+ lxrp->lxur_xss = rp->r_ss;
+ lxrp->lxur_xfs_base = pcb->pcb_fsbase;
+ lxrp->lxur_xgs_base = pcb->pcb_gsbase;
+
+ /* emulated %cs, see defines */
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64:
+ lxrp->lxur_xcs = LX_CS_64BIT;
+ break;
+ case DATAMODEL_ILP32:
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ break;
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ kpreempt_disable();
+ if (pcb->pcb_rupdate == 1) {
+ lxrp->lxur_xds = pcb->pcb_ds;
+ lxrp->lxur_xes = pcb->pcb_es;
+ lxrp->lxur_xfs = pcb->pcb_fs;
+ lxrp->lxur_xgs = pcb->pcb_gs;
+ } else {
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+ }
+ kpreempt_enable();
+
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+ lxrp->lxur_rax = -lx_errno(ENOTSUP, EINVAL);
+ lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+ } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+ }
+
+ return (0);
+}
+
+static int
+lx_set_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64: {
+ ucontext_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, REG_R15) = lxrp->lxur_r15;
+ LX_REG(&uc, REG_R14) = lxrp->lxur_r14;
+ LX_REG(&uc, REG_R13) = lxrp->lxur_r13;
+ LX_REG(&uc, REG_R12) = lxrp->lxur_r12;
+ LX_REG(&uc, REG_RBP) = lxrp->lxur_rbp;
+ LX_REG(&uc, REG_RBX) = lxrp->lxur_rbx;
+ LX_REG(&uc, REG_R11) = lxrp->lxur_r11;
+ LX_REG(&uc, REG_R10) = lxrp->lxur_r10;
+ LX_REG(&uc, REG_R9) = lxrp->lxur_r9;
+ LX_REG(&uc, REG_R8) = lxrp->lxur_r8;
+ LX_REG(&uc, REG_RAX) = lxrp->lxur_rax;
+ LX_REG(&uc, REG_RCX) = lxrp->lxur_rcx;
+ LX_REG(&uc, REG_RDX) = lxrp->lxur_rdx;
+ LX_REG(&uc, REG_RSI) = lxrp->lxur_rsi;
+ LX_REG(&uc, REG_RDI) = lxrp->lxur_rdi;
+ LX_REG(&uc, REG_RIP) = lxrp->lxur_rip;
+ LX_REG(&uc, REG_RFL) = PSLMERGE(LX_REG(&uc, REG_RFL),
+ lxrp->lxur_rflags);
+ LX_REG(&uc, REG_RSP) = lxrp->lxur_rsp;
+ LX_REG(&uc, REG_SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_FSBASE) = lxrp->lxur_xfs_base;
+ LX_REG(&uc, REG_GSBASE) = lxrp->lxur_xgs_base;
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, REG_DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+ DATAMODEL_LP64);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ case DATAMODEL_ILP32: {
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, EBP) = (int32_t)lxrp->lxur_rbp;
+ LX_REG(&uc, EBX) = (int32_t)lxrp->lxur_rbx;
+ LX_REG(&uc, EAX) = (int32_t)lxrp->lxur_rax;
+ LX_REG(&uc, ECX) = (int32_t)lxrp->lxur_rcx;
+ LX_REG(&uc, EDX) = (int32_t)lxrp->lxur_rdx;
+ LX_REG(&uc, ESI) = (int32_t)lxrp->lxur_rsi;
+ LX_REG(&uc, EDI) = (int32_t)lxrp->lxur_rdi;
+ LX_REG(&uc, EIP) = (int32_t)lxrp->lxur_rip;
+ LX_REG(&uc, EFL) = (int32_t)PSLMERGE(LX_REG(&uc, EFL),
+ lxrp->lxur_rflags);
+ LX_REG(&uc, UESP) = (int32_t)lxrp->lxur_rsp;
+ LX_REG(&uc, SS) = (int32_t)fix_segreg(lxrp->lxur_xss,
+ IS_NOT_CS, DATAMODEL_ILP32);
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, DS) = (int32_t)fix_segreg(lxrp->lxur_xds,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, ES) = (int32_t)fix_segreg(lxrp->lxur_xes,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, FS) = (int32_t)fix_segreg(lxrp->lxur_xfs,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, GS) = (int32_t)fix_segreg(lxrp->lxur_xgs,
+ IS_NOT_CS, DATAMODEL_ILP32);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+static int
+lx_set_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ struct pcb *pcb = &lwp->lwp_pcb;
+ void *ucp;
+
+ switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_set_user_regs64_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ rp->r_r15 = lxrp->lxur_r15;
+ rp->r_r14 = lxrp->lxur_r14;
+ rp->r_r13 = lxrp->lxur_r13;
+ rp->r_r12 = lxrp->lxur_r12;
+ rp->r_rbp = lxrp->lxur_rbp;
+ rp->r_rbx = lxrp->lxur_rbx;
+ rp->r_r11 = lxrp->lxur_r11;
+ rp->r_r10 = lxrp->lxur_r10;
+ rp->r_r9 = lxrp->lxur_r9;
+ rp->r_r8 = lxrp->lxur_r8;
+ rp->r_rax = lxrp->lxur_rax;
+ rp->r_rcx = lxrp->lxur_rcx;
+ rp->r_rdx = lxrp->lxur_rdx;
+ rp->r_rsi = lxrp->lxur_rsi;
+ rp->r_rdi = lxrp->lxur_rdi;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_rax;
+ rp->r_rip = lxrp->lxur_rip;
+ rp->r_rfl = PSLMERGE(rp->r_rfl, lxrp->lxur_rflags);
+ rp->r_rsp = lxrp->lxur_rsp;
+ rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_fsbase = lxrp->lxur_xfs_base;
+ pcb->pcb_gsbase = lxrp->lxur_xgs_base;
+
+ kpreempt_disable();
+ pcb->pcb_rupdate = 1;
+ pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_LP64);
+ kpreempt_enable();
+
+ return (0);
+}
+
+#endif /* __amd64 */
+
+static int
+lx_peekuser32(lx_lwp_data_t *lwpd, uintptr_t offset, uint32_t *res)
+{
+ lx_user32_t lxu;
+ boolean_t valid = B_FALSE;
+
+ bzero(&lxu, sizeof (lxu));
+ if (offset < sizeof (lx_user_regs32_t)) {
+ if (lx_get_user_regs32(lwpd, &lxu.lxu_regs) == 0) {
+ valid = B_TRUE;
+ }
+ }
+ if (valid) {
+ uint32_t *data = (uint32_t *)&lxu;
+ *res = data[offset / sizeof (uint32_t)];
+ return (0);
+ }
+ return (-1);
+}
+
+#ifdef __amd64
+static int
+lx_peekuser64(lx_lwp_data_t *lwpd, uintptr_t offset, uintptr_t *res)
+{
+ lx_user64_t lxu;
+ boolean_t valid = B_FALSE;
+
+ bzero(&lxu, sizeof (lxu));
+ if (offset < sizeof (lx_user_regs64_t)) {
+ if (lx_get_user_regs64(lwpd, &lxu.lxu_regs) == 0) {
+ valid = B_TRUE;
+ }
+ }
+ if (valid) {
+ uintptr_t *data = (uintptr_t *)&lxu;
+ *res = data[offset / sizeof (uintptr_t)];
+ return (0);
+ }
+ return (-1);
+}
+#endif /* __amd64 */
+
+int
+lx_user_regs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_regs32_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ if (lx_set_user_regs32(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_regs64_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ if (lx_set_user_regs64(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_regs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_regs32_t regs;
+
+ if (lx_get_user_regs32(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_regs64_t regs;
+
+ if (lx_get_user_regs64(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_fpregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_fpregs32_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ lx_setfpregs32(lwpd, &regs);
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_fpregs64_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ lx_setfpregs64(lwpd, &regs);
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_fpregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_fpregs32_t regs;
+
+ lx_getfpregs32(lwpd, &regs);
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_fpregs64_t regs;
+
+ lx_getfpregs64(lwpd, &regs);
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ /* Punt on fpxregs for now */
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ /* Punt on fpxregs for now */
+ return (EIO);
+}
+
+int
+lx_ptrace_peekuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if ((offset & (sizeof (uint32_t) - 1)) != 0) {
+ /* Must be aligned to 32bit boundary */
+ break;
+ }
+ if (target_model == DATAMODEL_ILP32) {
+ uint32_t res;
+
+ if (lx_peekuser32(lwpd, offset, &res) != 0) {
+ return (EIO);
+ }
+ if (copyout(&res, uptr, sizeof (res)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if ((offset & (sizeof (uintptr_t) - 1)) != 0) {
+ /* Must be aligned to 64bit boundary */
+ break;
+ }
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ uintptr_t res;
+
+ if (lx_peekuser64(lwpd, offset, &res) != 0) {
+ return (EIO);
+ }
+ if (copyout(&res, uptr, sizeof (res)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_ptrace_pokeuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+ return (EIO);
+}
+
+
+/*
+ * Load registers and repoint the stack and program counter. This function is
+ * used by the B_JUMP_TO_LINUX brand system call to revector to a Linux
+ * entrypoint.
+ */
+int
+lx_runexe(klwp_t *lwp, void *ucp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * We should only make it here when transitioning to Linux from
+ * the NATIVE or INIT mode.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_NATIVE ||
+ lwpd->br_stack_mode == LX_STACK_MODE_INIT);
+
+#if defined(__amd64)
+ if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ /*
+ * Preserve the %fs/%gsbase value for this LWP, as set and used
+ * by native illumos code.
+ */
+ lwpd->br_ntv_fsbase = pcb->pcb_fsbase;
+ lwpd->br_ntv_gsbase = pcb->pcb_gsbase;
+
+ return (getsetcontext(SETCONTEXT, ucp));
+ } else {
+ return (getsetcontext32(SETCONTEXT, ucp));
+ }
+#else
+ return (getsetcontext(SETCONTEXT, ucp));
+#endif
+}
+
+/*
+ * The usermode emulation code is illumos library code. This routine ensures
+ * the segment registers are set up correctly for native illumos code. It
+ * should be called _after_ we have stored the outgoing Linux machine state
+ * but _before_ we return from the kernel to any illumos native code; e.g. the
+ * usermode emulation library, or any interposed signal handlers.
+ *
+ * See the comment on lwp_segregs_save() for how we handle the usermode
+ * registers when we come into the kernel and see update_sregs() for how we
+ * restore.
+ */
+void
+lx_switch_to_native(klwp_t *lwp)
+{
+#if defined(__amd64)
+ model_t datamodel = lwp_getdatamodel(lwp);
+
+ switch (datamodel) {
+ case DATAMODEL_ILP32: {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ /*
+ * For 32-bit processes, we ensure that the correct %gs value
+ * is loaded:
+ */
+ kpreempt_disable();
+ if (pcb->pcb_rupdate == 1) {
+ /*
+ * If we are already flushing the segment registers,
+ * then ensure we are flushing the native %gs.
+ */
+ pcb->pcb_gs = LWPGS_SEL;
+ } else {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * If we are not flushing the segment registers yet,
+ * only do so if %gs is not correct already:
+ */
+ if (rp->r_gs != LWPGS_SEL) {
+ pcb->pcb_gs = LWPGS_SEL;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ pcb->pcb_rupdate = 1;
+ }
+ }
+ kpreempt_enable();
+ break;
+ }
+
+ case DATAMODEL_LP64: {
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * For 64-bit processes we ensure that the correct %fsbase
+ * value is loaded:
+ */
+ if (lwpd->br_ntv_fsbase != 0) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ kpreempt_disable();
+ if (pcb->pcb_fsbase != lwpd->br_ntv_fsbase) {
+ pcb->pcb_fsbase = lwpd->br_ntv_fsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ pcb->pcb_rupdate = 1;
+ }
+ kpreempt_enable();
+ }
+ /*
+ * ... and the correct %gsbase
+ */
+ if (lwpd->br_ntv_gsbase != 0) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ kpreempt_disable();
+ if (pcb->pcb_gsbase != lwpd->br_ntv_gsbase) {
+ pcb->pcb_gsbase = lwpd->br_ntv_gsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ pcb->pcb_rupdate = 1;
+ }
+ kpreempt_enable();
+ }
+ break;
+ }
+
+ default:
+ cmn_err(CE_PANIC, "unknown data model: %d", datamodel);
+ }
+#elif defined(__i386)
+ struct regs *rp = lwptoregs(lwp);
+
+ rp->r_gs = LWPGS_SEL;
+#else
+#error "unknown x86"
+#endif
+}
+
+#if defined(__amd64)
+/*
+ * Call frame for the 64-bit usermode emulation handler:
+ * lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ * | - ucontext_t (register state for emulation)
+ * | - uintptr_t[6] (system call arguments array)
+ * V --------------------------------------------------------------
+ * new sp: - bogus return address
+ *
+ * Arguments are passed in registers, per the AMD64 ABI: %rdi, %rsi and %rdx.
+ */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ label_t lab;
+ uintptr_t uc_addr;
+ uintptr_t args_addr;
+ uintptr_t top;
+ /*
+ * Variables used after on_fault() returns for a fault
+ * must be volatile.
+ */
+ volatile size_t frsz;
+ volatile uintptr_t sp;
+ volatile proc_t *p = lwptoproc(lwp);
+ volatile int watched;
+
+ /*
+ * We should not be able to get here unless we are running Linux
+ * code for a system call we cannot emulate in the kernel.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+ /*
+ * The AMD64 ABI requires us to align the return address on the stack
+ * so that when the called function pushes %rbp, the stack is 16-byte
+ * aligned.
+ *
+ * This routine, like the amd64 version of sendsig(), depends on
+ * STACK_ALIGN being 16 and STACK_ENTRY_ALIGN being 8.
+ */
+#if STACK_ALIGN != 16 || STACK_ENTRY_ALIGN != 8
+#error "lx_emulate_user() amd64 did not find the expected stack alignments"
+#endif
+
+ /*
+ * We begin at the current native stack pointer, and reserve space for
+ * the ucontext_t we are copying onto the stack, as well as the call
+ * arguments for the usermode emulation handler.
+ *
+ * We 16-byte align the entire frame, and then unalign it again by
+ * adding space for the return address.
+ */
+ frsz = SA(sizeof (ucontext_t)) + SA(6 * sizeof (uintptr_t)) +
+ sizeof (uintptr_t);
+ VERIFY((frsz & (STACK_ALIGN - 1UL)) == 8);
+ VERIFY((frsz & (STACK_ENTRY_ALIGN - 1UL)) == 0);
+
+ if (lwpd->br_ntv_stack == lwpd->br_ntv_stack_current) {
+ /*
+ * Nobody else is using the stack right now, so start at the
+ * top.
+ */
+ top = lwpd->br_ntv_stack_current;
+ } else {
+ /*
+ * Drop below the 128-byte reserved region of the stack frame
+ * we are interrupting.
+ */
+ top = lwpd->br_ntv_stack_current - STACK_RESERVE;
+ }
+ top = top & ~(STACK_ALIGN - 1);
+ sp = top - frsz;
+
+ uc_addr = top - SA(sizeof (ucontext_t));
+ args_addr = uc_addr - SA(6 * sizeof (uintptr_t));
+
+ watched = watch_disable_addr((caddr_t)sp, frsz, S_WRITE);
+
+ /*
+ * Save the register state we preserved on the way into this brand
+ * system call and drop it on the native stack.
+ */
+ {
+ /*
+ * Note: the amd64 ucontext_t is 864 bytes.
+ */
+ ucontext_t uc;
+
+ /*
+ * We do not want to save the signal mask for an emulation
+ * context. Some emulated system calls alter the signal mask;
+ * restoring it when the emulation is complete would clobber
+ * those intentional side effects.
+ */
+ savecontext(&uc, NULL);
+
+ if (on_fault(&lab)) {
+ goto badstack;
+ }
+
+ /*
+ * Mark this as a system call emulation context:
+ */
+ uc.uc_brand_data[0] = (void *)((uintptr_t)
+ uc.uc_brand_data[0] | LX_UC_FRAME_IS_SYSCALL);
+
+ copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+ lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+ /*
+ * Copy the system call arguments out to userland:
+ */
+ copyout_noerr(args, (void *)(uintptr_t)args_addr,
+ 6 * sizeof (uintptr_t));
+
+ /*
+ * Drop the bogus return address on the stack.
+ */
+ suword64_noerr((void *)sp, 0);
+
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+ }
+
+ /*
+ * Pass the arguments to lx_emulate() in the appropriate registers.
+ */
+ rp->r_rdi = uc_addr;
+ rp->r_rsi = syscall_num;
+ rp->r_rdx = args_addr;
+
+ /*
+ * In order to be able to restore %edx, we need to JUSTRETURN.
+ */
+ lwp->lwp_eosys = JUSTRETURN;
+ curthread->t_post_sys = 1;
+ aston(curthread);
+
+ /*
+ * Set stack pointer and return address to the usermode emulation
+ * handler:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, sp);
+
+ /*
+ * Divert execution, on our return, to the usermode emulation stack
+ * and handler:
+ */
+ rp->r_fp = 0;
+ rp->r_sp = sp;
+ rp->r_pc = ptolxproc(p)->l_handler;
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+
+ return;
+
+badstack:
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+ }
+
+#ifdef DEBUG
+ printf("lx_emulate_user: bad native stack cmd=%s, pid=%d, sp=0x%lx\n",
+ PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+ exit(CLD_KILLED, SIGSEGV);
+}
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * Call frame for the 32-bit usermode emulation handler:
+ * lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ * | - ucontext_t (register state for emulation)
+ * | - uintptr_t[6] (system call arguments array)
+ * | --------------------------------------------------------------
+ * | - arg2: uintptr_t * (pointer to arguments array above)
+ * | - arg1: int (system call number)
+ * V - arg0: ucontext_t * (pointer to context above)
+ * new sp: - bogus return address
+ */
+struct lx_emu_frame32 {
+ caddr32_t retaddr; /* 0 */
+ caddr32_t ucontextp; /* 4 */
+ int32_t syscall_num; /* 8 */
+ caddr32_t argsp; /* c */
+};
+
+/*
+ * This function arranges for the lwp to execute the usermode emulation handler
+ * for this system call. The mechanism is similar to signal handling, and this
+ * function is modelled on sendsig32().
+ */
+void
+lx_emulate_user32(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ label_t lab;
+ caddr32_t uc_addr;
+ caddr32_t args_addr;
+ caddr32_t top;
+ /*
+ * Variables used after on_fault() returns for a fault
+ * must be volatile.
+ */
+ volatile size_t frsz;
+ volatile caddr32_t sp;
+ volatile proc_t *p = lwptoproc(lwp);
+ volatile int watched;
+
+ /*
+ * We should not be able to get here unless we are running Linux
+ * code for a system call we cannot emulate in the kernel.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+ /*
+ * We begin at the current native stack pointer, and reserve space for
+ * the ucontext_t we are copying onto the stack, as well as the call
+ * arguments for the usermode emulation handler.
+ */
+ frsz = SA32(sizeof (ucontext32_t)) + SA32(6 * sizeof (uint32_t)) +
+ SA32(sizeof (struct lx_emu_frame32));
+ VERIFY((frsz & (STACK_ALIGN32 - 1)) == 0);
+
+ top = (caddr32_t)(lwpd->br_ntv_stack_current & ~(STACK_ALIGN32 - 1));
+ sp = top - frsz;
+
+ uc_addr = top - SA32(sizeof (ucontext32_t));
+ args_addr = uc_addr - SA32(6 * sizeof (uint32_t));
+
+ watched = watch_disable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+
+ /*
+ * Save the register state we preserved on the way into this brand
+ * system call and drop it on the native stack.
+ */
+ {
+ /*
+ * Note: ucontext32_t is 512 bytes.
+ */
+ ucontext32_t uc;
+
+ /*
+ * We do not want to save the signal mask for an emulation
+ * context. Some emulated system calls alter the signal mask;
+ * restoring it when the emulation is complete would clobber
+ * those intentional side effects.
+ */
+ savecontext32(&uc, NULL);
+
+ if (on_fault(&lab)) {
+ goto badstack;
+ }
+
+ /*
+ * Mark this as a system call emulation context:
+ */
+ uc.uc_brand_data[0] |= LX_UC_FRAME_IS_SYSCALL;
+ copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+ lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+ /*
+ * Copy the system call arguments out to userland:
+ */
+ {
+ uint32_t args32[6];
+
+ args32[0] = args[0];
+ args32[1] = args[1];
+ args32[2] = args[2];
+ args32[3] = args[3];
+ args32[4] = args[4];
+ args32[5] = args[5];
+
+ copyout_noerr(&args32, (void *)(uintptr_t)args_addr,
+ sizeof (args32));
+ }
+
+ /*
+ * Assemble the call frame on the stack.
+ */
+ {
+ struct lx_emu_frame32 frm;
+
+ frm.retaddr = 0;
+ frm.ucontextp = uc_addr;
+ frm.argsp = args_addr;
+ frm.syscall_num = syscall_num;
+
+ copyout_noerr(&frm, (void *)(uintptr_t)sp, sizeof (frm));
+ }
+
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+ }
+
+ /*
+ * Set stack pointer and return address to the usermode emulation
+ * handler:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, sp);
+
+ /*
+ * Divert execution, on our return, to the usermode emulation stack
+ * and handler:
+ */
+ rp->r_fp = 0;
+ rp->r_sp = sp;
+ rp->r_pc = ptolxproc(p)->l_handler;
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+
+ return;
+
+badstack:
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+ }
+
+#ifdef DEBUG
+ printf("lx_emulate_user32: bad native stack cmd=%s, pid=%d, sp=0x%x\n",
+ PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+ exit(CLD_KILLED, SIGSEGV);
+}
+#endif /* _SYSCALL32_IMPL */
+
+#else /* !__amd64 (__i386) */
+
+/* ARGSUSED */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__);
+ exit(CLD_KILLED, SIGSYS);
+}
+
+#endif /* __amd64 */
diff --git a/usr/src/uts/intel/core_pcbe/Makefile b/usr/src/uts/intel/core_pcbe/Makefile
index d9d9b02de0..7f2d1ad8e5 100644
--- a/usr/src/uts/intel/core_pcbe/Makefile
+++ b/usr/src/uts/intel/core_pcbe/Makefile
@@ -34,7 +34,7 @@ UTSBASE = ../..
MODULE = pcbe.GenuineIntel.6.15
OBJECTS = $(CORE_PCBE_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(CORE_PCBE_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
SOFTLINKS = pcbe.GenuineIntel.6.23 \
pcbe.GenuineIntel.6.26 \
pcbe.GenuineIntel.6.28 \
@@ -45,7 +45,7 @@ SOFTLINKS = pcbe.GenuineIntel.6.23 \
pcbe.GenuineIntel.6.44 \
pcbe.GenuineIntel.6.46 \
pcbe.GenuineIntel.6.47
-ROOTSOFTLINKS = $(SOFTLINKS:%=$(USR_PCBE_DIR)/%)
+ROOTSOFTLINKS = $(SOFTLINKS:%=$(ROOT_PSM_PCBE_DIR)/%)
#
# Include common rules.
diff --git a/usr/src/uts/intel/datafilt/Makefile b/usr/src/uts/intel/datafilt/Makefile
new file mode 100644
index 0000000000..bc72416406
--- /dev/null
+++ b/usr/src/uts/intel/datafilt/Makefile
@@ -0,0 +1,74 @@
+#
+# 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) 2011, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2012, Nexenta Systems, Inc. All rights reserved.
+#
+
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = datafilt
+OBJECTS = $(DATAFILT_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(DATAFILT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement and OS version
+#
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nfs/sockfs -Ndrv/ip
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/dev/Makefile b/usr/src/uts/intel/dev/Makefile
index b5c7c1a9c8..e7ae468c05 100644
--- a/usr/src/uts/intel/dev/Makefile
+++ b/usr/src/uts/intel/dev/Makefile
@@ -66,6 +66,7 @@ INC_PATH += -I$(UTSBASE)/common/io/bpf
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-unused-label
CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/intel/dld/Makefile b/usr/src/uts/intel/dld/Makefile
index e0b736c8d2..beeef419a3 100644
--- a/usr/src/uts/intel/dld/Makefile
+++ b/usr/src/uts/intel/dld/Makefile
@@ -55,7 +55,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/dls -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/intel/dls/Makefile b/usr/src/uts/intel/dls/Makefile
index dfa1cd74ec..4546ae19e6 100644
--- a/usr/src/uts/intel/dls/Makefile
+++ b/usr/src/uts/intel/dls/Makefile
@@ -53,7 +53,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/intel/dr_sas/Makefile b/usr/src/uts/intel/dr_sas/Makefile
new file mode 100644
index 0000000000..f4871b694a
--- /dev/null
+++ b/usr/src/uts/intel/dr_sas/Makefile
@@ -0,0 +1,90 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# uts/intel/dr_sas/Makefile
+#
+# This makefile drives the production of the dr_sas driver kernel module.
+#
+# intel implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = dr_sas
+OBJECTS = $(DR_SAS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(DR_SAS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io/dr_sas
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Kernel Module Dependencies
+#
+LDFLAGS += -dy -Nmisc/scsi
+
+CERRWARN += -_gcc=-Wno-unused-label
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-uninitialized
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/dtrace/fasttrap_isa.c b/usr/src/uts/intel/dtrace/fasttrap_isa.c
index 1b93869a73..f9eba2876c 100644
--- a/usr/src/uts/intel/dtrace/fasttrap_isa.c
+++ b/usr/src/uts/intel/dtrace/fasttrap_isa.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
#include <sys/fasttrap_isa.h>
#include <sys/fasttrap_impl.h>
@@ -38,6 +40,9 @@
#include <sys/sysmacros.h>
#include <sys/trap.h>
#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
/*
* Lossless User-Land Tracing on x86
@@ -1394,6 +1399,14 @@ fasttrap_pid_probe(struct regs *rp)
#if defined(__amd64)
if (p->p_model == DATAMODEL_LP64) {
addr = lwp->lwp_pcb.pcb_fsbase;
+
+ /*
+ * If we're branded, convert the fsbase from the
+ * brand's fsbase to the native fsbase.
+ */
+ if (PROC_IS_BRANDED(p) && BRMOP(p)->b_fsbase != NULL)
+ addr = BRMOP(p)->b_fsbase(lwp, addr);
+
addr += sizeof (void *);
} else {
addr = lwp->lwp_pcb.pcb_gsbase;
diff --git a/usr/src/uts/intel/e1000g/Makefile b/usr/src/uts/intel/e1000g/Makefile
index 7492e30935..65aa6b44bb 100644
--- a/usr/src/uts/intel/e1000g/Makefile
+++ b/usr/src/uts/intel/e1000g/Makefile
@@ -84,6 +84,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
# Driver depends on MAC
#
LDFLAGS += -dy -N misc/mac
+MAPFILES += ddi mac
#
# Default build targets.
@@ -110,4 +111,5 @@ install: $(INSTALL_DEPS)
#
# Include common targets.
#
+include $(UTSBASE)/Makefile.mapfile
include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/elfexec/Makefile b/usr/src/uts/intel/elfexec/Makefile
index 9751e04ba7..a52fc159f5 100644
--- a/usr/src/uts/intel/elfexec/Makefile
+++ b/usr/src/uts/intel/elfexec/Makefile
@@ -23,6 +23,7 @@
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
#
#
@@ -60,6 +61,8 @@ ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+INC_PATH += -I$(UTSBASE)/intel/ia32
+
#
# For now, disable these lint checks; maintainers should endeavor
# to investigate and remove these for maximum lint coverage.
@@ -102,12 +105,15 @@ include $(UTSBASE)/intel/Makefile.targ
$(OBJS_DIR)/elf32.o: $(UTSBASE)/common/exec/elf/elf.c
$(COMPILE.c) -o $@ -D_ELF32_COMPAT $(UTSBASE)/common/exec/elf/elf.c
+ $(CTFCONVERT_O)
$(OBJS_DIR)/elf32_notes.o: $(UTSBASE)/common/exec/elf/elf_notes.c
$(COMPILE.c) -o $@ -D_ELF32_COMPAT $(UTSBASE)/common/exec/elf/elf_notes.c
+ $(CTFCONVERT_O)
$(OBJS_DIR)/old32_notes.o: $(UTSBASE)/common/exec/elf/old_notes.c
$(COMPILE.c) -o $@ -D_ELF32_COMPAT $(UTSBASE)/common/exec/elf/old_notes.c
+ $(CTFCONVERT_O)
$(LINTS_DIR)/elf32.ln: $(UTSBASE)/common/exec/elf/elf.c
@($(LHEAD) $(LINT.c) -Celf32 -D_ELF32_COMPAT $(UTSBASE)/common/exec/elf/elf.c $(LTAIL))
diff --git a/usr/src/uts/intel/genassym/Makefile b/usr/src/uts/intel/genassym/Makefile
new file mode 100644
index 0000000000..ce01dc8610
--- /dev/null
+++ b/usr/src/uts/intel/genassym/Makefile
@@ -0,0 +1,85 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of genassym.h through
+# compile time intialized data.
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+GENASSYM_H = $(GENASSYM_DIR)/$(OBJS_DIR)/genassym.h
+OFFSETS_SRC = $(GENASSYM_DIR)/offsets.in
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(GENASSYM_H)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx
+
+#
+# Overrides
+#
+CLEANFILES = Nothing_to_remove
+CLOBBERFILES = $(GENASSYM_H) Nothing_to_remove
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+clean.lint:
+
+install: def
+
+#
+# Create genassym.h
+#
+$(GENASSYM_H): $(OFFSETS_SRC)
+ $(OFFSETS_CREATE) <$(OFFSETS_SRC) >$@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/genassym/offsets.in b/usr/src/uts/intel/genassym/offsets.in
new file mode 100644
index 0000000000..70221c02f9
--- /dev/null
+++ b/usr/src/uts/intel/genassym/offsets.in
@@ -0,0 +1,43 @@
+\
+\ 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 2010 Sun Microsystems, Inc. All rights reserved.
+\ Use is subject to license terms.
+\ Copyright 2015 Joyent, Inc.
+\
+
+\
+\ offsets.in: input file to produce the architecture-dependent genassym.h
+\ using the ctfstabs program
+\
+
+#ifndef _GENASSYM
+#define _GENASSYM
+#endif
+
+#include <sys/lx_brand.h>
+
+lx_proc_data
+ l_handler
+
+lx_lwp_data
+ br_lx_fsbase
+ br_ntv_fsbase
diff --git a/usr/src/uts/intel/gsqueue/Makefile b/usr/src/uts/intel/gsqueue/Makefile
new file mode 100644
index 0000000000..411e384309
--- /dev/null
+++ b/usr/src/uts/intel/gsqueue/Makefile
@@ -0,0 +1,49 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+UTSBASE = ../..
+
+MODULE = gsqueue
+OBJECTS = $(GSQUEUE_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(GSQUEUE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+LDFLAGS += -dy -Ndrv/ip
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/hyprlofs/Makefile b/usr/src/uts/intel/hyprlofs/Makefile
new file mode 100644
index 0000000000..919b045617
--- /dev/null
+++ b/usr/src/uts/intel/hyprlofs/Makefile
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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
+#
+#
+# uts/intel/hyprlofs/Makefile
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This makefile drives the production of the hyprlofs file system
+# kernel module.
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = hyprlofs
+OBJECTS = $(HYPRLOFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(HYPRLOFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_FS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/i40e/Makefile b/usr/src/uts/intel/i40e/Makefile
index 2595f7f701..98b0a77e9c 100644
--- a/usr/src/uts/intel/i40e/Makefile
+++ b/usr/src/uts/intel/i40e/Makefile
@@ -38,6 +38,8 @@ LINTTAGS += -erroff=E_STATIC_UNUSED
LDFLAGS += -dy -N misc/mac
+MAPFILES += ddi mac
+
.KEEP_STATE:
def: $(DEF_DEPS)
@@ -56,4 +58,5 @@ clean.lint: $(CLEAN_LINT_DEPS)
install: $(INSTALL_DEPS)
+include $(UTSBASE)/Makefile.mapfile
include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/ia32/ml/i86_subr.s b/usr/src/uts/intel/ia32/ml/i86_subr.s
index e9d0b8128f..d4ba6589bc 100644
--- a/usr/src/uts/intel/ia32/ml/i86_subr.s
+++ b/usr/src/uts/intel/ia32/ml/i86_subr.s
@@ -23,6 +23,7 @@
* Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -3590,29 +3591,13 @@ hres_tick(void)
{}
int64_t timedelta;
-hrtime_t hres_last_tick;
-volatile timestruc_t hrestime;
-int64_t hrestime_adj;
-volatile int hres_lock;
hrtime_t hrtime_base;
#else /* __lint */
- DGDEF3(hrestime, _MUL(2, CLONGSIZE), 8)
- .NWORD 0, 0
-
- DGDEF3(hrestime_adj, 8, 8)
- .long 0, 0
-
- DGDEF3(hres_last_tick, 8, 8)
- .long 0, 0
-
DGDEF3(timedelta, 8, 8)
.long 0, 0
- DGDEF3(hres_lock, 4, 8)
- .long 0
-
/*
* initialized to a non zero value to make pc_gethrtime()
* work correctly even before clock is initialized
diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s
index 642704436a..8c17ea0624 100644
--- a/usr/src/uts/intel/ia32/ml/modstubs.s
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s
@@ -50,7 +50,7 @@ char stubs_base[1], stubs_end[1];
* NOTE: Use NO_UNLOAD_STUBs if the module is NOT unloadable once it is
* loaded.
*/
-#define MAXNARG 10
+#define MAXNARG 12
/*
* WARNING: there is no check for forgetting to write END_MODULE,
@@ -184,7 +184,7 @@ fcnname/**/_info: \
pushq %rcx
pushq %r8
pushq %r9
- /* (next 4 args, if any, are already on the stack above %rbp) */
+ /* (next 6 args, if any, are already on the stack above %rbp) */
movq %r15, %rdi
call mod_hold_stub /* mod_hold_stub(mod_stub_info *) */
cmpl $-1, %eax /* error? */
@@ -195,7 +195,7 @@ fcnname/**/_info: \
jmp .L2
.L1:
/*
- * copy MAXNARG == 10 incoming arguments
+ * copy MAXNARG == 12 incoming arguments
*/
popq %r9
popq %r8
@@ -219,8 +219,10 @@ fcnname/**/_info: \
pushq (%rsp, %r11, 8)
pushq (%rsp, %r11, 8)
pushq (%rsp, %r11, 8)
+ pushq (%rsp, %r11, 8)
+ pushq (%rsp, %r11, 8)
call *(%r15) /* call the stub fn(arg, ..) */
- addq $0x20, %rsp /* pop off last 4 args */
+ addq $0x30, %rsp /* pop off last 6 args */
pushq %rax /* save any return values */
pushq %rdx
movq %r15, %rdi
@@ -345,6 +347,8 @@ fcnname/**/_info: \
pushl (%esp, %ecx, 4)
pushl (%esp, %ecx, 4)
pushl (%esp, %ecx, 4)
+ pushl (%esp, %ecx, 4)
+ pushl (%esp, %ecx, 4)
call *(%esi) / call the stub function(arg1,arg2, ...)
add $_MUL(MAXNARG, 4), %esp / pop off MAXNARG arguments
pushl %eax / save any return values from the stub
diff --git a/usr/src/uts/intel/ia32/ml/swtch.s b/usr/src/uts/intel/ia32/ml/swtch.s
index 67ba255cbc..0948fa7c93 100644
--- a/usr/src/uts/intel/ia32/ml/swtch.s
+++ b/usr/src/uts/intel/ia32/ml/swtch.s
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
/*
@@ -362,13 +362,12 @@ resume(kthread_t *t)
.setup_cpu:
/*
- * Setup rsp0 (kernel stack) in TSS to curthread's stack.
- * (Note: Since we don't have saved 'regs' structure for all
- * the threads we can't easily determine if we need to
- * change rsp0. So, we simply change the rsp0 to bottom
- * of the thread stack and it will work for all cases.)
- *
- * XX64 - Is this correct?
+ * Setup rsp0 (kernel stack) in TSS to curthread's saved regs
+ * structure. If this thread doesn't have a regs structure above
+ * the stack -- that is, if lwp_stk_init() was never called for the
+ * thread -- this will set rsp0 to the wrong value, but it's harmless
+ * as it's a kernel thread, and it won't actually attempt to implicitly
+ * use the rsp0 via a privilege change.
*/
movq CPU_TSS(%r13), %r14
movq T_STACK(%r12), %rax
@@ -542,11 +541,12 @@ resume_return:
jne .L5_2
.L5_1:
/*
- * Setup esp0 (kernel stack) in TSS to curthread's stack.
- * (Note: Since we don't have saved 'regs' structure for all
- * the threads we can't easily determine if we need to
- * change esp0. So, we simply change the esp0 to bottom
- * of the thread stack and it will work for all cases.)
+ * Setup esp0 (kernel stack) in TSS to curthread's stack. If this
+ * thread doesn't have a regs structure above the stack -- that is, if
+ * lwp_stk_init() was never called for the thread -- this will set
+ * esp0 to the wrong value, but it's harmless as it's a kernel thread,
+ * and it won't actually attempt to implicitly use the esp0 via a
+ * privilege change.
*/
movl CPU_TSS(%esi), %ecx
addl $REGSIZE+MINFRAME, %eax /* to the bottom of thread stack */
@@ -929,3 +929,81 @@ thread_start(void)
#endif /* __i386 */
#endif /* __lint */
+
+#if defined(__lint)
+
+void
+thread_splitstack_run(caddr_t stack, void (*func)(void *), void *arg)
+{}
+
+void
+thread_splitstack_cleanup(void)
+{}
+
+#else /* __lint */
+
+#if defined(__amd64)
+
+ ENTRY(thread_splitstack_run)
+ pushq %rbp /* push base pointer */
+ movq %rsp, %rbp /* construct frame */
+ movq %rdi, %rsp /* set stack pinter */
+ movq %rdx, %rdi /* load arg */
+ call *%rsi /* call specified function */
+ leave /* pop base pointer */
+ ret
+ SET_SIZE(thread_splitstack_run)
+
+ /*
+ * Once we're back on our own stack, we need to be sure to set the
+ * value of rsp0 in the TSS back to our original stack: if we gave
+ * up the CPU at all while on our split stack, the rsp0 will point
+ * to that stack from resume (above); if were to try to return to
+ * userland in that state, we will die absolutely horribly (namely,
+ * trying to iretq back to registers in a bunch of freed segkp). We
+ * are expecting this to be called after T_STACK has been restored,
+ * but before we return. It's okay if we are preempted in this code:
+ * when the new CPU picks us up, they will automatically set rsp0
+ * correctly, which is all we're trying to do here.
+ */
+ ENTRY(thread_splitstack_cleanup)
+ LOADCPU(%r8)
+ movq CPU_TSS(%r8), %r9
+ movq CPU_THREAD(%r8), %r10
+ movq T_STACK(%r10), %rax
+ addq $REGSIZE+MINFRAME, %rax
+ movq %rax, TSS_RSP0(%r9)
+ ret
+ SET_SIZE(thread_splitstack_cleanup)
+
+#elif defined(__i386)
+
+ ENTRY(thread_splitstack_run)
+ pushl %ebp /* push base pointer */
+ movl %esp, %ebp /* construct frame */
+ movl 8(%ebp), %esp /* set stack pointer */
+ movl 12(%ebp), %eax /* load func */
+ movl 16(%ebp), %edx /* load arg */
+ pushl %edx /* push arg */
+ call *%eax /* call specifed function */
+ addl $4, %esp /* restore stack pointer */
+ leave /* pop base pointer */
+ ret
+ SET_SIZE(thread_splitstack_run)
+
+ /*
+ * See comment in the amd64 code, above.
+ */
+ ENTRY(thread_splitstack_cleanup)
+ LOADCPU(%eax)
+ movl CPU_TSS(%eax), %ecx
+ movl CPU_THREAD(%eax), %edx
+ movl T_STACK(%edx), %edx
+ addl $REGSIZE+MINFRAME, %edx
+ movl %edx, TSS_ESP0(%ecx)
+ ret
+ SET_SIZE(thread_splitstack_cleanup)
+
+#endif /* __i386 */
+
+#endif /* __lint */
diff --git a/usr/src/uts/intel/ia32/os/archdep.c b/usr/src/uts/intel/ia32/os/archdep.c
index 2a33f306c2..c96aff4a19 100644
--- a/usr/src/uts/intel/ia32/os/archdep.c
+++ b/usr/src/uts/intel/ia32/os/archdep.c
@@ -25,7 +25,7 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
@@ -575,6 +575,13 @@ ucontext_32ton(const ucontext32_t *src, ucontext_t *dst)
if (src->uc_flags & UC_FPU)
fpregset_32ton(&src->uc_mcontext.fpregs,
&dst->uc_mcontext.fpregs);
+
+ /*
+ * Copy the brand-private data:
+ */
+ dst->uc_brand_data[0] = (void *)(uintptr_t)src->uc_brand_data[0];
+ dst->uc_brand_data[1] = (void *)(uintptr_t)src->uc_brand_data[1];
+ dst->uc_brand_data[2] = (void *)(uintptr_t)src->uc_brand_data[2];
}
#endif /* _SYSCALL32_IMPL */
@@ -629,9 +636,11 @@ getuserpc()
#define IS_NOT_CS 0
/*ARGSUSED*/
-static greg_t
+greg_t
fix_segreg(greg_t sr, int iscs, model_t datamodel)
{
+ kthread_t *t = curthread;
+
switch (sr &= 0xffff) {
case 0:
@@ -668,6 +677,19 @@ fix_segreg(greg_t sr, int iscs, model_t datamodel)
}
/*
+ * Allow this process's brand to do any necessary segment register
+ * manipulation.
+ */
+ if (PROC_IS_BRANDED(t->t_procp) && BRMOP(t->t_procp)->b_fixsegreg) {
+ greg_t bsr = BRMOP(t->t_procp)->b_fixsegreg(sr, datamodel);
+
+ if (bsr == 0 && iscs == IS_CS)
+ return (0 | SEL_UPL);
+ else
+ return (bsr);
+ }
+
+ /*
* Force it into the LDT in ring 3 for 32-bit processes, which by
* default do not have an LDT, so that any attempt to use an invalid
* selector will reference the (non-existant) LDT, and cause a #gp
diff --git a/usr/src/uts/intel/ia32/os/comm_page_util.c b/usr/src/uts/intel/ia32/os/comm_page_util.c
new file mode 100644
index 0000000000..14fcf9ca57
--- /dev/null
+++ b/usr/src/uts/intel/ia32/os/comm_page_util.c
@@ -0,0 +1,62 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+
+#include <sys/types.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <vm/as.h>
+#include <vm/seg_umap.h>
+
+#if !defined(__xpv)
+#include <sys/comm_page.h>
+#endif /* !defined(__xpv) */
+
+/*
+ * Map in the comm page.
+ *
+ * The contents of the comm page are only defined on non-xpv x86 at this time.
+ * Furthermore, the data is only valid in userspace (32-bit or 64-bit) when
+ * mapped from a 64-bit kernel.
+ * See: "uts/i86pc/sys/comm_page.h"
+ */
+caddr_t
+comm_page_mapin()
+{
+#if defined(__amd64) && !defined(__xpv)
+ proc_t *p = curproc;
+ caddr_t addr = (caddr_t)COMM_PAGE_ALIGN;
+ size_t len = COMM_PAGE_SIZE;
+ uint_t prot = PROT_USER | PROT_READ;
+ segumap_crargs_t suarg;
+
+ map_addr(&addr, len, (offset_t)0, 1, MAP_ALIGN);
+ if (addr == NULL || valid_usr_range(addr, len, prot, p->p_as,
+ p->p_as->a_userlimit) != RANGE_OKAY) {
+ return (NULL);
+ }
+
+ suarg.kaddr = (caddr_t)&comm_page;
+ suarg.prot = suarg.maxprot = prot;
+ if (as_map(p->p_as, addr, len, segumap_create, &suarg) != 0) {
+ return (NULL);
+ }
+ return (addr);
+#else /* defined(__amd64) && !defined(__xpv) */
+ return (NULL);
+#endif /* defined(__amd64) && !defined(__xpv) */
+}
diff --git a/usr/src/uts/intel/ia32/os/desctbls.c b/usr/src/uts/intel/ia32/os/desctbls.c
index a05137eee6..97024b7b59 100644
--- a/usr/src/uts/intel/ia32/os/desctbls.c
+++ b/usr/src/uts/intel/ia32/os/desctbls.c
@@ -161,7 +161,7 @@ struct interposing_handler {
* The brand infrastructure interposes on two handlers, and we use one as a
* NULL signpost.
*/
-static struct interposing_handler brand_tbl[2];
+static struct interposing_handler brand_tbl[3];
/*
* software prototypes for default local descriptor table
@@ -976,6 +976,12 @@ init_idt_common(gate_desc_t *idt)
set_gatesegd(&idt[T_MCE], &mcetrap, KCS_SEL, SDT_SYSIGT, TRP_KPL, 0);
set_gatesegd(&idt[T_SIMDFPE], &xmtrap, KCS_SEL, SDT_SYSIGT, TRP_KPL, 0);
+ /*
+ * install "int80" handler at, well, 0x80.
+ */
+ set_gatesegd(&idt0[T_INT80], &sys_int80, KCS_SEL, SDT_SYSIGT, TRP_UPL,
+ 0);
+
/*
* install fast trap handler at 210.
*/
@@ -1001,21 +1007,27 @@ init_idt_common(gate_desc_t *idt)
SDT_SYSIGT, TRP_UPL, 0);
/*
- * Prepare interposing descriptor for the syscall handler
- * and cache copy of the default descriptor.
+- * Prepare interposing descriptors for the branded "int80"
+- * and syscall handlers and cache copies of the default
+- * descriptors.
*/
- brand_tbl[0].ih_inum = T_SYSCALLINT;
- brand_tbl[0].ih_default_desc = idt0[T_SYSCALLINT];
+ brand_tbl[0].ih_inum = T_INT80;
+ brand_tbl[0].ih_default_desc = idt0[T_INT80];
+ set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_int80, KCS_SEL,
+ SDT_SYSIGT, TRP_UPL, 0);
+
+ brand_tbl[1].ih_inum = T_SYSCALLINT;
+ brand_tbl[1].ih_default_desc = idt0[T_SYSCALLINT];
#if defined(__amd64)
- set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_syscall_int,
+ set_gatesegd(&(brand_tbl[1].ih_interp_desc), &brand_sys_syscall_int,
KCS_SEL, SDT_SYSIGT, TRP_UPL, 0);
#elif defined(__i386)
- set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_call,
+ set_gatesegd(&(brand_tbl[1].ih_interp_desc), &brand_sys_call,
KCS_SEL, SDT_SYSIGT, TRP_UPL, 0);
#endif /* __i386 */
- brand_tbl[1].ih_inum = 0;
+ brand_tbl[2].ih_inum = 0;
}
#if defined(__xpv)
diff --git a/usr/src/uts/intel/ia32/os/sendsig.c b/usr/src/uts/intel/ia32/os/sendsig.c
index b7b79f38ca..cf6c623b7a 100644
--- a/usr/src/uts/intel/ia32/os/sendsig.c
+++ b/usr/src/uts/intel/ia32/os/sendsig.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -87,6 +90,8 @@
#include <sys/kdi.h>
#include <sys/contract_impl.h>
#include <sys/x86_archext.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
/*
* Construct the execution environment for the user's signal
@@ -186,7 +191,18 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
!(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
- if (newstack) {
+ /*
+ * If this is a branded process, the brand may provide an alternate
+ * stack pointer for signal delivery:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+ /*
+ * Use the stack pointer value provided by the brand,
+ * accounting for the 128-byte reserved region.
+ */
+ newstack = 0;
+ fp = BROP(p)->b_sendsig_stack(sig) - STACK_RESERVE;
+ } else if (newstack) {
fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN);
} else {
@@ -296,6 +312,8 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
kmem_free(tuc, sizeof (*tuc));
tuc = NULL;
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
lwp->lwp_oldcontext = (uintptr_t)uc;
if (newstack) {
@@ -345,6 +363,14 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
}
/*
+ * Allow the brand to perform additional book-keeping once the signal
+ * handling frame has been fully assembled:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+ BROP(p)->b_sendsig(sig);
+ }
+
+ /*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
@@ -420,7 +446,17 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
!(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
- if (newstack) {
+ /*
+ * If this is a branded process, the brand may provide an alternate
+ * stack pointer for signal delivery:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+ /*
+ * Use the stack pointer value provided by the brand:
+ */
+ newstack = 0;
+ fp = BROP(p)->b_sendsig_stack(sig);
+ } else if (newstack) {
fp = (caddr_t)(SA32((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
SA32(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN32);
} else if ((rp->r_ss & 0xffff) != UDS_SEL) {
@@ -435,8 +471,9 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]);
else
fp = (caddr_t)rp->r_sp;
- } else
+ } else {
fp = (caddr_t)rp->r_sp;
+ }
/*
* Force proper stack pointer alignment, even in the face of a
@@ -517,6 +554,8 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
kmem_free(tuc, sizeof (*tuc));
tuc = NULL;
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
lwp->lwp_oldcontext = (uintptr_t)uc;
if (newstack) {
@@ -566,6 +605,14 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
}
/*
+ * Allow the brand to perform additional book-keeping once the signal
+ * handling frame has been fully assembled:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+ BROP(p)->b_sendsig(sig);
+ }
+
+ /*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
@@ -643,7 +690,17 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
!(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
- if (newstack) {
+ /*
+ * If this is a branded process, the brand may provide an alternate
+ * stack pointer for signal delivery:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+ /*
+ * Use the stack pointer value provided by the brand:
+ */
+ newstack = 0;
+ fp = BROP(p)->b_sendsig_stack(sig);
+ } else if (newstack) {
fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN);
} else if ((rp->r_ss & 0xffff) != UDS_SEL) {
@@ -658,8 +715,9 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]);
else
fp = (caddr_t)rp->r_sp;
- } else
+ } else {
fp = (caddr_t)rp->r_sp;
+ }
/*
* Force proper stack pointer alignment, even in the face of a
@@ -737,6 +795,8 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
kmem_free(tuc, sizeof (*tuc));
tuc = NULL;
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
lwp->lwp_oldcontext = (uintptr_t)uc;
if (newstack) {
@@ -774,6 +834,14 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
}
/*
+ * Allow the brand to perform additional book-keeping once the signal
+ * handling frame has been fully assembled:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+ BROP(p)->b_sendsig(sig);
+ }
+
+ /*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
diff --git a/usr/src/uts/intel/ia32/sys/comm_page_util.h b/usr/src/uts/intel/ia32/sys/comm_page_util.h
new file mode 100644
index 0000000000..331162c5ca
--- /dev/null
+++ b/usr/src/uts/intel/ia32/sys/comm_page_util.h
@@ -0,0 +1,33 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _COMM_PAGE_UTIL_H
+#define _COMM_PAGE_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_KERNEL)
+
+extern caddr_t comm_page_mapin();
+
+#endif /* defined(_KERNEL) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMM_PAGE_UTIL_H */
diff --git a/usr/src/uts/intel/ia32/syscall/getcontext.c b/usr/src/uts/intel/ia32/syscall/getcontext.c
index cb5a5b52ba..8f72b5da72 100644
--- a/usr/src/uts/intel/ia32/syscall/getcontext.c
+++ b/usr/src/uts/intel/ia32/syscall/getcontext.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -46,6 +49,7 @@
#include <sys/schedctl.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
+#include <sys/sdt.h>
/*
* Save user context.
@@ -125,7 +129,23 @@ savecontext(ucontext_t *ucp, const k_sigset_t *mask)
else
ucp->uc_flags &= ~UC_FPU;
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
+
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext != NULL) {
+ /*
+ * Allow the brand the chance to modify the context we
+ * saved:
+ */
+ BROP(p)->b_savecontext(ucp);
+ }
}
/*
@@ -136,7 +156,19 @@ restorecontext(ucontext_t *ucp)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
+ proc_t *p = lwptoproc(lwp);
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_restorecontext != NULL) {
+ /*
+ * Allow the brand the chance to modify the context before
+ * we restore it:
+ */
+ BROP(p)->b_restorecontext(ucp);
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext,
+ uintptr_t, (uintptr_t)ucp->uc_link);
lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link;
if (ucp->uc_flags & UC_STACK) {
@@ -184,6 +216,7 @@ getsetcontext(int flag, void *arg)
ucontext_t *ucp;
klwp_t *lwp = ttolwp(curthread);
stack_t dummy_stk;
+ proc_t *p = lwptoproc(lwp);
/*
* In future releases, when the ucontext structure grows,
@@ -228,6 +261,15 @@ getsetcontext(int flag, void *arg)
return (set_errno(EFAULT));
}
+ /*
+ * If this is a branded process, copy in the brand-private
+ * data:
+ */
+ if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+ &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
restorecontext(&uc);
if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
@@ -311,7 +353,23 @@ savecontext32(ucontext32_t *ucp, const k_sigset_t *mask)
else
ucp->uc_flags &= ~UC_FPU;
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
+
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext32 != NULL) {
+ /*
+ * Allow the brand the chance to modify the context we
+ * saved:
+ */
+ BROP(p)->b_savecontext32(ucp);
+ }
}
int
@@ -323,6 +381,7 @@ getsetcontext32(int flag, void *arg)
klwp_t *lwp = ttolwp(curthread);
caddr32_t ustack32;
stack32_t dummy_stk32;
+ proc_t *p = lwptoproc(lwp);
switch (flag) {
default:
@@ -354,6 +413,15 @@ getsetcontext32(int flag, void *arg)
return (set_errno(EFAULT));
}
+ /*
+ * If this is a branded process, copy in the brand-private
+ * data:
+ */
+ if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+ &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
ucontext_32ton(&uc, &ucnat);
restorecontext(&ucnat);
diff --git a/usr/src/uts/intel/icmp/Makefile b/usr/src/uts/intel/icmp/Makefile
index 259530f9dc..b6b1048254 100644
--- a/usr/src/uts/intel/icmp/Makefile
+++ b/usr/src/uts/intel/icmp/Makefile
@@ -23,6 +23,7 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the icmp IP driver
#
@@ -63,6 +64,8 @@ ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+INC_PATH += -I$(UTSBASE)/common/io/bpf
+
#
# depends on ip and sockfs
#
diff --git a/usr/src/uts/intel/igb/Makefile b/usr/src/uts/intel/igb/Makefile
index 2d4a1f3556..b85fc2f5c2 100644
--- a/usr/src/uts/intel/igb/Makefile
+++ b/usr/src/uts/intel/igb/Makefile
@@ -72,6 +72,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
# Driver depends on MAC
#
LDFLAGS += -dy -N misc/mac
+MAPFILES += ddi mac random
#
# Default build targets.
@@ -97,4 +98,5 @@ install: $(INSTALL_DEPS)
#
# Include common targets.
#
+include $(UTSBASE)/Makefile.mapfile
include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/inotify/Makefile b/usr/src/uts/intel/inotify/Makefile
new file mode 100644
index 0000000000..80e7a80404
--- /dev/null
+++ b/usr/src/uts/intel/inotify/Makefile
@@ -0,0 +1,70 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = inotify
+OBJECTS = $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(INOTIFY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LINTTAGS += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
+CERRWARN += -_gcc=-Wno-parentheses
+LDFLAGS += -dy -Nfs/specfs
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/io/acpica/acpi_enum.c b/usr/src/uts/intel/io/acpica/acpi_enum.c
index 85502d4bc1..aec75712f5 100644
--- a/usr/src/uts/intel/io/acpica/acpi_enum.c
+++ b/usr/src/uts/intel/io/acpica/acpi_enum.c
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2016, Joyent, Inc.
* Copyright (c) 2012 Gary Mills
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
@@ -135,7 +136,7 @@ parse_resources_irq(ACPI_RESOURCE *resource_ptr, int *interrupt_count)
resource_ptr->Data.Irq.Interrupts[i];
used_interrupts |= 1 << resource_ptr->Data.Irq.Interrupts[i];
if (acpi_enum_debug & PARSE_RES_IRQ) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"IRQ num %u, intr # = %u",
i, resource_ptr->Data.Irq.Interrupts[i]);
}
@@ -151,7 +152,7 @@ parse_resources_dma(ACPI_RESOURCE *resource_ptr, int *dma_count)
dma[(*dma_count)++] = resource_ptr->Data.Dma.Channels[i];
used_dmas |= 1 << resource_ptr->Data.Dma.Channels[i];
if (acpi_enum_debug & PARSE_RES_DMA) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"DMA num %u, channel # = %u",
i, resource_ptr->Data.Dma.Channels[i]);
}
@@ -171,7 +172,7 @@ parse_resources_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
io[*io_count].regspec_size = acpi_io.AddressLength;
io[*io_count].regspec_addr = acpi_io.Minimum;
if (acpi_enum_debug & PARSE_RES_IO) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"IO min 0x%X, max 0x%X, length: 0x%X",
acpi_io.Minimum,
acpi_io.Maximum,
@@ -193,7 +194,7 @@ parse_resources_fixed_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
io[*io_count].regspec_addr = fixed_io.Address;
io[*io_count].regspec_size = fixed_io.AddressLength;
if (acpi_enum_debug & PARSE_RES_IO) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"Fixed IO 0x%X, length: 0x%X",
fixed_io.Address, fixed_io.AddressLength);
}
@@ -214,7 +215,7 @@ parse_resources_fixed_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
io[*io_count].regspec_addr = fixed_mem32.Address;
io[*io_count].regspec_size = fixed_mem32.AddressLength;
if (acpi_enum_debug & PARSE_RES_MEMORY) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"Fixed Mem 32 %ul, length: %ul",
fixed_mem32.Address, fixed_mem32.AddressLength);
}
@@ -237,16 +238,16 @@ parse_resources_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
io[*io_count].regspec_size = mem32.AddressLength;
(*io_count)++;
if (acpi_enum_debug & PARSE_RES_MEMORY) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"Mem 32 0x%X, length: 0x%X",
mem32.Minimum, mem32.AddressLength);
}
return;
}
if (acpi_enum_debug & PARSE_RES_MEMORY) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"MEM32 Min Max not equal!");
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"Mem 32 Minimum 0x%X, Maximum: 0x%X",
mem32.Minimum, mem32.Maximum);
}
@@ -259,22 +260,22 @@ parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
ACPI_RESOURCE_ADDRESS16 addr16 =
resource_ptr->Data.Address16;
- if (addr16.AddressLength == 0)
+ if (addr16.Address.AddressLength == 0)
return;
if (acpi_enum_debug & PARSE_RES_ADDRESS) {
if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 16 MEMORY RANGE");
} else
if (addr16.ResourceType == ACPI_IO_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 16 IO RANGE");
} else {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 16 OTHER");
}
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"%s "\
"MinAddressFixed 0x%X, "\
"MaxAddressFixed 0x%X, "\
@@ -285,16 +286,16 @@ parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
"CONSUMER" : "PRODUCER",
addr16.MinAddressFixed,
addr16.MaxAddressFixed,
- addr16.Minimum,
- addr16.Maximum,
- addr16.AddressLength);
+ addr16.Address.Minimum,
+ addr16.Address.Maximum,
+ addr16.Address.AddressLength);
}
if (addr16.ProducerConsumer == ACPI_PRODUCER ||
(addr16.ResourceType != ACPI_MEMORY_RANGE &&
addr16.ResourceType != ACPI_IO_RANGE)) {
return;
}
- if (addr16.AddressLength > 0) {
+ if (addr16.Address.AddressLength > 0) {
if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
/* memory */
io[*io_count].regspec_bustype = 0;
@@ -302,8 +303,8 @@ parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
/* io */
io[*io_count].regspec_bustype = 1;
}
- io[*io_count].regspec_addr = addr16.Minimum;
- io[*io_count].regspec_size = addr16.AddressLength;
+ io[*io_count].regspec_addr = addr16.Address.Minimum;
+ io[*io_count].regspec_size = addr16.Address.AddressLength;
(*io_count)++;
}
}
@@ -315,22 +316,22 @@ parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
ACPI_RESOURCE_ADDRESS32 addr32 =
resource_ptr->Data.Address32;
- if (addr32.AddressLength == 0)
+ if (addr32.Address.AddressLength == 0)
return;
if (acpi_enum_debug & PARSE_RES_ADDRESS) {
if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 32 MEMORY RANGE");
} else
if (addr32.ResourceType == ACPI_IO_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 32 IO RANGE");
} else {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 32 OTHER");
}
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"%s "\
"MinAddressFixed 0x%X, "\
"MaxAddressFixed 0x%X, "\
@@ -341,16 +342,16 @@ parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
"CONSUMER" : "PRODUCER",
addr32.MinAddressFixed,
addr32.MaxAddressFixed,
- addr32.Minimum,
- addr32.Maximum,
- addr32.AddressLength);
+ addr32.Address.Minimum,
+ addr32.Address.Maximum,
+ addr32.Address.AddressLength);
}
if (addr32.ProducerConsumer == ACPI_PRODUCER ||
(addr32.ResourceType != ACPI_MEMORY_RANGE &&
addr32.ResourceType != ACPI_IO_RANGE)) {
return;
}
- if (addr32.AddressLength > 0) {
+ if (addr32.Address.AddressLength > 0) {
if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
/* memory */
io[*io_count].regspec_bustype = 0;
@@ -358,8 +359,8 @@ parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
/* io */
io[*io_count].regspec_bustype = 1;
}
- io[*io_count].regspec_addr = addr32.Minimum;
- io[*io_count].regspec_size = addr32.AddressLength;
+ io[*io_count].regspec_addr = addr32.Address.Minimum;
+ io[*io_count].regspec_size = addr32.Address.AddressLength;
(*io_count)++;
}
}
@@ -371,23 +372,23 @@ parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
ACPI_RESOURCE_ADDRESS64 addr64 =
resource_ptr->Data.Address64;
- if (addr64.AddressLength == 0)
+ if (addr64.Address.AddressLength == 0)
return;
if (acpi_enum_debug & PARSE_RES_ADDRESS) {
if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 64 MEMORY RANGE");
} else
if (addr64.ResourceType == ACPI_IO_RANGE) {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 64 IO RANGE");
} else {
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"ADDRESS 64 OTHER");
}
#ifdef _LP64
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"%s "\
"MinAddressFixed 0x%X, "\
"MaxAddressFixed 0x%X, "\
@@ -398,11 +399,11 @@ parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
"CONSUMER" : "PRODUCER",
addr64.MinAddressFixed,
addr64.MaxAddressFixed,
- addr64.Minimum,
- addr64.Maximum,
- addr64.AddressLength);
+ addr64.Address.Minimum,
+ addr64.Address.Maximum,
+ addr64.Address.AddressLength);
#else
- cmn_err(CE_NOTE, "parse_resources() "\
+ cmn_err(CE_NOTE, "!parse_resources() "\
"%s "\
"MinAddressFixed 0x%X, "\
"MaxAddressFixed 0x%X, "\
@@ -413,9 +414,9 @@ parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
"CONSUMER" : "PRODUCER",
addr64.MinAddressFixed,
addr64.MaxAddressFixed,
- addr64.Minimum,
- addr64.Maximum,
- addr64.AddressLength);
+ addr64.Address.Minimum,
+ addr64.Address.Maximum,
+ addr64.Address.AddressLength);
#endif
}
if (addr64.ProducerConsumer == ACPI_PRODUCER ||
@@ -423,7 +424,7 @@ parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
addr64.ResourceType != ACPI_IO_RANGE)) {
return;
}
- if (addr64.AddressLength > 0) {
+ if (addr64.Address.AddressLength > 0) {
if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
/* memory */
io[*io_count].regspec_bustype = 0;
@@ -431,8 +432,8 @@ parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
/* io */
io[*io_count].regspec_bustype = 1;
}
- io[*io_count].regspec_addr = addr64.Minimum;
- io[*io_count].regspec_size = addr64.AddressLength;
+ io[*io_count].regspec_addr = addr64.Address.Minimum;
+ io[*io_count].regspec_size = addr64.Address.AddressLength;
(*io_count)++;
}
}
@@ -699,7 +700,7 @@ process_cids(ACPI_OBJECT *rv, device_id_t **dd)
break;
default:
if (acpi_enum_debug & PROCESS_CIDS) {
- cmn_err(CE_NOTE, "unexpected CID type: %d",
+ cmn_err(CE_NOTE, "!unexpected CID type: %d",
obj.Type);
}
break;
@@ -824,7 +825,7 @@ isa_acpi_callback(ACPI_HANDLE ObjHandle, uint32_t NestingLevel, void *a,
*/
if (!((info->CurrentStatus & 0x7) == 7)) {
if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
- cmn_err(CE_NOTE, "parse_resources() "
+ cmn_err(CE_NOTE, "!parse_resources() "
"Bad status 0x%x for %s",
info->CurrentStatus, path);
}
@@ -841,7 +842,7 @@ isa_acpi_callback(ACPI_HANDLE ObjHandle, uint32_t NestingLevel, void *a,
if (!(info->Valid & ACPI_VALID_HID)) {
/* No _HID, we skip this node */
if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
- cmn_err(CE_NOTE, "parse_resources() "
+ cmn_err(CE_NOTE, "!parse_resources() "
"No _HID for %s", path);
}
goto done;
@@ -1064,7 +1065,7 @@ acpi_isa_device_enum(dev_info_t *isa_dip)
}
if (acpi_enum_debug & ISA_DEVICE_ENUM) {
- cmn_err(CE_NOTE, "acpi_isa_device_enum() called");
+ cmn_err(CE_NOTE, "!acpi_isa_device_enum() called");
}
if (acpica_init() != AE_OK) {
diff --git a/usr/src/uts/intel/io/acpica/acpica.c b/usr/src/uts/intel/io/acpica/acpica.c
index 999002f5c3..0fba611701 100644
--- a/usr/src/uts/intel/io/acpica/acpica.c
+++ b/usr/src/uts/intel/io/acpica/acpica.c
@@ -20,8 +20,8 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/*
* Copyright (c) 2009, Intel Corporation.
@@ -96,6 +96,9 @@ static kstat_t *acpica_ksp;
*/
int acpica_init_state = ACPICA_NOT_INITIALIZED;
+void *AcpiGbl_DbBuffer;
+uint32_t AcpiGbl_DbConsoleDebugLevel;
+
/*
* Following are set by acpica_process_user_options()
*
@@ -192,37 +195,42 @@ static int
acpica_install_handlers()
{
ACPI_STATUS rv = AE_OK;
+ ACPI_STATUS res;
/*
* Install ACPI CA default handlers
*/
- if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ if ((res = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_SYSTEM_MEMORY,
- ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
+ ACPI_DEFAULT_HANDLER, NULL, NULL)) != AE_OK &&
+ res != AE_SAME_HANDLER) {
cmn_err(CE_WARN, "!acpica: no default handler for"
" system memory");
rv = AE_ERROR;
}
- if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ if ((res = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_SYSTEM_IO,
- ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
+ ACPI_DEFAULT_HANDLER, NULL, NULL)) != AE_OK &&
+ res != AE_SAME_HANDLER) {
cmn_err(CE_WARN, "!acpica: no default handler for"
" system I/O");
rv = AE_ERROR;
}
- if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ if ((res = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_PCI_CONFIG,
- ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
+ ACPI_DEFAULT_HANDLER, NULL, NULL)) != AE_OK &&
+ res != AE_SAME_HANDLER) {
cmn_err(CE_WARN, "!acpica: no default handler for"
" PCI Config");
rv = AE_ERROR;
}
- if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ if ((res = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_DATA_TABLE,
- ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
+ ACPI_DEFAULT_HANDLER, NULL, NULL)) != AE_OK &&
+ res != AE_SAME_HANDLER) {
cmn_err(CE_WARN, "!acpica: no default handler for"
" Data Table");
rv = AE_ERROR;
diff --git a/usr/src/uts/intel/io/acpica/ahids.c b/usr/src/uts/intel/io/acpica/ahids.c
new file mode 100644
index 0000000000..51dc8dc803
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/ahids.c
@@ -0,0 +1,245 @@
+/******************************************************************************
+ *
+ * Module Name: ahids - Table of ACPI/PNP _HID/_CID values
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("ahids")
+
+
+/*
+ * ACPI/PNP Device IDs with description strings
+ */
+const AH_DEVICE_ID AslDeviceIds[] =
+{
+ {"10EC5640", "Realtek I2S Audio Codec"},
+ {"80860F09", "Intel PWM Controller"},
+ {"80860F0A", "Intel Atom UART Controller"},
+ {"80860F0E", "Intel SPI Controller"},
+ {"80860F14", "Intel Baytrail SDIO/MMC Host Controller"},
+ {"80860F28", "Intel SST Audio DSP"},
+ {"80860F41", "Intel Baytrail I2C Host Controller"},
+ {"ACPI0001", "SMBus 1.0 Host Controller"},
+ {"ACPI0002", "Smart Battery Subsystem"},
+ {"ACPI0003", "Power Source Device"},
+ {"ACPI0004", "Module Device"},
+ {"ACPI0005", "SMBus 2.0 Host Controller"},
+ {"ACPI0006", "GPE Block Device"},
+ {"ACPI0007", "Processor Device"},
+ {"ACPI0008", "Ambient Light Sensor Device"},
+ {"ACPI0009", "I/O xAPIC Device"},
+ {"ACPI000A", "I/O APIC Device"},
+ {"ACPI000B", "I/O SAPIC Device"},
+ {"ACPI000C", "Processor Aggregator Device"},
+ {"ACPI000D", "Power Meter Device"},
+ {"ACPI000E", "Time and Alarm Device"},
+ {"ACPI000F", "User Presence Detection Device"},
+ {"ACPI0010", "Processor Container Device"},
+ {"ACPI0011", "Generic Buttons Device"},
+ {"ACPI0012", "NVDIMM Root Device"},
+ {"ACPI0013", "Generic Event Device"},
+ {"ADMA0F28", "Intel Audio DMA"},
+ {"AMCR0F28", "Intel Audio Machine Driver"},
+ {"ATK4001", "Asus Radio Control Button"},
+ {"ATML1000", "Atmel Touchscreen Controller"},
+ {"AUTH2750", "AuthenTec AES2750"},
+ {"BCM2E39", "Broadcom BT Serial Bus Driver over UART Bus Enumerator"},
+ {"BCM4752E", "Broadcom GPS Controller"},
+ {"BMG0160", "Bosch Gyro Sensor"},
+ {"CPLM3218", "Capella Micro CM3218x Ambient Light Sensor"},
+ {"DELLABCE", "Dell Airplane Mode Switch Driver"},
+ {"DLAC3002", "Qualcomm Atheros Bluetooth UART Transport"},
+ {"FTTH5506", "FocalTech 5506 Touch Controller"},
+ {"HAD0F28", "Intel HDMI Audio Driver"},
+ {"INBC0000", "GPIO Expander"},
+ {"INT0002", "Virtual GPIO Controller"},
+ {"INT0800", "Intel 82802 Firmware Hub Device"},
+ {"INT3394", "ACPI System Fan"},
+ {"INT3396", "Standard Power Management Controller"},
+ {"INT33A0", "Intel Smart Connect Technology Device"},
+ {"INT33A1", "Intel Power Engine"},
+ {"INT33BB", "Intel Baytrail SD Host Controller"},
+ {"INT33BD", "Intel Baytrail Mailbox Device"},
+ {"INT33BE", "Camera Sensor OV5693"},
+ {"INT33C0", "Intel Serial I/O SPI Host Controller"},
+ {"INT33C1", "Intel Serial I/O SPI Host Controller"},
+ {"INT33C2", "Intel Serial I/O I2C Host Controller"},
+ {"INT33C3", "Intel Serial I/O I2C Host Controller"},
+ {"INT33C4", "Intel Serial I/O UART Host Controller"},
+ {"INT33C5", "Intel Serial I/O UART Host Controller"},
+ {"INT33C6", "Intel SD Host Controller"},
+ {"INT33C7", "Intel Serial I/O GPIO Host Controller"},
+ {"INT33C8", "Intel Smart Sound Technology Host Controller"},
+ {"INT33C9", "Wolfson Microelectronics Audio WM5102"},
+ {"INT33CA", "Intel SPB Peripheral"},
+ {"INT33CB", "Intel Smart Sound Technology Audio Codec"},
+ {"INT33D1", "Intel GPIO Buttons"},
+ {"INT33D2", "Intel GPIO Buttons"},
+ {"INT33D3", "Intel GPIO Buttons"},
+ {"INT33D4", "Intel GPIO Buttons"},
+ {"INT33D6", "Intel Virtual Buttons Device"},
+ {"INT33F0", "Camera Sensor MT9M114"},
+ {"INT33F4", "XPOWER PMIC Controller"},
+ {"INT33F5", "TI PMIC Controller"},
+ {"INT33FB", "MIPI-CSI Camera Sensor OV2722"},
+ {"INT33FC", "Intel Baytrail GPIO Controller"},
+ {"INT33FD", "Intel Baytrail Power Management IC"},
+ {"INT33FE", "XPOWER Battery Device"},
+ {"INT3400", "Intel Dynamic Power Performance Management"},
+ {"INT3401", "Intel Extended Thermal Model CPU"},
+ {"INT3403", "DPTF Temperature Sensor"},
+ {"INT3406", "Intel Dynamic Platform & Thermal Framework Display Participant"},
+ {"INT3407", "DPTF Platform Power Meter"},
+ {"INT340E", "Motherboard Resources"},
+ {"INT3420", "Intel Bluetooth RF Kill"},
+ {"INT3F0D", "ACPI Motherboard Resources"},
+ {"INTCF1A", "Sony IMX175 Camera Sensor"},
+ {"INTCFD9", "Intel Baytrail SOC GPIO Controller"},
+ {"INTL9C60", "Intel Baytrail SOC DMA Controller"},
+ {"INVN6500", "InvenSense MPU-6500 Six Axis Gyroscope and Accelerometer"},
+ {"LNXCPU", "Linux Logical CPU"},
+ {"LNXPOWER", "ACPI Power Resource (power gating)"},
+ {"LNXPWRBN", "System Power Button"},
+ {"LNXSYBUS", "System Bus"},
+ {"LNXSYSTM", "ACPI Root Node"},
+ {"LNXTHERM", "ACPI Thermal Zone"},
+ {"LNXVIDEO", "ACPI Video Controller"},
+ {"MAX17047", "Fuel Gauge Controller"},
+ {"MSFT0101", "TPM 2.0 Security Device"},
+ {"NXP5442", "NXP 5442 Near Field Communications Controller"},
+ {"NXP5472", "NXP NFC"},
+ {"PNP0000", "8259-compatible Programmable Interrupt Controller"},
+ {"PNP0001", "EISA Interrupt Controller"},
+ {"PNP0002", "MCA Interrupt Controller"},
+ {"PNP0003", "IO-APIC Interrupt Controller"},
+ {"PNP0100", "PC-class System Timer"},
+ {"PNP0103", "HPET System Timer"},
+ {"PNP0200", "PC-class DMA Controller"},
+ {"PNP0300", "IBM PC/XT Keyboard Controller (83 key)"},
+ {"PNP0301", "IBM PC/XT Keyboard Controller (86 key)"},
+ {"PNP0302", "IBM PC/XT Keyboard Controller (84 key)"},
+ {"PNP0303", "IBM Enhanced Keyboard (101/102-key, PS/2 Mouse)"},
+ {"PNP0400", "Standard LPT Parallel Port"},
+ {"PNP0401", "ECP Parallel Port"},
+ {"PNP0500", "Standard PC COM Serial Port"},
+ {"PNP0501", "16550A-compatible COM Serial Port"},
+ {"PNP0510", "Generic IRDA-compatible Device"},
+ {"PNP0800", "Microsoft Sound System Compatible Device"},
+ {"PNP0A03", "PCI Bus"},
+ {"PNP0A05", "Generic Container Device"},
+ {"PNP0A06", "Generic Container Device"},
+ {"PNP0A08", "PCI Express Bus"},
+ {"PNP0B00", "AT Real-Time Clock"},
+ {"PNP0B01", "Intel PIIX4-compatible RTC/CMOS Device"},
+ {"PNP0B02", "Dallas Semiconductor-compatible RTC/CMOS Device"},
+ {"PNP0C01", "System Board"},
+ {"PNP0C02", "PNP Motherboard Resources"},
+ {"PNP0C04", "x87-compatible Floating Point Processing Unit"},
+ {"PNP0C08", "ACPI Core Hardware"},
+ {"PNP0C09", "Embedded Controller Device"},
+ {"PNP0C0A", "Control Method Battery"},
+ {"PNP0C0B", "Fan (Thermal Solution)"},
+ {"PNP0C0C", "Power Button Device"},
+ {"PNP0C0D", "Lid Device"},
+ {"PNP0C0E", "Sleep Button Device"},
+ {"PNP0C0F", "PCI Interrupt Link Device"},
+ {"PNP0C10", "System Indicator Device"},
+ {"PNP0C11", "Thermal Zone"},
+ {"PNP0C12", "Device Bay Controller"},
+ {"PNP0C14", "Windows Management Instrumentation Device"},
+ {"PNP0C15", "Docking Station"},
+ {"PNP0C33", "Error Device"},
+ {"PNP0C40", "Standard Button Controller"},
+ {"PNP0C50", "HID Protocol Device (I2C bus)"},
+ {"PNP0C60", "Display Sensor Device"},
+ {"PNP0C70", "Dock Sensor Device"},
+ {"PNP0C80", "Memory Device"},
+ {"PNP0D10", "XHCI USB Controller with debug"},
+ {"PNP0D15", "XHCI USB Controller without debug"},
+ {"PNP0D20", "EHCI USB Controller without debug"},
+ {"PNP0D25", "EHCI USB Controller with debug"},
+ {"PNP0D40", "SDA Standard Compliant SD Host Controller"},
+ {"PNP0D80", "Windows-compatible System Power Management Controller"},
+ {"PNP0F03", "Microsoft PS/2-style Mouse"},
+ {"PNP0F13", "PS/2 Mouse"},
+ {"RTL8723", "Realtek Wireless Controller"},
+ {"SMB0349", "Charger"},
+ {"SMO91D0", "Sensor Hub"},
+ {"SMSC3750", "SMSC 3750 USB MUX"},
+ {"SSPX0000", "Intel SSP Device"},
+ {"TBQ24296", "Charger"},
+
+ {NULL, NULL}
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiAhMatchHardwareId
+ *
+ * PARAMETERS: HardwareId - String representation of an _HID or _CID
+ *
+ * RETURN: ID info struct. NULL if HardwareId is not found
+ *
+ * DESCRIPTION: Lookup an _HID/_CID in the device ID table
+ *
+ ******************************************************************************/
+
+const AH_DEVICE_ID *
+AcpiAhMatchHardwareId (
+ char *HardwareId)
+{
+ const AH_DEVICE_ID *Info;
+
+
+ for (Info = AslDeviceIds; Info->Name; Info++)
+ {
+ if (!strcmp (HardwareId, Info->Name))
+ {
+ return (Info);
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/uts/intel/io/acpica/changes.txt b/usr/src/uts/intel/io/acpica/changes.txt
index f53fd426da..ff863a7c3c 100644
--- a/usr/src/uts/intel/io/acpica/changes.txt
+++ b/usr/src/uts/intel/io/acpica/changes.txt
@@ -1,11 +1,4408 @@
----------------------------------------
-27 May 2011. Summary of changes for version 20110527:
+27 May 2016. Summary of changes for version 20160527:
+
+This release is available at https://acpica.org/downloads
+
+
+1) ACPICA kernel-resident subsystem:
+
+Temporarily reverted the new arbitrary bit length/alignment support in
+AcpiHwRead/AcpiHwWrite for the Generic Address Structure. There have been
+a number of regressions with the new code that need to be fully resolved
+and tested before this support can be finally integrated into ACPICA.
+Apologies for any inconveniences these issues may have caused.
+
+The ACPI message macros are not configurable (ACPI_MSG_ERROR,
+ACPI_MSG_EXCEPTION, ACPI_MSG_WARNING, ACPI_MSG_INFO, ACPI_MSG_BIOS_ERROR,
+and ACPI_MSG_BIOS_WARNING). Lv Zheng.
+
+Fixed a couple of GCC warnings associated with the use of the -Wcast-qual
+option. Adds a new return macro, return_STR. Junk-uk Kim.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 136.8K Code, 51.6K Data, 188.4K Total
+ Debug Version: 201.5K Code, 82.2K Data, 283.7K Total
+ Previous Release:
+ Non-Debug Version: 137.4K Code, 52.6K Data, 190.0K Total
+ Debug Version: 200.9K Code, 82.2K Data, 283.1K Total
+
+----------------------------------------
+22 April 2016. Summary of changes for version 20160422:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a regression in the GAS (generic address structure) arbitrary bit
+support in AcpiHwRead/AcpiHwWrite. Problem could cause incorrect behavior
+and incorrect return values. Lv Zheng. ACPICA BZ 1270.
+
+ACPI 6.0: Added support for new/renamed resource macros. One new argument
+was added to each of these macros, and the original name has been
+deprecated. The AML disassembler will always disassemble to the new
+names. Support for the new macros was added to iASL, disassembler,
+resource manager, and the acpihelp utility. ACPICA BZ 1274.
+
+ I2cSerialBus -> I2cSerialBusV2
+ SpiSerialBus -> SpiSerialBusV2
+ UartSerialBus -> UartSerialBusV2
+
+ACPI 6.0: Added support for a new integer field that was appended to the
+package object returned by the _BIX method. This adds iASL compile-time
+and AML runtime error checking. ACPICA BZ 1273.
+
+ACPI 6.1: Added support for a new PCCT subtable, "HW-Reduced Comm
+Subspace Type2" (Headers, Disassembler, and data table compiler).
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 137.4K Code, 52.6K Data, 190.0K Total
+ Debug Version: 201.5K Code, 82.2K Data, 283.7K Total
+ Previous Release:
+ Non-Debug Version: 137.1K Code, 51.5K Data, 188.6K Total
+ Debug Version: 201.0K Code, 82.0K Data, 283.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented an ASL grammar extension to allow/enable executable
+"module-level code" to be created and executed under the various
+operators that create new scopes. This type of AML code is already
+supported in all known AML interpreters, and the grammar change will
+appear in the next version of the ACPI specification. Simplifies the
+conditional runtime creation of named objects under these object types:
+
+ Device
+ PowerResource
+ Processor
+ Scope
+ ThermalZone
+
+iASL: Implemented a new ASL extension, a "For" loop macro to add greater
+ease-of-use to the ASL language. The syntax is similar to the
+corresponding C operator, and is implemented with the existing AML While
+opcode -- thus requiring no changes to existing AML interpreters.
+
+ For (Initialize, Predicate, Update) {TermList}
+
+Grammar:
+ ForTerm :=
+ For (
+ Initializer // Nothing | TermArg => ComputationalData
+ Predicate // Nothing | TermArg => ComputationalData
+ Update // Nothing | TermArg => ComputationalData
+ ) {TermList}
+
+
+iASL: The _HID/_ADR detection and validation has been enhanced to search
+under conditionals in order to allow these objects to be conditionally
+created at runtime.
+
+iASL: Fixed several issues with the constant folding feature. The
+improvement allows better detection and resolution of statements that can
+be folded at compile time. ACPICA BZ 1266.
+
+iASL/Disassembler: Fixed a couple issues with the Else{If{}...}
+conversion to the ASL ElseIf operator where incorrect ASL code could be
+generated.
+
+iASL/Disassembler: Fixed a problem with the ASL+ code disassembly where
+sometimes an extra (and extraneous) set of parentheses were emitted for
+some combinations of operators. Although this did not cause any problems
+with recompilation of the disassembled code, it made the code more
+difficult to read. David Box. ACPICA BZ 1231.
+
+iASL: Changed to ignore the unreferenced detection for predefined names
+of resource descriptor elements, when the resource descriptor is
+created/defined within a control method.
+
+iASL: Disassembler: Fix a possible fault with externally declared Buffer
+objects.
+
+----------------------------------------
+18 March 2016. Summary of changes for version 20160318:
+
+1) ACPICA kernel-resident subsystem:
+
+Added support for arbitrary bit lengths and bit offsets for registers
+defined by the Generic Address Structure. Previously, only aligned bit
+lengths of 8/16/32/64 were supported. This was sufficient for many years,
+but recently some machines have been seen that require arbitrary bit-
+level support. ACPICA BZ 1240. Lv Zheng.
+
+Fixed an issue where the \_SB._INI method sometimes must be evaluated
+before any _REG methods are evaluated. Lv Zheng.
+
+Implemented several changes related to ACPI table support
+(Headers/Disassembler/TableCompiler):
+NFIT: For ACPI 6.1, updated to add some additional new fields and
+constants.
+FADT: Updated a warning message and set compliance to ACPI 6.1 (Version
+6).
+DMAR: Added new constants per the 10/2014 DMAR spec.
+IORT: Added new subtable per the 10/2015 IORT spec.
+HEST: For ACPI 6.1, added new constants and new subtable.
+DBG2: Added new constants per the 12/2015 DBG2 spec.
+FPDT: Fixed several incorrect fields, add the FPDT boot record structure.
+ACPICA BZ 1249.
+ERST/EINJ: Updated disassembler with new "Execute Timings" actions.
+
+Updated header support for the DMAR table to match the current version of
+the related spec.
+
+Added extensions to the ASL Concatenate operator to allow any ACPI object
+to be passed as an operand. Any object other than Integer/String/Buffer
+simply returns a string containing the object type. This extends the
+usefulness of the Printf macros. Previously, Concatenate would abort the
+control method if a non-data object was encountered.
+
+ACPICA source code: Deployed the C "const" keyword across the source code
+where appropriate. ACPICA BZ 732. Joerg Sonnenberger (NetBSD).
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 137.1K Code, 51.5K Data, 188.6K Total
+ Debug Version: 201.0K Code, 82.0K Data, 283.0K Total
+ Previous Release:
+ Non-Debug Version: 136.2K Code, 51.5K Data, 187.7K Total
+ Debug Version: 200.4K Code, 82.0K Data, 282.4K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL/Disassembler: Improved the heuristic used to determine the number of
+arguments for an externally defined control method (a method in another
+table). Although this is an improvement, there is no deterministic way to
+"guess" the number of method arguments. Only the ACPI 6.0 External opcode
+will completely solve this problem as it is deployed (automatically) in
+newer BIOS code.
+
+iASL/Disassembler: Fixed an ordering issue for emitted External() ASL
+statements that could cause errors when the disassembled file is
+compiled. ACPICA BZ 1243. David Box.
+
+iASL: Fixed a regression caused by the merger of the two versions of the
+local strtoul64. Because of a dependency on a global variable, strtoul64
+could return an error for integers greater than a 32-bit value. ACPICA BZ
+1260.
+
+iASL: Fixed a regression where a fault could occur for an ASL Return
+statement if it invokes a control method that is not resolved. ACPICA BZ
+1264.
+
+AcpiXtract: Improved input file validation: detection of binary files and
+non-acpidump text files.
+
+----------------------------------------
+12 February 2016. Summary of changes for version 20160212:
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented full support for the ACPI 6.1 specification (released in
+January). This version of the specification is available at:
+http://www.uefi.org/specifications
+
+Only a relatively small number of changes were required in ACPICA to
+support ACPI 6.1, in these areas:
+- New predefined names
+- New _HID values
+- A new subtable for HEST
+- A few other header changes for new values
+
+Ensure \_SB_._INI is executed before any _REG methods are executed. There
+appears to be existing BIOS code that relies on this behavior. Lv Zheng.
+
+Reverted a change made in version 20151218 which enabled method
+invocations to be targets of various ASL operators (SuperName and Target
+grammar elements). While the new behavior is supported by the ACPI
+specification, other AML interpreters do not support this behavior and
+never will. The ACPI specification will be updated for ACPI 6.2 to remove
+this support. Therefore, the change was reverted to the original ACPICA
+behavior.
+
+ACPICA now supports the GCC 6 compiler.
+
+Current Release: (Note: build changes increased sizes)
+ Non-Debug Version: 136.2K Code, 51.5K Data, 187.7K Total
+ Debug Version: 200.4K Code, 82.0K Data, 282.4K Total
+Previous Release:
+ Non-Debug Version: 102.7K Code, 28.4K Data, 131.1K Total
+ Debug Version: 200.4K Code, 81.9K Data, 282.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Completed full support for the ACPI 6.0 External() AML opcode. The
+compiler emits an external AML opcode for each ASL External statement.
+This opcode is used by the disassembler to assist with the disassembly of
+external control methods by specifying the required number of arguments
+for the method. AML interpreters do not use this opcode. To ensure that
+interpreters do not even see the opcode, a block of one or more external
+opcodes is surrounded by an "If(0)" construct. As this feature becomes
+commonly deployed in BIOS code, the ability of disassemblers to correctly
+disassemble AML code will be greatly improved. David Box.
+
+iASL: Implemented support for an optional cross-reference output file.
+The -lx option will create a the cross-reference file with the suffix
+"xrf". Three different types of cross-reference are created in this file:
+- List of object references made from within each control method
+- Invocation (caller) list for each user-defined control method
+- List of references to each non-method object in the namespace
+
+iASL: Method invocations as ASL Target operands are now disallowed and
+flagged as errors in preparation for ACPI 6.2 (see the description of the
+problem above).
+
+----------------------------------------
+8 January 2016. Summary of changes for version 20160108:
+
+1) ACPICA kernel-resident subsystem:
+
+Updated all ACPICA copyrights and signons to 2016: Added the 2016
+copyright to all source code module headers and utility/tool signons.
+This includes the standard Linux dual-license header. This affects
+virtually every file in the ACPICA core subsystem, iASL compiler, all
+ACPICA utilities, and the ACPICA test suite.
+
+Fixed a regression introduced in version 20151218 concerning the
+execution of so-called module-level ASL/AML code. Namespace objects
+created under a module-level If() construct were not properly/fully
+entered into the namespace and could cause an interpreter fault when
+accessed.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+Current Release:
+ Non-Debug Version: 102.7K Code, 28.4K Data, 131.1K Total
+ Debug Version: 200.4K Code, 81.9K Data, 282.4K Total
+ Previous Release:
+ Non-Debug Version: 102.6K Code, 28.4K Data, 131.0K Total
+ Debug Version: 200.3K Code, 81.9K Data, 282.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Fixed a problem with the compilation of the GpioIo and GpioInt resource
+descriptors. The _PIN field name was incorrectly defined to be an array
+of 32-bit values, but the _PIN values are in fact 16 bits each. This
+would cause incorrect bit width warnings when using Word (16-bit) fields
+to access the descriptors.
+
+
+----------------------------------------
+18 December 2015. Summary of changes for version 20151218:
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented per-AML-table execution of "module-level code" as individual
+ACPI tables are loaded into the namespace during ACPICA initialization.
+In other words, any module-level code within an AML table is executed
+immediately after the table is loaded, instead of batched and executed
+after all of the tables have been loaded. This provides compatibility
+with other ACPI implementations. ACPICA BZ 1219. Bob Moore, Lv Zheng,
+David Box.
+
+To fully support the feature above, the default operation region handlers
+for the SystemMemory, SystemIO, and PCI_Config address spaces are now
+installed before any ACPI tables are loaded. This enables module-level
+code to access these address spaces during the table load and module-
+level code execution phase. ACPICA BZ 1220. Bob Moore, Lv Zheng, David
+Box.
+
+Implemented several changes to the internal _REG support in conjunction
+with the changes above. Also, changes to the AcpiExec/AcpiNames/Examples
+utilities for the changes above. Although these tools were changed, host
+operating systems that simply use the default handlers for SystemMemory,
+SystemIO, and PCI_Config spaces should not require any update. Lv Zheng.
+
+For example, in the code below, DEV1 is conditionally added to the
+namespace by the DSDT via module-level code that accesses an operation
+region. The SSDT references DEV1 via the Scope operator. DEV1 must be
+created immediately after the DSDT is loaded in order for the SSDT to
+successfully reference DEV1. Previously, this code would cause an
+AE_NOT_EXIST exception during the load of the SSDT. Now, this code is
+fully supported by ACPICA.
+
+ DefinitionBlock ("", "DSDT", 2, "Intel", "DSDT1", 1)
+ {
+ OperationRegion (OPR1, SystemMemory, 0x400, 32)
+ Field (OPR1, AnyAcc, NoLock, Preserve)
+ {
+ FLD1, 1
+ }
+ If (FLD1)
+ {
+ Device (\DEV1)
+ {
+ }
+ }
+ }
+ DefinitionBlock ("", "SSDT", 2, "Intel", "SSDT1", 1)
+ {
+ External (\DEV1, DeviceObj)
+ Scope (\DEV1)
+ {
+ }
+ }
+
+Fixed an AML interpreter problem where control method invocations were
+not handled correctly when the invocation was itself a SuperName argument
+to another ASL operator. In these cases, the method was not invoked.
+ACPICA BZ 1002. Affects the following ASL operators that have a SuperName
+argument:
+ Store
+ Acquire, Wait
+ CondRefOf, RefOf
+ Decrement, Increment
+ Load, Unload
+ Notify
+ Signal, Release, Reset
+ SizeOf
+
+Implemented automatic String-to-ObjectReference conversion support for
+packages returned by predefined names (such as _DEP). A common BIOS error
+is to add double quotes around an ObjectReference namepath, which turns
+the reference into an unexpected string object. This support detects the
+problem and corrects it before the package is returned to the caller that
+invoked the method. Lv Zheng.
+
+Implemented extensions to the Concatenate operator. Concatenate now
+accepts any type of object, it is not restricted to simply
+Integer/String/Buffer. For objects other than these 3 basic data types,
+the argument is treated as a string containing the name of the object
+type. This expands the utility of Concatenate and the Printf/Fprintf
+macros. ACPICA BZ 1222.
+
+Cleaned up the output of the ASL Debug object. The timer() value is now
+optional and no longer emitted by default. Also, the basic data types of
+Integer/String/Buffer are simply emitted as their values, without a data
+type string -- since the data type is obvious from the output. ACPICA BZ
+1221.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 102.6K Code, 28.4K Data, 131.0K Total
+ Debug Version: 200.3K Code, 81.9K Data, 282.3K Total
+ Previous Release:
+ Non-Debug Version: 102.0K Code, 28.3K Data, 130.3K Total
+ Debug Version: 199.6K Code, 81.8K Data, 281.4K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed some issues with the ASL Include() operator. This operator
+was incorrectly defined in the iASL parser rules, causing a new scope to
+be opened for the code within the include file. This could lead to
+several issues, including allowing ASL code that is technically illegal
+and not supported by AML interpreters. Note, this does not affect the
+related #include preprocessor operator. ACPICA BZ 1212.
+
+iASL/Disassembler: Implemented support for the ASL ElseIf operator. This
+operator is essentially an ASL macro since there is no AML opcode
+associated with it. The code emitted by the iASL compiler for ElseIf is
+an Else opcode followed immediately by an If opcode. The disassembler
+will now emit an ElseIf if it finds an Else immediately followed by an
+If. This simplifies the decoded ASL, especially for deeply nested
+If..Else and large Switch constructs. Thus, the disassembled code more
+closely follows the original source ASL. ACPICA BZ 1211. Example:
+
+ Old disassembly:
+ Else
+ {
+ If (Arg0 == 0x02)
+ {
+ Local0 = 0x05
+ }
+ }
+
+ New disassembly:
+ ElseIf (Arg0 == 0x02)
+ {
+ Local0 = 0x05
+ }
+
+AcpiExec: Added support for the new module level code behavior and the
+early region installation. This required a small change to the
+initialization, since AcpiExec must install its own operation region
+handlers.
+
+AcpiExec: Added support to make the debug object timer optional. Default
+is timer disabled. This cleans up the debug object output -- the timer
+data is rarely used.
+
+AcpiExec: Multiple ACPI tables are now loaded in the order that they
+appear on the command line. This can be important when there are
+interdependencies/references between the tables.
+
+iASL/Templates. Add support to generate template files with multiple
+SSDTs within a single output file. Also added ommand line support to
+specify the number of SSDTs (in addition to a single DSDT). ACPICA BZ
+1223, 1225.
+
+
+----------------------------------------
+24 November 2015. Summary of changes for version 20151124:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a possible regression for a previous update to FADT handling. The
+FADT no longer has a fixed table ID, causing some issues with code that
+was hardwired to a specific ID. Lv Zheng.
+
+Fixed a problem where the method auto-serialization could interfere with
+the current SyncLevel. This change makes the auto-serialization support
+transparent to the SyncLevel support and management.
+
+Removed support for the _SUB predefined name in AcpiGetObjectInfo. This
+interface is intended for early access to the namespace during the
+initial namespace device discovery walk. The _SUB method has been seen to
+access operation regions in some cases, causing errors because the
+operation regions are not fully initialized.
+
+AML Debugger: Fixed some issues with the terminate/quit/exit commands
+that can cause faults. Lv Zheng.
+
+AML Debugger: Add thread ID support so that single-step mode only applies
+to the AML Debugger thread. This prevents runtime errors within some
+kernels. Lv Zheng.
+
+Eliminated extraneous warnings from AcpiGetSleepTypeData. Since the _Sx
+methods that are invoked by this interface are optional, removed warnings
+emitted for the case where one or more of these methods do not exist.
+ACPICA BZ 1208, original change by Prarit Bhargava.
+
+Made a major pass through the entire ACPICA source code base to
+standardize formatting that has diverged a bit over time. There are no
+functional changes, but this will of course cause quite a few code
+differences from the previous ACPICA release.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 102.0K Code, 28.3K Data, 130.3K Total
+ Debug Version: 199.6K Code, 81.8K Data, 281.4K Total
+ Previous Release:
+ Non-Debug Version: 101.7K Code, 27.9K Data, 129.6K Total
+ Debug Version: 199.3K Code, 81.4K Data, 280.7K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL/acpiexec/acpixtract/disassembler: Added support to allow multiple
+definition blocks within a single ASL file and the resulting AML file.
+Support for this type of file was also added to the various tools that
+use binary AML files: acpiexec, acpixtract, and the AML disassembler. The
+example code below shows two definition blocks within the same file:
+
+ DefinitionBlock ("dsdt.aml", "DSDT", 2, "Intel", "Template",
+0x12345678)
+ {
+ }
+ DefinitionBlock ("", "SSDT", 2, "Intel", "Template", 0xABCDEF01)
+ {
+ }
+
+iASL: Enhanced typechecking for the Name() operator. All expressions for
+the value of the named object must be reduced/folded to a single constant
+at compile time, as per the ACPI specification (the AML definition of
+Name()).
+
+iASL: Fixed some code indentation issues for the -ic and -ia options (C
+and assembly headers). Now all emitted code correctly begins in column 1.
+
+iASL: Added an error message for an attempt to open a Scope() on an
+object defined in an SSDT. The DSDT is always loaded into the namespace
+first, so any attempt to open a Scope on an SSDT object will fail at
+runtime.
+
+
+----------------------------------------
+30 September 2015. Summary of changes for version 20150930:
+
+1) ACPICA kernel-resident subsystem:
+
+Debugger: Implemented several changes and bug fixes to assist support for
+the in-kernel version of the AML debugger. Lv Zheng.
+- Fix the "predefined" command for in-kernel debugger.
+- Do not enter debug command loop for the help and version commands.
+- Disallow "execute" command during execution/single-step of a method.
+
+Interpreter: Updated runtime typechecking for all operators that have
+target operands. The operand is resolved and validated that it is legal.
+For example, the target cannot be a non-data object such as a Device,
+Mutex, ThermalZone, etc., as per the ACPI specification.
+
+Debugger: Fixed the double-mutex user I/O handshake to work when local
+deadlock detection is enabled.
+
+Debugger: limited display of method locals and arguments (LocalX and
+ArgX) to only those that have actually been initialized. This prevents
+lines of extraneous output.
+
+Updated the definition of the NFIT table to correct the bit polarity of
+one flag: ACPI_NFIT_MEM_ARMED --> ACPI_NFIT_MEM_NOT_ARMED
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 101.7K Code, 27.9K Data, 129.6K Total
+ Debug Version: 199.3K Code, 81.4K Data, 280.7K Total
+ Previous Release:
+ Non-Debug Version: 101.3K Code, 27.7K Data, 129.0K Total
+ Debug Version: 198.6K Code, 80.9K Data, 279.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Improved the compile-time typechecking for operands of many of the
+ASL operators:
+
+-- Added an option to disable compiler operand/operator typechecking (-
+ot).
+
+-- For the following operators, the TermArg operands are now validated
+when possible to be Integer data objects: BankField, OperationRegion,
+DataTableRegion, Buffer, and Package.
+
+-- Store (Source, Target): Both the source and target operands are
+resolved and checked that the operands are both legal. For example,
+neither operand can be a non-data object such as a Device, Mutex,
+ThermalZone, etc. Note, as per the ACPI specification, the CopyObject
+operator can be used to store an object to any type of target object.
+
+-- Store (Source, Target): If the source is a Package object, the target
+must be a Package object, LocalX, ArgX, or Debug. Likewise, if the target
+is a Package, the source must also be a Package.
+
+-- Store (Source, Target): A warning is issued if the source and target
+resolve to the identical named object.
+
+-- Store (Source, <method invocation>): An error is generated for the
+target method invocation, as this construct is not supported by the AML
+interpreter.
+
+-- For all ASL math and logic operators, the target operand must be a
+data object (Integer, String, Buffer, LocalX, ArgX, or Debug). This
+includes the function return value also.
+
+-- External declarations are also included in the typechecking where
+possible. External objects defined using the UnknownObj keyword cannot be
+typechecked, however.
+
+iASL and Disassembler: Added symbolic (ASL+) support for the ASL Index
+operator:
+- Legacy code: Index(PKG1, 3)
+- New ASL+ code: PKG1[3]
+This completes the ACPI 6.0 ASL+ support as it was the only operator not
+supported.
+
+iASL: Fixed the file suffix for the preprocessor output file (.i). Two
+spaces were inadvertently appended to the filename, causing file access
+and deletion problems on some systems.
+
+ASL Test Suite (ASLTS): Updated the master makefile to generate all
+possible compiler output files when building the test suite -- thus
+exercising these features of the compiler. These files are automatically
+deleted when the test suite exits.
+
+
+----------------------------------------
+18 August 2015. Summary of changes for version 20150818:
+
+1) ACPICA kernel-resident subsystem:
+
+Fix a regression for AcpiGetTableByIndex interface causing it to fail. Lv
+Zheng. ACPICA BZ 1186.
+
+Completed development to ensure that the ACPICA Disassembler and Debugger
+are fully standalone components of ACPICA. Removed cross-component
+dependences. Lv Zheng.
+
+The max-number-of-AML-loops is now runtime configurable (previously was
+compile-time only). This is essentially a loop timeout to force-abort
+infinite AML loops. ACPCIA BZ 1192.
+
+Debugger: Cleanup output to dump ACPI names and namepaths without any
+trailing underscores. Lv Zheng. ACPICA BZ 1135.
+
+Removed unnecessary conditional compilations across the Debugger and
+Disassembler components where entire modules could be left uncompiled.
+
+The aapits test is deprecated and has been removed from the ACPICA git
+tree. The test has never been completed and has not been maintained, thus
+becoming rather useless. ACPICA BZ 1015, 794.
+
+A batch of small changes to close bugzilla and other reports:
+- Remove duplicate code for _PLD processing. ACPICA BZ 1176.
+- Correctly cleanup after a ACPI table load failure. ACPICA BZ 1185.
+- iASL: Support POSIX yacc again in makefile. Jung-uk Kim.
+- ACPI table support: general cleanup and simplification. Lv Zheng, Bob
+Moore.
+- ACPI table support: fix for a buffer read overrun in AcpiTbFindTable.
+ACPICA BZ 1184.
+- Enhance parameter validation for DataTableRegion and LoadTable ASL/AML
+operators.
+- Debugger: Split debugger initialization/termination interfaces. Lv
+Zheng.
+- AcpiExec: Emit OemTableId for SSDTs during the load phase for table
+identification.
+- AcpiExec: Add debug message during _REG method phase during table
+load/init.
+- AcpiNames: Fix a regression where some output was missing and no longer
+emitted.
+- Debugger: General cleanup and simplification. Lv Zheng.
+- Disassembler: Cleanup use of several global option variables. Lv Zheng.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 101.3K Code, 27.7K Data, 129.0K Total
+ Debug Version: 198.6K Code, 80.9K Data, 279.5K Total
+ Previous Release:
+ Non-Debug Version: 100.9K Code, 24.5K Data, 125.4K Total
+ Debug Version: 197.8K Code, 81.5K Data, 279.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiExec: Fixed a problem where any more than 32 ACPI tables in the XSDT
+were not handled properly and caused load errors. Now, properly invoke
+and use the ACPICA auto-reallocate mechanism for ACPI table data
+structures. ACPICA BZ 1188
+
+AcpiNames: Add command-line wildcard support for ACPI table files. ACPICA
+BZ 1190.
+
+AcpiExec and AcpiNames: Add -l option to load ACPI tables only. For
+AcpiExec, this means that no control methods (like _REG/_INI/_STA) are
+executed during initialization. ACPICA BZ 1187, 1189.
+
+iASL/Disassembler: Implemented a prototype "listing" mode that emits AML
+that corresponds to each disassembled ASL statement, to simplify
+debugging. ACPICA BZ 1191.
+
+Debugger: Add option to the "objects" command to display a summary of the
+current namespace objects (Object type and count). This is displayed if
+the command is entered with no arguments.
+
+AcpiNames: Add -x option to specify debug level, similar to AcpiExec.
+
+
+----------------------------------------
+17 July 2015. Summary of changes for version 20150717:
+
+1) ACPICA kernel-resident subsystem:
+
+Improved the partitioning between the Debugger and Disassembler
+components. This allows the Debugger to be used standalone within kernel
+code without the Disassembler (which is used for single stepping also).
+This renames and moves one file, dmobject.c to dbobject.c. Lv Zheng.
+
+Debugger: Implemented a new command to trace the execution of control
+methods (Trace). This is especially useful for the in-kernel version of
+the debugger when file I/O may not be available for method trace output.
+See the ACPICA reference for more information. Lv Zheng.
+
+Moved all C library prototypes (used for the local versions of these
+functions when requested) to a new header, acclib.h
+Cleaned up the use of non-ANSI C library functions. These functions are
+implemented locally in ACPICA. Moved all such functions to a common
+source file, utnonansi.c
+
+Debugger: Fixed a problem with the "!!" command (get last command
+executed) where the debugger could enter an infinite loop and eventually
+crash.
+
+Removed the use of local macros that were used for some of the standard C
+library functions to automatically cast input parameters. This mostly
+affected the is* functions where the input parameter is defined to be an
+int. This required a few modifications to the main ACPICA source code to
+provide casting for these functions and eliminate possible compiler
+warnings for these parameters.
+
+Across the source code, added additional status/error checking to resolve
+issues discovered by static source code analysis tools such as Coverity.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 100.9K Code, 24.5K Data, 125.4K Total
+ Debug Version: 197.8K Code, 81.5K Data, 279.3K Total
+ Previous Release:
+ Non-Debug Version: 100.6K Code, 27.6K Data, 128.2K Total
+ Debug Version: 196.2K Code, 81.0K Data, 277.2K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed a regression where the device map file feature no longer
+worked properly when used in conjunction with the disassembler. It only
+worked properly with the compiler itself.
+
+iASL: Implemented a new warning for method LocalX variables that are set
+but never used (similar to a C compiler such as gcc). This also applies
+to ArgX variables that are not defined by the parent method, and are
+instead (legally) used as local variables.
+
+iASL/Preprocessor: Finished the pass-through of line numbers from the
+preprocessor to the compiler. This ensures that compiler errors/warnings
+have the correct original line numbers and filenames, regardless of any
+#include files.
+
+iASL/Preprocessor: Fixed a couple of issues with comment handling and the
+pass-through of comments to the preprocessor output file (which becomes
+the compiler input file). Also fixed a problem with // comments that
+appear after a math expression.
+
+iASL: Added support for the TCPA server table to the table compiler and
+template generator. (The client table was already previously supported)
+
+iASL/Preprocessor: Added a permanent #define of the symbol "__IASL__" to
+identify the iASL compiler.
+
+Cleaned up the use of the macros NEGATIVE and POSITIVE which were defined
+multiple times. The new names are ACPI_SIGN_NEGATIVE and
+ACPI_SIGN_POSITIVE.
+
+AcpiHelp: Update to expand help messages for the iASL preprocessor
+directives.
+
+
+----------------------------------------
+19 June 2015. Summary of changes for version 20150619:
+
+Two regressions in version 20150616 have been addressed:
+
+Fixes some problems/issues with the C library macro removal (ACPI_STRLEN,
+etc.) This update changes ACPICA to only use the standard headers for
+functions, or the prototypes for the local versions of the C library
+functions. Across the source code, this required some additional casts
+for some Clib invocations for portability. Moved all local prototypes to
+a new file, acclib.h
+
+Fixes several problems with recent changes to the handling of the FACS
+table that could cause some systems not to boot.
+
+
+----------------------------------------
+16 June 2015. Summary of changes for version 20150616:
+
+
+1) ACPICA kernel-resident subsystem:
+
+Across the entire ACPICA source code base, the various macros for the C
+library functions (such as ACPI_STRLEN, etc.) have been removed and
+replaced by the standard C library names (strlen, etc.) The original
+purpose for these macros is no longer applicable. This simplification
+reduces the number of macros used in the ACPICA source code
+significantly, improving readability and maintainability.
+
+Implemented support for a new ACPI table, the OSDT. This table, the
+"override" SDT, can be loaded directly by the host OS at boot time. It
+enables the replacement of existing namespace objects that were installed
+via the DSDT and/or SSDTs. The primary purpose for this is to replace
+buggy or incorrect ASL/AML code obtained via the BIOS. The OSDT is slated
+for inclusion in a future version of the ACPI Specification. Lv Zheng/Bob
+Moore.
+
+Added support for systems with (improperly) two FACS tables -- a "32-bit"
+table (via FADT 32-bit legacy field) and a "64-bit" table (via the 64-bit
+X field). This change will support both automatically. There continues to
+be systems found with this issue. This support requires a change to the
+AcpiSetFirmwareWakingVector interface. Also, a public global variable has
+been added to allow the host to select which FACS is desired
+(AcpiGbl_Use32BitFacsAddresses). See the ACPICA reference for more
+details Lv Zheng.
+
+Added a new feature to allow for systems that do not contain an FACS.
+Although this is already supported on hardware-reduced platforms, the
+feature has been extended for all platforms. The reasoning is that we do
+not want to abort the entire ACPICA initialization just because the
+system is seriously buggy and has no FACS.
+
+Fixed a problem where the GUID strings for NFIT tables (in acuuid.h) were
+not correctly transcribed from the ACPI specification in ACPICA version
+20150515.
+
+Implemented support for the _CLS object in the AcpiGetObjectInfo external
+interface.
+
+Updated the definitions of the TCPA and TPM2 ACPI tables to the more
+recent TCG ACPI Specification, December 14, 2014. Table disassembler and
+compiler also updated. Note: The TCPA "server" table is not supported by
+the disassembler/table-compiler at this time.
+
+ACPI 6.0: Added definitions for the new GIC version field in the MADT.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 100.6K Code, 27.6K Data, 128.2K Total
+ Debug Version: 196.2K Code, 81.0K Data, 277.2K Total
+ Previous Release:
+ Non-Debug Version: 99.9K Code, 27.5K Data, 127.4K Total
+ Debug Version: 195.2K Code, 80.8K Data, 276.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Fixed a problem with the new symbolic operator disassembler
+where incorrect ASL code could be emitted in some cases for the "non-
+commutative" operators -- Subtract, Divide, Modulo, ShiftLeft, and
+ShiftRight. The actual problem cases seem to be rather unusual in common
+ASL code, however. David Box.
+
+Modified the linux version of acpidump to obtain ACPI tables from not
+just /dev/mem (which may not exist) and /sys/firmware/acpi/tables. Lv
+Zheng.
+
+iASL: Fixed a problem where the user preprocessor output file (.i)
+contained extra data that was not expected. The compiler was using this
+file as a temporary file and passed through #line directives in order to
+keep compiler error messages in sync with the input file and line number
+across multiple include files. The (.i) is no longer a temporary file as
+the compiler uses a new, different file for the original purpose.
+
+iASL: Fixed a problem where comments within the original ASL source code
+file were not passed through to the preprocessor output file, nor any
+listing files.
+
+iASL: Fixed some issues for the handling of the "#include" preprocessor
+directive and the similar (but not the same) "Include" ASL operator.
+
+iASL: Add support for the new OSDT in both the disassembler and compiler.
+
+iASL: Fixed a problem with the constant folding support where a Buffer
+object could be incorrectly generated (incorrectly formed) during a
+conversion to a Store() operator.
+
+AcpiHelp: Updated for new NFIT GUIDs, "External" AML opcode, and new
+description text for the _REV predefined name. _REV now permanently
+returns 2, as per the ACPI 6.0 specification.
+
+Debugger: Enhanced the output of the Debug ASL object for references
+produced by the Index operator. For Buffers and strings, only output the
+actual byte pointed to by the index. For packages, only print the single
+package element decoded by the index. Previously, the entire
+buffer/string/package was emitted.
+
+iASL/Table-compiler: Fixed a regression where the "generic" data types
+were no longer recognized, causing errors.
+
+
+----------------------------------------
+15 May 2015. Summary of changes for version 20150515:
+
+This release implements most of ACPI 6.0 as described below.
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented runtime argument checking and return value checking for all
+new ACPI 6.0 predefined names. This includes: _BTH, _CR3, _DSD, _LPI,
+_MTL, _PRR, _RDI, _RST, _TFP, _TSN.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 99.9K Code, 27.5K Data, 127.4K Total
+ Debug Version: 195.2K Code, 80.8K Data, 276.0K Total
+ Previous Release:
+ Non-Debug Version: 99.1K Code, 27.3K Data, 126.4K Total
+ Debug Version: 192.8K Code, 79.9K Data, 272.7K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL compiler: Added compile-time support for all new ACPI 6.0 predefined
+names (argument count validation and return value typechecking.)
+
+iASL disassembler and table compiler: implemented support for all new
+ACPI 6.0 tables. This includes: DRTM, IORT, LPIT, NFIT, STAO, WPBT, XENV.
+
+iASL disassembler and table compiler: Added ACPI 6.0 changes to existing
+tables: FADT, MADT.
+
+iASL preprocessor: Added a new directive to enable inclusion of binary
+blobs into ASL code. The new directive is #includebuffer. It takes a
+binary file as input and emits a named ascii buffer object into the ASL
+code.
+
+AcpiHelp: Added support for all new ACPI 6.0 predefined names.
+
+AcpiHelp: Added a new option, -d, to display all iASL preprocessor
+directives.
+
+AcpiHelp: Added a new option, -t, to display all known/supported ACPI
+tables.
+
+
+----------------------------------------
+10 April 2015. Summary of changes for version 20150410:
+
+Reverted a change introduced in version 20150408 that caused
+a regression in the disassembler where incorrect operator
+symbols could be emitted.
+
+
+----------------------------------------
+08 April 2015. Summary of changes for version 20150408:
+
+
+1) ACPICA kernel-resident subsystem:
+
+Permanently set the return value for the _REV predefined name. It now
+returns 2 (was 5). This matches other ACPI implementations. _REV will be
+deprecated in the future, and is now defined to be 1 for ACPI 1.0, and 2
+for ACPI 2.0 and later. It should never be used to differentiate or
+identify operating systems.
+
+Added the "Windows 2015" string to the _OSI support. ACPICA will now
+return TRUE to a query with this string.
+
+Fixed several issues with the local version of the printf function.
+
+Added the C99 compiler option (-std=c99) to the Unix makefiles.
+
+ Current Release:
+ Non-Debug Version: 99.9K Code, 27.4K Data, 127.3K Total
+ Debug Version: 195.2K Code, 80.7K Data, 275.9K Total
+ Previous Release:
+ Non-Debug Version: 98.8K Code, 27.3K Data, 126.1K Total
+ Debug Version: 192.1K Code, 79.8K Data, 271.9K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented an enhancement to the constant folding feature to
+transform the parse tree to a simple Store operation whenever possible:
+ Add (2, 3, X) ==> is converted to: Store (5, X)
+ X = 2 + 3 ==> is converted to: Store (5, X)
+
+Updated support for the SLIC table (Software Licensing Description Table)
+in both the Data Table compiler and the disassembler. The SLIC table
+support now conforms to "Microsoft Software Licensing Tables (SLIC and
+MSDM). November 29, 2011. Copyright 2011 Microsoft". Note: Any SLIC data
+following the ACPI header is now defined to be "Proprietary Data", and as
+such, can only be entered or displayed as a hex data block.
+
+Implemented full support for the MSDM table as described in the document
+above. Note: The format of MSDM is similar to SLIC. Any MSDM data
+following the ACPI header is defined to be "Proprietary Data", and can
+only be entered or displayed as a hex data block.
+
+Implemented the -Pn option for the iASL Table Compiler (was only
+implemented for the ASL compiler). This option disables the iASL
+preprocessor.
+
+Disassembler: For disassembly of Data Tables, added a comment field
+around the Ascii equivalent data that is emitted as part of the "Raw
+Table Data" block. This prevents the iASL Preprocessor from possible
+confusion if/when the table is compiled.
+
+Disassembler: Added an option (-df) to force the disassembler to assume
+that the table being disassembled contains valid AML. This feature is
+useful for disassembling AML files that contain ACPI signatures other
+than DSDT or SSDT (such as OEMx or other signatures).
+
+Changes for the EFI version of the tools:
+1) Fixed a build error/issue
+2) Fixed a cast warning
+
+iASL: Fixed a path issue with the __FILE__ operator by making the
+directory prefix optional within the internal SplitInputFilename
+function.
+
+Debugger: Removed some unused global variables.
+
+Tests: Updated the makefile for proper generation of the AAPITS suite.
+
+
+----------------------------------------
+04 February 2015. Summary of changes for version 20150204:
+
+ACPICA kernel-resident subsystem:
+
+Updated all ACPICA copyrights and signons to 2014. Added the 2014
+copyright to all module headers and signons, including the standard Linux
+header. This affects virtually every file in the ACPICA core subsystem,
+iASL compiler, all ACPICA utilities, and the test suites.
+
+Events: Introduce ACPI_GPE_DISPATCH_RAW_HANDLER to fix GPE storm issues.
+A raw gpe handling mechanism was created to allow better handling of GPE
+storms that aren't easily managed by the normal handler. The raw handler
+allows disabling/renabling of the the GPE so that interrupt storms can be
+avoided in cases where events cannot be timely serviced. In this
+scenario, handlers should use the AcpiSetGpe() API to disable/enable the
+GPE. This API will leave the reference counts undisturbed, thereby
+preventing unintentional clearing of the GPE when the intent in only to
+temporarily disable it. Raw handlers allow enabling and disabling of a
+GPE by removing GPE register locking. As such, raw handlers much provide
+their own locks while using GPE API's to protect access to GPE data
+structures.
+Lv Zheng
+
+Events: Always modify GPE registers under the GPE lock.
+Applies GPE lock around AcpiFinishGpe() to protect access to GPE register
+values. Reported as bug by joe.liu@apple.com.
+
+Unix makefiles: Separate option to disable optimizations and
+_FORTIFY_SOURCE. This change removes the _FORTIFY_SOURCE flag from the
+NOOPT disable option and creates a separate flag (NOFORTIFY) for this
+purpose. Some toolchains may define _FORTIFY_SOURCE which leads redefined
+errors when building ACPICA. This allows disabling the option without
+also having to disable optimazations.
+David Box
+
+ Current Release:
+ Non-Debug Version: 101.7K Code, 27.9K Data, 129.6K Total
+ Debug Version: 199.2K Code, 82.4K Data, 281.6K Total
+
+--
+--------------------------------------
+07 November 2014. Summary of changes for version 20141107:
+
+This release is available at https://acpica.org/downloads
+
+This release introduces and implements language extensions to ASL that
+provide support for symbolic ("C-style") operators and expressions. These
+language extensions are known collectively as ASL+.
+
+
+1) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Fixed a problem with disassembly of the UartSerialBus
+macro. Changed "StopBitsNone" to the correct "StopBitsZero". David E.
+Box.
+
+Disassembler: Fixed the Unicode macro support to add escape sequences.
+All non-printable ASCII values are emitted as escape sequences, as well
+as the standard escapes for quote and backslash. Ensures that the
+disassembled macro can be correctly recompiled.
+
+iASL: Added Printf/Fprintf macros for formatted output. These macros are
+translated to existing AML Concatenate and Store operations. Printf
+writes to the ASL Debug object. Fprintf allows the specification of an
+ASL name as the target. Only a single format specifier is required, %o,
+since the AML interpreter dynamically converts objects to the required
+type. David E. Box.
+
+ (old) Store (Concatenate (Concatenate (Concatenate (Concatenate
+ (Concatenate (Concatenate (Concatenate ("", Arg0),
+ ": Unexpected value for "), Arg1), ", "), Arg2),
+ " at line "), Arg3), Debug)
+
+ (new) Printf ("%o: Unexpected value for %o, %o at line %o",
+ Arg0, Arg1, Arg2, Arg3)
+
+ (old) Store (Concatenate (Concatenate (Concatenate (Concatenate
+ ("", Arg1), ": "), Arg0), " Successful"), STR1)
+
+ (new) Fprintf (STR1, "%o: %o Successful", Arg1, Arg0)
+
+iASL: Added debug options (-bp, -bt) to dynamically prune levels of the
+ASL parse tree before the AML code is generated. This allows blocks of
+ASL code to be removed in order to help locate and identify problem
+devices and/or code. David E. Box.
+
+AcpiExec: Added support (-fi) for an optional namespace object
+initialization file. This file specifies initial values for namespace
+objects as necessary for debugging and testing different ASL code paths
+that may be taken as a result of BIOS options.
+
+
+2) Overview of symbolic operator support for ASL (ASL+)
+-------------------------------------------------------
+
+As an extension to the ASL language, iASL implements support for symbolic
+(C-style) operators for math and logical expressions. This can greatly
+simplify ASL code as well as improve both readability and
+maintainability. These language extensions can exist concurrently with
+all legacy ASL code and expressions.
+
+The symbolic extensions are 100% compatible with existing AML
+interpreters, since no new AML opcodes are created. To implement the
+extensions, the iASL compiler transforms the symbolic expressions into
+the legacy ASL/AML equivalents at compile time.
+
+Full symbolic expressions are supported, along with the standard C
+precedence and associativity rules.
+
+Full disassembler support for the symbolic expressions is provided, and
+creates an automatic migration path for existing ASL code to ASL+ code
+via the disassembly process. By default, the disassembler now emits ASL+
+code with symbolic expressions. An option (-dl) is provided to force the
+disassembler to emit legacy ASL code if desired.
+
+Below is the complete list of the currently supported symbolic operators
+with examples. See the iASL User Guide for additional information.
+
+
+ASL+ Syntax Legacy ASL Equivalent
+----------- ---------------------
+
+ // Math operators
+
+Z = X + Y Add (X, Y, Z)
+Z = X - Y Subtract (X, Y, Z)
+Z = X * Y Multiply (X, Y, Z)
+Z = X / Y Divide (X, Y, , Z)
+Z = X % Y Mod (X, Y, Z)
+Z = X << Y ShiftLeft (X, Y, Z)
+Z = X >> Y ShiftRight (X, Y, Z)
+Z = X & Y And (X, Y, Z)
+Z = X | Y Or (X, Y, Z)
+Z = X ^ Y Xor (X, Y, Z)
+Z = ~X Not (X, Z)
+X++ Increment (X)
+X-- Decrement (X)
+
+ // Logical operators
+
+(X == Y) LEqual (X, Y)
+(X != Y) LNotEqual (X, Y)
+(X < Y) LLess (X, Y)
+(X > Y) LGreater (X, Y)
+(X <= Y) LLessEqual (X, Y)
+(X >= Y) LGreaterEqual (X, Y)
+(X && Y) LAnd (X, Y)
+(X || Y) LOr (X, Y)
+(!X) LNot (X)
+
+ // Assignment and compound assignment operations
+
+X = Y Store (Y, X)
+X += Y Add (X, Y, X)
+X -= Y Subtract (X, Y, X)
+X *= Y Multiply (X, Y, X)
+X /= Y Divide (X, Y, , X)
+X %= Y Mod (X, Y, X)
+X <<= Y ShiftLeft (X, Y, X)
+X >>= Y ShiftRight (X, Y, X)
+X &= Y And (X, Y, X)
+X |= Y Or (X, Y, X)
+X ^= Y Xor (X, Y, X)
+
+
+3) ASL+ Examples:
+-----------------
+
+Legacy ASL:
+ If (LOr (LOr (LEqual (And (R510, 0x03FB), 0x02E0), LEqual (
+ And (R520, 0x03FB), 0x02E0)), LOr (LEqual (And (R530,
+0x03FB),
+ 0x02E0), LEqual (And (R540, 0x03FB), 0x02E0))))
+ {
+ And (MEMB, 0xFFFFFFF0, SRMB)
+ Store (MEMB, Local2)
+ Store (PDBM, Local1)
+ And (PDBM, 0xFFFFFFFFFFFFFFF9, PDBM)
+ Store (SRMB, MEMB)
+ Or (PDBM, 0x02, PDBM)
+ }
+
+ASL+ version:
+ If (((R510 & 0x03FB) == 0x02E0) ||
+ ((R520 & 0x03FB) == 0x02E0) ||
+ ((R530 & 0x03FB) == 0x02E0) ||
+ ((R540 & 0x03FB) == 0x02E0))
+ {
+ SRMB = (MEMB & 0xFFFFFFF0)
+ Local2 = MEMB
+ Local1 = PDBM
+ PDBM &= 0xFFFFFFFFFFFFFFF9
+ MEMB = SRMB
+ PDBM |= 0x02
+ }
+
+Legacy ASL:
+ Store (0x1234, Local1)
+ Multiply (Add (Add (Local1, TEST), 0x20), Local2, Local3)
+ Multiply (Local2, Add (Add (Local1, TEST), 0x20), Local3)
+ Add (Local1, Add (TEST, Multiply (0x20, Local2)), Local3)
+ Store (Index (PKG1, 0x03), Local6)
+ Store (Add (Local3, Local2), Debug)
+ Add (Local1, 0x0F, Local2)
+ Add (Local1, Multiply (Local2, Local3), Local2)
+ Multiply (Add (Add (Local1, TEST), 0x20), ToBCD (Local1), Local3)
+
+ASL+ version:
+ Local1 = 0x1234
+ Local3 = (((Local1 + TEST) + 0x20) * Local2)
+ Local3 = (Local2 * ((Local1 + TEST) + 0x20))
+ Local3 = (Local1 + (TEST + (0x20 * Local2)))
+ Local6 = Index (PKG1, 0x03)
+ Debug = (Local3 + Local2)
+ Local2 = (Local1 + 0x0F)
+ Local2 = (Local1 + (Local2 * Local3))
+ Local3 = (((Local1 + TEST) + 0x20) * ToBCD (Local1))
+
+
+----------------------------------------
+26 September 2014. Summary of changes for version 20140926:
+
+1) ACPICA kernel-resident subsystem:
+
+Updated the GPIO operation region handler interface (GeneralPurposeIo).
+In order to support GPIO Connection objects with multiple pins, along
+with the related Field objects, the following changes to the interface
+have been made: The Address is now defined to be the offset in bits of
+the field unit from the previous invocation of a Connection. It can be
+viewed as a "Pin Number Index" into the connection resource descriptor.
+The BitWidth is the exact bit width of the field. It is usually one bit,
+but not always. See the ACPICA reference guide (section 8.8.6.2.1) for
+additional information and examples.
+
+GPE support: During ACPICA/GPE initialization, ensure that all GPEs with
+corresponding _Lxx/_Exx methods are disabled (they may have been enabled
+by the firmware), so that they cannot fire until they are enabled via
+AcpiUpdateAllGpes. Rafael J. Wysocki.
+
+Added a new return flag for the Event/GPE status interfaces --
+AcpiGetEventStatus and AcpiGetGpeStatus. The new
+ACPI_EVENT_FLAGS_HAS_HANDLER flag is used to indicate that the event or
+GPE currently has a handler associated with it, and can thus actually
+affect the system. Lv Zheng.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 99.1K Code, 27.3K Data, 126.4K Total
+ Debug Version: 192.8K Code, 79.9K Data, 272.7K Total
+ Previous Release:
+ Non-Debug Version: 98.8K Code, 27.3K Data, 126.1K Total
+ Debug Version: 192.1K Code, 79.8K Data, 271.9K Total
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed a memory allocation/free regression introduced in 20140828
+that could cause the compiler to crash. This was introduced inadvertently
+during the effort to eliminate compiler memory leaks. ACPICA BZ 1111,
+1113.
+
+iASL: Removed two error messages that have been found to create false
+positives, until they can be fixed and fully validated (ACPICA BZ 1112):
+1) Illegal forward reference within a method
+2) Illegal reference across two methods
-This release is available at www.acpica.org/downloads
+iASL: Implemented a new option (-lm) to create a hardware mapping file
+that summarizes all GPIO, I2C, SPI, and UART connections. This option
+works for both the compiler and disassembler. See the iASL compiler user
+guide for additional information and examples (section 6.4.6).
+
+AcpiDump: Added support for the version 1 (ACPI 1.0) RSDP in addition to
+version 2. This corrects the AE_BAD_HEADER exception seen on systems with
+a version 1 RSDP. Lv Zheng ACPICA BZ 1097.
+
+AcpiExec: For Unix versions, don't attempt to put STDIN into raw mode
+unless STDIN is actually a terminal. Assists with batch-mode processing.
+ACPICA BZ 1114.
+
+Disassembler/AcpiHelp: Added another large group of recognized _HID
+values.
+
+
+----------------------------------------
+28 August 2014. Summary of changes for version 20140828:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a problem related to the internal use of the Timer() operator where
+a 64-bit divide could cause an attempted link to a double-precision math
+library. This divide is not actually necessary, so the code was
+restructured to eliminate it. Lv Zheng.
+
+ACPI 5.1: Added support for the runtime validation of the _DSD package
+(similar to the iASL support).
+
+ACPI 5.1/Headers: Added support for the GICC affinity subtable to the
+SRAT table. Hanjun Guo <hanjun.guo@linaro.org>.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 98.8K Code, 27.3K Data, 126.1K Total
+ Debug Version: 192.1K Code, 79.8K Data, 271.9K Total
+ Previous Release:
+ Non-Debug Version: 98.7K Code, 27.3K Data, 126.0K Total1
+ Debug Version: 192.0K Code, 79.7K Data, 271.7K Total
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiExec: Fixed a problem on unix systems where the original terminal
+state was not always properly restored upon exit. Seen when using the -v
+option. ACPICA BZ 1104.
+
+iASL: Fixed a problem with the validation of the ranges/length within the
+Memory24 resource descriptor. There was a boundary condition when the
+range was equal to the (length -1) caused by the fact that these values
+are defined in 256-byte blocks, not bytes. ACPICA BZ 1098
+
+Disassembler: Fixed a problem with the GpioInt descriptor interrupt
+polarity
+flags. The flags are actually 2 bits, not 1, and the "ActiveBoth" keyword
+is
+now supported properly.
+
+ACPI 5.1: Added the GICC affinity subtable to the SRAT table. Supported
+in the disassembler, data table compiler, and table template generator.
+
+iASL: Added a requirement for Device() objects that one of either a _HID
+or _ADR must exist within the scope of a Device, as per the ACPI
+specification. Remove a similar requirement that was incorrectly in place
+for the _DSD object.
+
+iASL: Added error detection for illegal named references within control
+methods that would cause runtime failures. Now trapped as errors are: 1)
+References to objects within a non-parent control method. 2) Forward
+references (within a method) -- for control methods, AML interpreters use
+a one-pass parse of control methods. ACPICA BZ 1008.
+
+iASL: Added error checking for dependencies related to the _PSx power
+methods. ACPICA BZ 1029.
+1) For _PS0, one of these must exist within the same scope: _PS1, _PS2,
+_PS3.
+2) For _PS1, _PS2, and PS3: A _PS0 object must exist within the same
+scope.
+
+iASL and table compiler: Cleanup miscellaneous memory leaks by fully
+deploying the existing object and string caches and adding new caches for
+the table compiler.
+
+iASL: Split the huge parser source file into multiple subfiles to improve
+manageability. Generation now requires the M4 macro preprocessor, which
+is part of the Bison distribution on both unix and windows platforms.
+
+AcpiSrc: Fixed and removed all extraneous warnings generated during
+entire ACPICA source code scan and/or conversion.
+
+
+----------------------------------------
+
+24 July 2014. Summary of changes for version 20140724:
+
+The ACPI 5.1 specification has been released and is available at:
+http://uefi.org/specs/access
+
+
+0) ACPI 5.1 support in ACPICA:
+
+ACPI 5.1 is fully supported in ACPICA as of this release.
+
+New predefined names. Support includes iASL and runtime ACPICA
+validation.
+ _CCA (Cache Coherency Attribute).
+ _DSD (Device-Specific Data). David Box.
+
+Modifications to existing ACPI tables. Support includes headers, iASL
+Data Table compiler, disassembler, and the template generator.
+ FADT - New fields and flags. Graeme Gregory.
+ GTDT - One new subtable and new fields. Tomasz Nowicki.
+ MADT - Two new subtables. Tomasz Nowicki.
+ PCCT - One new subtable.
+
+Miscellaneous.
+ New notification type for System Resource Affinity change events.
+
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a regression introduced in 20140627 where a fault can happen during
+the deletion of Alias AML namespace objects. The problem affected both
+the core ACPICA and the ACPICA tools including iASL and AcpiExec.
+
+Implemented a new GPE public interface, AcpiMarkGpeForWake. Provides a
+simple mechanism to enable wake GPEs that have no associated handler or
+control method. Rafael Wysocki.
+
+Updated the AcpiEnableGpe interface to disallow the enable if there is no
+handler or control method associated with the particular GPE. This will
+help avoid meaningless GPEs and even GPE floods. Rafael Wysocki.
+
+Updated GPE handling and dispatch by disabling the GPE before clearing
+the status bit for edge-triggered GPEs. Lv Zheng.
+
+Added Timer() support to the AML Debug object. The current timer value is
+now displayed with each invocation of (Store to) the debug object to
+enable simple generation of execution times for AML code (method
+execution for example.) ACPICA BZ 1093.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 98.7K Code, 27.3K Data, 126.0K Total
+ Debug Version: 192.0K Code, 79.7K Data, 271.7K Total
+ Previous Release:
+ Non-Debug Version: 98.7K Code, 27.2K Data, 125.9K Total
+ Debug Version: 191.7K Code, 79.6K Data, 271.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Fixed an issue with the recently added local printf implementation,
+concerning width/precision specifiers that could cause incorrect output.
+Lv Zheng. ACPICA BZ 1094.
+
+Disassembler: Added support to detect buffers that contain UUIDs and
+disassemble them to an invocation of the ToUUID operator. Also emit
+commented descriptions of known ACPI-related UUIDs.
+
+AcpiHelp: Added support to display known ACPI-related UUIDs. New option,
+-u. Adds three new files.
+
+iASL: Update table compiler and disassembler for DMAR table changes that
+were introduced in September 2013. With assistance by David Woodhouse.
+
+----------------------------------------
+27 June 2014. Summary of changes for version 20140627:
+
+1) ACPICA kernel-resident subsystem:
+
+Formatted Output: Implemented local versions of standard formatted output
+utilities such as printf, etc. Over time, it has been discovered that
+there are in fact many portability issues with printf, and the addition
+of this feature will fix/prevent these issues once and for all. Some
+known issues are summarized below:
+
+1) Output of 64-bit values is not portable. For example, UINT64 is %ull
+for the Linux kernel and is %uI64 for some MSVC versions.
+2) Invoking printf consistently in a manner that is portable across both
+32-bit and 64-bit platforms is difficult at best in many situations.
+3) The output format for pointers varies from system to system (leading
+zeros especially), and leads to inconsistent output from ACPICA across
+platforms.
+4) Certain platform-specific printf formats may conflict with ACPICA use.
+5) If there is no local C library available, ACPICA now has local support
+for printf.
+
+-- To address these printf issues in a complete manner, ACPICA now
+directly implements a small subset of printf format specifiers, only
+those that it requires. Adds a new file, utilities/utprint.c. Lv Zheng.
+
+Implemented support for ACPICA generation within the EFI environment.
+Initially, the AcpiDump utility is supported in the UEFI shell
+environment. Lv Zheng.
+
+Added a new external interface, AcpiLogError, to improve ACPICA
+portability. This allows the host to redirect error messages from the
+ACPICA utilities. Lv Zheng.
+
+Added and deployed new OSL file I/O interfaces to improve ACPICA
+portability:
+ AcpiOsOpenFile
+ AcpiOsCloseFile
+ AcpiOsReadFile
+ AcpiOsWriteFile
+ AcpiOsGetFileOffset
+ AcpiOsSetFileOffset
+There are C library implementations of these functions in the new file
+service_layers/oslibcfs.c -- however, the functions can be implemented by
+the local host in any way necessary. Lv Zheng.
+
+Implemented a mechanism to disable/enable ACPI table checksum validation
+at runtime. This can be useful when loading tables very early during OS
+initialization when it may not be possible to map the entire table in
+order to compute the checksum. Lv Zheng.
+
+Fixed a buffer allocation issue for the Generic Serial Bus support.
+Originally, a fixed buffer length was used. This change allows for
+variable-length buffers based upon the protocol indicated by the field
+access attributes. Reported by Lan Tianyu. Lv Zheng.
+
+Fixed a problem where an object detached from a namespace node was not
+properly terminated/cleared and could cause a circular list problem if
+reattached. ACPICA BZ 1063. David Box.
+
+Fixed a possible recursive lock acquisition in hwregs.c. Rakib Mullick.
+
+Fixed a possible memory leak in an error return path within the function
+AcpiUtCopyIobjectToIobject. ACPICA BZ 1087. Colin Ian King.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 98.7K Code, 27.2K Data, 125.9K Total
+ Debug Version: 191.7K Code, 79.6K Data, 271.3K Total
+ Previous Release:
+ Non-Debug Version: 96.8K Code, 27.2K Data, 124.0K Total
+ Debug Version: 189.5K Code, 79.7K Data, 269.2K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Add dump of ASCII equivalent text within a comment at the
+end of each line of the output for the Buffer() ASL operator.
+
+AcpiDump: Miscellaneous changes:
+ Fixed repetitive table dump in -n mode.
+ For older EFI platforms, use the ACPI 1.0 GUID during RSDP search if
+the ACPI 2.0 GUID fails.
+
+iASL: Fixed a problem where the compiler could fault if incorrectly given
+an acpidump output file as input. ACPICA BZ 1088. David Box.
+
+AcpiExec/AcpiNames: Fixed a problem where these utilities could fault if
+they are invoked without any arguments.
+
+Debugger: Fixed a possible memory leak in an error return path. ACPICA BZ
+1086. Colin Ian King.
+
+Disassembler: Cleaned up a block of code that extracts a parent Op
+object. Added a comment that explains that the parent is guaranteed to be
+valid in this case. ACPICA BZ 1069.
+
+
+----------------------------------------
+24 April 2014. Summary of changes for version 20140424:
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented support to skip/ignore NULL address entries in the RSDT/XSDT.
+Some of these tables are known to contain a trailing NULL entry. Lv
+Zheng.
+
+Removed an extraneous error message for the case where there are a large
+number of system GPEs (> 124). This was the "32-bit FADT register is too
+long to convert to GAS struct" message, which is irrelevant for GPEs
+since the GPEx_BLK_LEN fields of the FADT are always used instead of the
+(limited capacity) GAS bit length. Also, several changes to ensure proper
+support for GPE numbers > 255, where some "GPE number" fields were 8-bits
+internally.
+
+Implemented and deployed additional configuration support for the public
+ACPICA external interfaces. Entire classes of interfaces can now be
+easily modified or configured out, replaced by stubbed inline functions
+by default. Lv Zheng.
+
+Moved all public ACPICA runtime configuration globals to the public
+ACPICA external interface file for convenience. Also, removed some
+obsolete/unused globals. See the file acpixf.h. Lv Zheng.
+
+Documentation: Added a new section to the ACPICA reference describing the
+maximum number of GPEs that can be supported by the FADT-defined GPEs in
+block zero and one. About 1200 total. See section 4.4.1 of the ACPICA
+reference.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.8K Code, 27.2K Data, 124.0K Total
+ Debug Version: 189.5K Code, 79.7K Data, 269.2K Total
+ Previous Release:
+ Non-Debug Version: 97.0K Code, 27.2K Data, 124.2K Total
+ Debug Version: 189.7K Code, 79.5K Data, 269.2K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL and disassembler: Add full support for the LPIT table (Low Power
+Idle Table). Includes support in the disassembler, data table compiler,
+and template generator.
+
+AcpiDump utility:
+1) Add option to force the use of the RSDT (over the XSDT).
+2) Improve validation of the RSDP signature (use 8 chars instead of 4).
+
+iASL: Add check for predefined packages that are too large. For
+predefined names that contain subpackages, check if each subpackage is
+too large. (Check for too small already exists.)
+
+Debugger: Updated the GPE command (which simulates a GPE by executing the
+GPE code paths in ACPICA). The GPE device is now optional, and defaults
+to the GPE 0/1 FADT-defined blocks.
+
+Unix application OSL: Update line-editing support. Add additional error
+checking and take care not to reset terminal attributes on exit if they
+were never set. This should help guarantee that the terminal is always
+left in the previous state on program exit.
+
+
+----------------------------------------
+25 March 2014. Summary of changes for version 20140325:
+
+1) ACPICA kernel-resident subsystem:
+
+Updated the auto-serialize feature for control methods. This feature
+automatically serializes all methods that create named objects in order
+to prevent runtime errors. The update adds support to ignore the
+currently executing AML SyncLevel when invoking such a method, in order
+to prevent disruption of any existing SyncLevel priorities that may exist
+in the AML code. Although the use of SyncLevels is relatively rare, this
+change fixes a regression where an AE_AML_MUTEX_ORDER exception can
+appear on some machines starting with the 20140214 release.
+
+Added a new external interface to allow the host to install ACPI tables
+very early, before the namespace is even created. AcpiInstallTable gives
+the host additional flexibility for ACPI table management. Tables can be
+installed directly by the host as if they had originally appeared in the
+XSDT/RSDT. Installed tables can be SSDTs or other ACPI data tables
+(anything except the DSDT and FACS). Adds a new file, tbdata.c, along
+with additional internal restructuring and cleanup. See the ACPICA
+Reference for interface details. Lv Zheng.
+
+Added validation of the checksum for all incoming dynamically loaded
+tables (via external interfaces or via AML Load/LoadTable operators). Lv
+Zheng.
+
+Updated the use of the AcpiOsWaitEventsComplete interface during Notify
+and GPE handler removal. Restructured calls to eliminate possible race
+conditions. Lv Zheng.
+
+Added a warning for the use/execution of the ASL/AML Unload (table)
+operator. This will help detect and identify machines that use this
+operator if and when it is ever used. This operator has never been seen
+in the field and the usage model and possible side-effects of the drastic
+runtime action of a full table removal are unknown.
+
+Reverted the use of #pragma push/pop which was introduced in the 20140214
+release. It appears that push and pop are not implemented by enough
+compilers to make the use of this feature feasible for ACPICA at this
+time. However, these operators may be deployed in a future ACPICA
+release.
+
+Added the missing EXPORT_SYMBOL macros for the install and remove SCI
+handler interfaces.
+
+Source code generation:
+1) Disabled the use of the "strchr" macro for the gcc-specific
+generation. For some versions of gcc, this macro can periodically expose
+a compiler bug which in turn causes compile-time error(s).
+2) Added support for PPC64 compilation. Colin Ian King.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 97.0K Code, 27.2K Data, 124.2K Total
+ Debug Version: 189.7K Code, 79.5K Data, 269.2K Total
+ Previous Release:
+ Non-Debug Version: 96.5K Code, 27.2K Data, 123.7K Total
+ Debug Version: 188.6K Code, 79.0K Data, 267.6K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Added several new features to improve the readability of
+the resulting ASL code. Extra information is emitted within comment
+fields in the ASL code:
+1) Known _HID/_CID values are decoded to descriptive text.
+2) Standard values for the Notify() operator are decoded to descriptive
+text.
+3) Target operands are expanded to full pathnames (in a comment) when
+possible.
+
+Disassembler: Miscellaneous updates for extern() handling:
+1) Abort compiler if file specified by -fe option does not exist.
+2) Silence unnecessary warnings about argument count mismatches.
+3) Update warning messages concerning unresolved method externals.
+4) Emit "UnknownObj" keyword for externals whose type cannot be
+determined.
+
+AcpiHelp utility:
+1) Added the -a option to display both the ASL syntax and the AML
+encoding for an input ASL operator. This effectively displays all known
+information about an ASL operator with one AcpiHelp invocation.
+2) Added substring match support (similar to a wildcard) for the -i
+(_HID/PNP IDs) option.
+
+iASL/Disassembler: Since this tool does not yet support execution on big-
+endian machines, added detection of endianness and an error message if
+execution is attempted on big-endian. Support for big-endian within iASL
+is a feature that is on the ACPICA to-be-done list.
+
+AcpiBin utility:
+1) Remove option to extract binary files from an acpidump; this function
+is made obsolete by the AcpiXtract utility.
+2) General cleanup of open files and allocated buffers.
+
+
+----------------------------------------
+14 February 2014. Summary of changes for version 20140214:
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented a new mechanism to proactively prevent problems with ill-
+behaved reentrant control methods that create named ACPI objects. This
+behavior is illegal as per the ACPI specification, but is nonetheless
+frequently seen in the field. Previously, this could lead to an
+AE_ALREADY_EXISTS exception if the method was actually entered by more
+than one thread. This new mechanism detects such methods at table load
+time and marks them "serialized" to prevent reentrancy. A new global
+option, AcpiGbl_AutoSerializeMethods, has been added to disable this
+feature if desired. This mechanism and global option obsoletes and
+supersedes the previous AcpiGbl_SerializeAllMethods option.
+
+Added the "Windows 2013" string to the _OSI support. ACPICA will now
+respond TRUE to _OSI queries with this string. It is the stated policy of
+ACPICA to add new strings to the _OSI support as soon as possible after
+they are defined. See the full ACPICA _OSI policy which has been added to
+the utilities/utosi.c file.
+
+Hardened/updated the _PRT return value auto-repair code:
+1) Do not abort the repair on a single subpackage failure, continue to
+check all subpackages.
+2) Add check for the minimum subpackage length (4).
+3) Properly handle extraneous NULL package elements.
+
+Added support to avoid the possibility of infinite loops when traversing
+object linked lists. Never allow an infinite loop, even in the face of
+corrupted object lists.
+
+ACPICA headers: Deployed the use of #pragma pack(push) and #pragma
+pack(pop) directives to ensure that the ACPICA headers are independent of
+compiler settings or other host headers.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.5K Code, 27.2K Data, 123.7K Total
+ Debug Version: 188.6K Code, 79.0K Data, 267.6K Total
+ Previous Release:
+ Non-Debug Version: 96.2K Code, 27.0K Data, 123.2K Total
+ Debug Version: 187.5K Code, 78.3K Data, 265.8K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL/Table-compiler: Fixed a problem with support for the SPMI table. The
+first reserved field was incorrectly forced to have a value of zero. This
+change correctly forces the field to have a value of one. ACPICA BZ 1081.
+
+Debugger: Added missing support for the "Extra" and "Data" subobjects
+when displaying object data.
+
+Debugger: Added support to display entire object linked lists when
+displaying object data.
+
+iASL: Removed the obsolete -g option to obtain ACPI tables from the
+Windows registry. This feature has been superseded by the acpidump
+utility.
+
+
+----------------------------------------
+14 January 2014. Summary of changes for version 20140114:
+
+1) ACPICA kernel-resident subsystem:
+
+Updated all ACPICA copyrights and signons to 2014. Added the 2014
+copyright to all module headers and signons, including the standard Linux
+header. This affects virtually every file in the ACPICA core subsystem,
+iASL compiler, all ACPICA utilities, and the test suites.
+
+Improved parameter validation for AcpiInstallGpeBlock. Added the
+following checks:
+1) The incoming device handle refers to type ACPI_TYPE_DEVICE.
+2) There is not already a GPE block attached to the device.
+Likewise, with AcpiRemoveGpeBlock, ensure that the incoming object is a
+device.
+
+Correctly support "references" in the ACPI_OBJECT. This change fixes the
+support to allow references (namespace nodes) to be passed as arguments
+to control methods via the evaluate object interface. This is probably
+most useful for testing purposes, however.
+
+Improved support for 32/64 bit physical addresses in printf()-like
+output. This change improves the support for physical addresses in printf
+debug statements and other output on both 32-bit and 64-bit hosts. It
+consistently outputs the appropriate number of bytes for each host. The
+%p specifier is unsatisfactory since it does not emit uniform output on
+all hosts/clib implementations (on some, leading zeros are not supported,
+leading to difficult-to-read output).
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.2K Code, 27.0K Data, 123.2K Total
+ Debug Version: 187.5K Code, 78.3K Data, 265.8K Total
+ Previous Release:
+ Non-Debug Version: 96.1K Code, 27.0K Data, 123.1K Total
+ Debug Version: 185.6K Code, 77.3K Data, 262.9K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fix a possible fault when using the Connection() operator. Fixes a
+problem if the parent Field definition for the Connection operator refers
+to an operation region that does not exist. ACPICA BZ 1064.
+
+AcpiExec: Load of local test tables is now optional. The utility has the
+capability to load some various tables to test features of ACPICA.
+However, there are enough of them that the output of the utility became
+confusing. With this change, only the required local tables are displayed
+(RSDP, XSDT, etc.) along with the actual tables loaded via the command
+line specification. This makes the default output simler and easier to
+understand. The -el command line option restores the original behavior
+for testing purposes.
+
+AcpiExec: Added support for overlapping operation regions. This change
+expands the simulation of operation regions by supporting regions that
+overlap within the given address space. Supports SystemMemory and
+SystemIO. ASLTS test suite updated also. David Box. ACPICA BZ 1031.
+
+AcpiExec: Added region handler support for PCI_Config and EC spaces. This
+allows AcpiExec to simulate these address spaces, similar to the current
+support for SystemMemory and SystemIO.
+
+Debugger: Added new command to read/write/compare all namespace objects.
+The command "test objects" will exercise the entire namespace by writing
+new values to each data object, and ensuring that the write was
+successful. The original value is then restored and verified.
+
+Debugger: Added the "test predefined" command. This change makes this
+test public and puts it under the new "test" command. The test executes
+each and every predefined name within the current namespace.
+
+
+----------------------------------------
+18 December 2013. Summary of changes for version 20131218:
+
+Global note: The ACPI 5.0A specification was released this month. There
+are no changes needed for ACPICA since this release of ACPI is an
+errata/clarification release. The specification is available at
+acpi.info.
+
+
+1) ACPICA kernel-resident subsystem:
+
+Added validation of the XSDT root table if it is present. Some older
+platforms contain an XSDT that is ill-formed or otherwise invalid (such
+as containing some or all entries that are NULL pointers). This change
+adds a new function to validate the XSDT before actually using it. If the
+XSDT is found to be invalid, ACPICA will now automatically fall back to
+using the RSDT instead. Original implementation by Zhao Yakui. Ported to
+ACPICA and enhanced by Lv Zheng and Bob Moore.
+
+Added a runtime option to ignore the XSDT and force the use of the RSDT.
+This change adds a runtime option that will force ACPICA to use the RSDT
+instead of the XSDT (AcpiGbl_DoNotUseXsdt). Although the ACPI spec
+requires that an XSDT be used instead of the RSDT, the XSDT has been
+found to be corrupt or ill-formed on some machines. Lv Zheng.
+
+Added a runtime option to favor 32-bit FADT register addresses over the
+64-bit addresses. This change adds an option to favor 32-bit FADT
+addresses when there is a conflict between the 32-bit and 64-bit versions
+of the same register. The default behavior is to use the 64-bit version
+in accordance with the ACPI specification. This can now be overridden via
+the AcpiGbl_Use32BitFadtAddresses flag. ACPICA BZ 885. Lv Zheng.
+
+During the change above, the internal "Convert FADT" and "Verify FADT"
+functions have been merged to simplify the code, making it easier to
+understand and maintain. ACPICA BZ 933.
+
+Improve exception reporting and handling for GPE block installation.
+Return an actual status from AcpiEvGetGpeXruptBlock and don't clobber the
+status when exiting AcpiEvInstallGpeBlock. ACPICA BZ 1019.
+
+Added helper macros to extract bus/segment numbers from the HEST table.
+This change adds two macros to extract the encoded bus and segment
+numbers from the HEST Bus field - ACPI_HEST_BUS and ACPI_HEST_SEGMENT.
+Betty Dall <betty.dall@hp.com>
+
+Removed the unused ACPI_FREE_BUFFER macro. This macro is no longer used
+by ACPICA. It is not a public macro, so it should have no effect on
+existing OSV code. Lv Zheng.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.1K Code, 27.0K Data, 123.1K Total
+ Debug Version: 185.6K Code, 77.3K Data, 262.9K Total
+ Previous Release:
+ Non-Debug Version: 95.9K Code, 27.0K Data, 122.9K Total
+ Debug Version: 185.1K Code, 77.2K Data, 262.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Improved pathname support for emitted External()
+statements. This change adds full pathname support for external names
+that have been resolved internally by the inclusion of additional ACPI
+tables (via the iASL -e option). Without this change, the disassembler
+can emit multiple externals for the same object, or it become confused
+when the Scope() operator is used on an external object. Overall, greatly
+improves the ability to actually recompile the emitted ASL code when
+objects a referenced across multiple ACPI tables. Reported by Michael
+Tsirkin (mst@redhat.com).
+
+Tests/ASLTS: Updated functional control suite to execute with no errors.
+David Box. Fixed several errors related to the testing of the interpreter
+slack mode. Lv Zheng.
+
+iASL: Added support to detect names that are declared within a control
+method, but are unused (these are temporary names that are only valid
+during the time the method is executing). A remark is issued for these
+cases. ACPICA BZ 1022.
+
+iASL: Added full support for the DBG2 table. Adds full disassembler,
+table compiler, and template generator support for the DBG2 table (Debug
+Port 2 table).
+
+iASL: Added full support for the PCCT table, update the table definition.
+Updates the PCCT table definition in the actbl3.h header and adds table
+compiler and template generator support.
+
+iASL: Added an option to emit only error messages (no warnings/remarks).
+The -ve option will enable only error messages, warnings and remarks are
+suppressed. This can simplify debugging when only the errors are
+important, such as when an ACPI table is disassembled and there are many
+warnings and remarks -- but only the actual errors are of real interest.
+
+Example ACPICA code (source/tools/examples): Updated the example code so
+that it builds to an actual working program, not just example code. Added
+ACPI tables and execution of an example control method in the DSDT. Added
+makefile support for Unix generation.
+
+
+----------------------------------------
+15 November 2013. Summary of changes for version 20131115:
+
+This release is available at https://acpica.org/downloads
+
+
+1) ACPICA kernel-resident subsystem:
+
+Resource Manager: Fixed loop termination for the "get AML length"
+function. The loop previously had an error termination on a NULL resource
+pointer, which can never happen since the loop simply increments a valid
+resource pointer. This fix changes the loop to terminate with an error on
+an invalid end-of-buffer condition. The problem can be seen as an
+infinite loop by callers to AcpiSetCurrentResources with an invalid or
+corrupted resource descriptor, or a resource descriptor that is missing
+an END_TAG descriptor. Reported by Dan Carpenter
+<dan.carpenter@oracle.com>. Lv Zheng, Bob Moore.
+
+Table unload and ACPICA termination: Delete all attached data objects
+during namespace node deletion. This fix updates namespace node deletion
+to delete the entire list of attached objects (attached via
+AcpiAttachObject) instead of just one of the attached items. ACPICA BZ
+1024. Tomasz Nowicki (tomasz.nowicki@linaro.org).
+
+ACPICA termination: Added support to delete all objects attached to the
+root namespace node. This fix deletes any and all objects that have been
+attached to the root node via AcpiAttachData. Previously, none of these
+objects were deleted. Reported by Tomasz Nowicki. ACPICA BZ 1026.
+
+Debug output: Do not emit the function nesting level for the in-kernel
+build. The nesting level is really only useful during a single-thread
+execution. Therefore, only enable this output for the AcpiExec utility.
+Also, only emit the thread ID when executing under AcpiExec (Context
+switches are still always detected and a message is emitted). ACPICA BZ
+972.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 95.9K Code, 27.0K Data, 122.9K Total
+ Debug Version: 185.1K Code, 77.2K Data, 262.3K Total
+ Previous Release:
+ Non-Debug Version: 95.8K Code, 27.0K Data, 122.8K Total
+ Debug Version: 185.2K Code, 77.2K Data, 262.4K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiExec/Unix-OSL: Use <termios.h> instead of <termio.h>. This is the
+correct portable POSIX header for terminal control functions.
+
+Disassembler: Fixed control method invocation issues related to the use
+of the CondRefOf() operator. The problem is seen in the disassembly where
+control method invocations may not be disassembled properly if the
+control method name has been used previously as an argument to CondRefOf.
+The solution is to not attempt to emit an external declaration for the
+CondRefOf target (it is not necessary in the first place). This prevents
+disassembler object type confusion. ACPICA BZ 988.
+
+Unix Makefiles: Added an option to disable compiler optimizations and the
+_FORTIFY_SOURCE flag. Some older compilers have problems compiling ACPICA
+with optimizations (reportedly, gcc 4.4 for example). This change adds a
+command line option for make (NOOPT) that disables all compiler
+optimizations and the _FORTIFY_SOURCE compiler flag. The default
+optimization is -O2 with the _FORTIFY_SOURCE flag specified. ACPICA BZ
+1034. Lv Zheng, Bob Moore.
+
+Tests/ASLTS: Added options to specify individual test cases and modes.
+This allows testers running aslts.sh to optionally specify individual
+test modes and test cases. Also added an option to disable the forced
+generation of the ACPICA tools from source if desired. Lv Zheng.
+
+----------------------------------------
+27 September 2013. Summary of changes for version 20130927:
+
+This release is available at https://acpica.org/downloads
+
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a problem with store operations to reference objects. This change
+fixes a problem where a Store operation to an ArgX object that contained
+a
+reference to a field object did not complete the automatic dereference
+and
+then write to the actual field object. Instead, the object type of the
+field object was inadvertently changed to match the type of the source
+operand. The new behavior will actually write to the field object (buffer
+field or field unit), thus matching the correct ACPI-defined behavior.
+
+Implemented support to allow the host to redefine individual OSL
+prototypes. This change enables the host to redefine OSL prototypes found
+in the acpiosxf.h file. This allows the host to implement OSL interfaces
+with a macro or inlined function. Further, it allows the host to add any
+additional required modifiers such as __iomem, __init, __exit, etc., as
+necessary on a per-interface basis. Enables maximum flexibility for the
+OSL interfaces. Lv Zheng.
+
+Hardcoded the access width for the FADT-defined reset register. The ACPI
+specification requires the reset register width to be 8 bits. ACPICA now
+hardcodes the width to 8 and ignores the FADT width value. This provides
+compatibility with other ACPI implementations that have allowed BIOS code
+with bad register width values to go unnoticed. Matthew Garett, Bob
+Moore,
+Lv Zheng.
+
+Changed the position/use of the ACPI_PRINTF_LIKE macro. This macro is
+used
+in the OSL header (acpiosxf). The change modifies the position of this
+macro in each instance where it is used (AcpiDebugPrint, etc.) to avoid
+build issues if the OSL defines the implementation of the interface to be
+an inline stub function. Lv Zheng.
+
+Deployed a new macro ACPI_EXPORT_SYMBOL_INIT for the main ACPICA
+initialization interfaces. This change adds a new macro for the main init
+and terminate external interfaces in order to support hosts that require
+additional or different processing for these functions. Changed from
+ACPI_EXPORT_SYMBOL to ACPI_EXPORT_SYMBOL_INIT for these functions. Lv
+Zheng, Bob Moore.
+
+Cleaned up the memory allocation macros for configurability. In the
+common
+case, the ACPI_ALLOCATE and related macros now resolve directly to their
+respective AcpiOs* OSL interfaces. Two options:
+1) The ACPI_ALLOCATE_ZEROED macro uses a simple local implementation by
+default, unless overridden by the USE_NATIVE_ALLOCATE_ZEROED define.
+2) For AcpiExec (and for debugging), the macros can optionally be
+resolved
+to the local ACPICA interfaces that track each allocation (local tracking
+is used to immediately detect memory leaks).
+Lv Zheng.
+
+Simplified the configuration for ACPI_REDUCED_HARDWARE. Allows the kernel
+to predefine this macro to either TRUE or FALSE during the system build.
+
+Replaced __FUNCTION_ with __func__ in the gcc-specific header.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 95.8K Code, 27.0K Data, 122.8K Total
+ Debug Version: 185.2K Code, 77.2K Data, 262.4K Total
+ Previous Release:
+ Non-Debug Version: 96.7K Code, 27.1K Data, 123.9K Total
+ Debug Version: 184.4K Code, 76.8K Data, 261.2K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented wildcard support for the -e option. This simplifies use
+when there are many SSDTs that must be included to resolve external
+method
+declarations. ACPICA BZ 1041. Example:
+ iasl -e ssdt*.dat -d dsdt.dat
+
+AcpiExec: Add history/line-editing for Unix/Linux systems. This change
+adds a portable module that implements full history and limited line
+editing for Unix and Linux systems. It does not use readline() due to
+portability issues. Instead it uses the POSIX termio interface to put the
+terminal in raw input mode so that the various special keys can be
+trapped
+(such as up/down-arrow for history support and left/right-arrow for line
+editing). Uses the existing debugger history mechanism. ACPICA BZ 1036.
+
+AcpiXtract: Add support to handle (ignore) "empty" lines containing only
+one or more spaces. This provides compatible with early or different
+versions of the AcpiDump utility. ACPICA BZ 1044.
+
+AcpiDump: Do not ignore tables that contain only an ACPI table header.
+Apparently, some BIOSs create SSDTs that contain an ACPI table header but
+no other data. This change adds support to dump these tables. Any tables
+shorter than the length of an ACPI table header remain in error (an error
+message is emitted). Reported by Yi Li.
+
+Debugger: Echo actual command along with the "unknown command" message.
+
+----------------------------------------
+23 August 2013. Summary of changes for version 20130823:
+
+1) ACPICA kernel-resident subsystem:
+
+Implemented support for host-installed System Control Interrupt (SCI)
+handlers. Certain ACPI functionality requires the host to handle raw
+SCIs. For example, the "SCI Doorbell" that is defined for memory power
+state support requires the host device driver to handle SCIs to examine
+if the doorbell has been activated. Multiple SCI handlers can be
+installed to allow for future expansion. New external interfaces are
+AcpiInstallSciHandler, AcpiRemoveSciHandler; see the ACPICA reference for
+details. Lv Zheng, Bob Moore. ACPICA BZ 1032.
+
+Operation region support: Never locally free the handler "context"
+pointer. This change removes some dangerous code that attempts to free
+the handler context pointer in some (rare) circumstances. The owner of
+the handler owns this pointer and the ACPICA code should never touch it.
+Although not seen to be an issue in any kernel, it did show up as a
+problem (fault) under AcpiExec. Also, set the internal storage field for
+the context pointer to zero when the region is deactivated, simply for
+sanity. David Box. ACPICA BZ 1039.
+
+AcpiRead: On error, do not modify the return value target location. If an
+error happens in the middle of a split 32/32 64-bit I/O operation, do not
+modify the target of the return value pointer. Makes the code consistent
+with the rest of ACPICA. Bjorn Helgaas.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.7K Code, 27.1K Data, 123.9K Total
+ Debug Version: 184.4K Code, 76.8K Data, 261.2K Total
+ Previous Release:
+ Non-Debug Version: 96.2K Code, 27.1K Data, 123.3K Total
+ Debug Version: 185.4K Code, 77.1K Data, 262.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiDump: Implemented several new features and fixed some problems:
+1) Added support to dump the RSDP, RSDT, and XSDT tables.
+2) Added support for multiple table instances (SSDT, UEFI).
+3) Added option to dump "customized" (overridden) tables (-c).
+4) Fixed a problem where some table filenames were improperly
+constructed.
+5) Improved some error messages, removed some unnecessary messages.
+
+iASL: Implemented additional support for disassembly of ACPI tables that
+contain invocations of external control methods. The -fe<file> option
+allows the import of a file that specifies the external methods along
+with the required number of arguments for each -- allowing for the
+correct disassembly of the table. This is a workaround for a limitation
+of AML code where the disassembler often cannot determine the number of
+arguments required for an external control method and generates incorrect
+ASL code. See the iASL reference for details. ACPICA BZ 1030.
+
+Debugger: Implemented a new command (paths) that displays the full
+pathnames (namepaths) and object types of all objects in the namespace.
+This is an alternative to the namespace command.
+
+Debugger: Implemented a new command (sci) that invokes the SCI dispatch
+mechanism and any installed handlers.
+
+iASL: Fixed a possible segfault for "too many parent prefixes" condition.
+This can occur if there are too many parent prefixes in a namepath (for
+example, ^^^^^^PCI0.ECRD). ACPICA BZ 1035.
+
+Application OSLs: Set the return value for the PCI read functions. These
+functions simply return AE_OK, but should set the return value to zero
+also. This change implements this. ACPICA BZ 1038.
+
+Debugger: Prevent possible command line buffer overflow. Increase the
+size of a couple of the debugger line buffers, and ensure that overflow
+cannot happen. ACPICA BZ 1037.
+
+iASL: Changed to abort immediately on serious errors during the parsing
+phase. Due to the nature of ASL, there is no point in attempting to
+compile these types of errors, and they typically end up causing a
+cascade of hundreds of errors which obscure the original problem.
+
+----------------------------------------
+25 July 2013. Summary of changes for version 20130725:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a problem with the DerefOf operator where references to FieldUnits
+and BufferFields incorrectly returned the parent object, not the actual
+value of the object. After this change, a dereference of a FieldUnit
+reference results in a read operation on the field to get the value, and
+likewise, the appropriate BufferField value is extracted from the target
+buffer.
+
+Fixed a problem where the _WAK method could cause a fault under these
+circumstances: 1) Interpreter slack mode was not enabled, and 2) the _WAK
+method returned no value. The problem is rarely seen because most kernels
+run ACPICA in slack mode.
+
+For the DerefOf operator, a fatal error now results if an attempt is made
+to dereference a reference (created by the Index operator) to a NULL
+package element. Provides compatibility with other ACPI implementations,
+and this behavior will be added to a future version of the ACPI
+specification.
+
+The ACPI Power Management Timer (defined in the FADT) is now optional.
+This provides compatibility with other ACPI implementations and will
+appear in the next version of the ACPI specification. If there is no PM
+Timer on the platform, AcpiGetTimer returns AE_SUPPORT. An address of
+zero in the FADT indicates no PM timer.
+
+Implemented a new interface for _OSI support, AcpiUpdateInterfaces. This
+allows the host to globally enable/disable all vendor strings, all
+feature strings, or both. Intended to be primarily used for debugging
+purposes only. Lv Zheng.
+
+Expose the collected _OSI data to the host via a global variable. This
+data tracks the highest level vendor ID that has been invoked by the BIOS
+so that the host (and potentially ACPICA itself) can change behaviors
+based upon the age of the BIOS.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.2K Code, 27.1K Data, 123.3K Total
+ Debug Version: 184.4K Code, 76.8K Data, 261.2K Total
+ Previous Release:
+ Non-Debug Version: 95.9K Code, 26.9K Data, 122.8K Total
+ Debug Version: 184.1K Code, 76.7K Data, 260.8K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Created the following enhancements for the -so option (create
+offset table):
+1)Add offsets for the last nameseg in each namepath for every supported
+object type
+2)Add support for Processor, Device, Thermal Zone, and Scope objects
+3)Add the actual AML opcode for the parent object of every supported
+object type
+4)Add support for the ZERO/ONE/ONES AML opcodes for integer objects
+
+Disassembler: Emit all unresolved external symbols in a single block.
+These are external references to control methods that could not be
+resolved, and thus, the disassembler had to make a guess at the number of
+arguments to parse.
+
+iASL: The argument to the -T option (create table template) is now
+optional. If not specified, the default table is a DSDT, typically the
+most common case.
+
+----------------------------------------
+26 June 2013. Summary of changes for version 20130626:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed an issue with runtime repair of the _CST object. Null or invalid
+elements were not always removed properly. Lv Zheng.
+
+Removed an arbitrary restriction of 256 GPEs per GPE block (such as the
+FADT-defined GPE0 and GPE1). For GPE0, GPE1, and each GPE Block Device,
+the maximum number of GPEs is 1016. Use of multiple GPE block devices
+makes the system-wide number of GPEs essentially unlimited.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 95.9K Code, 26.9K Data, 122.8K Total
+ Debug Version: 184.1K Code, 76.7K Data, 260.8K Total
+ Previous Release:
+ Non-Debug Version: 96.0K Code, 27.0K Data, 123.0K Total
+ Debug Version: 184.1K Code, 76.8K Data, 260.9K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Portable AcpiDump: Implemented full support for the Linux and FreeBSD
+hosts. Now supports Linux, FreeBSD, and Windows.
+
+Disassembler: Added some missing types for the HEST and EINJ tables: "Set
+Error Type With Address", "CMCI", "MCE", and "Flush Cacheline".
+
+iASL/Preprocessor: Implemented full support for nested
+#if/#else/#elif/#endif blocks. Allows arbitrary depth of nested blocks.
+
+Disassembler: Expanded maximum output string length to 64K. Was 256 bytes
+max. The original purpose of this constraint was to limit the amount of
+debug output. However, the string function in question (UtPrintString) is
+now used for the disassembler also, where 256 bytes is insufficient.
+Reported by RehabMan@GitHub.
+
+iASL/DataTables: Fixed some problems and issues with compilation of DMAR
+tables. ACPICA BZ 999. Lv Zheng.
+
+iASL: Fixed a couple of error exit issues that could result in a "Could
+not delete <file>" message during ASL compilation.
+
+AcpiDump: Allow "FADT" and "MADT" as valid table signatures, even though
+the actual signatures for these tables are "FACP" and "APIC",
+respectively.
+
+AcpiDump: Added support for multiple UEFI tables. Only SSDT and UEFI
+tables are allowed to have multiple instances.
+
+----------------------------------------
+17 May 2013. Summary of changes for version 20130517:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a regression introduced in version 20130328 for _INI methods. This
+change fixes a problem introduced in 20130328 where _INI methods are no
+longer executed properly because of a memory block that was not
+initialized correctly. ACPICA BZ 1016. Tomasz Nowicki
+<tomasz.nowicki@linaro.org>.
+
+Fixed a possible problem with the new extended sleep registers in the
+ACPI
+5.0 FADT. Do not use these registers (even if populated) unless the HW-
+reduced bit is set in the FADT (as per the ACPI specification). ACPICA BZ
+1020. Lv Zheng.
+
+Implemented return value repair code for _CST predefined objects: Sort
+the
+list and detect/remove invalid entries. ACPICA BZ 890. Lv Zheng.
+
+Implemented a debug-only option to disable loading of SSDTs from the
+RSDT/XSDT during ACPICA initialization. This can be useful for debugging
+ACPI problems on some machines. Set AcpiGbl_DisableSsdtTableLoad in
+acglobal.h - ACPICA BZ 1005. Lv Zheng.
+
+Fixed some issues in the ACPICA initialization and termination code:
+Tomasz Nowicki <tomasz.nowicki@linaro.org>
+1) Clear events initialized flag upon event component termination. ACPICA
+BZ 1013.
+2) Fixed a possible memory leak in GPE init error path. ACPICA BZ 1018.
+3) Delete global lock pending lock during termination. ACPICA BZ 1012.
+4) Clear debug buffer global on termination to prevent possible multiple
+delete. ACPICA BZ 1010.
+
+Standardized all switch() blocks across the entire source base. After
+many
+years, different formatting for switch() had crept in. This change makes
+the formatting of every switch block identical. ACPICA BZ 997. Chao Guan.
+
+Split some files to enhance ACPICA modularity and configurability:
+1) Split buffer dump routines into utilities/utbuffer.c
+2) Split internal error message routines into utilities/uterror.c
+3) Split table print utilities into tables/tbprint.c
+4) Split iASL command-line option processing into asloptions.c
+
+Makefile enhancements:
+1) Support for all new files above.
+2) Abort make on errors from any subcomponent. Chao Guan.
+3) Add build support for Apple Mac OS X. Liang Qi.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 96.0K Code, 27.0K Data, 123.0K Total
+ Debug Version: 184.1K Code, 76.8K Data, 260.9K Total
+ Previous Release:
+ Non-Debug Version: 95.6K Code, 26.8K Data, 122.4K Total
+ Debug Version: 183.5K Code, 76.6K Data, 260.1K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+New utility: Implemented an easily portable version of the acpidump
+utility to extract ACPI tables from the system (or a file) in an ASCII
+hex
+dump format. The top-level code implements the various command line
+options, file I/O, and table dump routines. To port to a new host, only
+three functions need to be implemented to get tables -- since this
+functionality is OS-dependent. See the tools/acpidump/apmain.c module and
+the ACPICA reference for porting instructions. ACPICA BZ 859. Notes:
+1) The Windows version obtains the ACPI tables from the Registry.
+2) The Linux version is under development.
+3) Other hosts - If an OS-dependent module is submitted, it will be
+distributed with ACPICA.
+
+iASL: Fixed a regression for -D preprocessor option (define symbol). A
+restructuring/change to the initialization sequence caused this option to
+no longer work properly.
+
+iASL: Implemented a mechanism to disable specific warnings and remarks.
+Adds a new command line option, "-vw <messageid> as well as "#pragma
+disable <messageid>". ACPICA BZ 989. Chao Guan, Bob Moore.
+
+iASL: Fix for too-strict package object validation. The package object
+validation for return values from the predefined names is a bit too
+strict, it does not allow names references within the package (which will
+be resolved at runtime.) These types of references cannot be validated at
+compile time. This change ignores named references within package objects
+for names that return or define static packages.
+
+Debugger: Fixed the 80-character command line limitation for the History
+command. Now allows lines of arbitrary length. ACPICA BZ 1000. Chao Guan.
+
+iASL: Added control method and package support for the -so option
+(generates AML offset table for BIOS support.)
+
+iASL: issue a remark if a non-serialized method creates named objects. If
+a thread blocks within the method for any reason, and another thread
+enters the method, the method will fail because an attempt will be made
+to
+create the same (named) object twice. In this case, issue a remark that
+the method should be marked serialized. NOTE: may become a warning later.
+ACPICA BZ 909.
+
+----------------------------------------
+18 April 2013. Summary of changes for version 20130418:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed a possible buffer overrun during some rare but specific field unit
+read operations. This overrun can only happen if the DSDT version is 1 --
+meaning that all AML integers are 32 bits -- and the field length is
+between 33 and 55 bits long. During the read, an internal buffer object
+is
+created for the field unit because the field is larger than an integer
+(32
+bits). However, in this case, the buffer will be incorrectly written
+beyond the end because the buffer length is less than the internal
+minimum
+of 64 bits (8 bytes) long. The buffer will be either 5, 6, or 7 bytes
+long, but a full 8 bytes will be written.
+
+Updated the Embedded Controller "orphan" _REG method support. This refers
+to _REG methods under the EC device that have no corresponding operation
+region. This is allowed by the ACPI specification. This update removes a
+dependency on the existence an ECDT table. It will execute an orphan _REG
+method as long as the operation region handler for the EC is installed at
+the EC device node and not the namespace root. Rui Zhang (original
+update), Bob Moore (update/integrate).
+
+Implemented run-time argument typechecking for all predefined ACPI names
+(_STA, _BIF, etc.) This change performs object typechecking on all
+incoming arguments for all predefined names executed via
+AcpiEvaluateObject. This ensures that ACPI-related device drivers are
+passing correct object types as well as the correct number of arguments
+(therefore identifying any issues immediately). Also, the ASL/namespace
+definition of the predefined name is checked against the ACPI
+specification for the proper argument count. Adds one new file,
+nsarguments.c
+
+Changed an exception code for the ASL UnLoad() operator. Changed the
+exception code for the case where the input DdbHandle is invalid, from
+AE_BAD_PARAMETER to the more appropriate AE_AML_OPERAND_TYPE.
+
+Unix/Linux makefiles: Removed the use of the -O2 optimization flag in the
+global makefile. The use of this flag causes compiler errors on earlier
+versions of GCC, so it has been removed for compatibility.
+
+Miscellaneous cleanup:
+1) Removed some unused/obsolete macros
+2) Fixed a possible memory leak in the _OSI support
+3) Removed an unused variable in the predefined name support
+4) Windows OSL: remove obsolete reference to a memory list field
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Current Release:
+ Non-Debug Version: 95.2K Code, 26.4K Data, 121.6K Total
+ Debug Version: 183.0K Code, 76.0K Data, 259.0K Total
+ Previous Release:
+ Non-Debug Version: 95.6K Code, 26.8K Data, 122.4K Total
+ Debug Version: 183.5K Code, 76.6K Data, 260.1K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiExec: Added installation of a handler for the SystemCMOS address
+space. This prevents control method abort if a method accesses this
+space.
+
+AcpiExec: Added support for multiple EC devices, and now install EC
+operation region handler(s) at the actual EC device instead of the
+namespace root. This reflects the typical behavior of host operating
+systems.
+
+AcpiExec: Updated to ensure that all operation region handlers are
+installed before the _REG methods are executed. This prevents a _REG
+method from aborting if it accesses an address space has no handler.
+AcpiExec installs a handler for every possible address space.
+
+Debugger: Enhanced the "handlers" command to display non-root handlers.
+This change enhances the handlers command to display handlers associated
+with individual devices throughout the namespace, in addition to the
+currently supported display of handlers associated with the root
+namespace
+node.
+
+ASL Test Suite: Several test suite errors have been identified and
+resolved, reducing the total error count during execution. Chao Guan.
+
+----------------------------------------
+28 March 2013. Summary of changes for version 20130328:
+
+1) ACPICA kernel-resident subsystem:
+
+Fixed several possible race conditions with the internal object reference
+counting mechanism. Some of the external ACPICA interfaces update object
+reference counts without holding the interpreter or namespace lock. This
+change adds a spinlock to protect reference count updates on the internal
+ACPICA objects. Reported by and with assistance from Andriy Gapon
+(avg@FreeBSD.org).
+
+FADT support: Removed an extraneous warning for very large GPE register
+sets. This change removes a size mismatch warning if the legacy length
+field for a GPE register set is larger than the 64-bit GAS structure can
+accommodate. GPE register sets can be larger than the 255-bit width
+limitation of the GAS structure. Linn Crosetto (linn@hp.com).
+
+_OSI Support: handle any errors from AcpiOsAcquireMutex. Check for error
+return from this interface. Handles a possible timeout case if
+ACPI_WAIT_FOREVER is modified by the host to be a value less than
+"forever". Jung-uk Kim.
+
+Predefined name support: Add allowed/required argument type information
+to
+the master predefined info table. This change adds the infrastructure to
+enable typechecking on incoming arguments for all predefined
+methods/objects. It does not actually contain the code that will fully
+utilize this information, this is still under development. Also condenses
+some duplicate code for the predefined names into a new module,
+utilities/utpredef.c
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 95.0K Code, 25.9K Data, 120.9K Total
+ Debug Version: 182.9K Code, 75.6K Data, 258.5K Total
+ Current Release:
+ Non-Debug Version: 95.2K Code, 26.4K Data, 121.6K Total
+ Debug Version: 183.0K Code, 76.0K Data, 259.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented a new option to simplify the development of ACPI-
+related
+BIOS code. Adds support for a new "offset table" output file. The -so
+option will create a C table containing the AML table offsets of various
+named objects in the namespace so that BIOS code can modify them easily
+at
+boot time. This can simplify BIOS runtime code by eliminating expensive
+searches for "magic values", enhancing boot times and adding greater
+reliability. With assistance from Lee Hamel.
+
+iASL: Allow additional predefined names to return zero-length packages.
+Now, all predefined names that are defined by the ACPI specification to
+return a "variable-length package of packages" are allowed to return a
+zero length top-level package. This allows the BIOS to tell the host that
+the requested feature is not supported, and supports existing BIOS/ASL
+code and practices.
+
+iASL: Changed the "result not used" warning to an error. This is the case
+where an ASL operator is effectively a NOOP because the result of the
+operation is not stored anywhere. For example:
+ Add (4, Local0)
+There is no target (missing 3rd argument), nor is the function return
+value used. This is potentially a very serious problem -- since the code
+was probably intended to do something, but for whatever reason, the value
+was not stored. Therefore, this issue has been upgraded from a warning to
+an error.
+
+AcpiHelp: Added allowable/required argument types to the predefined names
+info display. This feature utilizes the recent update to the predefined
+names table (above).
+
+----------------------------------------
+14 February 2013. Summary of changes for version 20130214:
+
+1) ACPICA Kernel-resident Subsystem:
+
+Fixed a possible regression on some hosts: Reinstated the safe return
+macros (return_ACPI_STATUS, etc.) that ensure that the argument is
+evaluated only once. Although these macros are not needed for the ACPICA
+code itself, they are often used by ACPI-related host device drivers
+where
+the safe feature may be necessary.
+
+Fixed several issues related to the ACPI 5.0 reduced hardware support
+(SOC): Now ensure that if the platform declares itself as hardware-
+reduced
+via the FADT, the following functions become NOOPs (and always return
+AE_OK) because ACPI is always enabled by definition on these machines:
+ AcpiEnable
+ AcpiDisable
+ AcpiHwGetMode
+ AcpiHwSetMode
+
+Dynamic Object Repair: Implemented additional runtime repairs for
+predefined name return values. Both of these repairs can simplify code in
+the related device drivers that invoke these methods:
+1) For the _STR and _MLS names, automatically repair/convert an ASCII
+string to a Unicode buffer.
+2) For the _CRS, _PRS, and _DMA names, return a resource descriptor with
+a
+lone end tag descriptor in the following cases: A Return(0) was executed,
+a null buffer was returned, or no object at all was returned (non-slack
+mode only). Adds a new file, nsconvert.c
+ACPICA BZ 998. Bob Moore, Lv Zheng.
+
+Resource Manager: Added additional code to prevent possible infinite
+loops
+while traversing corrupted or ill-formed resource template buffers. Check
+for zero-length resource descriptors in all code that loops through
+resource templates (the length field is used to index through the
+template). This change also hardens the external AcpiWalkResources and
+AcpiWalkResourceBuffer interfaces.
+
+Local Cache Manager: Enhanced the main data structure to eliminate an
+unnecessary mechanism to access the next object in the list. Actually
+provides a small performance enhancement for hosts that use the local
+ACPICA cache manager. Jung-uk Kim.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 94.5K Code, 25.4K Data, 119.9K Total
+ Debug Version: 182.3K Code, 75.0K Data, 257.3K Total
+ Current Release:
+ Non-Debug Version: 95.0K Code, 25.9K Data, 120.9K Total
+ Debug Version: 182.9K Code, 75.6K Data, 258.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL/Disassembler: Fixed several issues with the definition of the ACPI
+5.0 RASF table (RAS Feature Table). This change incorporates late changes
+that were made to the ACPI 5.0 specification.
+
+iASL/Disassembler: Added full support for the following new ACPI tables:
+ 1) The MTMR table (MID Timer Table)
+ 2) The VRTC table (Virtual Real Time Clock Table).
+Includes header file, disassembler, table compiler, and template support
+for both tables.
+
+iASL: Implemented compile-time validation of package objects returned by
+predefined names. This new feature validates static package objects
+returned by the various predefined names defined to return packages. Both
+object types and package lengths are validated, for both parent packages
+and sub-packages, if any. The code is similar in structure and behavior
+to
+the runtime repair mechanism within the AML interpreter and uses the
+existing predefined name information table. Adds a new file, aslprepkg.c.
+ACPICA BZ 938.
+
+iASL: Implemented auto-detection of binary ACPI tables for disassembly.
+This feature detects a binary file with a valid ACPI table header and
+invokes the disassembler automatically. Eliminates the need to
+specifically invoke the disassembler with the -d option. ACPICA BZ 862.
+
+iASL/Disassembler: Added several warnings for the case where there are
+unresolved control methods during the disassembly. This can potentially
+cause errors when the output file is compiled, because the disassembler
+assumes zero method arguments in these cases (it cannot determine the
+actual number of arguments without resolution/definition of the method).
+
+Debugger: Added support to display all resources with a single command.
+Invocation of the resources command with no arguments will now display
+all
+resources within the current namespace.
+
+AcpiHelp: Added descriptive text for each ACPICA exception code displayed
+via the -e option.
+
+----------------------------------------
+17 January 2013. Summary of changes for version 20130117:
+
+1) ACPICA Kernel-resident Subsystem:
+
+Updated the AcpiGetSleepTypeData interface: Allow the \_Sx methods to
+return either 1 or 2 integers. Although the ACPI spec defines the \_Sx
+objects to return a package containing one integer, most BIOS code
+returns
+two integers and the previous code reflects that. However, we also need
+to
+support BIOS code that actually implements to the ACPI spec, and this
+change reflects this.
+
+Fixed two issues with the ACPI_DEBUG_PRINT macros:
+1) Added the ACPI_DO_WHILE macro to the main DEBUG_PRINT helper macro for
+C compilers that require this support.
+2) Renamed the internal ACPI_DEBUG macro to ACPI_DO_DEBUG_PRINT since
+ACPI_DEBUG is already used by many of the various hosts.
+
+Updated all ACPICA copyrights and signons to 2013. Added the 2013
+copyright to all module headers and signons, including the standard Linux
+header. This affects virtually every file in the ACPICA core subsystem,
+iASL compiler, all ACPICA utilities, and the test suites.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 94.5K Code, 25.5K Data, 120.0K Total
+ Debug Version: 182.2K Code, 74.9K Data, 257.1K Total
+ Current Release:
+ Non-Debug Version: 94.5K Code, 25.4K Data, 119.9K Total
+ Debug Version: 182.3K Code, 75.0K Data, 257.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Generic Unix OSL: Use a buffer to eliminate multiple vfprintf()s and
+prevent a possible fault on some hosts. Some C libraries modify the arg
+pointer parameter to vfprintf making it difficult to call it twice in the
+AcpiOsVprintf function. Use a local buffer to workaround this issue. This
+does not affect the Windows OSL since the Win C library does not modify
+the arg pointer. Chao Guan, Bob Moore.
+
+iASL: Fixed a possible infinite loop when the maximum error count is
+reached. If an output file other than the .AML file is specified (such as
+a listing file), and the maximum number of errors is reached, do not
+attempt to flush data to the output file(s) as the compiler is aborting.
+This can cause an infinite loop as the max error count code essentially
+keeps calling itself.
+
+iASL/Disassembler: Added an option (-in) to ignore NOOP
+opcodes/operators.
+Implemented for both the compiler and the disassembler. Often, the NOOP
+opcode is used as padding for packages that are changed dynamically by
+the
+BIOS. When disassembled and recompiled, these NOOPs will cause syntax
+errors. This option causes the disassembler to ignore all NOOP opcodes
+(0xA3), and it also causes the compiler to ignore all ASL source code
+NOOP
+statements as well.
+
+Debugger: Enhanced the Sleep command to execute all sleep states. This
+change allows Sleep to be invoked with no arguments and causes the
+debugger to execute all of the sleep states, 0-5, automatically.
+
+----------------------------------------
+20 December 2012. Summary of changes for version 20121220:
+
+1) ACPICA Kernel-resident Subsystem:
+
+Implemented a new interface, AcpiWalkResourceBuffer. This interface is an
+alternate entry point for AcpiWalkResources and improves the usability of
+the resource manager by accepting as input a buffer containing the output
+of either a _CRS, _PRS, or _AEI method. The key functionality is that the
+input buffer is not deleted by this interface so that it can be used by
+the host later. See the ACPICA reference for details.
+
+Interpreter: Add a warning if a 64-bit constant appears in a 32-bit table
+(DSDT version < 2). The constant will be truncated and this warning
+reflects that behavior.
+
+Resource Manager: Add support for the new ACPI 5.0 wake bit in the IRQ,
+ExtendedInterrupt, and GpioInt descriptors. This change adds support to
+both get and set the new wake bit in these descriptors, separately from
+the existing share bit. Reported by Aaron Lu.
+
+Interpreter: Fix Store() when an implicit conversion is not possible. For
+example, in the cases such as a store of a string to an existing package
+object, implement the store as a CopyObject(). This is a small departure
+from the ACPI specification which states that the control method should
+be
+aborted in this case. However, the ASLTS suite depends on this behavior.
+
+Performance improvement for the various FUNCTION_TRACE and DEBUG_PRINT
+macros: check if debug output is currently enabled as soon as possible to
+minimize performance impact if debug is in fact not enabled.
+
+Source code restructuring: Cleanup to improve modularity. The following
+new files have been added: dbconvert.c, evhandler.c, nsprepkg.c,
+psopinfo.c, psobject.c, rsdumpinfo.c, utstring.c, and utownerid.c.
+Associated makefiles and project files have been updated.
+
+Changed an exception code for LoadTable operator. For the case where one
+of the input strings is too long, change the returned exception code from
+AE_BAD_PARAMETER to AE_AML_STRING_LIMIT.
+
+Fixed a possible memory leak in dispatcher error path. On error, delete
+the mutex object created during method mutex creation. Reported by
+tim.gardner@canonical.com.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 94.3K Code, 25.3K Data, 119.6K Total
+ Debug Version: 175.5K Code, 74.5K Data, 250.0K Total
+ Current Release:
+ Non-Debug Version: 94.5K Code, 25.5K Data, 120.0K Total
+ Debug Version: 182.2K Code, 74.9K Data, 257.1K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Disallow a method call as argument to the ObjectType ASL operator.
+This change tracks an errata to the ACPI 5.0 document. The AML grammar
+will not allow the interpreter to differentiate between a method and a
+method invocation when these are used as an argument to the ObjectType
+operator. The ACPI specification change is to disallow a method
+invocation
+(UserTerm) for the ObjectType operator.
+
+Finish support for the TPM2 and CSRT tables in the headers, table
+compiler, and disassembler.
+
+Unix user-space OSL: Fix a problem with WaitSemaphore where the timeout
+always expires immediately if the semaphore is not available. The
+original
+code was using a relative-time timeout, but sem_timedwait requires the
+use
+of an absolute time.
+
+iASL: Added a remark if the Timer() operator is used within a 32-bit
+table. This operator returns a 64-bit time value that will be truncated
+within a 32-bit table.
+
+iASL Source code restructuring: Cleanup to improve modularity. The
+following new files have been added: aslhex.c, aslxref.c, aslnamesp.c,
+aslmethod.c, and aslfileio.c. Associated makefiles and project files have
+been updated.
+
+
+----------------------------------------
+14 November 2012. Summary of changes for version 20121114:
+
+1) ACPICA Kernel-resident Subsystem:
+
+Implemented a performance enhancement for ACPI/AML Package objects. This
+change greatly increases the performance of Package objects within the
+interpreter. It changes the processing of reference counts for packages
+by
+optimizing for the most common case where the package sub-objects are
+either Integers, Strings, or Buffers. Increases the overall performance
+of
+the ASLTS test suite by 1.5X (Increases the Slack Mode performance by
+2X.)
+Chao Guan. ACPICA BZ 943.
+
+Implemented and deployed common macros to extract flag bits from resource
+descriptors. Improves readability and maintainability of the code. Fixes
+a
+problem with the UART serial bus descriptor for the number of data bits
+flags (was incorrectly 2 bits, should be 3).
+
+Enhanced the ACPI_GETx and ACPI_SETx macros. Improved the implementation
+of the macros and changed the SETx macros to the style of (destination,
+source). Also added ACPI_CASTx companion macros. Lv Zheng.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.9K Code, 25.2K Data, 119.1K Total
+ Debug Version: 175.5K Code, 74.5K Data, 250.0K Total
+ Current Release:
+ Non-Debug Version: 94.3K Code, 25.3K Data, 119.6K Total
+ Debug Version: 175.5K Code, 74.5K Data, 250.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Added the new ACPI 5.0 interrupt sharing flags. This change
+adds the ShareAndWake and ExclusiveAndWake flags which were added to the
+Irq, Interrupt, and Gpio resource descriptors in ACPI 5.0. ACPICA BZ 986.
+
+Disassembler: Fixed a problem with external declaration generation. Fixes
+a problem where an incorrect pathname could be generated for an external
+declaration if the original reference to the object includes leading
+carats (^). ACPICA BZ 984.
+
+Debugger: Completed a major update for the Disassemble<method> command.
+This command was out-of-date and did not properly disassemble control
+methods that had any reasonable complexity. This fix brings the command
+up
+to the same level as the rest of the disassembler. Adds one new file,
+dmdeferred.c, which is existing code that is now common with the main
+disassembler and the debugger disassemble command. ACPICA MZ 978.
+
+iASL: Moved the parser entry prototype to avoid a duplicate declaration.
+Newer versions of Bison emit this prototype, so moved the prototype out
+of
+the iASL header to where it is actually used in order to avoid a
+duplicate
+declaration.
+
+iASL/Tools: Standardized use of the stream I/O functions:
+ 1) Ensure check for I/O error after every fopen/fread/fwrite
+ 2) Ensure proper order of size/count arguments for fread/fwrite
+ 3) Use test of (Actual != Requested) after all fwrite, and most fread
+ 4) Standardize I/O error messages
+Improves reliability and maintainability of the code. Bob Moore, Lv
+Zheng.
+ACPICA BZ 981.
+
+Disassembler: Prevent duplicate External() statements. During generation
+of external statements, detect similar pathnames that are actually
+duplicates such as these:
+ External (\ABCD)
+ External (ABCD)
+Remove all leading '\' characters from pathnames during the external
+statement generation so that duplicates will be detected and tossed.
+ACPICA BZ 985.
+
+Tools: Replace low-level I/O with stream I/O functions. Replace
+open/read/write/close with the stream I/O equivalents
+fopen/fread/fwrite/fclose for portability and performance. Lv Zheng, Bob
+Moore.
+
+AcpiBin: Fix for the dump-to-hex function. Now correctly output the table
+name header so that AcpiXtract recognizes the output file/table.
+
+iASL: Remove obsolete -2 option flag. Originally intended to force the
+compiler/disassembler into an ACPI 2.0 mode, this was never implemented
+and the entire concept is now obsolete.
+
+----------------------------------------
+18 October 2012. Summary of changes for version 20121018:
+
+
+1) ACPICA Kernel-resident Subsystem:
+
+Updated support for the ACPI 5.0 MPST table. Fixes some problems
+introduced by late changes to the table as it was added to the ACPI 5.0
+specification. Includes header, disassembler, and data table compiler
+support as well as a new version of the MPST template.
+
+AcpiGetObjectInfo: Enhanced the device object support to include the ACPI
+5.0 _SUB method. Now calls _SUB in addition to the other PNP-related ID
+methods: _HID, _CID, and _UID.
+
+Changed ACPI_DEVICE_ID to ACPI_PNP_DEVICE_ID. Also changed
+ACPI_DEVICE_ID_LIST to ACPI_PNP_DEVICE_ID_LIST. These changes prevent
+name collisions on hosts that reserve the *_DEVICE_ID (or *DeviceId)
+names for their various drivers. Affects the AcpiGetObjectInfo external
+interface, and other internal interfaces as well.
+
+Added and deployed a new macro for ACPI_NAME management: ACPI_MOVE_NAME.
+This macro resolves to a simple 32-bit move of the 4-character ACPI_NAME
+on machines that support non-aligned transfers. Optimizes for this case
+rather than using a strncpy. With assistance from Zheng Lv.
+
+Resource Manager: Small fix for buffer size calculation. Fixed a one byte
+error in the output buffer calculation. Feng Tang. ACPICA BZ 849.
+
+Added a new debug print message for AML mutex objects that are force-
+released. At control method termination, any currently acquired mutex
+objects are force-released. Adds a new debug-only message for each one
+that is released.
+
+Audited/updated all ACPICA return macros and the function debug depth
+counter: 1) Ensure that all functions that use the various TRACE macros
+also use the appropriate ACPICA return macros. 2) Ensure that all normal
+return statements surround the return expression (value) with parens to
+ensure consistency across the ACPICA code base. Guan Chao, Tang Feng,
+Zheng Lv, Bob Moore. ACPICA Bugzilla 972.
+
+Global source code changes/maintenance: All extra lines at the start and
+end of each source file have been removed for consistency. Also, within
+comments, all new sentences start with a single space instead of a double
+space, again for consistency across the code base.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.7K Code, 25.3K Data, 119.0K Total
+ Debug Version: 175.0K Code, 74.4K Data, 249.4K Total
+ Current Release:
+ Non-Debug Version: 93.9K Code, 25.2K Data, 119.1K Total
+ Debug Version: 175.5K Code, 74.5K Data, 250.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+AcpiExec: Improved the algorithm used for memory leak/corruption
+detection. Added some intelligence to the code that maintains the global
+list of allocated memory. The list is now ordered by allocated memory
+address, significantly improving performance. When running AcpiExec on
+the ASLTS test suite, speed improvements of 3X to 5X are seen, depending
+on the platform and/or the environment. Note, this performance
+enhancement affects the AcpiExec utility only, not the kernel-resident
+ACPICA code.
+
+Enhanced error reporting for invalid AML opcodes and bad ACPI_NAMEs. For
+the disassembler, dump the 48 bytes surrounding the invalid opcode. Fix
+incorrect table offset reported for invalid opcodes. Report the original
+32-bit value for bad ACPI_NAMEs (as well as the repaired name.)
+
+Disassembler: Enhanced the -vt option to emit the binary table data in
+hex format to assist with debugging.
+
+Fixed a potential filename buffer overflow in osunixdir.c. Increased the
+size of file structure. Colin Ian King.
+
+----------------------------------------
+13 September 2012. Summary of changes for version 20120913:
+
+
+1) ACPICA Kernel-resident Subsystem:
+
+ACPI 5.0: Added two new notify types for the Hardware Error Notification
+Structure within the Hardware Error Source Table (HEST) table -- CMCI(5)
+and
+MCE(6).
+
+Table Manager: Merged/removed duplicate code in the root table resize
+functions. One function is external, the other is internal. Lv Zheng,
+ACPICA
+BZ 846.
+
+Makefiles: Completely removed the obsolete "Linux" makefiles under
+acpica/generate/linux. These makefiles are obsolete and have been
+replaced
+by
+the generic unix makefiles under acpica/generate/unix.
+
+Makefiles: Ensure that binary files always copied properly. Minor rule
+change
+to ensure that the final binary output files are always copied up to the
+appropriate binary directory (bin32 or bin64.)
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.8K Code, 25.3K Data, 119.1K Total
+ Debug Version: 175.7K Code, 74.8K Data, 250.5K Total
+ Current Release:
+ Non-Debug Version: 93.7K Code, 25.3K Data, 119.0K Total
+ Debug Version: 175.0K Code, 74.4K Data, 249.4K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Fixed a possible fault during the disassembly of resource
+descriptors when a second parse is required because of the invocation of
+external control methods within the table. With assistance from
+adq@lidskialf.net. ACPICA BZ 976.
+
+iASL: Fixed a namepath optimization problem. An error can occur if the
+parse
+node that contains the namepath to be optimized does not have a parent
+node
+that is a named object. This change fixes the problem.
+
+iASL: Fixed a regression where the AML file is not deleted on errors. The
+AML
+output file should be deleted if there are any errors during the
+compiler.
+The
+only exception is if the -f (force output) option is used. ACPICA BZ 974.
+
+iASL: Added a feature to automatically increase internal line buffer
+sizes.
+Via realloc(), automatically increase the internal line buffer sizes as
+necessary to support very long source code lines. The current version of
+the
+preprocessor requires a buffer long enough to contain full source code
+lines.
+This change increases the line buffer(s) if the input lines go beyond the
+current buffer size. This eliminates errors that occurred when a source
+code
+line was longer than the buffer.
+
+iASL: Fixed a problem with constant folding in method declarations. The
+SyncLevel term is a ByteConstExpr, and incorrect code would be generated
+if a
+Type3 opcode was used.
+
+Debugger: Improved command help support. For incorrect argument count,
+display
+full help for the command. For help command itself, allow an argument to
+specify a command.
+
+Test Suites: Several bug fixes for the ASLTS suite reduces the number of
+errors during execution of the suite. Guan Chao.
+
+----------------------------------------
+16 August 2012. Summary of changes for version 20120816:
+
+
+1) ACPICA Kernel-resident Subsystem:
+
+Removed all use of the deprecated _GTS and _BFS predefined methods. The
+_GTS
+(Going To Sleep) and _BFS (Back From Sleep) methods are essentially
+deprecated and will probably be removed from the ACPI specification.
+Windows
+does not invoke them, and reportedly never will. The final nail in the
+coffin
+is that the ACPI specification states that these methods must be run with
+interrupts off, which is not going to happen in a kernel interpreter.
+Note:
+Linux has removed all use of the methods also. It was discovered that
+invoking these functions caused failures on some machines, probably
+because
+they were never tested since Windows does not call them. Affects two
+external
+interfaces, AcpiEnterSleepState and AcpiLeaveSleepStatePrep. Tang Feng.
+ACPICA BZ 969.
+
+Implemented support for complex bit-packed buffers returned from the _PLD
+(Physical Location of Device) predefined method. Adds a new external
+interface, AcpiDecodePldBuffer that parses the buffer into a more usable
+C
+structure. Note: C Bitfields cannot be used for this type of predefined
+structure since the memory layout of individual bitfields is not defined
+by
+the C language. In addition, there are endian concerns where a compiler
+will
+change the bitfield ordering based on the machine type. The new ACPICA
+interface eliminates these issues, and should be called after _PLD is
+executed. ACPICA BZ 954.
+
+Implemented a change to allow a scope change to root (via "Scope (\)")
+during
+execution of module-level ASL code (code that is executed at table load
+time.) Lin Ming.
+
+Added the Windows8/Server2012 string for the _OSI method. This change
+adds
+a
+new _OSI string, "Windows 2012" for both Windows 8 and Windows Server
+2012.
+
+Added header support for the new ACPI tables DBG2 (Debug Port Table Type
+2)
+and CSRT (Core System Resource Table).
+
+Added struct header support for the _FDE, _GRT, _GTM, and _SRT predefined
+names. This simplifies access to the buffers returned by these predefined
+names. Adds a new file, include/acbuffer.h. ACPICA BZ 956.
+
+GPE support: Removed an extraneous parameter from the various low-level
+internal GPE functions. Tang Feng.
+
+Removed the linux makefiles from the unix packages. The generate/linux
+makefiles are obsolete and have been removed from the unix tarball
+release
+packages. The replacement makefiles are under generate/unix, and there is
+a
+top-level makefile under the main acpica directory. ACPICA BZ 967, 912.
+
+Updates for Unix makefiles:
+1) Add -D_FORTIFY_SOURCE=2 for gcc generation. Arjan van de Ven.
+2) Update linker flags (move to end of command line) for AcpiExec
+utility.
+Guan Chao.
+
+Split ACPICA initialization functions to new file, utxfinit.c. Split from
+utxface.c to improve modularity and reduce file size.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a
+much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.5K Code, 25.3K Data, 118.8K Total
+ Debug Version: 173.7K Code, 74.0K Data, 247.7K Total
+ Current Release:
+ Non-Debug Version: 93.8K Code, 25.3K Data, 119.1K Total
+ Debug Version: 175.7K Code, 74.8K Data, 250.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed a problem with constant folding for fixed-length constant
+expressions. The constant-folding code was not being invoked for constant
+expressions that allow the use of type 3/4/5 opcodes to generate
+constants
+for expressions such as ByteConstExpr, WordConstExpr, etc. This could
+result
+in the generation of invalid AML bytecode. ACPICA BZ 970.
+
+iASL: Fixed a generation issue on newer versions of Bison. Newer versions
+apparently automatically emit some of the necessary externals. This
+change
+handles these versions in order to eliminate generation warnings.
+
+Disassembler: Added support to decode the DBG2 and CSRT ACPI tables.
+
+Disassembler: Add support to decode _PLD buffers. The decoded buffer
+appears
+within comments in the output file.
+
+Debugger: Fixed a regression with the "Threads" command where
+AE_BAD_PARAMETER was always returned.
+
+----------------------------------------
+11 July 2012. Summary of changes for version 20120711:
+
+1) ACPICA Kernel-resident Subsystem:
+
+Fixed a possible fault in the return package object repair code. Fixes a
+problem that can occur when a lone package object is wrapped with an
+outer
+package object in order to force conformance to the ACPI specification.
+Can
+affect these predefined names: _ALR, _MLS, _PSS, _TRT, _TSS, _PRT, _HPX,
+_DLM,
+_CSD, _PSD, _TSD.
+
+Removed code to disable/enable bus master arbitration (ARB_DIS bit in the
+PM2_CNT register) in the ACPICA sleep/wake interfaces. Management of the
+ARB_DIS bit must be implemented in the host-dependent C3 processor power
+state
+support. Note, ARB_DIS is obsolete and only applies to older chipsets,
+both
+Intel and other vendors. (for Intel: ICH4-M and earlier)
+
+This change removes the code to disable/enable bus master arbitration
+during
+suspend/resume. Use of the ARB_DIS bit in the optional PM2_CNT register
+causes
+resume problems on some machines. The change has been in use for over
+seven
+years within Linux.
+
+Implemented two new external interfaces to support host-directed dynamic
+ACPI
+table load and unload. They are intended to simplify the host
+implementation
+of hot-plug support:
+ AcpiLoadTable: Load an SSDT from a buffer into the namespace.
+ AcpiUnloadParentTable: Unload an SSDT via a named object owned by the
+table.
+See the ACPICA reference for additional details. Adds one new file,
+components/tables/tbxfload.c
+
+Implemented and deployed two new interfaces for errors and warnings that
+are
+known to be caused by BIOS/firmware issues:
+ AcpiBiosError: Prints "ACPI Firmware Error" message.
+ AcpiBiosWarning: Prints "ACPI Firmware Warning" message.
+Deployed these new interfaces in the ACPICA Table Manager code for ACPI
+table
+and FADT errors. Additional deployment to be completed as appropriate in
+the
+future. The associated conditional macros are ACPI_BIOS_ERROR and
+ACPI_BIOS_WARNING. See the ACPICA reference for additional details.
+ACPICA
+BZ
+843.
+
+Implicit notify support: ensure that no memory allocation occurs within a
+critical region. This fix moves a memory allocation outside of the time
+that a
+spinlock is held. Fixes issues on systems that do not allow this
+behavior.
+Jung-uk Kim.
+
+Split exception code utilities and tables into a new file,
+utilities/utexcep.c
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.1K Code, 25.1K Data, 118.2K Total
+ Debug Version: 172.9K Code, 73.6K Data, 246.5K Total
+ Current Release:
+ Non-Debug Version: 93.5K Code, 25.3K Data, 118.8K Total
+ Debug Version: 173.7K Code, 74.0K Data, 247.7K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed a parser problem for hosts where EOF is defined as -1 instead
+of
+0. Jung-uk Kim.
+
+Debugger: Enhanced the "tables" command to emit additional information
+about
+the current set of ACPI tables, including the owner ID and flags decode.
+
+Debugger: Reimplemented the "unload" command to use the new
+AcpiUnloadParentTable external interface. This command was disable
+previously
+due to need for an unload interface.
+
+AcpiHelp: Added a new option to decode ACPICA exception codes. The -e
+option
+will decode 16-bit hex status codes (ACPI_STATUS) to name strings.
+
+----------------------------------------
+20 June 2012. Summary of changes for version 20120620:
+
+
+1) ACPICA Kernel-resident Subsystem:
+
+Implemented support to expand the "implicit notify" feature to allow
+multiple
+devices to be notified by a single GPE. This feature automatically
+generates a
+runtime device notification in the absence of a BIOS-provided GPE control
+method (_Lxx/_Exx) or a host-installed handler for the GPE. Implicit
+notify is
+provided by ACPICA for Windows compatibility, and is a workaround for
+BIOS
+AML
+code errors. See the description of the AcpiSetupGpeForWake interface in
+the
+APCICA reference. Bob Moore, Rafael Wysocki. ACPICA BZ 918.
+
+Changed some comments and internal function names to simplify and ensure
+correctness of the Linux code translation. No functional changes.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.0K Code, 25.1K Data, 118.1K Total
+ Debug Version: 172.7K Code, 73.6K Data, 246.3K Total
+ Current Release:
+ Non-Debug Version: 93.1K Code, 25.1K Data, 118.2K Total
+ Debug Version: 172.9K Code, 73.6K Data, 246.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Added support to emit short, commented descriptions for the
+ACPI
+predefined names in order to improve the readability of the disassembled
+output. ACPICA BZ 959. Changes include:
+ 1) Emit descriptions for all standard predefined names (_INI, _STA,
+_PRW,
+etc.)
+ 2) Emit generic descriptions for the special names (_Exx, _Qxx, etc.)
+ 3) Emit descriptions for the resource descriptor names (_MIN, _LEN,
+etc.)
+
+AcpiSrc: Fixed several long-standing Linux code translation issues.
+Argument
+descriptions in function headers are now translated properly to lower
+case
+and
+underscores. ACPICA BZ 961. Also fixes translation problems such as
+these:
+(old -> new)
+ i_aSL -> iASL
+ 00-7_f -> 00-7F
+ 16_k -> 16K
+ local_fADT -> local_FADT
+ execute_oSI -> execute_OSI
+
+iASL: Fixed a problem where null bytes were inadvertently emitted into
+some
+listing files.
+
+iASL: Added the existing debug options to the standard help screen. There
+are
+no longer two different help screens. ACPICA BZ 957.
+
+AcpiHelp: Fixed some typos in the various predefined name descriptions.
+Also
+expand some of the descriptions where appropriate.
+
+iASL: Fixed the -ot option (display compile times/statistics). Was not
+working
+properly for standard output; only worked for the debug file case.
+
+----------------------------------------
+18 May 2012. Summary of changes for version 20120518:
+
+
+1) ACPICA Core Subsystem:
+
+Added a new OSL interface, AcpiOsWaitEventsComplete. This interface is
+defined
+to block until asynchronous events such as notifies and GPEs have
+completed.
+Within ACPICA, it is only called before a notify or GPE handler is
+removed/uninstalled. It also may be useful for the host OS within related
+drivers such as the Embedded Controller driver. See the ACPICA reference
+for
+additional information. ACPICA BZ 868.
+
+ACPI Tables: Added a new error message for a possible overflow failure
+during
+the conversion of FADT 32-bit legacy register addresses to internal
+common
+64-
+bit GAS structure representation. The GAS has a one-byte "bit length"
+field,
+thus limiting the register length to 255 bits. ACPICA BZ 953.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 92.9K Code, 25.0K Data, 117.9K Total
+ Debug Version: 172.6K Code, 73.4K Data, 246.0K Total
+ Current Release:
+ Non-Debug Version: 93.0K Code, 25.1K Data, 118.1K Total
+ Debug Version: 172.7K Code, 73.6K Data, 246.3K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Added the ACPI 5.0 "PCC" keyword for use in the Register() ASL
+macro.
+This keyword was added late in the ACPI 5.0 release cycle and was not
+implemented until now.
+
+Disassembler: Added support for Operation Region externals. Adds missing
+support for operation regions that are defined in another table, and
+referenced locally via a Field or BankField ASL operator. Now generates
+the
+correct External statement.
+
+Disassembler: Several additional fixes for the External() statement
+generation
+related to some ASL operators. Also, order the External() statements
+alphabetically in the disassembler output. Fixes the External()
+generation
+for
+the Create* field, Alias, and Scope operators:
+ 1) Create* buffer field operators - fix type mismatch warning on
+disassembly
+ 2) Alias - implement missing External support
+ 3) Scope - fix to make sure all necessary externals are emitted.
+
+iASL: Improved pathname support. For include files, merge the prefix
+pathname
+with the file pathname and eliminate unnecessary components. Convert
+backslashes in all pathnames to forward slashes, for readability. Include
+file
+pathname changes affect both #include and Include() type operators.
+
+iASL/DTC/Preprocessor: Gracefully handle early EOF. Handle an EOF at the
+end
+of a valid line by inserting a newline and then returning the EOF during
+the
+next call to GetNextLine. Prevents the line from being ignored due to EOF
+condition.
+
+iASL: Implemented some changes to enhance the IDE support (-vi option.)
+Error
+and Warning messages are now correctly recognized for both the source
+code
+browser and the global error and warning counts.
+
+----------------------------------------
+20 April 2012. Summary of changes for version 20120420:
+
+
+1) ACPICA Core Subsystem:
+
+Implemented support for multiple notify handlers. This change adds
+support
+to
+allow multiple system and device notify handlers on Device, Thermal Zone,
+and
+Processor objects. This can simplify the host OS notification
+implementation.
+Also re-worked and restructured the entire notify support code to
+simplify
+handler installation, handler removal, notify event queuing, and notify
+dispatch to handler(s). Note: there can still only be two global notify
+handlers - one for system notifies and one for device notifies. There are
+no
+changes to the existing handler install/remove interfaces. Lin Ming, Bob
+Moore, Rafael Wysocki.
+
+Fixed a regression in the package repair code where the object reference
+count was calculated incorrectly. Regression was introduced in the commit
+"Support to add Package wrappers".
+
+Fixed a couple possible memory leaks in the AML parser, in the error
+recovery
+path. Jesper Juhl, Lin Ming.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug version of the code includes the debug output trace mechanism and
+has a
+much larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 92.9K Code, 25.0K Data, 117.9K Total
+ Debug Version: 172.5K Code, 73.2K Data, 245.7K Total
+ Current Release:
+ Non-Debug Version: 92.9K Code, 25.0K Data, 117.9K Total
+ Debug Version: 172.6K Code, 73.4K Data, 246.0K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Fixed a problem with the resource descriptor support where the
+length
+of the StartDependentFn and StartDependentFnNoPrio descriptors were not
+included in cumulative descriptor offset, resulting in incorrect values
+for
+resource tags within resource descriptors appearing after a
+StartDependent*
+descriptor. Reported by Petr Vandrovec. ACPICA BZ 949.
+
+iASL and Preprocessor: Implemented full support for the #line directive
+to
+correctly track original source file line numbers through the .i
+preprocessor
+output file - for error and warning messages.
+
+iASL: Expand the allowable byte constants for address space IDs.
+Previously,
+the allowable range was 0x80-0xFF (user-defined spaces), now the range is
+0x0A-0xFF to allow for custom and new IDs without changing the compiler.
+
+iASL: Add option to treat all warnings as errors (-we). ACPICA BZ 948.
+
+iASL: Add option to completely disable the preprocessor (-Pn).
+
+iASL: Now emit all error/warning messages to standard error (stderr) by
+default (instead of the previous stdout).
+
+ASL Test Suite (ASLTS): Reduce iASL warnings due to use of Switch().
+Update
+for resource descriptor offset fix above. Update/cleanup error output
+routines. Enable and send iASL errors/warnings to an error logfile
+(error.txt). Send all other iASL output to a logfile (compiler.txt).
+Fixed
+several extraneous "unrecognized operator" messages.
+
+----------------------------------------
+20 March 2012. Summary of changes for version 20120320:
+
+
+1) ACPICA Core Subsystem:
+
+Enhanced the sleep/wake interfaces to optionally execute the _GTS method
+(Going To Sleep) and the _BFS method (Back From Sleep). Windows
+apparently
+does not execute these methods, and therefore these methods are often
+untested. It has been seen on some systems where the execution of these
+methods causes errors and also prevents the machine from entering S5. It
+is
+therefore suggested that host operating systems do not execute these
+methods
+by default. In the future, perhaps these methods can be optionally
+executed
+based on the age of the system and/or what is the newest version of
+Windows
+that the BIOS asks for via _OSI. Changed interfaces: AcpiEnterSleepState
+and
+AcpileaveSleepStatePrep. See the ACPICA reference and Linux BZ 13041. Lin
+Ming.
+
+Fixed a problem where the length of the local/common FADT was set too
+early.
+The local FADT table length cannot be set to the common length until the
+original length has been examined. There is code that checks the table
+length
+and sets various fields appropriately. This can affect older machines
+with
+early FADT versions. For example, this can cause inadvertent writes to
+the
+CST_CNT register. Julian Anastasov.
+
+Fixed a mapping issue related to a physical table override. Use the
+deferred
+mapping mechanism for tables loaded via the physical override OSL
+interface.
+This allows for early mapping before the virtual memory manager is
+available.
+Thomas Renninger, Bob Moore.
+
+Enhanced the automatic return-object repair code: Repair a common problem
+with
+predefined methods that are defined to return a variable-length Package
+of
+sub-objects. If there is only one sub-object, some BIOS ASL code
+mistakenly
+simply returns the single object instead of a Package with one sub-
+object.
+This new support will repair this error by wrapping a Package object
+around
+the original object, creating the correct and expected Package with one
+sub-
+object. Names that can be repaired in this manner include: _ALR, _CSD,
+_HPX,
+_MLS, _PLD, _PRT, _PSS, _TRT, _TSS, _BCL, _DOD, _FIX, and _Sx. ACPICA BZ
+939.
+
+Changed the exception code returned for invalid ACPI paths passed as
+parameters to external interfaces such as AcpiEvaluateObject. Was
+AE_BAD_PARAMETER, now is the more sensible AE_BAD_PATHNAME.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 93.0K Code, 25.0K Data, 118.0K Total
+ Debug Version: 172.5K Code, 73.2K Data, 245.7K Total
+ Current Release:
+ Non-Debug Version: 92.9K Code, 25.0K Data, 117.9K Total
+ Debug Version: 172.5K Code, 73.2K Data, 245.7K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Added the infrastructure and initial implementation of a integrated
+C-
+like preprocessor. This will simplify BIOS development process by
+eliminating
+the need for a separate preprocessing step during builds. On Windows, it
+also
+eliminates the need to install a separate C compiler. ACPICA BZ 761. Some
+features including full #define() macro support are still under
+development.
+These preprocessor directives are supported:
+ #define
+ #elif
+ #else
+ #endif
+ #error
+ #if
+ #ifdef
+ #ifndef
+ #include
+ #pragma message
+ #undef
+ #warning
+In addition, these new command line options are supported:
+ -D <symbol> Define symbol for preprocessor use
+ -li Create preprocessed output file (*.i)
+ -P Preprocess only and create preprocessor output file (*.i)
+
+Table Compiler: Fixed a problem where the equals operator within an
+expression
+did not work properly.
+
+Updated iASL to use the current versions of Bison/Flex. Updated the
+Windows
+project file to invoke these tools from the standard location. ACPICA BZ
+904.
+Versions supported:
+ Flex for Windows: V2.5.4
+ Bison for Windows: V2.4.1
+
+----------------------------------------
+15 February 2012. Summary of changes for version 20120215:
+
+
+1) ACPICA Core Subsystem:
+
+There have been some major changes to the sleep/wake support code, as
+described below (a - e).
+
+a) The AcpiLeaveSleepState has been split into two interfaces, similar to
+AcpiEnterSleepStatePrep and AcpiEnterSleepState. The new interface is
+AcpiLeaveSleepStatePrep. This allows the host to perform actions between
+the
+time the _BFS method is called and the _WAK method is called. NOTE: all
+hosts
+must update their wake/resume code or else sleep/wake will not work
+properly.
+Rafael Wysocki.
+
+b) In AcpiLeaveSleepState, now enable all runtime GPEs before calling the
+_WAK
+method. Some machines require that the GPEs are enabled before the _WAK
+method
+is executed. Thomas Renninger.
+
+c) In AcpiLeaveSleepState, now always clear the WAK_STS (wake status)
+bit.
+Some BIOS code assumes that WAK_STS will be cleared on resume and use it
+to
+determine whether the system is rebooting or resuming. Matthew Garrett.
+
+d) Move the invocations of _GTS (Going To Sleep) and _BFS (Back From
+Sleep) to
+match the ACPI specification requirement. Rafael Wysocki.
+
+e) Implemented full support for the ACPI 5.0 SleepStatus and SleepControl
+registers within the V5 FADT. This support adds two new files:
+hardware/hwesleep.c implements the support for the new registers. Moved
+all
+sleep/wake external interfaces to hardware/hwxfsleep.c.
+
+
+Added a new OSL interface for ACPI table overrides,
+AcpiOsPhysicalTableOverride. This interface allows the host to override a
+table via a physical address, instead of the logical address required by
+AcpiOsTableOverride. This simplifies the host implementation. Initial
+implementation by Thomas Renninger. The ACPICA implementation creates a
+single
+shared function for table overrides that attempts both a logical and a
+physical override.
+
+Expanded the OSL memory read/write interfaces to 64-bit data
+(AcpiOsReadMemory, AcpiOsWriteMemory.) This enables full 64-bit memory
+transfer support for GAS register structures passed to AcpiRead and
+AcpiWrite.
+
+Implemented the ACPI_REDUCED_HARDWARE option to allow the creation of a
+custom
+build of ACPICA that supports only the ACPI 5.0 reduced hardware (SoC)
+model.
+See the ACPICA reference for details. ACPICA BZ 942. This option removes
+about
+10% of the code and 5% of the static data, and the following hardware
+ACPI
+features become unavailable:
+ PM Event and Control registers
+ SCI interrupt (and handler)
+ Fixed Events
+ General Purpose Events (GPEs)
+ Global Lock
+ ACPI PM timer
+ FACS table (Waking vectors and Global Lock)
+
+Updated the unix tarball directory structure to match the ACPICA git
+source
+tree. This ensures that the generic unix makefiles work properly (in
+generate/unix). Also updated the Linux makefiles to match. ACPICA BZ
+867.
+
+Updated the return value of the _REV predefined method to integer value 5
+to
+reflect ACPI 5.0 support.
+
+Moved the external ACPI PM timer interface prototypes to the public
+acpixf.h
+file where they belong.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 92.8K Code, 24.9K Data, 117.7K Total
+ Debug Version: 171.7K Code, 72.9K Data, 244.5K Total
+ Current Release:
+ Non-Debug Version: 93.0K Code, 25.0K Data, 118.0K Total
+ Debug Version: 172.5K Code, 73.2K Data, 245.7K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: Fixed a problem with the new ACPI 5.0 serial resource
+descriptors (I2C, SPI, UART) where the resource produce/consumer bit was
+incorrectly displayed.
+
+AcpiHelp: Add display of ACPI/PNP device IDs that are defined in the ACPI
+specification.
+
+----------------------------------------
+11 January 2012. Summary of changes for version 20120111:
+
+
+1) ACPICA Core Subsystem:
+
+Implemented a new mechanism to allow host device drivers to check for
+address
+range conflicts with ACPI Operation Regions. Both SystemMemory and
+SystemIO
+address spaces are supported. A new external interface,
+AcpiCheckAddressRange,
+allows drivers to check an address range against the ACPI namespace. See
+the
+ACPICA reference for additional details. Adds one new file,
+utilities/utaddress.c. Lin Ming, Bob Moore.
+
+Fixed several issues with the ACPI 5.0 FADT support: Add the sleep
+Control
+and
+Status registers, update the ACPI 5.0 flags, and update internal data
+structures to handle an FADT larger than 256 bytes. The size of the ACPI
+5.0
+FADT is 268 bytes.
+
+Updated all ACPICA copyrights and signons to 2012. Added the 2012
+copyright to
+all module headers and signons, including the standard Linux header. This
+affects virtually every file in the ACPICA core subsystem, iASL compiler,
+and
+all ACPICA utilities.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release:
+ Non-Debug Version: 92.3K Code, 24.9K Data, 117.2K Total
+ Debug Version: 170.8K Code, 72.6K Data, 243.4K Total
+ Current Release:
+ Non-Debug Version: 92.8K Code, 24.9K Data, 117.7K Total
+ Debug Version: 171.7K Code, 72.9K Data, 244.5K Total
+
+
+2) iASL Compiler/Disassembler and Tools:
+
+Disassembler: fixed a problem with the automatic resource tag generation
+support. Fixes a problem where the resource tags are inadvertently not
+constructed if the table being disassembled contains external references
+to
+control methods. Moved the actual construction of the tags to after the
+final
+namespace is constructed (after 2nd parse is invoked due to external
+control
+method references.) ACPICA BZ 941.
+
+Table Compiler: Make all "generic" operators caseless. These are the
+operators
+like UINT8, String, etc. Making these caseless improves ease-of-use.
+ACPICA BZ
+934.
+
+----------------------------------------
+23 November 2011. Summary of changes for version 20111123:
+
+0) ACPI 5.0 Support:
+
+This release contains full support for the ACPI 5.0 specification, as
+summarized below.
+
+Reduced Hardware Support:
+-------------------------
+
+This support allows for ACPI systems without the usual ACPI hardware.
+This
+support is enabled by a flag in the revision 5 FADT. If it is set, ACPICA
+will
+not attempt to initialize or use any of the usual ACPI hardware. Note,
+when
+this flag is set, all of the following ACPI hardware is assumed to be not
+present and is not initialized or accessed:
+
+ General Purpose Events (GPEs)
+ Fixed Events (PM1a/PM1b and PM Control)
+ Power Management Timer and Console Buttons (power/sleep)
+ Real-time Clock Alarm
+ Global Lock
+ System Control Interrupt (SCI)
+ The FACS is assumed to be non-existent
+
+ACPI Tables:
+------------
+
+All new tables and updates to existing tables are fully supported in the
+ACPICA headers (for use by device drivers), the disassembler, and the
+iASL
+Data Table Compiler. ACPI 5.0 defines these new tables:
+
+ BGRT /* Boot Graphics Resource Table */
+ DRTM /* Dynamic Root of Trust for Measurement table */
+ FPDT /* Firmware Performance Data Table */
+ GTDT /* Generic Timer Description Table */
+ MPST /* Memory Power State Table */
+ PCCT /* Platform Communications Channel Table */
+ PMTT /* Platform Memory Topology Table */
+ RASF /* RAS Feature table */
+
+Operation Regions/SpaceIDs:
+---------------------------
+
+All new operation regions are fully supported by the iASL compiler, the
+disassembler, and the ACPICA runtime code (for dispatch to region
+handlers.)
+The new operation region Space IDs are:
+
+ GeneralPurposeIo
+ GenericSerialBus
+
+Resource Descriptors:
+---------------------
+
+All new ASL resource descriptors are fully supported by the iASL
+compiler,
+the
+ASL/AML disassembler, and the ACPICA runtime Resource Manager code
+(including
+all new predefined resource tags). New descriptors are:
+
+ FixedDma
+ GpioIo
+ GpioInt
+ I2cSerialBus
+ SpiSerialBus
+ UartSerialBus
+
+ASL/AML Operators, New and Modified:
+------------------------------------
+
+One new operator is added, the Connection operator, which is used to
+associate
+a GeneralPurposeIo or GenericSerialBus resource descriptor with
+individual
+field objects within an operation region. Several new protocols are
+associated
+with the AccessAs operator. All are fully supported by the iASL compiler,
+disassembler, and runtime ACPICA AML interpreter:
+
+ Connection // Declare Field Connection
+attributes
+ AccessAs: AttribBytes (n) // Read/Write N-Bytes Protocol
+ AccessAs: AttribRawBytes (n) // Raw Read/Write N-Bytes
+Protocol
+ AccessAs: AttribRawProcessBytes (n) // Raw Process Call Protocol
+ RawDataBuffer // Data type for Vendor Data
+fields
+
+Predefined ASL/AML Objects:
+---------------------------
+
+All new predefined objects/control-methods are supported by the iASL
+compiler
+and the ACPICA runtime validation/repair (arguments and return values.)
+New
+predefined names include the following:
+
+Standard Predefined Names (Objects or Control Methods):
+ _AEI, _CLS, _CPC, _CWS, _DEP,
+ _DLM, _EVT, _GCP, _CRT, _GWS,
+ _HRV, _PRE, _PSE, _SRT, _SUB.
+
+Resource Tags (Names used to access individual fields within resource
+descriptors):
+ _DBT, _DPL, _DRS, _END, _FLC,
+ _IOR, _LIN, _MOD, _PAR, _PHA,
+ _PIN, _PPI, _POL, _RXL, _SLV,
+ _SPE, _STB, _TXL, _VEN.
+
+ACPICA External Interfaces:
+---------------------------
+
+Several new interfaces have been defined for use by ACPI-related device
+drivers and other host OS services:
+
+AcpiAcquireMutex and AcpiReleaseMutex: These interfaces allow the host OS
+to
+acquire and release AML mutexes that are defined in the DSDT/SSDT tables
+provided by the BIOS. They are intended to be used in conjunction with
+the
+ACPI 5.0 _DLM (Device Lock Method) in order to provide transaction-level
+mutual exclusion with the AML code/interpreter.
+
+AcpiGetEventResources: Returns the (formatted) resource descriptors as
+defined
+by the ACPI 5.0 _AEI object (ACPI Event Information). This object
+provides
+resource descriptors associated with hardware-reduced platform events,
+similar
+to the AcpiGetCurrentResources interface.
+
+Operation Region Handlers: For General Purpose IO and Generic Serial Bus
+operation regions, information about the Connection() object and any
+optional
+length information is passed to the region handler within the Context
+parameter.
+
+AcpiBufferToResource: This interface converts a raw AML buffer containing
+a
+resource template or resource descriptor to the ACPI_RESOURCE internal
+format
+suitable for use by device drivers. Can be used by an operation region
+handler
+to convert the Connection() buffer object into a ACPI_RESOURCE.
+
+Miscellaneous/Tools/TestSuites:
+-------------------------------
+
+Support for extended _HID names (Four alpha characters instead of three).
+Support for ACPI 5.0 features in the AcpiExec and AcpiHelp utilities.
+Support for ACPI 5.0 features in the ASLTS test suite.
+Fully updated documentation (ACPICA and iASL reference documents.)
+
+ACPI Table Definition Language:
+-------------------------------
+
+Support for this language was implemented and released as a subsystem of
+the
+iASL compiler in 2010. (See the iASL compiler User Guide.)
+
+
+Non-ACPI 5.0 changes for this release:
+--------------------------------------
+
+1) ACPICA Core Subsystem:
+
+Fix a problem with operation region declarations where a failure can
+occur
+if
+the region name and an argument that evaluates to an object (such as the
+region address) are in different namespace scopes. Lin Ming, ACPICA BZ
+937.
+
+Do not abort an ACPI table load if an invalid space ID is found within.
+This
+will be caught later if the offending method is executed. ACPICA BZ 925.
+
+Fixed an issue with the FFixedHW space ID where the ID was not always
+recognized properly (Both ACPICA and iASL). ACPICA BZ 926.
+
+Fixed a problem with the 32-bit generation of the unix-specific OSL
+(osunixxf.c). Lin Ming, ACPICA BZ 936.
+
+Several changes made to enable generation with the GCC 4.6 compiler.
+ACPICA BZ
+935.
+
+New error messages: Unsupported I/O requests (not 8/16/32 bit), and
+Index/Bank
+field registers out-of-range.
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented the __PATH__ operator, which returns the full pathname
+of
+the current source file.
+
+AcpiHelp: Automatically display expanded keyword information for all ASL
+operators.
+
+Debugger: Add "Template" command to disassemble/dump resource template
+buffers.
+
+Added a new master script to generate and execute the ASLTS test suite.
+Automatically handles 32- and 64-bit generation. See tests/aslts.sh
+
+iASL: Fix problem with listing generation during processing of the
+Switch()
+operator where AML listing was disabled until the entire Switch block was
+completed.
+
+iASL: Improve support for semicolon statement terminators. Fix "invalid
+character" message for some cases when the semicolon is used. Semicolons
+are
+now allowed after every <Term> grammar element. ACPICA BZ 927.
+
+iASL: Fixed some possible aliasing warnings during generation. ACPICA BZ
+923.
+
+Disassembler: Fix problem with disassembly of the DataTableRegion
+operator
+where an inadvertent "Unhandled deferred opcode" message could be
+generated.
+
+3) Example Code and Data Size
+
+These are the sizes for the OS-independent acpica.lib produced by the
+Microsoft Visual C++ 9.0 32-bit compiler. The debug version of the code
+includes the debug output trace mechanism and has a much larger code and
+data
+size.
+
+ Previous Release:
+ Non-Debug Version: 90.2K Code, 23.9K Data, 114.1K Total
+ Debug Version: 165.6K Code, 68.4K Data, 234.0K Total
+ Current Release:
+ Non-Debug Version: 92.3K Code, 24.9K Data, 117.2K Total
+ Debug Version: 170.8K Code, 72.6K Data, 243.4K Total
+
+----------------------------------------
+22 September 2011. Summary of changes for version 20110922:
+
+0) ACPI 5.0 News:
+
+Support for ACPI 5.0 in ACPICA has been underway for several months and
+will
+be released at the same time that ACPI 5.0 is officially released.
+
+The ACPI 5.0 specification is on track for release in the next few
+months.
+
+1) ACPICA Core Subsystem:
+
+Fixed a problem where the maximum sleep time for the Sleep() operator was
+intended to be limited to two seconds, but was inadvertently limited to
+20
+seconds instead.
+
+Linux and Unix makefiles: Added header file dependencies to ensure
+correct
+generation of ACPICA core code and utilities. Also simplified the
+makefiles
+considerably through the use of the vpath variable to specify search
+paths.
+ACPICA BZ 924.
+
+2) iASL Compiler/Disassembler and Tools:
+
+iASL: Implemented support to check the access length for all fields
+created to
+access named Resource Descriptor fields. For example, if a resource field
+is
+defined to be two bits, a warning is issued if a CreateXxxxField() is
+used
+with an incorrect bit length. This is implemented for all current
+resource
+descriptor names. ACPICA BZ 930.
+
+Disassembler: Fixed a byte ordering problem with the output of 24-bit and
+56-
+bit integers.
+
+iASL: Fixed a couple of issues associated with variable-length package
+objects. 1) properly handle constants like One, Ones, Zero -- do not make
+a
+VAR_PACKAGE when these are used as a package length. 2) Allow the
+VAR_PACKAGE
+opcode (in addition to PACKAGE) when validating object types for
+predefined
+names.
+
+iASL: Emit statistics for all output files (instead of just the ASL input
+and
+AML output). Includes listings, hex files, etc.
+
+iASL: Added -G option to the table compiler to allow the compilation of
+custom
+ACPI tables. The only part of a table that is required is the standard
+36-
+byte
+ACPI header.
+
+AcpiXtract: Ported to the standard ACPICA environment (with ACPICA
+headers),
+which also adds correct 64-bit support. Also, now all output filenames
+are
+completely lower case.
+
+AcpiExec: Ignore any non-AML tables (tables other than DSDT or SSDT) when
+loading table files. A warning is issued for any such tables. The only
+exception is an FADT. This also fixes a possible fault when attempting to
+load
+non-AML tables. ACPICA BZ 932.
+
+AcpiHelp: Added the AccessAs and Offset operators. Fixed a problem where
+a
+missing table terminator could cause a fault when using the -p option.
+
+AcpiSrc: Fixed a possible divide-by-zero fault when generating file
+statistics.
+
+3) Example Code and Data Size
+
+These are the sizes for the OS-independent acpica.lib produced by the
+Microsoft Visual C++ 9.0 32-bit compiler. The debug version of the code
+includes the debug output trace mechanism and has a much larger code and
+data
+size.
+
+ Previous Release (VC 9.0):
+ Non-Debug Version: 90.2K Code, 23.9K Data, 114.1K Total
+ Debug Version: 165.6K Code, 68.4K Data, 234.0K Total
+ Current Release (VC 9.0):
+ Non-Debug Version: 90.2K Code, 23.9K Data, 114.1K Total
+ Debug Version: 165.6K Code, 68.4K Data, 234.0K Total
+
+
+----------------------------------------
+23 June 2011. Summary of changes for version 20110623:
1) ACPI CA Core Subsystem:
-ASL Load() operator: Reinstate most restrictions on the incoming ACPI table
+Updated the predefined name repair mechanism to not attempt repair of a
+_TSS
+return object if a _PSS object is present. We can only sort the _TSS
+return
+package if there is no _PSS within the same scope. This is because if
+_PSS
+is
+present, the ACPI specification dictates that the _TSS Power Dissipation
+field
+is to be ignored, and therefore some BIOSs leave garbage values in the
+_TSS
+Power field(s). In this case, it is best to just return the _TSS package
+as-
+is. Reported by, and fixed with assistance from Fenghua Yu.
+
+Added an option to globally disable the control method return value
+validation
+and repair. This runtime option can be used to disable return value
+repair
+if
+this is causing a problem on a particular machine. Also added an option
+to
+AcpiExec (-dr) to set this disable flag.
+
+All makefiles and project files: Major changes to improve generation of
+ACPICA
+tools. ACPICA BZ 912:
+ Reduce default optimization levels to improve compatibility
+ For Linux, add strict-aliasing=0 for gcc 4
+ Cleanup and simplify use of command line defines
+ Cleanup multithread library support
+ Improve usage messages
+
+Linux-specific header: update handling of THREAD_ID and pthread. For the
+32-
+bit case, improve casting to eliminate possible warnings, especially with
+the
+acpica tools.
+
+Example Code and Data Size: These are the sizes for the OS-independent
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
+larger code and data size.
+
+ Previous Release (VC 9.0):
+ Non-Debug Version: 90.1K Code, 23.9K Data, 114.0K Total
+ Debug Version: 165.6K Code, 68.4K Data, 234.0K Total
+ Current Release (VC 9.0):
+ Non-Debug Version: 90.2K Code, 23.9K Data, 114.1K Total
+ Debug Version: 165.6K Code, 68.4K Data, 234.0K Total
+
+2) iASL Compiler/Disassembler and Tools:
+
+With this release, a new utility named "acpihelp" has been added to the
+ACPICA
+package. This utility summarizes the ACPI specification chapters for the
+ASL
+and AML languages. It generates under Linux/Unix as well as Windows, and
+provides the following functionality:
+ Find/display ASL operator(s) -- with description and syntax.
+ Find/display ASL keyword(s) -- with exact spelling and descriptions.
+ Find/display ACPI predefined name(s) -- with description, number
+ of arguments, and the return value data type.
+ Find/display AML opcode name(s) -- with opcode, arguments, and
+grammar.
+ Decode/display AML opcode -- with opcode name, arguments, and
+grammar.
+
+Service Layers: Make multi-thread support configurable. Conditionally
+compile
+the multi-thread support so that threading libraries will not be linked
+if
+not
+necessary. The only tool that requires multi-thread support is AcpiExec.
+
+iASL: Update yyerrror/AslCompilerError for "const" errors. Newer versions
+of
+Bison appear to want the interface to yyerror to be a const char * (or at
+least this is a problem when generating iASL on some systems.) ACPICA BZ
+923
+Pierre Lejeune.
+
+Tools: Fix for systems where O_BINARY is not defined. Only used for
+Windows
+versions of the tools.
+
+----------------------------------------
+27 May 2011. Summary of changes for version 20110527:
+
+1) ACPI CA Core Subsystem:
+
+ASL Load() operator: Reinstate most restrictions on the incoming ACPI
+table
signature. Now, only allow SSDT, OEMx, and a null signature. History:
1) Originally, we checked the table signature for "SSDT" or "PSDT".
(PSDT is now obsolete.)
@@ -17,12 +4414,16 @@ signature. Now, only allow SSDT, OEMx, and a null signature. History:
interpreter errors and kernel faults. So now, we once again allow
only SSDT, OEMx, and now, also a null signature. (05/2011).
-Added the missing _TDL predefined name to the global name list in order to
-enable validation. Affects both the core ACPICA code and the iASL compiler.
+Added the missing _TDL predefined name to the global name list in order
+to
+enable validation. Affects both the core ACPICA code and the iASL
+compiler.
Example Code and Data Size: These are the sizes for the OS-independent
-acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The debug
-version of the code includes the debug output trace mechanism and has a much
+acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
+debug
+version of the code includes the debug output trace mechanism and has a
+much
larger code and data size.
Previous Release (VC 9.0):
@@ -34,20 +4435,28 @@ larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Debugger/AcpiExec: Implemented support for "complex" method arguments on the
-debugger command line. This adds support beyond simple integers -- including
+Debugger/AcpiExec: Implemented support for "complex" method arguments on
+the
+debugger command line. This adds support beyond simple integers --
+including
Strings, Buffers, and Packages. Includes support for nested packages.
-Increased the default command line buffer size to accommodate these arguments.
+Increased the default command line buffer size to accommodate these
+arguments.
See the ACPICA reference for details and syntax. ACPICA BZ 917.
-Debugger/AcpiExec: Implemented support for "default" method arguments for the
-Execute/Debug command. Now, the debugger will always invoke a control method
-with the required number of arguments -- even if the command line specifies
-none or insufficient arguments. It uses default integer values for any missing
+Debugger/AcpiExec: Implemented support for "default" method arguments for
+the
+Execute/Debug command. Now, the debugger will always invoke a control
+method
+with the required number of arguments -- even if the command line
+specifies
+none or insufficient arguments. It uses default integer values for any
+missing
arguments. Also fixes a bug where only six method arguments maximum were
supported instead of the required seven.
-Debugger/AcpiExec: Add a maximum buffer length parameter to AcpiOsGetLine and
+Debugger/AcpiExec: Add a maximum buffer length parameter to AcpiOsGetLine
+and
also return status in order to prevent buffer overruns. See the ACPICA
reference for details and syntax. ACPICA BZ 921
@@ -55,7 +4464,8 @@ iASL: Cleaned up support for Berkeley yacc. A general cleanup of code and
makefiles to simplify support for the two different but similar parser
generators, bison and yacc.
-Updated the generic unix makefile for gcc 4. The default gcc version is now
+Updated the generic unix makefile for gcc 4. The default gcc version is
+now
expected to be 4 or greater, since options specific to gcc 4 are used.
----------------------------------------
@@ -63,36 +4473,47 @@ expected to be 4 or greater, since options specific to gcc 4 are used.
1) ACPI CA Core Subsystem:
-Implemented support to execute a so-called "orphan" _REG method under the EC
-device. This change will force the execution of a _REG method underneath the
+Implemented support to execute a so-called "orphan" _REG method under the
+EC
+device. This change will force the execution of a _REG method underneath
+the
EC
device even if there is no corresponding operation region of type
EmbeddedControl. Fixes a problem seen on some machines and apparently is
compatible with Windows behavior. ACPICA BZ 875.
-Added more predefined methods that are eligible for automatic NULL package
-element removal. This change adds another group of predefined names to the
+Added more predefined methods that are eligible for automatic NULL
+package
+element removal. This change adds another group of predefined names to
+the
list
of names that can be repaired by having NULL package elements dynamically
-removed. This group are those methods that return a single variable-length
-package containing simple data types such as integers, buffers, strings. This
-includes: _ALx, _BCL, _CID,_ DOD, _EDL, _FIX, _PCL, _PLD, _PMD, _PRx, _PSL,
+removed. This group are those methods that return a single variable-
+length
+package containing simple data types such as integers, buffers, strings.
+This
+includes: _ALx, _BCL, _CID,_ DOD, _EDL, _FIX, _PCL, _PLD, _PMD, _PRx,
+_PSL,
_Sx,
and _TZD. ACPICA BZ 914.
Split and segregated all internal global lock functions to a new file,
evglock.c.
-Updated internal address SpaceID for DataTable regions. Moved this internal
+Updated internal address SpaceID for DataTable regions. Moved this
+internal
space
-id in preparation for ACPI 5.0 changes that will include some new space IDs.
+id in preparation for ACPI 5.0 changes that will include some new space
+IDs.
This
change should not affect user/host code.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib
-produced by the Microsoft Visual C++ 9.0 32-bit compiler. The debug version of
-the code includes the debug output trace mechanism and has a much larger code
+produced by the Microsoft Visual C++ 9.0 32-bit compiler. The debug
+version of
+the code includes the debug output trace mechanism and has a much larger
+code
and
data size.
@@ -105,51 +4526,71 @@ data size.
2) iASL Compiler/Disassembler and Tools:
-iASL/DTC: Major update for new grammar features. Allow generic data types in
-custom ACPI tables. Field names are now optional. Any line can be split to
-multiple lines using the continuation char (\). Large buffers now use line-
+iASL/DTC: Major update for new grammar features. Allow generic data types
+in
+custom ACPI tables. Field names are now optional. Any line can be split
+to
+multiple lines using the continuation char (\). Large buffers now use
+line-
continuation character(s) and no colon on the continuation lines. See the
grammar
-update in the iASL compiler reference. ACPI BZ 910,911. Lin Ming, Bob Moore.
+update in the iASL compiler reference. ACPI BZ 910,911. Lin Ming, Bob
+Moore.
-iASL: Mark ASL "Return()" and the simple "Return" as "Null" return statements.
-Since the parser stuffs a "zero" as the return value for these statements (due
+iASL: Mark ASL "Return()" and the simple "Return" as "Null" return
+statements.
+Since the parser stuffs a "zero" as the return value for these statements
+(due
to
-the underlying AML grammar), they were seen as "return with value" by the iASL
-semantic checking. They are now seen correctly as "null" return statements.
-
-iASL: Check if a_REG declaration has a corresponding Operation Region. Adds a
-check for each _REG to ensure that there is in fact a corresponding operation
+the underlying AML grammar), they were seen as "return with value" by the
+iASL
+semantic checking. They are now seen correctly as "null" return
+statements.
+
+iASL: Check if a_REG declaration has a corresponding Operation Region.
+Adds a
+check for each _REG to ensure that there is in fact a corresponding
+operation
region declaration in the same scope. If not, the _REG method is not very
useful
since it probably won't be executed. ACPICA BZ 915.
-iASL/DTC: Finish support for expression evaluation. Added a new expression
+iASL/DTC: Finish support for expression evaluation. Added a new
+expression
parser
that implements c-style operator precedence and parenthesization. ACPICA
bugzilla
908.
-Disassembler/DTC: Remove support for () and <> style comments in data tables.
+Disassembler/DTC: Remove support for () and <> style comments in data
+tables.
Now
-that DTC has full expression support, we don't want to have comment strings
+that DTC has full expression support, we don't want to have comment
+strings
that
-start with a parentheses or a less-than symbol. Now, only the standard /* and
+start with a parentheses or a less-than symbol. Now, only the standard /*
+and
//
comments are supported, as well as the bracket [] comments.
AcpiXtract: Fix for RSDP and dynamic SSDT extraction. These tables have
"unusual"
-headers in the acpidump file. Update the header validation to support these
-tables. Problem introduced in previous AcpiXtract version in the change to
+headers in the acpidump file. Update the header validation to support
+these
+tables. Problem introduced in previous AcpiXtract version in the change
+to
support "wrong checksum" error messages emitted by acpidump utility.
-iASL: Add a * option to generate all template files (as a synonym for ALL) as
+iASL: Add a * option to generate all template files (as a synonym for
+ALL)
+as
in
"iasl -T *" or "iasl -T ALL".
-iASL/DTC: Do not abort compiler on fatal errors. We do not want to completely
-abort the compiler on "fatal" errors, simply should abort the current compile.
+iASL/DTC: Do not abort compiler on fatal errors. We do not want to
+completely
+abort the compiler on "fatal" errors, simply should abort the current
+compile.
This allows multiple compiles with a single (possibly wildcard) compiler
invocation.
@@ -158,20 +4599,28 @@ invocation.
1) ACPI CA Core Subsystem:
-Fixed a problem caused by a _PRW method appearing at the namespace root scope
-during the setup of wake GPEs. A fault could occur if a _PRW directly under
+Fixed a problem caused by a _PRW method appearing at the namespace root
+scope
+during the setup of wake GPEs. A fault could occur if a _PRW directly
+under
the
root object was passed to the AcpiSetupGpeForWake interface. Lin Ming.
-Implemented support for "spurious" Global Lock interrupts. On some systems, a
-global lock interrupt can occur without the pending flag being set. Upon a GL
-interrupt, we now ensure that a thread is actually waiting for the lock before
+Implemented support for "spurious" Global Lock interrupts. On some
+systems, a
+global lock interrupt can occur without the pending flag being set. Upon
+a
+GL
+interrupt, we now ensure that a thread is actually waiting for the lock
+before
signaling GL availability. Rafael Wysocki, Bob Moore.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib
-produced by the Microsoft Visual C++ 9.0 32-bit compiler. The debug version of
-the code includes the debug output trace mechanism and has a much larger code
+produced by the Microsoft Visual C++ 9.0 32-bit compiler. The debug
+version of
+the code includes the debug output trace mechanism and has a much larger
+code
and
data size.
@@ -184,51 +4633,67 @@ data size.
2) iASL Compiler/Disassembler and Tools:
-Implemented full support for the "SLIC" ACPI table. Includes support in the
-header files, disassembler, table compiler, and template generator. Bob Moore,
+Implemented full support for the "SLIC" ACPI table. Includes support in
+the
+header files, disassembler, table compiler, and template generator. Bob
+Moore,
Lin Ming.
-AcpiXtract: Correctly handle embedded comments and messages from AcpiDump.
-Apparently some or all versions of acpidump will occasionally emit a comment
+AcpiXtract: Correctly handle embedded comments and messages from
+AcpiDump.
+Apparently some or all versions of acpidump will occasionally emit a
+comment
like
"Wrong checksum", etc., into the dump file. This was causing problems for
AcpiXtract. ACPICA BZ 905.
-iASL: Fix the Linux makefile by removing an inadvertent double file inclusion.
+iASL: Fix the Linux makefile by removing an inadvertent double file
+inclusion.
ACPICA BZ 913.
AcpiExec: Update installation of operation region handlers. Install one
handler
-for a user-defined address space. This is used by the ASL test suite (ASLTS).
+for a user-defined address space. This is used by the ASL test suite
+(ASLTS).
----------------------------------------
11 February 2011. Summary of changes for version 20110211:
1) ACPI CA Core Subsystem:
-Added a mechanism to defer _REG methods for some early-installed handlers.
-Most user handlers should be installed before call to AcpiEnableSubsystem.
+Added a mechanism to defer _REG methods for some early-installed
+handlers.
+Most user handlers should be installed before call to
+AcpiEnableSubsystem.
However, Event handlers and region handlers should be installed after
-AcpiInitializeObjects. Override handlers for the "default" regions should be
+AcpiInitializeObjects. Override handlers for the "default" regions should
+be
installed early, however. This change executes all _REG methods for the
default regions (Memory/IO/PCI/DataTable) simultaneously to prevent any
chicken/egg issues between them. ACPICA BZ 848.
-Implemented an optimization for GPE detection. This optimization will simply
+Implemented an optimization for GPE detection. This optimization will
+simply
ignore GPE registers that contain no enabled GPEs -- there is no need to
read the register since this information is available internally. This
-becomes more important on machines with a large GPE space. ACPICA bugzilla
+becomes more important on machines with a large GPE space. ACPICA
+bugzilla
884. Lin Ming. Suggestion from Joe Liu.
-Removed all use of the highly unreliable FADT revision field. The revision
-number in the FADT has been found to be completely unreliable and cannot be
-trusted. Only the actual table length can be used to infer the version. This
-change updates the ACPICA core and the disassembler so that both no longer
+Removed all use of the highly unreliable FADT revision field. The
+revision
+number in the FADT has been found to be completely unreliable and cannot
+be
+trusted. Only the actual table length can be used to infer the version.
+This
+change updates the ACPICA core and the disassembler so that both no
+longer
even look at the FADT version and instead depend solely upon the FADT
length.
Fix an unresolved name issue for the no-debug and no-error-message source
-generation cases. The _AcpiModuleName was left undefined in these cases, but
+generation cases. The _AcpiModuleName was left undefined in these cases,
+but
it is actually needed as a parameter to some interfaces. Define
_AcpiModuleName as a null string in these cases. ACPICA Bugzilla 888.
@@ -241,7 +4706,8 @@ Split several large files (makefiles and project files updated)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release (VC 9.0):
@@ -257,32 +4723,42 @@ iASL: Implemented the predefined macros __LINE__, __FILE__, and __DATE__.
These are useful C-style macros with the standard definitions. ACPICA
bugzilla 898.
-iASL/DTC: Added support for integer expressions and labels. Support for full
-expressions for all integer fields in all ACPI tables. Support for labels in
+iASL/DTC: Added support for integer expressions and labels. Support for
+full
+expressions for all integer fields in all ACPI tables. Support for labels
+in
"generic" portions of tables such as UEFI. See the iASL reference manual.
Debugger: Added a command to display the status of global handlers. The
"handlers" command will display op region, fixed event, and miscellaneous
-global handlers. installation status -- and for op regions, whether default
+global handlers. installation status -- and for op regions, whether
+default
or user-installed handler will be used.
-iASL: Warn if reserved method incorrectly returns a value. Many predefined
-names are defined such that they do not return a value. If implemented as a
+iASL: Warn if reserved method incorrectly returns a value. Many
+predefined
+names are defined such that they do not return a value. If implemented as
+a
method, issue a warning if such a name explicitly returns a value. ACPICA
Bugzilla 855.
-iASL: Added detection of GPE method name conflicts. Detects a conflict where
-there are two GPE methods of the form _Lxy and _Exy in the same scope. (For
+iASL: Added detection of GPE method name conflicts. Detects a conflict
+where
+there are two GPE methods of the form _Lxy and _Exy in the same scope.
+(For
example, _L1D and _E1D in the same scope.) ACPICA bugzilla 848.
iASL/DTC: Fixed a couple input scanner issues with comments and line
-numbers. Comment remover could get confused and miss a comment ending. Fixed
+numbers. Comment remover could get confused and miss a comment ending.
+Fixed
a problem with line counter maintenance.
-iASL/DTC: Reduced the severity of some errors from fatal to error. There is
+iASL/DTC: Reduced the severity of some errors from fatal to error. There
+is
no need to abort on simple errors within a field definition.
-Debugger: Simplified the output of the help command. All help output now in
+Debugger: Simplified the output of the help command. All help output now
+in
a single screen, instead of help subcommands. ACPICA Bugzilla 897.
----------------------------------------
@@ -290,18 +4766,24 @@ a single screen, instead of help subcommands. ACPICA Bugzilla 897.
1) ACPI CA Core Subsystem:
-Fixed a race condition between method execution and namespace walks that can
+Fixed a race condition between method execution and namespace walks that
+can
possibly cause a fault. The problem was apparently introduced in version
-20100528 as a result of a performance optimization that reduces the number of
+20100528 as a result of a performance optimization that reduces the
+number
+of
namespace walks upon method exit by using the delete_namespace_subtree
-function instead of the delete_namespace_by_owner function used previously.
+function instead of the delete_namespace_by_owner function used
+previously.
Bug is a missing namespace lock in the delete_namespace_subtree function.
dana.myers@oracle.com
Fixed several issues and a possible fault with the automatic "serialized"
-method support. History: This support changes a method to "serialized" on the
+method support. History: This support changes a method to "serialized" on
+the
fly if the method generates an AE_ALREADY_EXISTS error, indicating the
-possibility that it cannot handle reentrancy. This fix repairs a couple of
+possibility that it cannot handle reentrancy. This fix repairs a couple
+of
issues seen in the field, especially on machines with many cores:
1) Delete method children only upon the exit of the last thread,
@@ -315,29 +4797,36 @@ issues seen in the field, especially on machines with many cores:
Lin Ming, Bob Moore. Reported by dana.myers@oracle.com.
-Debugger: Now lock the namespace for duration of a namespace dump. Prevents
+Debugger: Now lock the namespace for duration of a namespace dump.
+Prevents
issues if the namespace is changing dynamically underneath the debugger.
Especially affects temporary namespace nodes, since the debugger displays
these also.
Updated the ordering of include files. The ACPICA headers should appear
-before any compiler-specific headers (stdio.h, etc.) so that acenv.h can set
-any necessary compiler-specific defines, etc. Affects the ACPI-related tools
+before any compiler-specific headers (stdio.h, etc.) so that acenv.h can
+set
+any necessary compiler-specific defines, etc. Affects the ACPI-related
+tools
and utilities.
-Updated all ACPICA copyrights and signons to 2011. Added the 2011 copyright
-to all module headers and signons, including the Linux header. This affects
+Updated all ACPICA copyrights and signons to 2011. Added the 2011
+copyright
+to all module headers and signons, including the Linux header. This
+affects
virtually every file in the ACPICA core subsystem, iASL compiler, and all
utilities.
Added project files for MS Visual Studio 2008 (VC++ 9.0). The original
-project files for VC++ 6.0 are now obsolete. New project files can be found
+project files for VC++ 6.0 are now obsolete. New project files can be
+found
under acpica/generate/msvc9. See acpica/generate/msvc9/readme.txt for
details.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 9.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release (VC 6.0):
@@ -349,11 +4838,14 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Added generic data types to the Data Table compiler. Add "generic" data
-types such as UINT32, String, Unicode, etc., to simplify the generation of
+iASL: Added generic data types to the Data Table compiler. Add "generic"
+data
+types such as UINT32, String, Unicode, etc., to simplify the generation
+of
platform-defined tables such as UEFI. Lin Ming.
-iASL: Added listing support for the Data Table Compiler. Adds listing support
+iASL: Added listing support for the Data Table Compiler. Adds listing
+support
(-l) to display actual binary output for each line of input code.
----------------------------------------
@@ -361,10 +4853,12 @@ iASL: Added listing support for the Data Table Compiler. Adds listing support
1) ACPI CA Core Subsystem:
-Completed the major overhaul of the GPE support code that was begun in July
+Completed the major overhaul of the GPE support code that was begun in
+July
2010. Major features include: removal of _PRW execution in ACPICA (host
executes _PRWs anyway), cleanup of "wake" GPE interfaces and processing,
-changes to existing interfaces, simplification of GPE handler operation, and
+changes to existing interfaces, simplification of GPE handler operation,
+and
a handful of new interfaces:
AcpiUpdateAllGpes
@@ -374,24 +4868,33 @@ a handful of new interfaces:
One new file, evxfgpe.c to consolidate all external GPE interfaces.
See the ACPICA Programmer Reference for full details and programming
-information. See the new section 4.4 "General Purpose Event (GPE) Support"
-for a full overview, and section 8.7 "ACPI General Purpose Event Management"
-for programming details. ACPICA BZ 858,870,877. Matthew Garrett, Lin Ming,
+information. See the new section 4.4 "General Purpose Event (GPE)
+Support"
+for a full overview, and section 8.7 "ACPI General Purpose Event
+Management"
+for programming details. ACPICA BZ 858,870,877. Matthew Garrett, Lin
+Ming,
Bob Moore, Rafael Wysocki.
-Implemented a new GPE feature for Windows compatibility, the "Implicit Wake
-GPE Notify". This feature will automatically issue a Notify(2) on a device
+Implemented a new GPE feature for Windows compatibility, the "Implicit
+Wake
+GPE Notify". This feature will automatically issue a Notify(2) on a
+device
when a Wake GPE is received if there is no corresponding GPE method or
handler. ACPICA BZ 870.
-Fixed a problem with the Scope() operator during table parse and load phase.
-During load phase (table load or method execution), the scope operator should
-not enter the target into the namespace. Instead, it should open a new scope
+Fixed a problem with the Scope() operator during table parse and load
+phase.
+During load phase (table load or method execution), the scope operator
+should
+not enter the target into the namespace. Instead, it should open a new
+scope
at the target location. Linux BZ 19462, ACPICA BZ 882.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -403,27 +4906,35 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Relax the alphanumeric restriction on _CID strings. These strings are
-"bus-specific" per the ACPI specification, and therefore any characters are
-acceptable. The only checks that can be performed are for a null string and
+iASL: Relax the alphanumeric restriction on _CID strings. These strings
+are
+"bus-specific" per the ACPI specification, and therefore any characters
+are
+acceptable. The only checks that can be performed are for a null string
+and
perhaps for a leading asterisk. ACPICA BZ 886.
iASL: Fixed a problem where a syntax error that caused a premature EOF
condition on the source file emitted a very confusing error message. The
premature EOF is now detected correctly. ACPICA BZ 891.
-Disassembler: Decode the AccessSize within a Generic Address Structure (byte
+Disassembler: Decode the AccessSize within a Generic Address Structure
+(byte
access, word access, etc.) Note, this field does not allow arbitrary bit
access, the size is encoded as 1=byte, 2=word, 3=dword, and 4=qword.
-New: AcpiNames utility - Example namespace dump utility. Shows an example of
+New: AcpiNames utility - Example namespace dump utility. Shows an example
+of
ACPICA configuration for a minimal namespace dump utility. Uses table and
-namespace managers, but no AML interpreter. Does not add any functionality
+namespace managers, but no AML interpreter. Does not add any
+functionality
over AcpiExec, it is a subset of AcpiExec. The purpose is to show how to
partition and configure ACPICA. ACPICA BZ 883.
-AML Debugger: Increased the debugger buffer size for method return objects.
-Was 4K, increased to 16K. Also enhanced error messages for debugger method
+AML Debugger: Increased the debugger buffer size for method return
+objects.
+Was 4K, increased to 16K. Also enhanced error messages for debugger
+method
execution, including the buffer overflow case.
----------------------------------------
@@ -431,26 +4942,34 @@ execution, including the buffer overflow case.
1) ACPI CA Core Subsystem:
-Added support to clear the PCIEXP_WAKE event. When clearing ACPI events, now
+Added support to clear the PCIEXP_WAKE event. When clearing ACPI events,
+now
clear the PCIEXP_WAKE_STS bit in the ACPI PM1 Status Register, via
HwClearAcpiStatus. Original change from Colin King. ACPICA BZ 880.
-Changed the type of the predefined namespace object _TZ from ThermalZone to
-Device. This was found to be confusing to the host software that processes
-the various thermal zones, since _TZ is not really a ThermalZone. However, a
+Changed the type of the predefined namespace object _TZ from ThermalZone
+to
+Device. This was found to be confusing to the host software that
+processes
+the various thermal zones, since _TZ is not really a ThermalZone.
+However,
+a
Notify() can still be performed on it. ACPICA BZ 876. Suggestion from Rui
Zhang.
Added Windows Vista SP2 to the list of supported _OSI strings. The actual
string is "Windows 2006 SP2".
-Eliminated duplicate code in AcpiUtExecute* functions. Now that the nsrepair
+Eliminated duplicate code in AcpiUtExecute* functions. Now that the
+nsrepair
code automatically repairs _HID-related strings, this type of code is no
-longer needed in Execute_HID, Execute_CID, and Execute_UID. ACPICA BZ 878.
+longer needed in Execute_HID, Execute_CID, and Execute_UID. ACPICA BZ
+878.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -462,24 +4981,33 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Implemented additional compile-time validation for _HID strings. The
-non-hex prefix (such as "PNP" or "ACPI") must be uppercase, and the length of
-the string must be exactly seven or eight characters. For both _HID and _CID
+iASL: Implemented additional compile-time validation for _HID strings.
+The
+non-hex prefix (such as "PNP" or "ACPI") must be uppercase, and the
+length
+of
+the string must be exactly seven or eight characters. For both _HID and
+_CID
strings, all characters must be alphanumeric. ACPICA BZ 874.
iASL: Allow certain "null" resource descriptors. Some BIOS code creates
-descriptors that are mostly or all zeros, with the expectation that they will
-be filled in at runtime. iASL now allows this as long as there is a "resource
+descriptors that are mostly or all zeros, with the expectation that they
+will
+be filled in at runtime. iASL now allows this as long as there is a
+"resource
tag" (name) associated with the descriptor, which gives the ASL a handle
needed to modify the descriptor. ACPICA BZ 873.
-Added single-thread support to the generic Unix application OSL. Primarily
-for iASL support, this change removes the use of semaphores in the single-
+Added single-thread support to the generic Unix application OSL.
+Primarily
+for iASL support, this change removes the use of semaphores in the
+single-
threaded ACPICA tools/applications - increasing performance. The
_MULTI_THREADED option was replaced by the (reverse) ACPI_SINGLE_THREADED
option. ACPICA BZ 879.
-AcpiExec: several fixes for the 64-bit version. Adds XSDT support and support
+AcpiExec: several fixes for the 64-bit version. Adds XSDT support and
+support
for 64-bit DSDT/FACS addresses in the FADT. Lin Ming.
iASL: Moved all compiler messages to a new file, aslmessages.h.
@@ -489,7 +5017,8 @@ iASL: Moved all compiler messages to a new file, aslmessages.h.
1) ACPI CA Core Subsystem:
-Removed the AcpiOsDerivePciId OSL interface. The various host implementations
+Removed the AcpiOsDerivePciId OSL interface. The various host
+implementations
of this function were not OS-dependent and are now obsolete and can be
removed from all host OSLs. This function has been replaced by
AcpiHwDerivePciId, which is now part of the ACPICA core code.
@@ -499,33 +5028,44 @@ module, hwpci.c. ACPICA BZ 857.
Implemented a dynamic repair for _HID and _CID strings. The following
problems are now repaired at runtime: 1) Remove a leading asterisk in the
string, and 2) the entire string is uppercased. Both repairs are in
-accordance with the ACPI specification and will simplify host driver code.
+accordance with the ACPI specification and will simplify host driver
+code.
ACPICA BZ 871.
The ACPI_THREAD_ID type is no longer configurable, internally it is now
-always UINT64. This simplifies the ACPICA code, especially any printf output.
+always UINT64. This simplifies the ACPICA code, especially any printf
+output.
UINT64 is the only common data type for all thread_id types across all
-operating systems. It is now up to the host OSL to cast the native thread_id
-type to UINT64 before returning the value to ACPICA (via AcpiOsGetThreadId).
+operating systems. It is now up to the host OSL to cast the native
+thread_id
+type to UINT64 before returning the value to ACPICA (via
+AcpiOsGetThreadId).
Lin Ming, Bob Moore.
-Added the ACPI_INLINE type to enhance the ACPICA configuration. The "inline"
-keyword is not standard across compilers, and this type allows inline to be
+Added the ACPI_INLINE type to enhance the ACPICA configuration. The
+"inline"
+keyword is not standard across compilers, and this type allows inline to
+be
configured on a per-compiler basis. Lin Ming.
-Made the system global AcpiGbl_SystemAwakeAndRunning publically available.
-Added an extern for this boolean in acpixf.h. Some hosts utilize this value
+Made the system global AcpiGbl_SystemAwakeAndRunning publically
+available.
+Added an extern for this boolean in acpixf.h. Some hosts utilize this
+value
during suspend/restore operations. ACPICA BZ 869.
-All code that implements error/warning messages with the "ACPI:" prefix has
+All code that implements error/warning messages with the "ACPI:" prefix
+has
been moved to a new module, utxferror.c.
-The UINT64_OVERLAY was moved to utmath.c, which is the only module where it
+The UINT64_OVERLAY was moved to utmath.c, which is the only module where
+it
is used. ACPICA BZ 829. Lin Ming, Bob Moore.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -537,13 +5077,17 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL/Disassembler: Write ACPI errors to stderr instead of the output file.
-This keeps the output files free of random error messages that may originate
-from within the namespace/interpreter code. Used this opportunity to merge
+iASL/Disassembler: Write ACPI errors to stderr instead of the output
+file.
+This keeps the output files free of random error messages that may
+originate
+from within the namespace/interpreter code. Used this opportunity to
+merge
all ACPI:-style messages into a single new module, utxferror.c. ACPICA BZ
866. Lin Ming, Bob Moore.
-Tools: update some printfs for ansi warnings on size_t. Handle width change
+Tools: update some printfs for ansi warnings on size_t. Handle width
+change
of size_t on 32-bit versus 64-bit generations. Lin Ming.
----------------------------------------
@@ -551,11 +5095,16 @@ of size_t on 32-bit versus 64-bit generations. Lin Ming.
1) ACPI CA Core Subsystem:
-Designed and implemented a new host interface to the _OSI support code. This
-will allow the host to dynamically add or remove multiple _OSI strings, as
-well as install an optional handler that is called for each _OSI invocation.
-Also added a new AML debugger command, 'osi' to display and modify the global
-_OSI string table, and test support in the AcpiExec utility. See the ACPICA
+Designed and implemented a new host interface to the _OSI support code.
+This
+will allow the host to dynamically add or remove multiple _OSI strings,
+as
+well as install an optional handler that is called for each _OSI
+invocation.
+Also added a new AML debugger command, 'osi' to display and modify the
+global
+_OSI string table, and test support in the AcpiExec utility. See the
+ACPICA
reference manual for full details. Lin Ming, Bob Moore. ACPICA BZ 836.
New Functions:
AcpiInstallInterface - Add an _OSI string.
@@ -567,28 +5116,37 @@ New Files:
source/components/utilities/utosi.c
Re-introduced the support to enable multi-byte transfers for Embedded
-Controller (EC) operation regions. A reported problem was found to be a bug
-in the host OS, not in the multi-byte support. Previously, the maximum data
-size passed to the EC operation region handler was a single byte. There are
-often EC Fields larger than one byte that need to be transferred, and it is
-useful for the EC driver to lock these as a single transaction. This change
+Controller (EC) operation regions. A reported problem was found to be a
+bug
+in the host OS, not in the multi-byte support. Previously, the maximum
+data
+size passed to the EC operation region handler was a single byte. There
+are
+often EC Fields larger than one byte that need to be transferred, and it
+is
+useful for the EC driver to lock these as a single transaction. This
+change
enables single transfers larger than 8 bits. This effectively changes the
access to the EC space from ByteAcc to AnyAcc, and will probably require
-changes to the host OS Embedded Controller driver to enable 16/32/64/256-bit
+changes to the host OS Embedded Controller driver to enable 16/32/64/256-
+bit
transfers in addition to 8-bit transfers. Alexey Starikovskiy, Lin Ming.
Fixed a problem with the prototype for AcpiOsReadPciConfiguration. The
prototype in acpiosxf.h had the output value pointer as a (void *).
It should be a (UINT64 *). This may affect some host OSL code.
-Fixed a couple problems with the recently modified Linux makefiles for iASL
+Fixed a couple problems with the recently modified Linux makefiles for
+iASL
and AcpiExec. These new makefiles place the generated object files in the
-local directory so that there can be no collisions between the files that are
+local directory so that there can be no collisions between the files that
+are
shared between them that are compiled with different options.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -600,13 +5158,17 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL/Disassembler: Added a new option (-da, "disassemble all") to load the
+iASL/Disassembler: Added a new option (-da, "disassemble all") to load
+the
namespace from and disassemble an entire group of AML files. Useful for
-loading all of the AML tables for a given machine (DSDT, SSDT1...SSDTn) and
+loading all of the AML tables for a given machine (DSDT, SSDT1...SSDTn)
+and
disassembling with one simple command. ACPICA BZ 865. Lin Ming.
-iASL: Allow multiple invocations of -e option. This change allows multiple
-uses of -e on the command line: "-e ssdt1.dat -e ssdt2.dat". ACPICA BZ 834.
+iASL: Allow multiple invocations of -e option. This change allows
+multiple
+uses of -e on the command line: "-e ssdt1.dat -e ssdt2.dat". ACPICA BZ
+834.
Lin Ming.
----------------------------------------
@@ -615,9 +5177,12 @@ Lin Ming.
1) ACPI CA Core Subsystem:
Implemented several updates to the recently added GPE reference count
-support. The model for "wake" GPEs is changing to give the host OS complete
-control of these GPEs. Eventually, the ACPICA core will not execute any _PRW
-methods, since the host already must execute them. Also, additional changes
+support. The model for "wake" GPEs is changing to give the host OS
+complete
+control of these GPEs. Eventually, the ACPICA core will not execute any
+_PRW
+methods, since the host already must execute them. Also, additional
+changes
were made to help ensure that the reference counts are kept in proper
synchronization with reality. Rafael J. Wysocki.
@@ -626,28 +5191,38 @@ synchronization with reality. Rafael J. Wysocki.
3) Do not inadvertently enable GPEs when writing GPE registers.
4) Remove the internal wake reference counter and add new AcpiGpeWakeup
interface. This interface will set or clear individual GPEs for wakeup.
-5) Remove GpeType argument from AcpiEnable and AcpiDisable. These interfaces
+5) Remove GpeType argument from AcpiEnable and AcpiDisable. These
+interfaces
are now used for "runtime" GPEs only.
-Changed the behavior of the GPE install/remove handler interfaces. The GPE is
-no longer disabled during this process, as it was found to cause problems on
+Changed the behavior of the GPE install/remove handler interfaces. The
+GPE
+is
+no longer disabled during this process, as it was found to cause problems
+on
some machines. Rafael J. Wysocki.
Reverted a change introduced in version 20100528 to enable Embedded
-Controller multi-byte transfers. This change was found to cause problems with
+Controller multi-byte transfers. This change was found to cause problems
+with
Index Fields and possibly Bank Fields. It will be reintroduced when these
problems have been resolved.
-Fixed a problem with references to Alias objects within Package Objects. A
+Fixed a problem with references to Alias objects within Package Objects.
+A
reference to an Alias within the definition of a Package was not always
-resolved properly. Aliases to objects like Processors, Thermal zones, etc.
-were resolved to the actual object instead of a reference to the object as it
+resolved properly. Aliases to objects like Processors, Thermal zones,
+etc.
+were resolved to the actual object instead of a reference to the object
+as
+it
should be. Package objects are only allowed to contain integer, string,
buffer, package, and reference objects. Redhat bugzilla 608648.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -660,14 +5235,17 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
iASL: Implemented a new compiler subsystem to allow definition and
-compilation of the non-AML ACPI tables such as FADT, MADT, SRAT, etc. These
+compilation of the non-AML ACPI tables such as FADT, MADT, SRAT, etc.
+These
are called "ACPI Data Tables", and the new compiler is the "Data Table
Compiler". This compiler is intended to simplify the existing error-prone
process of creating these tables for the BIOS, as well as allowing the
-disassembly, modification, recompilation, and override of existing ACPI data
+disassembly, modification, recompilation, and override of existing ACPI
+data
tables. See the iASL User Guide for detailed information.
-iASL: Implemented a new Template Generator option in support of the new Data
+iASL: Implemented a new Template Generator option in support of the new
+Data
Table Compiler. This option will create examples of all known ACPI tables
that can be used as the basis for table development. See the iASL
documentation and the -T option.
@@ -680,7 +5258,8 @@ object files in the local directory so that there can be no collisions
between the shared files between them that are generated with different
options.
-Added support for Mac OS X in the Unix OSL used for iASL and AcpiExec. Use
+Added support for Mac OS X in the Unix OSL used for iASL and AcpiExec.
+Use
the #define __APPLE__ to enable this support.
----------------------------------------
@@ -691,37 +5270,58 @@ available at www.acpi.info. This is primarily an errata release.
1) ACPI CA Core Subsystem:
-Undefined ACPI tables: We are looking for the definitions for the following
+Undefined ACPI tables: We are looking for the definitions for the
+following
ACPI tables that have been seen in the field: ATKG, IEIT, GSCI.
-Implemented support to enable multi-byte transfers for Embedded Controller
-(EC) operation regions. Previously, the maximum data size passed to the EC
-operation region handler was a single byte. There are often EC Fields larger
-than one byte that need to be transferred, and it is useful for the EC driver
-to lock these as a single transaction. This change enables single transfers
-larger than 8 bits. This effectively changes the access to the EC space from
-ByteAcc to AnyAcc, and will probably require changes to the host OS Embedded
-Controller driver to enable 16/32/64/256-bit transfers in addition to 8-bit
+Implemented support to enable multi-byte transfers for Embedded
+Controller
+(EC) operation regions. Previously, the maximum data size passed to the
+EC
+operation region handler was a single byte. There are often EC Fields
+larger
+than one byte that need to be transferred, and it is useful for the EC
+driver
+to lock these as a single transaction. This change enables single
+transfers
+larger than 8 bits. This effectively changes the access to the EC space
+from
+ByteAcc to AnyAcc, and will probably require changes to the host OS
+Embedded
+Controller driver to enable 16/32/64/256-bit transfers in addition to 8-
+bit
transfers. Alexey Starikovskiy, Lin Ming
-Implemented a performance enhancement for namespace search and access. This
-change enhances the performance of namespace searches and walks by adding a
-backpointer to the parent in each namespace node. On large namespaces, this
-change can improve overall ACPI performance by up to 9X. Adding a pointer to
-each namespace node increases the overall size of the internal namespace by
+Implemented a performance enhancement for namespace search and access.
+This
+change enhances the performance of namespace searches and walks by adding
+a
+backpointer to the parent in each namespace node. On large namespaces,
+this
+change can improve overall ACPI performance by up to 9X. Adding a pointer
+to
+each namespace node increases the overall size of the internal namespace
+by
about 5%, since each namespace entry usually consists of both a namespace
node and an ACPI operand object. However, this is the first growth of the
namespace in ten years. ACPICA bugzilla 817. Alexey Starikovskiy.
-Implemented a performance optimization that reduces the number of namespace
-walks. On control method exit, only walk the namespace if the method is known
-to have created namespace objects outside of its local scope. Previously, the
-entire namespace was traversed on each control method exit. This change can
-improve overall ACPI performance by up to 3X. Alexey Starikovskiy, Bob Moore.
+Implemented a performance optimization that reduces the number of
+namespace
+walks. On control method exit, only walk the namespace if the method is
+known
+to have created namespace objects outside of its local scope. Previously,
+the
+entire namespace was traversed on each control method exit. This change
+can
+improve overall ACPI performance by up to 3X. Alexey Starikovskiy, Bob
+Moore.
-Added support to truncate I/O addresses to 16 bits for Windows compatibility.
+Added support to truncate I/O addresses to 16 bits for Windows
+compatibility.
Some ASL code has been seen in the field that inadvertently has bits set
-above bit 15. This feature is optional and is enabled if the BIOS requests
+above bit 15. This feature is optional and is enabled if the BIOS
+requests
any Windows OSI strings. It can also be enabled by the host OS. Matthew
Garrett, Bob Moore.
@@ -730,22 +5330,28 @@ prevent accidental deep sleeps, limit the maximum time that Sleep() will
actually sleep. Configurable, the default maximum is two seconds. ACPICA
bugzilla 854.
-Added run-time validation support for the _WDG and_WED Microsoft predefined
-methods. These objects are defined by "Windows Instrumentation", and are not
+Added run-time validation support for the _WDG and_WED Microsoft
+predefined
+methods. These objects are defined by "Windows Instrumentation", and are
+not
part of the ACPI spec. ACPICA BZ 860.
Expanded all statistic counters used during namespace and device
-initialization from 16 to 32 bits in order to support very large namespaces.
+initialization from 16 to 32 bits in order to support very large
+namespaces.
-Replaced all instances of %d in printf format specifiers with %u since nearly
+Replaced all instances of %d in printf format specifiers with %u since
+nearly
all integers in ACPICA are unsigned.
-Fixed the exception namestring for AE_WAKE_ONLY_GPE. Was incorrectly returned
+Fixed the exception namestring for AE_WAKE_ONLY_GPE. Was incorrectly
+returned
as AE_NO_HANDLER.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -758,7 +5364,8 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
iASL: Added compiler support for the _WDG and_WED Microsoft predefined
-methods. These objects are defined by "Windows Instrumentation", and are not
+methods. These objects are defined by "Windows Instrumentation", and are
+not
part of the ACPI spec. ACPICA BZ 860.
AcpiExec: added option to disable the memory tracking mechanism. The -dt
@@ -774,29 +5381,38 @@ AcpiExec: Restructured the command line options into -d (disable) and -e
1) ACPI CA Core Subsystem:
Implemented GPE support for dynamically loaded ACPI tables. For all GPEs,
-including FADT-based and GPE Block Devices, execute any _PRW methods in the
+including FADT-based and GPE Block Devices, execute any _PRW methods in
+the
new table, and process any _Lxx/_Exx GPE methods in the new table. Any
runtime GPE that is referenced by an _Lxx/_Exx method in the new table is
immediately enabled. Handles the FADT-defined GPEs as well as GPE Block
Devices. Provides compatibility with other ACPI implementations. Two new
-files added, evgpeinit.c and evgpeutil.c. ACPICA BZ 833. Lin Ming, Bob Moore.
-
-Fixed a regression introduced in version 20100331 within the table manager
-where initial table loading could fail. This was introduced in the fix for
-AcpiReallocateRootTable. Also, renamed some of fields in the table manager
+files added, evgpeinit.c and evgpeutil.c. ACPICA BZ 833. Lin Ming, Bob
+Moore.
+
+Fixed a regression introduced in version 20100331 within the table
+manager
+where initial table loading could fail. This was introduced in the fix
+for
+AcpiReallocateRootTable. Also, renamed some of fields in the table
+manager
data structures to clarify their meaning and use.
Fixed a possible allocation overrun during internal object copy in
-AcpiUtCopySimpleObject. The original code did not correctly handle the case
-where the object to be copied was a namespace node. Lin Ming. ACPICA BZ 847.
+AcpiUtCopySimpleObject. The original code did not correctly handle the
+case
+where the object to be copied was a namespace node. Lin Ming. ACPICA BZ
+847.
Updated the allocation dump routine, AcpiUtDumpAllocation and fixed a
-possible access beyond end-of-allocation. Also, now fully validate descriptor
+possible access beyond end-of-allocation. Also, now fully validate
+descriptor
(size and type) before output. Lin Ming, Bob Moore. ACPICA BZ 847
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -809,25 +5425,35 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
iASL: Implemented Min/Max/Len/Gran validation for address resource
-descriptors. This change implements validation for the address fields that
+descriptors. This change implements validation for the address fields
+that
are common to all address-type resource descriptors. These checks are
implemented: Checks for valid Min/Max, length within the Min/Max window,
-valid granularity, Min/Max a multiple of granularity, and _MIF/_MAF as per
-table 6-40 in the ACPI 4.0a specification. Also split the large aslrestype1.c
+valid granularity, Min/Max a multiple of granularity, and _MIF/_MAF as
+per
+table 6-40 in the ACPI 4.0a specification. Also split the large
+aslrestype1.c
and aslrestype2.c files into five new files. ACPICA BZ 840.
-iASL: Added support for the _Wxx predefined names. This support was missing
+iASL: Added support for the _Wxx predefined names. This support was
+missing
and these names were not recognized by the compiler as valid predefined
names. ACPICA BZ 851.
-iASL: Added an error for all predefined names that are defined to return no
-value and thus must be implemented as Control Methods. These include all of
+iASL: Added an error for all predefined names that are defined to return
+no
+value and thus must be implemented as Control Methods. These include all
+of
the _Lxx, _Exx, _Wxx, and _Qxx names, as well as some other miscellaneous
names such as _DIS, _INI, _IRC, _OFF, _ON, and _PSx. ACPICA BZ 850, 856.
-iASL: Implemented the -ts option to emit hex AML data in ASL format, as an
-ASL Buffer. Allows ACPI tables to be easily included within ASL files, to be
-dynamically loaded via the Load() operator. Also cleaned up output for the -
+iASL: Implemented the -ts option to emit hex AML data in ASL format, as
+an
+ASL Buffer. Allows ACPI tables to be easily included within ASL files, to
+be
+dynamically loaded via the Load() operator. Also cleaned up output for
+the
+-
ta and -tc options. ACPICA BZ 853.
Tests: Added a new file with examples of extended iASL error checking.
@@ -839,14 +5465,19 @@ Available at tests/misc/badcode.asl.
1) ACPI CA Core Subsystem:
-Completed a major update for the GPE support in order to improve support for
-shared GPEs and to simplify both host OS and ACPICA code. Added a reference
-count mechanism to support shared GPEs that require multiple device drivers.
+Completed a major update for the GPE support in order to improve support
+for
+shared GPEs and to simplify both host OS and ACPICA code. Added a
+reference
+count mechanism to support shared GPEs that require multiple device
+drivers.
Several external interfaces have changed. One external interface has been
removed. One new external interface was added. Most of the GPE external
interfaces now use the GPE spinlock instead of the events mutex (and the
-Flags parameter for many GPE interfaces has been removed.) See the updated
-ACPICA Programmer Reference for details. Matthew Garrett, Bob Moore, Rafael
+Flags parameter for many GPE interfaces has been removed.) See the
+updated
+ACPICA Programmer Reference for details. Matthew Garrett, Bob Moore,
+Rafael
Wysocki. ACPICA BZ 831.
Changed:
@@ -856,32 +5487,44 @@ Removed:
New:
AcpiSetGpe
-Implemented write support for DataTable operation regions. These regions are
-defined via the DataTableRegion() operator. Previously, only read support was
-implemented. The ACPI specification allows DataTableRegions to be read/write,
+Implemented write support for DataTable operation regions. These regions
+are
+defined via the DataTableRegion() operator. Previously, only read support
+was
+implemented. The ACPI specification allows DataTableRegions to be
+read/write,
however.
Implemented a new subsystem option to force a copy of the DSDT to local
-memory. Optionally copy the entire DSDT to local memory (instead of simply
-mapping it.) There are some (albeit very rare) BIOSs that corrupt or replace
-the original DSDT, creating the need for this option. Default is FALSE, do
+memory. Optionally copy the entire DSDT to local memory (instead of
+simply
+mapping it.) There are some (albeit very rare) BIOSs that corrupt or
+replace
+the original DSDT, creating the need for this option. Default is FALSE,
+do
not copy the DSDT.
Implemented detection of a corrupted or replaced DSDT. This change adds
-support to detect a DSDT that has been corrupted and/or replaced from outside
-the OS (by firmware). This is typically catastrophic for the system, but has
+support to detect a DSDT that has been corrupted and/or replaced from
+outside
+the OS (by firmware). This is typically catastrophic for the system, but
+has
been seen on some machines. Once this problem has been detected, the DSDT
copy option can be enabled via system configuration. Lin Ming, Bob Moore.
-Fixed two problems with AcpiReallocateRootTable during the root table copy.
+Fixed two problems with AcpiReallocateRootTable during the root table
+copy.
When copying the root table to the new allocation, the length used was
-incorrect. The new size was used instead of the current table size, meaning
-too much data was copied. Also, the count of available slots for ACPI tables
+incorrect. The new size was used instead of the current table size,
+meaning
+too much data was copied. Also, the count of available slots for ACPI
+tables
was not set correctly. Alexey Starikovskiy, Bob Moore.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -897,16 +5540,20 @@ iASL: Implement limited typechecking for values returned from predefined
control methods. The type of any returned static (unnamed) object is now
validated. For example, Return(1). ACPICA BZ 786.
-iASL: Fixed a predefined name object verification regression. Fixes a problem
+iASL: Fixed a predefined name object verification regression. Fixes a
+problem
introduced in version 20100304. An error is incorrectly generated if a
predefined name is declared as a static named object with a value defined
using the keywords "Zero", "One", or "Ones". Lin Ming.
-iASL: Added Windows 7 support for the -g option (get local ACPI tables) by
+iASL: Added Windows 7 support for the -g option (get local ACPI tables)
+by
reducing the requested registry access rights. ACPICA BZ 842.
-Disassembler: fixed a possible fault when generating External() statements.
-Introduced in commit ae7d6fd: Properly handle externals with parent-prefix
+Disassembler: fixed a possible fault when generating External()
+statements.
+Introduced in commit ae7d6fd: Properly handle externals with parent-
+prefix
(carat). Fixes a string length allocation calculation. Lin Ming.
----------------------------------------
@@ -916,32 +5563,42 @@ Introduced in commit ae7d6fd: Properly handle externals with parent-prefix
Fixed a possible problem with the AML Mutex handling function
AcpiExReleaseMutex where the function could fault under the very rare
-condition when the interpreter has blocked, the interpreter lock is released,
+condition when the interpreter has blocked, the interpreter lock is
+released,
the interpreter is then reentered via the same thread, and attempts to
-acquire an AML mutex that was previously acquired. FreeBSD report 140979. Lin
+acquire an AML mutex that was previously acquired. FreeBSD report 140979.
+Lin
Ming.
Implemented additional configuration support for the AML "Debug Object".
Output from the debug object can now be enabled via a global variable,
-AcpiGbl_EnableAmlDebugObject. This will assist with remote machine debugging.
-This debug output is now available in the release version of ACPICA instead
-of just the debug version. Also, the entire debug output module can now be
+AcpiGbl_EnableAmlDebugObject. This will assist with remote machine
+debugging.
+This debug output is now available in the release version of ACPICA
+instead
+of just the debug version. Also, the entire debug output module can now
+be
configured out of the ACPICA build if desired. One new file added,
executer/exdebug.c. Lin Ming, Bob Moore.
Added header support for the ACPI MCHI table (Management Controller Host
-Interface Table). This table was added in ACPI 4.0, but the defining document
+Interface Table). This table was added in ACPI 4.0, but the defining
+document
has only recently become available.
-Standardized output of integer values for ACPICA warnings/errors. Always use
-0x prefix for hex output, always use %u for unsigned integer decimal output.
-Affects ACPI_INFO, ACPI_ERROR, ACPI_EXCEPTION, and ACPI_WARNING (about 400
+Standardized output of integer values for ACPICA warnings/errors. Always
+use
+0x prefix for hex output, always use %u for unsigned integer decimal
+output.
+Affects ACPI_INFO, ACPI_ERROR, ACPI_EXCEPTION, and ACPI_WARNING (about
+400
invocations.) These invocations were converted from the original
ACPI_DEBUG_PRINT invocations and were not consistent. ACPICA BZ 835.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -956,7 +5613,8 @@ much larger code and data size.
iASL: Implemented typechecking support for static (non-control method)
predefined named objects that are declared with the Name() operator. For
example, the type of this object is now validated to be of type Integer:
-Name(_BBN, 1). This change migrates the compiler to using the core predefined
+Name(_BBN, 1). This change migrates the compiler to using the core
+predefined
name table instead of maintaining a local version. Added a new file,
aslpredef.c. ACPICA BZ 832.
@@ -971,34 +5629,50 @@ Added the 2010 copyright to all module headers and signons. This affects
virtually every file in the ACPICA core subsystem, the iASL compiler, the
tools/utilities, and the test suites.
-Implemented a change to the AcpiGetDevices interface to eliminate unnecessary
+Implemented a change to the AcpiGetDevices interface to eliminate
+unnecessary
invocations of the _STA method. In the case where a specific _HID is
requested, do not run _STA until a _HID match is found. This eliminates
-potentially dozens of _STA calls during a search for a particular device/HID,
+potentially dozens of _STA calls during a search for a particular
+device/HID,
which in turn can improve boot times. ACPICA BZ 828. Lin Ming.
-Implemented an additional repair for predefined method return values. Attempt
-to repair unexpected NULL elements within returned Package objects. Create an
-Integer of value zero, a NULL String, or a zero-length Buffer as appropriate.
+Implemented an additional repair for predefined method return values.
+Attempt
+to repair unexpected NULL elements within returned Package objects.
+Create
+an
+Integer of value zero, a NULL String, or a zero-length Buffer as
+appropriate.
ACPICA BZ 818. Lin Ming, Bob Moore.
-Removed the obsolete ACPI_INTEGER data type. This type was introduced as the
-code was migrated from ACPI 1.0 (with 32-bit AML integers) to ACPI 2.0 (with
-64-bit AML integers). It is now obsolete and this change removes it from the
-ACPICA code base, replaced by UINT64. The original typedef has been retained
-for now for compatibility with existing device driver code. ACPICA BZ 824.
+Removed the obsolete ACPI_INTEGER data type. This type was introduced as
+the
+code was migrated from ACPI 1.0 (with 32-bit AML integers) to ACPI 2.0
+(with
+64-bit AML integers). It is now obsolete and this change removes it from
+the
+ACPICA code base, replaced by UINT64. The original typedef has been
+retained
+for now for compatibility with existing device driver code. ACPICA BZ
+824.
-Removed the unused UINT32_STRUCT type, and the obsolete Integer64 field in
+Removed the unused UINT32_STRUCT type, and the obsolete Integer64 field
+in
the parse tree object.
-Added additional warning options for the gcc-4 generation. Updated the source
-accordingly. This includes some code restructuring to eliminate unreachable
-code, elimination of some gotos, elimination of unused return values, some
+Added additional warning options for the gcc-4 generation. Updated the
+source
+accordingly. This includes some code restructuring to eliminate
+unreachable
+code, elimination of some gotos, elimination of unused return values,
+some
additional casting, and removal of redundant declarations.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1017,47 +5691,72 @@ No functional changes for this release.
1) ACPI CA Core Subsystem:
-Enhanced automatic data type conversions for predefined name repairs. This
-change expands the automatic repairs/conversions for predefined name return
-values to make Integers, Strings, and Buffers fully interchangeable. Also, a
-Buffer can be converted to a Package of Integers if necessary. The nsrepair.c
+Enhanced automatic data type conversions for predefined name repairs.
+This
+change expands the automatic repairs/conversions for predefined name
+return
+values to make Integers, Strings, and Buffers fully interchangeable.
+Also,
+a
+Buffer can be converted to a Package of Integers if necessary. The
+nsrepair.c
module was completely restructured. Lin Ming, Bob Moore.
-Implemented automatic removal of null package elements during predefined name
+Implemented automatic removal of null package elements during predefined
+name
repairs. This change will automatically remove embedded and trailing NULL
-package elements from returned package objects that are defined to contain a
-variable number of sub-packages. The driver is then presented with a package
+package elements from returned package objects that are defined to
+contain
+a
+variable number of sub-packages. The driver is then presented with a
+package
with no null elements to deal with. ACPICA BZ 819.
Implemented a repair for the predefined _FDE and _GTM names. The expected
-return value for both names is a Buffer of 5 DWORDs. This repair fixes two
-possible problems (both seen in the field), where a package of integers is
-returned, or a buffer of BYTEs is returned. With assistance from Jung-uk Kim.
-
-Implemented additional module-level code support. This change will properly
-execute module-level code that is not at the root of the namespace (under a
-Device object, etc.). Now executes the code within the current scope instead
+return value for both names is a Buffer of 5 DWORDs. This repair fixes
+two
+possible problems (both seen in the field), where a package of integers
+is
+returned, or a buffer of BYTEs is returned. With assistance from Jung-uk
+Kim.
+
+Implemented additional module-level code support. This change will
+properly
+execute module-level code that is not at the root of the namespace (under
+a
+Device object, etc.). Now executes the code within the current scope
+instead
of the root. ACPICA BZ 762. Lin Ming.
-Fixed possible mutex acquisition errors when running _REG methods. Fixes a
-problem where mutex errors can occur when running a _REG method that is in
-the same scope as a method-defined operation region or an operation region
-under a module-level IF block. This type of code is rare, so the problem has
+Fixed possible mutex acquisition errors when running _REG methods. Fixes
+a
+problem where mutex errors can occur when running a _REG method that is
+in
+the same scope as a method-defined operation region or an operation
+region
+under a module-level IF block. This type of code is rare, so the problem
+has
not been seen before. ACPICA BZ 826. Lin Ming, Bob Moore.
-Fixed a possible memory leak during module-level code execution. An object
+Fixed a possible memory leak during module-level code execution. An
+object
could be leaked for each block of executed module-level code if the
-interpreter slack mode is enabled This change deletes any implicitly returned
+interpreter slack mode is enabled This change deletes any implicitly
+returned
object from the module-level code block. Lin Ming.
-Removed messages for successful predefined repair(s). The repair mechanism
-was considered too wordy. Now, messages are only unconditionally emitted if
+Removed messages for successful predefined repair(s). The repair
+mechanism
+was considered too wordy. Now, messages are only unconditionally emitted
+if
the return object cannot be repaired. Existing messages for successful
-repairs were converted to ACPI_DEBUG_PRINT messages for now. ACPICA BZ 827.
+repairs were converted to ACPI_DEBUG_PRINT messages for now. ACPICA BZ
+827.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1069,12 +5768,14 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Fixed a regression introduced in 20091112 where intermediate .SRC files
+iASL: Fixed a regression introduced in 20091112 where intermediate .SRC
+files
were no longer automatically removed at the termination of the compile.
acpiexec: Implemented the -f option to specify default region fill value.
This option specifies the value used to initialize buffers that simulate
-operation regions. Default value is zero. Useful for debugging problems that
+operation regions. Default value is zero. Useful for debugging problems
+that
depend on a specific initial value for a region or field.
----------------------------------------
@@ -1084,43 +5785,58 @@ depend on a specific initial value for a region or field.
Implemented a post-order callback to AcpiWalkNamespace. The existing
interface only has a pre-order callback. This change adds an additional
-parameter for a post-order callback which will be more useful for bus scans.
+parameter for a post-order callback which will be more useful for bus
+scans.
ACPICA BZ 779. Lin Ming. Updated the ACPICA Programmer Reference.
Modified the behavior of the operation region memory mapping cache for
-SystemMemory. Ensure that the memory mappings created for operation regions
+SystemMemory. Ensure that the memory mappings created for operation
+regions
do not cross 4K page boundaries. Crossing a page boundary while mapping
-regions can cause kernel warnings on some hosts if the pages have different
-attributes. Such regions are probably BIOS bugs, and this is the workaround.
+regions can cause kernel warnings on some hosts if the pages have
+different
+attributes. Such regions are probably BIOS bugs, and this is the
+workaround.
Linux BZ 14445. Lin Ming.
Implemented an automatic repair for predefined methods that must return
-sorted lists. This change will repair (by sorting) packages returned by _ALR,
-_PSS, and _TSS. Drivers can now assume that the packages are correctly sorted
+sorted lists. This change will repair (by sorting) packages returned by
+_ALR,
+_PSS, and _TSS. Drivers can now assume that the packages are correctly
+sorted
and do not contain NULL package elements. Adds one new file,
namespace/nsrepair2.c. ACPICA BZ 784. Lin Ming, Bob Moore.
-Fixed a possible fault during predefined name validation if a return Package
+Fixed a possible fault during predefined name validation if a return
+Package
object contains NULL elements. Also adds a warning if a NULL element is
-followed by any non-null elements. ACPICA BZ 813, 814. Future enhancement may
+followed by any non-null elements. ACPICA BZ 813, 814. Future enhancement
+may
include repair or removal of all such NULL elements where possible.
-Implemented additional module-level executable AML code support. This change
+Implemented additional module-level executable AML code support. This
+change
will execute module-level code that is not at the root of the namespace
-(under a Device object, etc.) at table load time. Module-level executable AML
+(under a Device object, etc.) at table load time. Module-level executable
+AML
code has been illegal since ACPI 2.0. ACPICA BZ 762. Lin Ming.
-Implemented a new internal function to create Integer objects. This function
+Implemented a new internal function to create Integer objects. This
+function
simplifies miscellaneous object creation code. ACPICA BZ 823.
-Reduced the severity of predefined repair messages, Warning to Info. Since
-the object was successfully repaired, a warning is too severe. Reduced to an
-info message for now. These messages may eventually be changed to debug-only.
+Reduced the severity of predefined repair messages, Warning to Info.
+Since
+the object was successfully repaired, a warning is too severe. Reduced to
+an
+info message for now. These messages may eventually be changed to debug-
+only.
ACPICA BZ 812.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1132,20 +5848,26 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Implemented Switch() with While(1) so that Break works correctly. This
-change correctly implements the Switch operator with a surrounding While(1)
+iASL: Implemented Switch() with While(1) so that Break works correctly.
+This
+change correctly implements the Switch operator with a surrounding
+While(1)
so that the Break operator works as expected. ACPICA BZ 461. Lin Ming.
-iASL: Added a message if a package initializer list is shorter than package
-length. Adds a new remark for a Package() declaration if an initializer list
+iASL: Added a message if a package initializer list is shorter than
+package
+length. Adds a new remark for a Package() declaration if an initializer
+list
exists, but is shorter than the declared length of the package. Although
technically legal, this is probably a coding error and it is seen in the
field. ACPICA BZ 815. Lin Ming, Bob Moore.
-iASL: Fixed a problem where the compiler could fault after the maximum number
+iASL: Fixed a problem where the compiler could fault after the maximum
+number
of errors was reached (200).
-acpixtract: Fixed a possible warning for pointer cast if the compiler warning
+acpixtract: Fixed a possible warning for pointer cast if the compiler
+warning
level set very high.
----------------------------------------
@@ -1153,9 +5875,12 @@ level set very high.
1) ACPI CA Core Subsystem:
-Fixed a problem where an Operation Region _REG method could be executed more
-than once. If a custom address space handler is installed by the host before
-the "initialize operation regions" phase of the ACPICA initialization, any
+Fixed a problem where an Operation Region _REG method could be executed
+more
+than once. If a custom address space handler is installed by the host
+before
+the "initialize operation regions" phase of the ACPICA initialization,
+any
_REG methods for that address space could be executed twice. This change
fixes the problem. ACPICA BZ 427. Lin Ming.
@@ -1165,20 +5890,24 @@ operand object was leaked. Lin Ming.
Implemented a run-time repair for the _MAT predefined method. If the _MAT
return value is defined as a Field object in the AML, and the field
-size is less than or equal to the default width of an integer (32 or 64),_MAT
+size is less than or equal to the default width of an integer (32 or
+64),_MAT
can incorrectly return an Integer instead of a Buffer. ACPICA now
automatically repairs this problem. ACPICA BZ 810.
-Implemented a run-time repair for the _BIF and _BIX predefined methods. The
+Implemented a run-time repair for the _BIF and _BIX predefined methods.
+The
"OEM Information" field is often incorrectly returned as an Integer with
-value zero if the field is not supported by the platform. This is due to an
+value zero if the field is not supported by the platform. This is due to
+an
ambiguity in the ACPI specification. The field should always be a string.
ACPICA now automatically repairs this problem by returning a NULL string
within the returned Package. ACPICA BZ 807.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1191,7 +5920,8 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
Disassembler: Fixed a problem where references to external symbols that
-contained one or more parent-prefixes (carats) were not handled correctly,
+contained one or more parent-prefixes (carats) were not handled
+correctly,
possibly causing a fault. ACPICA BZ 806. Lin Ming.
Disassembler: Restructured the code so that all functions that handle
@@ -1214,26 +5944,36 @@ files.
For Windows Vista compatibility, added the automatic execution of an _INI
method located at the namespace root (\_INI). This method is executed at
-table load time. This support is in addition to the automatic execution of
+table load time. This support is in addition to the automatic execution
+of
\_SB._INI. Lin Ming.
-Fixed a possible memory leak in the interpreter for AML package objects if
-the package initializer list is longer than the defined size of the package.
-This apparently can only happen if the BIOS changes the package size on the
+Fixed a possible memory leak in the interpreter for AML package objects
+if
+the package initializer list is longer than the defined size of the
+package.
+This apparently can only happen if the BIOS changes the package size on
+the
fly (seen in a _PSS object), as ASL compilers do not allow this. The
-interpreter will truncate the package to the defined size (and issue an error
-message), but previously could leave the extra objects undeleted if they were
-pre-created during the argument processing (such is the case if the package
+interpreter will truncate the package to the defined size (and issue an
+error
+message), but previously could leave the extra objects undeleted if they
+were
+pre-created during the argument processing (such is the case if the
+package
consists of a number of sub-packages as in the _PSS.) ACPICA BZ 805.
Fixed a problem seen when a Buffer or String is stored to itself via ASL.
-This has been reported in the field. Previously, ACPICA would zero out the
+This has been reported in the field. Previously, ACPICA would zero out
+the
buffer/string. Now, the operation is treated as a noop. Provides Windows
compatibility. ACPICA BZ 803. Lin Ming.
Removed an extraneous error message for ASL constructs of the form
-Store(LocalX,LocalX) when LocalX is uninitialized. These curious statements
-are seen in many BIOSs and are once again treated as NOOPs and no error is
+Store(LocalX,LocalX) when LocalX is uninitialized. These curious
+statements
+are seen in many BIOSs and are once again treated as NOOPs and no error
+is
emitted when they are encountered. ACPICA BZ 785.
Fixed an extraneous warning message if a _DSM reserved method returns a
@@ -1242,7 +5982,8 @@ return type cannot be performed. ACPICA BZ 802.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1258,9 +5999,11 @@ iASL: Fixed a problem with the use of the Alias operator and Resource
Templates. The correct alias is now constructed and no error is emitted.
ACPICA BZ 738.
-iASL: Implemented the -I option to specify additional search directories for
+iASL: Implemented the -I option to specify additional search directories
+for
include files. Allows multiple additional search paths for include files.
-Directories are searched in the order specified on the command line (after
+Directories are searched in the order specified on the command line
+(after
the local directory is searched.) ACPICA BZ 800.
iASL: Fixed a problem where the full pathname for include files was not
@@ -1270,12 +6013,15 @@ properly. ACPICA BZ 765.
iASL: Implemented the -@ option to specify a Windows-style response file
containing additional command line options. ACPICA BZ 801.
-AcpiExec: Added support to load multiple AML files simultaneously (such as a
+AcpiExec: Added support to load multiple AML files simultaneously (such
+as
+a
DSDT and multiple SSDTs). Also added support for wildcards within the AML
pathname. These features allow all machine tables to be easily loaded and
debugged together. ACPICA BZ 804.
-Disassembler: Added missing support for disassembly of HEST table Error Bank
+Disassembler: Added missing support for disassembly of HEST table Error
+Bank
subtables.
----------------------------------------
@@ -1286,54 +6032,75 @@ The ACPI 4.0 implementation for ACPICA is complete with this release.
1) ACPI CA Core Subsystem:
ACPI 4.0: Added header file support for all new and changed ACPI tables.
-Completely new tables are: IBFT, IVRS, MSCT, and WAET. Tables that are new
-for ACPI 4.0, but have previously been supported in ACPICA are: CPEP, BERT,
-EINJ, ERST, and HEST. Other newly supported tables are: UEFI and WDAT. There
+Completely new tables are: IBFT, IVRS, MSCT, and WAET. Tables that are
+new
+for ACPI 4.0, but have previously been supported in ACPICA are: CPEP,
+BERT,
+EINJ, ERST, and HEST. Other newly supported tables are: UEFI and WDAT.
+There
have been some ACPI 4.0 changes to other existing tables. Split the large
actbl1.h header into the existing actbl2.h header. ACPICA BZ 774.
-ACPI 4.0: Implemented predefined name validation for all new names. There are
-31 new names in ACPI 4.0. The predefined validation module was split into two
+ACPI 4.0: Implemented predefined name validation for all new names. There
+are
+31 new names in ACPI 4.0. The predefined validation module was split into
+two
files. The new file is namespace/nsrepair.c. ACPICA BZ 770.
Implemented support for so-called "module-level executable code". This is
-executable AML code that exists outside of any control method and is intended
-to be executed at table load time. Although illegal since ACPI 2.0, this type
-of code still exists and is apparently still being created. Blocks of this
-code are now detected and executed as intended. Currently, the code blocks
+executable AML code that exists outside of any control method and is
+intended
+to be executed at table load time. Although illegal since ACPI 2.0, this
+type
+of code still exists and is apparently still being created. Blocks of
+this
+code are now detected and executed as intended. Currently, the code
+blocks
must exist under either an If, Else, or While construct; these are the
typical cases seen in the field. ACPICA BZ 762. Lin Ming.
Implemented an automatic dynamic repair for predefined names that return
-nested Package objects. This applies to predefined names that are defined to
+nested Package objects. This applies to predefined names that are defined
+to
return a variable-length Package of sub-packages. If the number of sub-
-packages is one, BIOS code is occasionally seen that creates a simple single
+packages is one, BIOS code is occasionally seen that creates a simple
+single
package with no sub-packages. This code attempts to fix the problem by
-wrapping a new package object around the existing package. These methods can
-be repaired: _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, and _TSS. ACPICA BZ
+wrapping a new package object around the existing package. These methods
+can
+be repaired: _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, and _TSS. ACPICA
+BZ
790.
-Fixed a regression introduced in 20090625 for the AcpiGetDevices interface.
-The _HID/_CID matching was broken and no longer matched IDs correctly. ACPICA
+Fixed a regression introduced in 20090625 for the AcpiGetDevices
+interface.
+The _HID/_CID matching was broken and no longer matched IDs correctly.
+ACPICA
BZ 793.
Fixed a problem with AcpiReset where the reset would silently fail if the
-register was one of the protected I/O ports. AcpiReset now bypasses the port
-validation mechanism. This may eventually be driven into the AcpiRead/Write
+register was one of the protected I/O ports. AcpiReset now bypasses the
+port
+validation mechanism. This may eventually be driven into the
+AcpiRead/Write
interfaces.
Fixed a regression related to the recent update of the AcpiRead/Write
-interfaces. A sleep/suspend could fail if the optional PM2 Control register
+interfaces. A sleep/suspend could fail if the optional PM2 Control
+register
does not exist during an attempt to write the Bus Master Arbitration bit.
-(However, some hosts already delete the code that writes this bit, and the
+(However, some hosts already delete the code that writes this bit, and
+the
code may in fact be obsolete at this date.) ACPICA BZ 799.
-Fixed a problem where AcpiTerminate could fault if inadvertently called twice
+Fixed a problem where AcpiTerminate could fault if inadvertently called
+twice
in succession. ACPICA BZ 795.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1358,55 +6125,74 @@ continue for the next few releases.
1) ACPI CA Core Subsystem:
ACPI 4.0: Implemented interpreter support for the IPMI operation region
-address space. Includes support for bi-directional data buffers and an IPMI
-address space handler (to be installed by an IPMI device driver.) ACPICA BZ
+address space. Includes support for bi-directional data buffers and an
+IPMI
+address space handler (to be installed by an IPMI device driver.) ACPICA
+BZ
773. Lin Ming.
-ACPI 4.0: Added changes for existing ACPI tables - FACS and SRAT. Includes
+ACPI 4.0: Added changes for existing ACPI tables - FACS and SRAT.
+Includes
support in both the header files and the disassembler.
Completed a major update for the AcpiGetObjectInfo external interface.
Changes include:
- Support for variable, unlimited length HID, UID, and CID strings.
- - Support Processor objects the same as Devices (HID,UID,CID,ADR,STA, etc.)
+ - Support Processor objects the same as Devices (HID,UID,CID,ADR,STA,
+etc.)
- Call the _SxW power methods on behalf of a device object.
- Determine if a device is a PCI root bridge.
- Change the ACPI_BUFFER parameter to ACPI_DEVICE_INFO.
-These changes will require an update to all callers of this interface. See
-the updated ACPICA Programmer Reference for details. One new source file has
+These changes will require an update to all callers of this interface.
+See
+the updated ACPICA Programmer Reference for details. One new source file
+has
been added - utilities/utids.c. ACPICA BZ 368, 780.
Updated the AcpiRead and AcpiWrite external interfaces to support 64-bit
-transfers. The Value parameter has been extended from 32 bits to 64 bits in
-order to support new ACPI 4.0 tables. These changes will require an update to
+transfers. The Value parameter has been extended from 32 bits to 64 bits
+in
+order to support new ACPI 4.0 tables. These changes will require an
+update
+to
all callers of these interfaces. See the ACPICA Programmer Reference for
details. ACPICA BZ 768.
-Fixed several problems with AcpiAttachData. The handler was not invoked when
-the host node was deleted. The data sub-object was not automatically deleted
-when the host node was deleted. The interface to the handler had an unused
+Fixed several problems with AcpiAttachData. The handler was not invoked
+when
+the host node was deleted. The data sub-object was not automatically
+deleted
+when the host node was deleted. The interface to the handler had an
+unused
parameter, this was removed. ACPICA BZ 778.
Enhanced the function that dumps ACPI table headers. All non-printable
-characters in the string fields are now replaced with '?' (Signature, OemId,
+characters in the string fields are now replaced with '?' (Signature,
+OemId,
OemTableId, and CompilerId.) ACPI tables with non-printable characters in
these fields are occasionally seen in the field. ACPICA BZ 788.
Fixed a problem with predefined method repair code where the code that
-attempts to repair/convert an object of incorrect type is only executed on
-the first time the predefined method is called. The mechanism that disables
+attempts to repair/convert an object of incorrect type is only executed
+on
+the first time the predefined method is called. The mechanism that
+disables
warnings on subsequent calls was interfering with the repair mechanism.
ACPICA BZ 781.
-Fixed a possible memory leak in the predefined validation/repair code when a
+Fixed a possible memory leak in the predefined validation/repair code
+when
+a
buffer is automatically converted to an expected string object.
-Removed obsolete 16-bit files from the distribution and from the current git
+Removed obsolete 16-bit files from the distribution and from the current
+git
tree head. ACPICA BZ 776.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1429,8 +6215,11 @@ predefined names and control methods (31 total). ACPICA BZ 769.
1) ACPI CA Core Subsystem:
-Disabled the preservation of the SCI enable bit in the PM1 control register.
-The SCI enable bit (bit 0, SCI_EN) is defined by the ACPI specification to be
+Disabled the preservation of the SCI enable bit in the PM1 control
+register.
+The SCI enable bit (bit 0, SCI_EN) is defined by the ACPI specification
+to
+be
a "preserved" bit - "OSPM always preserves this bit position", section
4.7.3.2.1. However, some machines fail if this bit is in fact preserved
because the bit needs to be explicitly set by the OS as a workaround. No
@@ -1441,45 +6230,61 @@ Fixed a problem in AcpiRsGetPciRoutingTableLength where an invalid or
incorrectly formed _PRT package could cause a fault. Added validation to
ensure that each package element is actually a sub-package.
-Implemented a new interface to install or override a single control method,
-AcpiInstallMethod. This interface is useful when debugging in order to repair
-an existing method or to install a missing method without having to override
+Implemented a new interface to install or override a single control
+method,
+AcpiInstallMethod. This interface is useful when debugging in order to
+repair
+an existing method or to install a missing method without having to
+override
the entire ACPI table. See the ACPICA Programmer Reference for use and
examples. Lin Ming, Bob Moore.
Fixed several reference count issues with the DdbHandle object that is
-created from a Load or LoadTable operator. Prevent premature deletion of the
-object. Also, mark the object as invalid once the table has been unloaded.
-This is needed because the handle itself may not be deleted after the table
+created from a Load or LoadTable operator. Prevent premature deletion of
+the
+object. Also, mark the object as invalid once the table has been
+unloaded.
+This is needed because the handle itself may not be deleted after the
+table
unload, depending on whether it has been stored in a named object by the
caller. Lin Ming.
Fixed a problem with Mutex Sync Levels. Fixed a problem where if multiple
-mutexes of the same sync level are acquired but then not released in strict
-opposite order, the internally maintained Current Sync Level becomes confused
+mutexes of the same sync level are acquired but then not released in
+strict
+opposite order, the internally maintained Current Sync Level becomes
+confused
and can cause subsequent execution errors. ACPICA BZ 471.
Changed the allowable release order for ASL mutex objects. The ACPI 4.0
-specification has been changed to make the SyncLevel for mutex objects more
-useful. When releasing a mutex, the SyncLevel of the mutex must now be the
-same as the current sync level. This makes more sense than the previous rule
+specification has been changed to make the SyncLevel for mutex objects
+more
+useful. When releasing a mutex, the SyncLevel of the mutex must now be
+the
+same as the current sync level. This makes more sense than the previous
+rule
(SyncLevel less than or equal). This change updates the code to match the
specification.
-Fixed a problem with the local version of the AcpiOsPurgeCache function. The
+Fixed a problem with the local version of the AcpiOsPurgeCache function.
+The
(local) cache must be locked during all cache object deletions. Andrew
Baumann.
-Updated the Load operator to use operation region interfaces. This replaces
-direct memory mapping with region access calls. Now, all region accesses go
+Updated the Load operator to use operation region interfaces. This
+replaces
+direct memory mapping with region access calls. Now, all region accesses
+go
through the installed region handler as they should.
-Simplified and optimized the NsGetNextNode function. Reduced parameter count
+Simplified and optimized the NsGetNextNode function. Reduced parameter
+count
and reduced code for this frequently used function.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1491,8 +6296,10 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Disassembler: Fixed some issues with DMAR, HEST, MADT tables. Some problems
-with sub-table disassembly and handling invalid sub-tables. Attempt recovery
+Disassembler: Fixed some issues with DMAR, HEST, MADT tables. Some
+problems
+with sub-table disassembly and handling invalid sub-tables. Attempt
+recovery
after an invalid sub-table ID.
----------------------------------------
@@ -1500,14 +6307,18 @@ after an invalid sub-table ID.
1) ACPI CA Core Subsystem:
-Fixed a compatibility issue with the recently released I/O port protection
+Fixed a compatibility issue with the recently released I/O port
+protection
mechanism. For windows compatibility, 1) On a port protection violation,
-simply ignore the request and do not return an exception (allow the control
+simply ignore the request and do not return an exception (allow the
+control
method to continue execution.) 2) If only part of the request overlaps a
-protected port, read/write the individual ports that are not protected. Linux
+protected port, read/write the individual ports that are not protected.
+Linux
BZ 13036. Lin Ming
-Enhanced the execution of the ASL/AML BreakPoint operator so that it actually
+Enhanced the execution of the ASL/AML BreakPoint operator so that it
+actually
breaks into the AML debugger if the debugger is present. This matches the
ACPI-defined behavior.
@@ -1517,15 +6328,19 @@ pointer with no warnings. Also fixes several warnings in printf-like
statements for the 64-bit build when the type is configured as a pointer.
ACPICA BZ 766, 767.
-Fixed a number of possible warnings when compiling with gcc 4+ (depending on
-warning options.) Examples include printf formats, aliasing, unused globals,
+Fixed a number of possible warnings when compiling with gcc 4+ (depending
+on
+warning options.) Examples include printf formats, aliasing, unused
+globals,
missing prototypes, missing switch default statements, use of non-ANSI
-library functions, use of non-ANSI constructs. See generate/unix/Makefile for
+library functions, use of non-ANSI constructs. See generate/unix/Makefile
+for
a list of warning options used with gcc 3 and 4. ACPICA BZ 735.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1537,28 +6352,38 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Fixed a generation warning from Bison 2.3 and fixed several warnings on
+iASL: Fixed a generation warning from Bison 2.3 and fixed several
+warnings
+on
the 64-bit build.
-iASL: Fixed a problem where the Unix/Linux versions of the compiler could not
+iASL: Fixed a problem where the Unix/Linux versions of the compiler could
+not
correctly digest Windows/DOS formatted files (with CR/LF).
iASL: Added a new option for "quiet mode" (-va) that produces only the
compilation summary, not individual errors and warnings. Useful for large
batch compilations.
-AcpiExec: Implemented a new option (-z) to enable a forced semaphore/mutex
-timeout that can be used to detect hang conditions during execution of AML
-code (includes both internal semaphores and AML-defined mutexes and events.)
+AcpiExec: Implemented a new option (-z) to enable a forced
+semaphore/mutex
+timeout that can be used to detect hang conditions during execution of
+AML
+code (includes both internal semaphores and AML-defined mutexes and
+events.)
Added new makefiles for the generation of acpica in a generic unix-like
-environment. These makefiles are intended to generate the acpica tools and
+environment. These makefiles are intended to generate the acpica tools
+and
utilities from the original acpica git source tree structure.
Test Suites: Updated and cleaned up the documentation files. Updated the
-copyrights to 2009, affecting all source files. Use the new version of iASL
-with quiet mode. Increased the number of available semaphores in the Windows
-OSL, allowing the aslts to execute fully on Windows. For the Unix OSL, added
+copyrights to 2009, affecting all source files. Use the new version of
+iASL
+with quiet mode. Increased the number of available semaphores in the
+Windows
+OSL, allowing the aslts to execute fully on Windows. For the Unix OSL,
+added
an alternate implementation of the semaphore timeout to allow aslts to
execute fully on Cygwin.
@@ -1567,25 +6392,37 @@ execute fully on Cygwin.
1) ACPI CA Core Subsystem:
-Fixed a possible race condition between AcpiWalkNamespace and dynamic table
-unloads. Added a reader/writer locking mechanism to allow multiple concurrent
-namespace walks (readers), but block a dynamic table unload until it can gain
-exclusive write access to the namespace. This fixes a problem where a table
-unload could (possibly catastrophically) delete the portion of the namespace
-that is currently being examined by a walk. Adds a new file, utlock.c, that
+Fixed a possible race condition between AcpiWalkNamespace and dynamic
+table
+unloads. Added a reader/writer locking mechanism to allow multiple
+concurrent
+namespace walks (readers), but block a dynamic table unload until it can
+gain
+exclusive write access to the namespace. This fixes a problem where a
+table
+unload could (possibly catastrophically) delete the portion of the
+namespace
+that is currently being examined by a walk. Adds a new file, utlock.c,
+that
implements the reader/writer lock mechanism. ACPICA BZ 749.
-Fixed a regression introduced in version 20090220 where a change to the FADT
-handling could cause the ACPICA subsystem to access non-existent I/O ports.
+Fixed a regression introduced in version 20090220 where a change to the
+FADT
+handling could cause the ACPICA subsystem to access non-existent I/O
+ports.
-Modified the handling of FADT register and table (FACS/DSDT) addresses. The
+Modified the handling of FADT register and table (FACS/DSDT) addresses.
+The
FADT can contain both 32-bit and 64-bit versions of these addresses.
-Previously, the 64-bit versions were favored, meaning that if both 32 and 64
+Previously, the 64-bit versions were favored, meaning that if both 32 and
+64
versions were valid, but not equal, the 64-bit version was used. This was
-found to cause some machines to fail. Now, in this case, the 32-bit version
+found to cause some machines to fail. Now, in this case, the 32-bit
+version
is used instead. This now matches the Windows behavior.
-Implemented a new mechanism to protect certain I/O ports. Provides Microsoft
+Implemented a new mechanism to protect certain I/O ports. Provides
+Microsoft
compatibility and protects the standard PC I/O ports from access via AML
code. Adds a new file, hwvalid.c
@@ -1593,30 +6430,40 @@ Fixed a possible extraneous warning message from the FADT support. The
message warns of a 32/64 length mismatch between the legacy and GAS
definitions for a register.
-Removed the obsolete AcpiOsValidateAddress OSL interface. This interface is
-made obsolete by the port protection mechanism above. It was previously used
-to validate the entire address range of an operation region, which could be
+Removed the obsolete AcpiOsValidateAddress OSL interface. This interface
+is
+made obsolete by the port protection mechanism above. It was previously
+used
+to validate the entire address range of an operation region, which could
+be
incorrect if the range included illegal ports, but fields within the
operation region did not actually access those ports. Validation is now
performed on a per-field basis instead of the entire region.
Modified the handling of the PM1 Status Register ignored bit (bit 11.)
-Ignored bits must be "preserved" according to the ACPI spec. Usually, this
-means a read/modify/write when writing to the register. However, for status
-registers, writing a one means clear the event. Writing a zero means preserve
-the event (do not clear.) This behavior is clarified in the ACPI 4.0 spec,
+Ignored bits must be "preserved" according to the ACPI spec. Usually,
+this
+means a read/modify/write when writing to the register. However, for
+status
+registers, writing a one means clear the event. Writing a zero means
+preserve
+the event (do not clear.) This behavior is clarified in the ACPI 4.0
+spec,
and the ACPICA code now simply always writes a zero to the ignored bit.
-Modified the handling of ignored bits for the PM1 A/B Control Registers. As
+Modified the handling of ignored bits for the PM1 A/B Control Registers.
+As
per the ACPI specification, for the control registers, preserve
-(read/modify/write) all bits that are defined as either reserved or ignored.
+(read/modify/write) all bits that are defined as either reserved or
+ignored.
Updated the handling of write-only bits in the PM1 A/B Control Registers.
When reading the register, zero the write-only bits as per the ACPI spec.
ACPICA BZ 443. Lin Ming.
Removed "Linux" from the list of supported _OSI strings. Linux no longer
-wants to reply true to this request. The Windows strings are the only paths
+wants to reply true to this request. The Windows strings are the only
+paths
through the AML that are tested and known to work properly.
Previous Release:
@@ -1628,7 +6475,8 @@ through the AML that are tested and known to work properly.
2) iASL Compiler/Disassembler and Tools:
-Acpiexec: Split the large aeexec.c file into two new files, aehandlers.c and
+Acpiexec: Split the large aeexec.c file into two new files, aehandlers.c
+and
aetables.c
----------------------------------------
@@ -1636,51 +6484,77 @@ aetables.c
1) ACPI CA Core Subsystem:
-Optimized the ACPI register locking. Removed locking for reads from the ACPI
-bit registers in PM1 Status, Enable, Control, and PM2 Control. The lock is
+Optimized the ACPI register locking. Removed locking for reads from the
+ACPI
+bit registers in PM1 Status, Enable, Control, and PM2 Control. The lock
+is
not required when reading the single-bit registers. The
-AcpiGetRegisterUnlocked function is no longer needed and has been removed.
-This will improve performance for reads on these registers. ACPICA BZ 760.
+AcpiGetRegisterUnlocked function is no longer needed and has been
+removed.
+This will improve performance for reads on these registers. ACPICA BZ
+760.
Fixed the parameter validation for AcpiRead/Write. Now return
-AE_BAD_PARAMETER if the input register pointer is null, and AE_BAD_ADDRESS if
-the register has an address of zero. Previously, these cases simply returned
-AE_OK. For optional registers such as PM1B status/enable/control, the caller
+AE_BAD_PARAMETER if the input register pointer is null, and
+AE_BAD_ADDRESS
+if
+the register has an address of zero. Previously, these cases simply
+returned
+AE_OK. For optional registers such as PM1B status/enable/control, the
+caller
should check for a valid register address before calling. ACPICA BZ 748.
Renamed the external ACPI bit register access functions. Renamed
AcpiGetRegister and AcpiSetRegister to clarify the purpose of these
-functions. The new names are AcpiReadBitRegister and AcpiWriteBitRegister.
-Also, restructured the code for these functions by simplifying the code path
+functions. The new names are AcpiReadBitRegister and
+AcpiWriteBitRegister.
+Also, restructured the code for these functions by simplifying the code
+path
and condensing duplicate code to reduce code size.
Added new functions to transparently handle the possibly split PM1 A/B
-registers. AcpiHwReadMultiple and AcpiHwWriteMultiple. These two functions
-now handle the split registers for PM1 Status, Enable, and Control. ACPICA BZ
+registers. AcpiHwReadMultiple and AcpiHwWriteMultiple. These two
+functions
+now handle the split registers for PM1 Status, Enable, and Control.
+ACPICA
+BZ
746.
-Added a function to handle the PM1 control registers, AcpiHwWritePm1Control.
-This function writes both of the PM1 control registers (A/B). These registers
-are different than the PM1 A/B status and enable registers in that different
-values can be written to the A/B registers. Most notably, the SLP_TYP bits
-can be different, as per the values returned from the _Sx predefined methods.
-
-Removed an extra register write within AcpiHwClearAcpiStatus. This function
-was writing an optional PM1B status register twice. The existing call to the
-low-level AcpiHwRegisterWrite automatically handles a possibly split PM1 A/B
+Added a function to handle the PM1 control registers,
+AcpiHwWritePm1Control.
+This function writes both of the PM1 control registers (A/B). These
+registers
+are different than the PM1 A/B status and enable registers in that
+different
+values can be written to the A/B registers. Most notably, the SLP_TYP
+bits
+can be different, as per the values returned from the _Sx predefined
+methods.
+
+Removed an extra register write within AcpiHwClearAcpiStatus. This
+function
+was writing an optional PM1B status register twice. The existing call to
+the
+low-level AcpiHwRegisterWrite automatically handles a possibly split PM1
+A/B
register. ACPICA BZ 751.
-Split out the PM1 Status registers from the FADT. Added new globals for these
+Split out the PM1 Status registers from the FADT. Added new globals for
+these
registers (A/B), similar to the way the PM1 Enable registers are handled.
-Instead of overloading the FADT Event Register blocks. This makes the code
+Instead of overloading the FADT Event Register blocks. This makes the
+code
clearer and less prone to error.
-Fixed the warning message for when the platform contains too many ACPI tables
-for the default size of the global root table data structure. The calculation
+Fixed the warning message for when the platform contains too many ACPI
+tables
+for the default size of the global root table data structure. The
+calculation
for the truncation value was incorrect.
Removed the ACPI_GET_OBJECT_TYPE macro. Removed all instances of this
-obsolete macro, since it is now a simple reference to ->common.type. There
+obsolete macro, since it is now a simple reference to ->common.type.
+There
were about 150 invocations of the macro across 41 files. ACPICA BZ 755.
Removed the redundant ACPI_BITREG_SLEEP_TYPE_B. This type is the same as
@@ -1691,13 +6565,16 @@ Conditionally compile the AcpiSetFirmwareWakingVector64 function. This
function is only needed on 64-bit host operating systems and is thus not
included for 32-bit hosts.
-Debug output: print the input and result for invocations of the _OSI reserved
-control method via the ACPI_LV_INFO debug level. Also, reduced some of the
+Debug output: print the input and result for invocations of the _OSI
+reserved
+control method via the ACPI_LV_INFO debug level. Also, reduced some of
+the
verbosity of this debug level. Len Brown.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1721,21 +6598,28 @@ Added the 2009 copyright to all module headers and signons. This affects
virtually every file in the ACPICA core subsystem, the iASL compiler, and
the tools/utilities.
-Implemented a change to allow the host to override any ACPI table, including
-dynamically loaded tables. Previously, only the DSDT could be replaced by the
-host. With this change, the AcpiOsTableOverride interface is called for each
-table found in the RSDT/XSDT during ACPICA initialization, and also whenever
+Implemented a change to allow the host to override any ACPI table,
+including
+dynamically loaded tables. Previously, only the DSDT could be replaced by
+the
+host. With this change, the AcpiOsTableOverride interface is called for
+each
+table found in the RSDT/XSDT during ACPICA initialization, and also
+whenever
a table is dynamically loaded via the AML Load operator.
Updated FADT flag definitions, especially the Boot Architecture flags.
-Debugger: For the Find command, automatically pad the input ACPI name with
-underscores if the name is shorter than 4 characters. This enables a match
+Debugger: For the Find command, automatically pad the input ACPI name
+with
+underscores if the name is shorter than 4 characters. This enables a
+match
with the actual namespace entry which is itself padded with underscores.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1749,11 +6633,13 @@ much larger code and data size.
Fix build error under Bison-2.4.
-Dissasembler: Enhanced FADT support. Added decoding of the Boot Architecture
+Dissasembler: Enhanced FADT support. Added decoding of the Boot
+Architecture
flags. Now decode all flags, regardless of the FADT version. Flag output
includes the FADT version which first defined each flag.
-The iASL -g option now dumps the RSDT to a file (in addition to the FADT and
+The iASL -g option now dumps the RSDT to a file (in addition to the FADT
+and
DSDT). Windows only.
----------------------------------------
@@ -1761,7 +6647,8 @@ DSDT). Windows only.
1) ACPI CA Core Subsystem:
-The ACPICA Programmer Reference has been completely updated and revamped for
+The ACPICA Programmer Reference has been completely updated and revamped
+for
this release. This includes updates to the external interfaces, OSL
interfaces, the overview sections, and the debugger reference.
@@ -1775,31 +6662,38 @@ AcpiGbl_CurrentGpeCount - Tracks the current number of available GPEs.
AcpiRead - Low-level read ACPI register (was HwLowLevelRead.)
AcpiWrite - Low-level write ACPI register (was HwLowLevelWrite.)
-Most of the public ACPI hardware-related interfaces have been moved to a new
+Most of the public ACPI hardware-related interfaces have been moved to a
+new
file, components/hardware/hwxface.c
Enhanced the FADT parsing and low-level ACPI register access: The ACPI
register lengths within the FADT are now used, and the low level ACPI
register access no longer hardcodes the ACPI register lengths. Given that
-there may be some risk in actually trusting the FADT register lengths, a run-
-time option was added to fall back to the default hardcoded lengths if the
+there may be some risk in actually trusting the FADT register lengths, a
+run-
+time option was added to fall back to the default hardcoded lengths if
+the
FADT proves to contain incorrect values - UseDefaultRegisterWidths. This
-option is set to true for now, and a warning is issued if a suspicious FADT
+option is set to true for now, and a warning is issued if a suspicious
+FADT
register length is overridden with the default value.
-Fixed a reference count issue in NsRepairObject. This problem was introduced
+Fixed a reference count issue in NsRepairObject. This problem was
+introduced
in version 20081031 as part of a fix to repair Buffer objects within
Packages. Lin Ming.
Added semaphore support to the Linux/Unix application OS-services layer
(OSL). ACPICA BZ 448. Lin Ming.
-Added the ACPI_MUTEX_TYPE configuration option to select whether mutexes will
+Added the ACPI_MUTEX_TYPE configuration option to select whether mutexes
+will
be implemented in the OSL, or will binary semaphores be used instead.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1811,88 +6705,124 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Completed the '-e' option to include additional ACPI tables in order to
-aid with disassembly and External statement generation. ACPICA BZ 742. Lin
+iASL: Completed the '-e' option to include additional ACPI tables in
+order
+to
+aid with disassembly and External statement generation. ACPICA BZ 742.
+Lin
Ming.
iASL: Removed the "named object in while loop" error. The compiler cannot
determine how many times a loop will execute. ACPICA BZ 730.
-Disassembler: Implemented support for FADT revision 2 (MS extension). ACPICA
+Disassembler: Implemented support for FADT revision 2 (MS extension).
+ACPICA
BZ 743.
-Disassembler: Updates for several ACPI data tables (HEST, EINJ, and MCFG).
+Disassembler: Updates for several ACPI data tables (HEST, EINJ, and
+MCFG).
----------------------------------------
31 October 2008. Summary of changes for version 20081031:
1) ACPI CA Core Subsystem:
-Restructured the ACPICA header files into public/private. acpi.h now includes
-only the "public" acpica headers. All other acpica headers are "private" and
-should not be included by acpica users. One new file, accommon.h is used to
-include the commonly used private headers for acpica code generation. Future
+Restructured the ACPICA header files into public/private. acpi.h now
+includes
+only the "public" acpica headers. All other acpica headers are "private"
+and
+should not be included by acpica users. One new file, accommon.h is used
+to
+include the commonly used private headers for acpica code generation.
+Future
plans include moving all private headers to a new subdirectory.
Implemented an automatic Buffer->String return value conversion for
-predefined ACPI methods. For these methods (such as _BIF), added automatic
-conversion for return objects that are required to be a String, but a Buffer
-was found instead. This can happen when reading string battery data from an
-operation region, because it used to be difficult to convert the data from
-buffer to string from within the ASL. Ensures that the host OS is provided
+predefined ACPI methods. For these methods (such as _BIF), added
+automatic
+conversion for return objects that are required to be a String, but a
+Buffer
+was found instead. This can happen when reading string battery data from
+an
+operation region, because it used to be difficult to convert the data
+from
+buffer to string from within the ASL. Ensures that the host OS is
+provided
with a valid null-terminated string. Linux BZ 11822.
-Updated the FACS waking vector interfaces. Split AcpiSetFirmwareWakingVector
-into two: one for the 32-bit vector, another for the 64-bit vector. This is
-required because the host OS must setup the wake much differently for each
-vector (real vs. protected mode, etc.) and the interface itself should not be
-deciding which vector to use. Also, eliminated the GetFirmwareWakingVector
-interface, as it served no purpose (only the firmware reads the vector, OS
+Updated the FACS waking vector interfaces. Split
+AcpiSetFirmwareWakingVector
+into two: one for the 32-bit vector, another for the 64-bit vector. This
+is
+required because the host OS must setup the wake much differently for
+each
+vector (real vs. protected mode, etc.) and the interface itself should
+not
+be
+deciding which vector to use. Also, eliminated the
+GetFirmwareWakingVector
+interface, as it served no purpose (only the firmware reads the vector,
+OS
only writes the vector.) ACPICA BZ 731.
-Implemented a mechanism to escape infinite AML While() loops. Added a loop
-counter to force exit from AML While loops if the count becomes too large.
+Implemented a mechanism to escape infinite AML While() loops. Added a
+loop
+counter to force exit from AML While loops if the count becomes too
+large.
This can occur in poorly written AML when the hardware does not respond
-within a while loop and the loop does not implement a timeout. The maximum
-loop count is configurable. A new exception code is returned when a loop is
+within a while loop and the loop does not implement a timeout. The
+maximum
+loop count is configurable. A new exception code is returned when a loop
+is
broken, AE_AML_INFINITE_LOOP. Alexey Starikovskiy, Bob Moore.
Optimized the execution of AML While loops. Previously, a control state
object was allocated and freed for each execution of the loop. The
-optimization is to simply reuse the control state for each iteration. This
+optimization is to simply reuse the control state for each iteration.
+This
speeds up the raw loop execution time by about 5%.
-Enhanced the implicit return mechanism. For Windows compatibility, return an
-implicit integer of value zero for methods that contain no executable code.
+Enhanced the implicit return mechanism. For Windows compatibility, return
+an
+implicit integer of value zero for methods that contain no executable
+code.
Such methods are seen in the field as stubs (presumably), and can cause
drivers to fail if they expect a return value. Lin Ming.
Allow multiple backslashes as root prefixes in namepaths. In a fully
-qualified namepath, allow multiple backslash prefixes. This can happen (and
+qualified namepath, allow multiple backslash prefixes. This can happen
+(and
is seen in the field) because of the use of a double-backslash in strings
-(since backslash is the escape character) causing confusion. ACPICA BZ 739
+(since backslash is the escape character) causing confusion. ACPICA BZ
+739
Lin Ming.
Emit a warning if two different FACS or DSDT tables are discovered in the
-FADT. Checks if there are two valid but different addresses for the FACS and
+FADT. Checks if there are two valid but different addresses for the FACS
+and
DSDT within the FADT (mismatch between the 32-bit and 64-bit fields.)
-Consolidated the method argument count validation code. Merged the code that
+Consolidated the method argument count validation code. Merged the code
+that
validates control method argument counts into the predefined validation
-module. Eliminates possible multiple warnings for incorrect argument counts.
+module. Eliminates possible multiple warnings for incorrect argument
+counts.
Implemented ACPICA example code. Includes code for ACPICA initialization,
handler installation, and calling a control method. Available at
source/tools/examples.
-Added a global pointer for FACS table to simplify internal FACS access. Use
-the global pointer instead of using AcpiGetTableByIndex for each FACS access.
+Added a global pointer for FACS table to simplify internal FACS access.
+Use
+the global pointer instead of using AcpiGetTableByIndex for each FACS
+access.
This simplifies the code for the Global Lock and the Firmware Waking
Vector(s).
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1904,14 +6834,20 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-iASL: Improved disassembly of external method calls. Added the -e option to
-allow the inclusion of additional ACPI tables to help with the disassembly of
+iASL: Improved disassembly of external method calls. Added the -e option
+to
+allow the inclusion of additional ACPI tables to help with the
+disassembly
+of
method invocations and the generation of external declarations during the
disassembly. Certain external method invocations cannot be disassembled
-properly without the actual declaration of the method. Use the -e option to
-include the table where the external method(s) are actually declared. Most
+properly without the actual declaration of the method. Use the -e option
+to
+include the table where the external method(s) are actually declared.
+Most
useful for disassembling SSDTs that make method calls back to the master
-DSDT. Lin Ming. Example: To disassemble an SSDT with calls to DSDT: iasl -d
+DSDT. Lin Ming. Example: To disassemble an SSDT with calls to DSDT: iasl
+-d
-e dsdt.aml ssdt1.aml
iASL: Fix to allow references to aliases within ASL namepaths. Fixes a
@@ -1924,46 +6860,61 @@ references from the Alias operator itself. ACPICA BZ 738.
1) ACPI CA Core Subsystem:
-Designed and implemented a mechanism to validate predefined ACPI methods and
-objects. This code validates the predefined ACPI objects (objects whose names
+Designed and implemented a mechanism to validate predefined ACPI methods
+and
+objects. This code validates the predefined ACPI objects (objects whose
+names
start with underscore) that appear in the namespace, at the time they are
evaluated. The argument count and the type of the returned object are
-validated against the ACPI specification. The purpose of this validation is
-to detect problems with the BIOS-implemented predefined ACPI objects before
-the results are returned to the ACPI-related drivers. Future enhancements may
+validated against the ACPI specification. The purpose of this validation
+is
+to detect problems with the BIOS-implemented predefined ACPI objects
+before
+the results are returned to the ACPI-related drivers. Future enhancements
+may
include actual repair of incorrect return objects where possible. Two new
files are nspredef.c and acpredef.h.
-Fixed a fault in the AML parser if a memory allocation fails during the Op
+Fixed a fault in the AML parser if a memory allocation fails during the
+Op
completion routine AcpiPsCompleteThisOp. Lin Ming. ACPICA BZ 492.
-Fixed an issue with implicit return compatibility. This change improves the
-implicit return mechanism to be more compatible with the MS interpreter. Lin
+Fixed an issue with implicit return compatibility. This change improves
+the
+implicit return mechanism to be more compatible with the MS interpreter.
+Lin
Ming, ACPICA BZ 349.
-Implemented support for zero-length buffer-to-string conversions. Allow zero
-length strings during interpreter buffer-to-string conversions. For example,
+Implemented support for zero-length buffer-to-string conversions. Allow
+zero
+length strings during interpreter buffer-to-string conversions. For
+example,
during the ToDecimalString and ToHexString operators, as well as implicit
conversions. Fiodor Suietov, ACPICA BZ 585.
Fixed two possible memory leaks in the error exit paths of
-AcpiUtUpdateObjectReference and AcpiUtWalkPackageTree. These functions are
+AcpiUtUpdateObjectReference and AcpiUtWalkPackageTree. These functions
+are
similar in that they use a stack of state objects in order to eliminate
recursion. The stack must be fully unwound and deallocated if an error
occurs. Lin Ming. ACPICA BZ 383.
-Removed the unused ACPI_BITREG_WAKE_ENABLE definition and entry in the global
+Removed the unused ACPI_BITREG_WAKE_ENABLE definition and entry in the
+global
ACPI register table. This bit does not exist and is unused. Lin Ming, Bob
Moore ACPICA BZ 442.
Removed the obsolete version number in module headers. Removed the
-"$Revision" number that appeared in each module header. This version number
-was useful under SourceSafe and CVS, but has no meaning under git. It is not
+"$Revision" number that appeared in each module header. This version
+number
+was useful under SourceSafe and CVS, but has no meaning under git. It is
+not
only incorrect, it could also be misleading.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -1979,43 +6930,57 @@ much larger code and data size.
1) ACPI CA Core Subsystem:
Completed a major cleanup of the internal ACPI_OPERAND_OBJECT of type
-Reference. Changes include the elimination of cheating on the Object field
+Reference. Changes include the elimination of cheating on the Object
+field
for the DdbHandle subtype, addition of a reference class field to
-differentiate the various reference types (instead of an AML opcode), and the
+differentiate the various reference types (instead of an AML opcode), and
+the
cleanup of debug output for this object. Lin Ming, Bob Moore. BZ 723
Reduce an error to a warning for an incorrect method argument count.
Previously aborted with an error if too few arguments were passed to a
-control method via the external ACPICA interface. Now issue a warning instead
-and continue. Handles the case where the method inadvertently declares too
-many arguments, but does not actually use the extra ones. Applies mainly to
+control method via the external ACPICA interface. Now issue a warning
+instead
+and continue. Handles the case where the method inadvertently declares
+too
+many arguments, but does not actually use the extra ones. Applies mainly
+to
the predefined methods. Lin Ming. Linux BZ 11032.
-Disallow the evaluation of named object types with no intrinsic value. Return
-AE_TYPE for objects that have no value and therefore evaluation is undefined:
-Device, Event, Mutex, Region, Thermal, and Scope. Previously, evaluation of
-these types were allowed, but an exception would be generated at some point
+Disallow the evaluation of named object types with no intrinsic value.
+Return
+AE_TYPE for objects that have no value and therefore evaluation is
+undefined:
+Device, Event, Mutex, Region, Thermal, and Scope. Previously, evaluation
+of
+these types were allowed, but an exception would be generated at some
+point
during the evaluation. Now, the error is generated up front.
Fixed a possible memory leak in the AcpiNsGetExternalPathname function
(nsnames.c). Fixes a leak in the error exit path.
-Removed the obsolete debug levels ACPI_DB_WARN and ACPI_DB_ERROR. These debug
-levels were made obsolete by the ACPI_WARNING, ACPI_ERROR, and ACPI_EXCEPTION
+Removed the obsolete debug levels ACPI_DB_WARN and ACPI_DB_ERROR. These
+debug
+levels were made obsolete by the ACPI_WARNING, ACPI_ERROR, and
+ACPI_EXCEPTION
interfaces. Also added ACPI_DB_EVENTS to correspond with the existing
ACPI_LV_EVENTS.
Removed obsolete and/or unused exception codes from the acexcep.h header.
-There is the possibility that certain device drivers may be affected if they
+There is the possibility that certain device drivers may be affected if
+they
use any of these exceptions.
-The ACPICA documentation has been added to the public git source tree, under
+The ACPICA documentation has been added to the public git source tree,
+under
acpica/documents. Included are the ACPICA programmer reference, the iASL
compiler reference, and the changes.txt release logfile.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -2031,14 +6996,18 @@ Allow multiple argument counts for the predefined _SCP method. ACPI 3.0
defines _SCP with 3 arguments. Previous versions defined it with only 1
argument. iASL now allows both definitions.
-iASL/disassembler: avoid infinite loop on bad ACPI tables. Check for zero-
+iASL/disassembler: avoid infinite loop on bad ACPI tables. Check for
+zero-
length subtables when disassembling ACPI tables. Also fixed a couple of
-errors where a full 16-bit table type field was not extracted from the input
+errors where a full 16-bit table type field was not extracted from the
+input
properly.
acpisrc: Improve comment counting mechanism for generating source code
-statistics. Count first and last lines of multi-line comments as whitespace,
-not comment lines. Handle Linux legal header in addition to standard acpica
+statistics. Count first and last lines of multi-line comments as
+whitespace,
+not comment lines. Handle Linux legal header in addition to standard
+acpica
header.
----------------------------------------
@@ -2048,41 +7017,57 @@ header.
1) ACPI CA Core Subsystem:
Fix a possible deadlock in the GPE dispatch. Remove call to
-AcpiHwDisableAllGpes during wake in AcpiEvGpeDispatch. This call will attempt
-to acquire the GPE lock but can deadlock since the GPE lock is already held
-at dispatch time. This code was introduced in version 20060831 as a response
+AcpiHwDisableAllGpes during wake in AcpiEvGpeDispatch. This call will
+attempt
+to acquire the GPE lock but can deadlock since the GPE lock is already
+held
+at dispatch time. This code was introduced in version 20060831 as a
+response
to Linux BZ 6881 and has since been removed from Linux.
-Add a function to dereference returned reference objects. Examines the return
-object from a call to AcpiEvaluateObject. Any Index or RefOf references are
-automatically dereferenced in an attempt to return something useful (these
-reference types cannot be converted into an external ACPI_OBJECT.) Provides
+Add a function to dereference returned reference objects. Examines the
+return
+object from a call to AcpiEvaluateObject. Any Index or RefOf references
+are
+automatically dereferenced in an attempt to return something useful
+(these
+reference types cannot be converted into an external ACPI_OBJECT.)
+Provides
MS compatibility. Lin Ming, Bob Moore. Linux BZ 11105
x2APIC support: changes for MADT and SRAT ACPI tables. There are 2 new
subtables for the MADT and one new subtable for the SRAT. Includes
-disassembler and AcpiSrc support. Data from the Intel 64 Architecture x2APIC
+disassembler and AcpiSrc support. Data from the Intel 64 Architecture
+x2APIC
Specification, June 2008.
-Additional error checking for pathname utilities. Add error check after all
+Additional error checking for pathname utilities. Add error check after
+all
calls to AcpiNsGetPathnameLength. Add status return from
-AcpiNsBuildExternalPath and check after all calls. Add parameter validation
+AcpiNsBuildExternalPath and check after all calls. Add parameter
+validation
to AcpiUtInitializeBuffer. Reported by and initial patch by Ingo Molnar.
-Return status from the global init function AcpiUtGlobalInitialize. This is
-used by both the kernel subsystem and the utilities such as iASL compiler.
-The function could possibly fail when the caches are initialized. Yang Yi.
+Return status from the global init function AcpiUtGlobalInitialize. This
+is
+used by both the kernel subsystem and the utilities such as iASL
+compiler.
+The function could possibly fail when the caches are initialized. Yang
+Yi.
Add a function to decode reference object types to strings. Created for
improved error messages.
-Improve object conversion error messages. Better error messages during object
-conversion from internal to the external ACPI_OBJECT. Used for external calls
+Improve object conversion error messages. Better error messages during
+object
+conversion from internal to the external ACPI_OBJECT. Used for external
+calls
to AcpiEvaluateObject.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -2094,9 +7079,12 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Debugger: fix a possible hang when evaluating non-methods. Fixes a problem
-introduced in version 20080701. If the object being evaluated (via execute
-command) is not a method, the debugger can hang while trying to obtain non-
+Debugger: fix a possible hang when evaluating non-methods. Fixes a
+problem
+introduced in version 20080701. If the object being evaluated (via
+execute
+command) is not a method, the debugger can hang while trying to obtain
+non-
existent parameters.
iASL: relax error for using reserved "_T_x" identifiers. These names can
@@ -2104,16 +7092,24 @@ appear in a disassembled ASL file if they were emitted by the original
compiler. Instead of issuing an error or warning and forcing the user to
manually change these names, issue a remark instead.
-iASL: error if named object created in while loop. Emit an error if any named
-object is created within a While loop. If allowed, this code will generate a
-run-time error on the second iteration of the loop when an attempt is made to
+iASL: error if named object created in while loop. Emit an error if any
+named
+object is created within a While loop. If allowed, this code will
+generate
+a
+run-time error on the second iteration of the loop when an attempt is
+made
+to
create the same named object twice. ACPICA bugzilla 730.
-iASL: Support absolute pathnames for include files. Add support for absolute
-pathnames within the Include operator. previously, only relative pathnames
+iASL: Support absolute pathnames for include files. Add support for
+absolute
+pathnames within the Include operator. previously, only relative
+pathnames
were supported.
-iASL: Enforce minimum 1 interrupt in interrupt macro and Resource Descriptor.
+iASL: Enforce minimum 1 interrupt in interrupt macro and Resource
+Descriptor.
The ACPI spec requires one interrupt minimum. BZ 423
iASL: Handle a missing ResourceSource arg, with a present SourceIndex.
@@ -2121,13 +7117,16 @@ Handles the case for the Interrupt Resource Descriptor where
the ResourceSource argument is omitted but ResourceSourceIndex
is present. Now leave room for the Index. BZ 426
-iASL: Prevent error message if CondRefOf target does not exist. Fixes cases
+iASL: Prevent error message if CondRefOf target does not exist. Fixes
+cases
where an error message is emitted if the target does not exist. BZ 516
iASL: Fix broken -g option (get Windows ACPI tables). Fixes the -g option
-(get ACPI tables on Windows). This was apparently broken in version 20070919.
+(get ACPI tables on Windows). This was apparently broken in version
+20070919.
-AcpiXtract: Handle EOF while extracting data. Correctly handle the case where
+AcpiXtract: Handle EOF while extracting data. Correctly handle the case
+where
the EOF happens immediately after the last table in the input file. Print
completion message. Previously, no message was displayed in this case.
@@ -2142,9 +7141,12 @@ source tree.
1) ACPI CA Core Subsystem:
Implemented a "careful" GPE disable in AcpiEvDisableGpe, only modify one
-enable bit. Now performs a read-change-write of the enable register instead
-of simply writing out the cached enable mask. This will prevent inadvertent
-enabling of GPEs if a rogue GPE is received during initialization (before GPE
+enable bit. Now performs a read-change-write of the enable register
+instead
+of simply writing out the cached enable mask. This will prevent
+inadvertent
+enabling of GPEs if a rogue GPE is received during initialization (before
+GPE
handlers are installed.)
Implemented a copy for dynamically loaded tables. Previously, dynamically
@@ -2154,32 +7156,42 @@ OpRegion case, added checksum verify. Use the table length from the table
header, not the region length. For the Buffer case, use the table length
also. Dennis Noordsij, Bob Moore. BZ 10734
-Fixed a problem where the same ACPI table could not be dynamically loaded and
-unloaded more than once. Without this change, a table cannot be loaded again
+Fixed a problem where the same ACPI table could not be dynamically loaded
+and
+unloaded more than once. Without this change, a table cannot be loaded
+again
once it has been loaded/unloaded one time. The current mechanism does not
-unregister a table upon an unload. During a load, if the same table is found,
+unregister a table upon an unload. During a load, if the same table is
+found,
this no longer returns an exception. BZ 722
Fixed a problem where the wrong descriptor length was calculated for the
-EndTag descriptor in 64-bit mode. The "minimal" descriptors such as EndTag
+EndTag descriptor in 64-bit mode. The "minimal" descriptors such as
+EndTag
are calculated as 12 bytes long, but the actual length in the internal
-descriptor is 16 because of the round-up to 8 on the 64-bit build. Reported
+descriptor is 16 because of the round-up to 8 on the 64-bit build.
+Reported
by Linn Crosetto. BZ 728
-Fixed a possible memory leak in the Unload operator. The DdbHandle returned
-by Load() did not have its reference count decremented during unload, leading
+Fixed a possible memory leak in the Unload operator. The DdbHandle
+returned
+by Load() did not have its reference count decremented during unload,
+leading
to a memory leak. Lin Ming. BZ 727
Fixed a possible memory leak when deleting thermal/processor objects. Any
associated notify handlers (and objects) were not being deleted. Fiodor
Suietov. BZ 506
-Fixed the ordering of the ASCII names in the global mutex table to match the
-actual mutex IDs. Used by AcpiUtGetMutexName, a function used for debug only.
+Fixed the ordering of the ASCII names in the global mutex table to match
+the
+actual mutex IDs. Used by AcpiUtGetMutexName, a function used for debug
+only.
Vegard Nossum. BZ 726
Enhanced the AcpiGetObjectInfo interface to return the number of required
-arguments if the object is a control method. Added this call to the debugger
+arguments if the object is a control method. Added this call to the
+debugger
so the proper number of default arguments are passed to a method. This
prevents a warning when executing methods from AcpiExec.
@@ -2190,7 +7202,8 @@ Fixed an extraneous warning from exconfig.c on the 64-bit build.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -2205,20 +7218,28 @@ much larger code and data size.
iASL: Added two missing ACPI reserved names. Added _MTP and _ASZ, both
resource descriptor names.
-iASL: Detect invalid ASCII characters in input (windows version). Removed the
+iASL: Detect invalid ASCII characters in input (windows version). Removed
+the
"-CF" flag from the flex compile, enables correct detection of non-ASCII
characters in the input. BZ 441
-iASL: Eliminate warning when result of LoadTable is not used. Eliminate the
+iASL: Eliminate warning when result of LoadTable is not used. Eliminate
+the
"result of operation not used" warning when the DDB handle returned from
LoadTable is not used. The warning is not needed. BZ 590
-AcpiExec: Add support for dynamic table load/unload. Now calls _CFG method to
-pass address of table to the AML. Added option to disable OpRegion simulation
-to allow creation of an OpRegion with a real address that was passed to _CFG.
-All of this allows testing of the Load and Unload operators from AcpiExec.
-
-Debugger: update tables command for unloaded tables. Handle unloaded tables
+AcpiExec: Add support for dynamic table load/unload. Now calls _CFG
+method
+to
+pass address of table to the AML. Added option to disable OpRegion
+simulation
+to allow creation of an OpRegion with a real address that was passed to
+_CFG.
+All of this allows testing of the Load and Unload operators from
+AcpiExec.
+
+Debugger: update tables command for unloaded tables. Handle unloaded
+tables
and use the standard table header output routine.
----------------------------------------
@@ -2226,49 +7247,64 @@ and use the standard table header output routine.
1) ACPI CA Core Subsystem:
-Implemented a workaround for reversed _PRT entries. A significant number of
+Implemented a workaround for reversed _PRT entries. A significant number
+of
BIOSs erroneously reverse the _PRT SourceName and the SourceIndex. This
-change dynamically detects and repairs this problem. Provides compatibility
+change dynamically detects and repairs this problem. Provides
+compatibility
with MS ACPI. BZ 6859
Simplified the internal ACPI hardware interfaces to eliminate the locking
flag parameter from Register Read/Write. Added a new external interface,
AcpiGetRegisterUnlocked.
-Fixed a problem where the invocation of a GPE control method could hang. This
+Fixed a problem where the invocation of a GPE control method could hang.
+This
was a regression introduced in 20080514. The new method argument count
validation mechanism can enter an infinite loop when a GPE method is
-dispatched. Problem fixed by removing the obsolete code that passed GPE block
-information to the notify handler via the control method parameter pointer.
+dispatched. Problem fixed by removing the obsolete code that passed GPE
+block
+information to the notify handler via the control method parameter
+pointer.
-Fixed a problem where the _SST execution status was incorrectly returned to
-the caller of AcpiEnterSleepStatePrep. This was a regression introduced in
+Fixed a problem where the _SST execution status was incorrectly returned
+to
+the caller of AcpiEnterSleepStatePrep. This was a regression introduced
+in
20080514. _SST is optional and a NOT_FOUND exception should never be
returned. BZ 716
-Fixed a problem where a deleted object could be accessed from within the AML
-parser. This was a regression introduced in version 20080123 as a fix for the
+Fixed a problem where a deleted object could be accessed from within the
+AML
+parser. This was a regression introduced in version 20080123 as a fix for
+the
Unload operator. Lin Ming. BZ 10669
-Cleaned up the debug operand dump mechanism. Eliminated unnecessary operands
+Cleaned up the debug operand dump mechanism. Eliminated unnecessary
+operands
and eliminated the use of a negative index in a loop. Operands are now
-displayed in the correct order, not backwards. This also fixes a regression
+displayed in the correct order, not backwards. This also fixes a
+regression
introduced in 20080514 on 64-bit systems where the elimination of
-ACPI_NATIVE_UINT caused the negative index to go large and positive. BZ 715
+ACPI_NATIVE_UINT caused the negative index to go large and positive. BZ
+715
-Fixed a possible memory leak in EvPciConfigRegionSetup where the error exit
+Fixed a possible memory leak in EvPciConfigRegionSetup where the error
+exit
path did not delete a locally allocated structure.
Updated definitions for the DMAR and SRAT tables to synchronize with the
current specifications. Includes disassembler support.
Fixed a problem in the mutex debug code (in utmutex.c) where an incorrect
-loop termination value was used. Loop terminated on iteration early, missing
+loop termination value was used. Loop terminated on iteration early,
+missing
one mutex. Linn Crosetto
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -2294,39 +7330,54 @@ Disassembler: Added support for DMAR and SRAT table definition changes.
Fixed a problem where GPEs were enabled too early during the ACPICA
initialization. This could lead to "handler not installed" errors on some
-machines. Moved GPE enable until after _REG/_STA/_INI methods are run. This
-ensures that all operation regions and devices throughout the namespace have
+machines. Moved GPE enable until after _REG/_STA/_INI methods are run.
+This
+ensures that all operation regions and devices throughout the namespace
+have
been initialized before GPEs are enabled. Alexey Starikovskiy, BZ 9916.
Implemented a change to the enter sleep code. Moved execution of the _GTS
-method to just before setting sleep enable bit. The execution was moved from
+method to just before setting sleep enable bit. The execution was moved
+from
AcpiEnterSleepStatePrep to AcpiEnterSleepState. _GTS is now executed
immediately before the SLP_EN bit is set, as per the ACPI specification.
Luming Yu, BZ 1653.
-Implemented a fix to disable unknown GPEs (2nd version). Now always disable
+Implemented a fix to disable unknown GPEs (2nd version). Now always
+disable
the GPE, even if ACPICA thinks that that it is already disabled. It is
-possible that the AML or some other code has enabled the GPE unbeknownst to
+possible that the AML or some other code has enabled the GPE unbeknownst
+to
the ACPICA code.
-Fixed a problem with the Field operator where zero-length fields would return
-an AE_AML_NO_OPERAND exception during table load. Fix enables zero-length ASL
+Fixed a problem with the Field operator where zero-length fields would
+return
+an AE_AML_NO_OPERAND exception during table load. Fix enables zero-length
+ASL
field declarations in Field(), BankField(), and IndexField(). BZ 10606.
-Implemented a fix for the Load operator, now load the table at the namespace
-root. This reverts a change introduced in version 20071019. The table is now
+Implemented a fix for the Load operator, now load the table at the
+namespace
+root. This reverts a change introduced in version 20071019. The table is
+now
loaded at the namespace root even though this goes against the ACPI
-specification. This provides compatibility with other ACPI implementations.
-The ACPI specification will be updated to reflect this in ACPI 4.0. Lin Ming.
+specification. This provides compatibility with other ACPI
+implementations.
+The ACPI specification will be updated to reflect this in ACPI 4.0. Lin
+Ming.
-Fixed a problem where ACPICA would not Load() tables with unusual signatures.
+Fixed a problem where ACPICA would not Load() tables with unusual
+signatures.
Now ignore ACPI table signature for Load() operator. Only "SSDT" is
acceptable to the ACPI spec, but tables are seen with OEMx and null sigs.
-Therefore, signature validation is worthless. Apparently MS ACPI accepts such
+Therefore, signature validation is worthless. Apparently MS ACPI accepts
+such
signatures, ACPICA must be compatible. BZ 10454.
-Fixed a possible negative array index in AcpiUtValidateException. Added NULL
-fields to the exception string arrays to eliminate a -1 subtraction on the
+Fixed a possible negative array index in AcpiUtValidateException. Added
+NULL
+fields to the exception string arrays to eliminate a -1 subtraction on
+the
SubStatus field.
Updated the debug tracking macros to reduce overall code and data size.
@@ -2334,13 +7385,19 @@ Changed ACPI_MODULE_NAME and ACPI_FUNCTION_NAME to use arrays of strings
instead of pointers to static strings. Jan Beulich and Bob Moore.
Implemented argument count checking in control method invocation via
-AcpiEvaluateObject. Now emit an error if too few arguments, warning if too
-many. This applies only to extern programmatic control method execution, not
+AcpiEvaluateObject. Now emit an error if too few arguments, warning if
+too
+many. This applies only to extern programmatic control method execution,
+not
method-to-method calls within the AML. Lin Ming.
-Eliminated the ACPI_NATIVE_UINT type across all ACPICA code. This type is no
-longer needed, especially with the removal of 16-bit support. It was replaced
-mostly with UINT32, but also ACPI_SIZE where a type that changes 32/64 bit on
+Eliminated the ACPI_NATIVE_UINT type across all ACPICA code. This type is
+no
+longer needed, especially with the removal of 16-bit support. It was
+replaced
+mostly with UINT32, but also ACPI_SIZE where a type that changes 32/64
+bit
+on
32/64-bit platforms is required.
Added the C const qualifier for appropriate string constants -- mostly
@@ -2348,7 +7405,8 @@ MODULE_NAME and printf format strings. Jan Beulich.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has a
+debug version of the code includes the debug output trace mechanism and
+has a
much larger code and data size.
Previous Release:
@@ -2360,8 +7418,10 @@ much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Implemented ACPI table revision ID validation in the disassembler. Zero is
-always invalid. For DSDTs, the ID controls the interpreter integer width. 1
+Implemented ACPI table revision ID validation in the disassembler. Zero
+is
+always invalid. For DSDTs, the ID controls the interpreter integer width.
+1
means 32-bit and this is unusual. 2 or greater is 64-bit.
----------------------------------------
@@ -2370,54 +7430,75 @@ means 32-bit and this is unusual. 2 or greater is 64-bit.
1) ACPI CA Core Subsystem:
Implemented an additional change to the GPE support in order to suppress
-spurious or stray GPEs. The AcpiEvDisableGpe function will now permanently
-disable incoming GPEs that are neither enabled nor disabled -- meaning that
-the GPE is unknown to the system. This should prevent future interrupt floods
+spurious or stray GPEs. The AcpiEvDisableGpe function will now
+permanently
+disable incoming GPEs that are neither enabled nor disabled -- meaning
+that
+the GPE is unknown to the system. This should prevent future interrupt
+floods
from that GPE. BZ 6217 (Zhang Rui)
Fixed a problem where NULL package elements were not returned to the
AcpiEvaluateObject interface correctly. The element was simply ignored
-instead of returning a NULL ACPI_OBJECT package element, potentially causing
-a buffer overflow and/or confusing the caller who expected a fixed number of
+instead of returning a NULL ACPI_OBJECT package element, potentially
+causing
+a buffer overflow and/or confusing the caller who expected a fixed number
+of
elements. BZ 10132 (Lin Ming, Bob Moore)
-Fixed a problem with the CreateField, CreateXXXField (Bit, Byte, Word, Dword,
-Qword), Field, BankField, and IndexField operators when invoked from inside
-an executing control method. In this case, these operators created namespace
+Fixed a problem with the CreateField, CreateXXXField (Bit, Byte, Word,
+Dword,
+Qword), Field, BankField, and IndexField operators when invoked from
+inside
+an executing control method. In this case, these operators created
+namespace
nodes that were incorrectly left marked as permanent nodes instead of
temporary nodes. This could cause a problem if there is race condition
-between an exiting control method and a running namespace walk. (Reported by
+between an exiting control method and a running namespace walk. (Reported
+by
Linn Crosetto)
Fixed a problem where the CreateField and CreateXXXField operators would
-incorrectly allow duplicate names (the name of the field) with no exception
+incorrectly allow duplicate names (the name of the field) with no
+exception
generated.
-Implemented several changes for Notify handling. Added support for new Notify
+Implemented several changes for Notify handling. Added support for new
+Notify
values (ACPI 2.0+) and improved the Notify debug output. Notify on
-PowerResource objects is no longer allowed, as per the ACPI specification.
+PowerResource objects is no longer allowed, as per the ACPI
+specification.
(Bob Moore, Zhang Rui)
-All Reference Objects returned via the AcpiEvaluateObject interface are now
-marked as type "REFERENCE" instead of "ANY". The type ANY is now reserved for
-NULL objects - either NULL package elements or unresolved named references.
+All Reference Objects returned via the AcpiEvaluateObject interface are
+now
+marked as type "REFERENCE" instead of "ANY". The type ANY is now reserved
+for
+NULL objects - either NULL package elements or unresolved named
+references.
-Fixed a problem where an extraneous debug message was produced for package
+Fixed a problem where an extraneous debug message was produced for
+package
objects (when debugging enabled). The message "Package List length larger
-than NumElements count" is now produced in the correct case, and is now an
+than NumElements count" is now produced in the correct case, and is now
+an
error message rather than a debug message. Added a debug message for the
-opposite case, where NumElements is larger than the Package List (the package
+opposite case, where NumElements is larger than the Package List (the
+package
will be padded out with NULL elements as per the ACPI spec.)
-Implemented several improvements for the output of the ASL "Debug" object to
+Implemented several improvements for the output of the ASL "Debug" object
+to
clarify and keep all data for a given object on one output line.
-Fixed two size calculation issues with the variable-length Start Dependent
+Fixed two size calculation issues with the variable-length Start
+Dependent
resource descriptor.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2429,38 +7510,51 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Fixed a problem with the use of the Switch operator where execution of the
+Fixed a problem with the use of the Switch operator where execution of
+the
containing method by multiple concurrent threads could cause an
AE_ALREADY_EXISTS exception. This is caused by the fact that there is no
actual Switch opcode, it must be simulated with local named temporary
-variables and if/else pairs. The solution chosen was to mark any method that
-uses Switch as Serialized, thus preventing multiple thread entries. BZ 469.
+variables and if/else pairs. The solution chosen was to mark any method
+that
+uses Switch as Serialized, thus preventing multiple thread entries. BZ
+469.
----------------------------------------
13 February 2008. Summary of changes for version 20080213:
1) ACPI CA Core Subsystem:
-Implemented another MS compatibility design change for GPE/Notify handling.
-GPEs are now cleared/enabled asynchronously to allow all pending notifies to
+Implemented another MS compatibility design change for GPE/Notify
+handling.
+GPEs are now cleared/enabled asynchronously to allow all pending notifies
+to
complete first. It is expected that the OSL will queue the enable request
-behind all pending notify requests (may require changes to the local host OSL
+behind all pending notify requests (may require changes to the local host
+OSL
in AcpiOsExecute). Alexey Starikovskiy.
Fixed a problem where buffer and package objects passed as arguments to a
-control method via the external AcpiEvaluateObject interface could cause an
+control method via the external AcpiEvaluateObject interface could cause
+an
AE_AML_INTERNAL exception depending on the order and type of operators
executed by the target control method.
Fixed a problem where resource descriptor size optimization could cause a
-problem when a _CRS resource template is passed to a _SRS method. The _SRS
+problem when a _CRS resource template is passed to a _SRS method. The
+_SRS
resource template must use the same descriptors (with the same size) as
-returned from _CRS. This change affects the following resource descriptors:
-IRQ / IRQNoFlags and StartDependendentFn / StartDependentFnNoPri. (BZ 9487)
-
-Fixed a problem where a CopyObject to RegionField, BankField, and IndexField
-objects did not perform an implicit conversion as it should. These types must
-retain their initial type permanently as per the ACPI specification. However,
+returned from _CRS. This change affects the following resource
+descriptors:
+IRQ / IRQNoFlags and StartDependendentFn / StartDependentFnNoPri. (BZ
+9487)
+
+Fixed a problem where a CopyObject to RegionField, BankField, and
+IndexField
+objects did not perform an implicit conversion as it should. These types
+must
+retain their initial type permanently as per the ACPI specification.
+However,
a CopyObject to all other object types should not perform an implicit
conversion, as per the ACPI specification. (Lin Ming, Bob Moore) BZ 388
@@ -2468,7 +7562,8 @@ Fixed a problem with the AcpiGetDevices interface where the mechanism to
match device CIDs did not examine the entire list of available CIDs, but
instead aborted on the first non-matching CID. Andrew Patterson.
-Fixed a regression introduced in version 20071114. The ACPI_HIDWORD macro was
+Fixed a regression introduced in version 20071114. The ACPI_HIDWORD macro
+was
inadvertently changed to return a 16-bit value instead of a 32-bit value,
truncating the upper dword of a 64-bit value. This macro is only used to
display debug output, so no incorrect calculations were made. Also,
@@ -2480,7 +7575,8 @@ statement.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2506,39 +7602,56 @@ Added the 2008 copyright to all module headers and signons. This affects
virtually every file in the ACPICA core subsystem, the iASL compiler, and
the tools/utilities.
-Fixed a problem with the SizeOf operator when used with Package and Buffer
-objects. These objects have deferred execution for some arguments, and the
-execution is now completed before the SizeOf is executed. This problem caused
-unexpected AE_PACKAGE_LIMIT errors on some systems (Lin Ming, Bob Moore) BZ
+Fixed a problem with the SizeOf operator when used with Package and
+Buffer
+objects. These objects have deferred execution for some arguments, and
+the
+execution is now completed before the SizeOf is executed. This problem
+caused
+unexpected AE_PACKAGE_LIMIT errors on some systems (Lin Ming, Bob Moore)
+BZ
9558
-Implemented an enhancement to the interpreter "slack mode". In the absence of
-an explicit return or an implicitly returned object from the last executed
-opcode, a control method will now implicitly return an integer of value 0 for
+Implemented an enhancement to the interpreter "slack mode". In the
+absence
+of
+an explicit return or an implicitly returned object from the last
+executed
+opcode, a control method will now implicitly return an integer of value 0
+for
Microsoft compatibility. (Lin Ming) BZ 392
-Fixed a problem with the Load operator where an exception was not returned in
+Fixed a problem with the Load operator where an exception was not
+returned
+in
the case where the table is already loaded. (Lin Ming) BZ 463
-Implemented support for the use of DDBHandles as an Indexed Reference, as per
+Implemented support for the use of DDBHandles as an Indexed Reference, as
+per
the ACPI spec. (Lin Ming) BZ 486
-Implemented support for UserTerm (Method invocation) for the Unload operator
+Implemented support for UserTerm (Method invocation) for the Unload
+operator
as per the ACPI spec. (Lin Ming) BZ 580
-Fixed a problem with the LoadTable operator where the OemId and OemTableId
-input strings could cause unexpected failures if they were shorter than the
+Fixed a problem with the LoadTable operator where the OemId and
+OemTableId
+input strings could cause unexpected failures if they were shorter than
+the
maximum lengths allowed. (Lin Ming, Bob Moore) BZ 576
-Implemented support for UserTerm (Method invocation) for the Unload operator
+Implemented support for UserTerm (Method invocation) for the Unload
+operator
as per the ACPI spec. (Lin Ming) BZ 580
-Implemented header file support for new ACPI tables - BERT, ERST, EINJ, HEST,
+Implemented header file support for new ACPI tables - BERT, ERST, EINJ,
+HEST,
IBFT, UEFI, WDAT. Disassembler support is forthcoming.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2550,25 +7663,32 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Implemented support in the disassembler for checksum validation on incoming
-binary DSDTs and SSDTs. If incorrect, a message is displayed within the table
+Implemented support in the disassembler for checksum validation on
+incoming
+binary DSDTs and SSDTs. If incorrect, a message is displayed within the
+table
header dump at the start of the disassembly.
-Implemented additional debugging information in the namespace listing file
-created during compilation. In addition to the namespace hierarchy, the full
+Implemented additional debugging information in the namespace listing
+file
+created during compilation. In addition to the namespace hierarchy, the
+full
pathname to each namespace object is displayed.
-Fixed a problem with the disassembler where invalid ACPI tables could cause
+Fixed a problem with the disassembler where invalid ACPI tables could
+cause
faults or infinite loops.
Fixed an unexpected parse error when using the optional "parameter types"
list in a control method declaration. (Lin Ming) BZ 397
-Fixed a problem where two External declarations with the same name did not
+Fixed a problem where two External declarations with the same name did
+not
cause an error (Lin Ming) BZ 509
Implemented support for full TermArgs (adding Argx, Localx and method
-invocation) for the ParameterData parameter to the LoadTable operator. (Lin
+invocation) for the ParameterData parameter to the LoadTable operator.
+(Lin
Ming) BZ 583,587
----------------------------------------
@@ -2578,33 +7698,44 @@ Ming) BZ 583,587
Implemented full support for deferred execution for the TermArg string
arguments for DataTableRegion. This enables forward references and full
-operand resolution for the three string arguments. Similar to OperationRegion
+operand resolution for the three string arguments. Similar to
+OperationRegion
deferred argument execution.) Lin Ming. BZ 430
-Implemented full argument resolution support for the BankValue argument to
-BankField. Previously, only constants were supported, now any TermArg may be
+Implemented full argument resolution support for the BankValue argument
+to
+BankField. Previously, only constants were supported, now any TermArg may
+be
used. Lin Ming BZ 387, 393
Fixed a problem with AcpiGetDevices where the search of a branch of the
device tree could be terminated prematurely. In accordance with the ACPI
-specification, the search down the current branch is terminated if a device
-is both not present and not functional (instead of just not present.) Yakui
+specification, the search down the current branch is terminated if a
+device
+is both not present and not functional (instead of just not present.)
+Yakui
Zhao.
-Fixed a problem where "unknown" GPEs could be allowed to fire repeatedly if
-the underlying AML code changed the GPE enable registers. Now, any unknown
-incoming GPE (no _Lxx/_Exx method and not the EC GPE) is immediately disabled
+Fixed a problem where "unknown" GPEs could be allowed to fire repeatedly
+if
+the underlying AML code changed the GPE enable registers. Now, any
+unknown
+incoming GPE (no _Lxx/_Exx method and not the EC GPE) is immediately
+disabled
instead of simply ignored. Rui Zhang.
-Fixed a problem with Index Fields where the Index register was incorrectly
+Fixed a problem with Index Fields where the Index register was
+incorrectly
limited to a maximum of 32 bits. Now any size may be used.
-Fixed a couple memory leaks associated with "implicit return" objects when
+Fixed a couple memory leaks associated with "implicit return" objects
+when
the AML Interpreter slack mode is enabled. Lin Ming BZ 349
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2621,37 +7752,49 @@ a much larger code and data size.
Implemented event counters for each of the Fixed Events, the ACPI SCI
(interrupt) itself, and control methods executed. Named
-AcpiFixedEventCount[], AcpiSciCount, and AcpiMethodCount respectively. These
+AcpiFixedEventCount[], AcpiSciCount, and AcpiMethodCount respectively.
+These
should be useful for debugging and statistics.
Implemented a new external interface, AcpiGetStatistics, to retrieve the
contents of the various event counters. Returns the current values for
AcpiSciCount, AcpiGpeCount, the AcpiFixedEventCount array, and
-AcpiMethodCount. The interface can be expanded in the future if new counters
-are added. Device drivers should use this interface rather than access the
+AcpiMethodCount. The interface can be expanded in the future if new
+counters
+are added. Device drivers should use this interface rather than access
+the
counters directly.
-Fixed a problem with the FromBCD and ToBCD operators. With some compilers,
-the ShortDivide function worked incorrectly, causing problems with the BCD
+Fixed a problem with the FromBCD and ToBCD operators. With some
+compilers,
+the ShortDivide function worked incorrectly, causing problems with the
+BCD
functions with large input values. A truncation from 64-bit to 32-bit
inadvertently occurred. Internal BZ 435. Lin Ming
-Fixed a problem with Index references passed as method arguments. References
-passed as arguments to control methods were dereferenced immediately (before
-control was passed to the called method). The references are now correctly
+Fixed a problem with Index references passed as method arguments.
+References
+passed as arguments to control methods were dereferenced immediately
+(before
+control was passed to the called method). The references are now
+correctly
passed directly to the called method. BZ 5389. Lin Ming
-Fixed a problem with CopyObject used in conjunction with the Index operator.
-The reference was incorrectly dereferenced before the copy. The reference is
+Fixed a problem with CopyObject used in conjunction with the Index
+operator.
+The reference was incorrectly dereferenced before the copy. The reference
+is
now correctly copied. BZ 5391. Lin Ming
-Fixed a problem with Control Method references within Package objects. These
+Fixed a problem with Control Method references within Package objects.
+These
references are now correctly generated. This completes the package
construction overhaul that began in version 20071019.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2668,9 +7811,11 @@ The AcpiExec utility now installs handlers for all of the predefined
Operation Region types. New types supported are: PCI_Config, CMOS, and
PCIBARTarget.
-Fixed a problem with the 64-bit version of AcpiExec where the extended (64-
+Fixed a problem with the 64-bit version of AcpiExec where the extended
+(64-
bit) address fields for the DSDT and FACS within the FADT were not being
-used, causing truncation of the upper 32-bits of these addresses. Lin Ming
+used, causing truncation of the upper 32-bits of these addresses. Lin
+Ming
and Bob Moore
----------------------------------------
@@ -2679,14 +7824,18 @@ and Bob Moore
1) ACPI CA Core Subsystem:
Fixed a problem with the Alias operator when the target of the alias is a
-named ASL operator that opens a new scope -- Scope, Device, PowerResource,
+named ASL operator that opens a new scope -- Scope, Device,
+PowerResource,
Processor, and ThermalZone. In these cases, any children of the original
-operator could not be accessed via the alias, potentially causing unexpected
+operator could not be accessed via the alias, potentially causing
+unexpected
AE_NOT_FOUND exceptions. (BZ 9067)
Fixed a problem with the Package operator where all named references were
-created as object references and left otherwise unresolved. According to the
-ACPI specification, a Package can only contain Data Objects or references to
+created as object references and left otherwise unresolved. According to
+the
+ACPI specification, a Package can only contain Data Objects or references
+to
control methods. The implication is that named references to Data Objects
(Integer, Buffer, String, Package, BufferField, Field) should be resolved
immediately upon package creation. This is the approach taken with this
@@ -2695,11 +7844,13 @@ etc.) are all now properly created as reference objects. (BZ 5328)
Reverted a change to Notify handling that was introduced in version
20070508. This version changed the Notify handling from asynchronous to
-fully synchronous (Device driver Notify handling with respect to the Notify
+fully synchronous (Device driver Notify handling with respect to the
+Notify
ASL operator). It was found that this change caused more problems than it
solved and was removed by most users.
-Fixed a problem with the Increment and Decrement operators where the type of
+Fixed a problem with the Increment and Decrement operators where the type
+of
the target object could be unexpectedly and incorrectly changed. (BZ 353)
Lin Ming.
@@ -2710,19 +7861,24 @@ loaded into the root or current scope. Lin Ming.
Fixed a problem with the Load operator when loading a table from a buffer
object. The input buffer was prematurely zeroed and/or deleted. (BZ 577)
-Fixed a problem with the Debug object where a store of a DdbHandle reference
+Fixed a problem with the Debug object where a store of a DdbHandle
+reference
object to the Debug object could cause a fault.
-Added a table checksum verification for the Load operator, in the case where
+Added a table checksum verification for the Load operator, in the case
+where
the load is from a buffer. (BZ 578).
-Implemented additional parameter validation for the LoadTable operator. The
-length of the input strings SignatureString, OemIdString, and OemTableId are
+Implemented additional parameter validation for the LoadTable operator.
+The
+length of the input strings SignatureString, OemIdString, and OemTableId
+are
now checked for maximum lengths. (BZ 582) Lin Ming.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2745,7 +7901,8 @@ version 20070917.)
1) ACPI CA Core Subsystem:
Designed and implemented new external interfaces to install and remove
-handlers for ACPI table-related events. Current events that are defined are
+handlers for ACPI table-related events. Current events that are defined
+are
LOAD and UNLOAD. These interfaces allow the host to track ACPI tables as
they are dynamically loaded and unloaded. See AcpiInstallTableHandler and
AcpiRemoveTableHandler. (Lin Ming and Bob Moore)
@@ -2760,7 +7917,8 @@ referenced from within the same control method (Lin Ming) BZ 341
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2773,22 +7931,30 @@ a much larger code and data size.
2) iASL Compiler/Disassembler:
-Implemented support to allow multiple files to be compiled/disassembled in a
-single invocation. This includes command line wildcard support for both the
+Implemented support to allow multiple files to be compiled/disassembled
+in
+a
+single invocation. This includes command line wildcard support for both
+the
Windows and Unix versions of the compiler. This feature simplifies the
-disassembly and compilation of multiple ACPI tables in a single directory.
+disassembly and compilation of multiple ACPI tables in a single
+directory.
----------------------------------------
08 May 2007. Summary of changes for version 20070508:
1) ACPI CA Core Subsystem:
-Implemented a Microsoft compatibility design change for the handling of the
+Implemented a Microsoft compatibility design change for the handling of
+the
Notify AML operator. Previously, notify handlers were dispatched and
executed completely asynchronously in a deferred thread. The new design
-still executes the notify handlers in a different thread, but the original
-thread that executed the Notify() now waits at a synchronization point for
-the notify handler to complete. Some machines depend on a synchronous Notify
+still executes the notify handlers in a different thread, but the
+original
+thread that executed the Notify() now waits at a synchronization point
+for
+the notify handler to complete. Some machines depend on a synchronous
+Notify
operator in order to operate correctly.
Implemented support to allow Package objects to be passed as method
@@ -2797,15 +7963,21 @@ would return the AE_NOT_IMPLEMENTED exception. This feature had not been
implemented since there were no reserved control methods that required it
until recently.
-Fixed a problem with the internal FADT conversion where ACPI 1.0 FADTs that
+Fixed a problem with the internal FADT conversion where ACPI 1.0 FADTs
+that
contained invalid non-zero values in reserved fields could cause later
-failures because these fields have meaning in later revisions of the FADT.
-For incoming ACPI 1.0 FADTs, these fields are now always zeroed. (The fields
+failures because these fields have meaning in later revisions of the
+FADT.
+For incoming ACPI 1.0 FADTs, these fields are now always zeroed. (The
+fields
are: Preferred_PM_Profile, PSTATE_CNT, CST_CNT, and IAPC_BOOT_FLAGS.)
-Fixed a problem where the Global Lock handle was not properly updated if a
-thread that acquired the Global Lock via executing AML code then attempted
-to acquire the lock via the AcpiAcquireGlobalLock interface. Reported by Joe
+Fixed a problem where the Global Lock handle was not properly updated if
+a
+thread that acquired the Global Lock via executing AML code then
+attempted
+to acquire the lock via the AcpiAcquireGlobalLock interface. Reported by
+Joe
Liu.
Fixed a problem in AcpiEvDeleteGpeXrupt where the global interrupt list
@@ -2814,7 +7986,8 @@ list. Reported by Linn Crosetto.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2832,46 +8005,61 @@ a much larger code and data size.
Implemented a change to the order of interpretation and evaluation of AML
operand objects within the AML interpreter. The interpreter now evaluates
operands in the order that they appear in the AML stream (and the
-corresponding ASL code), instead of in the reverse order (after the entire
-operand list has been parsed). The previous behavior caused several subtle
+corresponding ASL code), instead of in the reverse order (after the
+entire
+operand list has been parsed). The previous behavior caused several
+subtle
incompatibilities with the Microsoft AML interpreter as well as being
somewhat non-intuitive. BZ 7871, local BZ 263. Valery Podrezov.
-Implemented a change to the ACPI Global Lock support. All interfaces to the
+Implemented a change to the ACPI Global Lock support. All interfaces to
+the
global lock now allow the same thread to acquire the lock multiple times.
-This affects the AcpiAcquireGlobalLock external interface to the global lock
+This affects the AcpiAcquireGlobalLock external interface to the global
+lock
as well as the internal use of the global lock to support AML fields -- a
-control method that is holding the global lock can now simultaneously access
-AML fields that require global lock protection. Previously, in both cases,
-this would have resulted in an AE_ALREADY_ACQUIRED exception. The change to
+control method that is holding the global lock can now simultaneously
+access
+AML fields that require global lock protection. Previously, in both
+cases,
+this would have resulted in an AE_ALREADY_ACQUIRED exception. The change
+to
AcpiAcquireGlobalLock is of special interest to drivers for the Embedded
-Controller. There is no change to the behavior of the AML Acquire operator,
+Controller. There is no change to the behavior of the AML Acquire
+operator,
as this can already be used to acquire a mutex multiple times by the same
thread. BZ 8066. With assistance from Alexey Starikovskiy.
Fixed a problem where invalid objects could be referenced in the AML
-Interpreter after error conditions. During operand evaluation, ensure that
+Interpreter after error conditions. During operand evaluation, ensure
+that
the internal "Return Object" field is cleared on error and only valid
-pointers are stored there. Caused occasional access to deleted objects that
+pointers are stored there. Caused occasional access to deleted objects
+that
resulted in "large reference count" warning messages. Valery Podrezov.
-Fixed a problem where an AE_STACK_OVERFLOW internal exception could occur on
+Fixed a problem where an AE_STACK_OVERFLOW internal exception could occur
+on
deeply nested control method invocations. BZ 7873, local BZ 487. Valery
Podrezov.
Fixed an internal problem with the handling of result objects on the
interpreter result stack. BZ 7872. Valery Podrezov.
-Removed obsolete code that handled the case where AML_NAME_OP is the target
+Removed obsolete code that handled the case where AML_NAME_OP is the
+target
of a reference (Reference.Opcode). This code was no longer necessary. BZ
7874. Valery Podrezov.
-Removed obsolete ACPI_NO_INTEGER64_SUPPORT from two header files. This was a
+Removed obsolete ACPI_NO_INTEGER64_SUPPORT from two header files. This
+was
+a
remnant from the previously discontinued 16-bit support.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2891,12 +8079,14 @@ virtually every file in the ACPICA core subsystem, the iASL compiler, and
the utilities.
Implemented a fix for an incorrect parameter passed to AcpiTbDeleteTable
-during a table load. A bad pointer was passed in the case where the DSDT is
+during a table load. A bad pointer was passed in the case where the DSDT
+is
overridden, causing a fault in this case.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2911,9 +8101,11 @@ a much larger code and data size.
1) ACPI CA Core Subsystem:
-Support for 16-bit ACPICA has been completely removed since it is no longer
+Support for 16-bit ACPICA has been completely removed since it is no
+longer
necessary and it clutters the code. All 16-bit macros, types, and
-conditional compiles have been removed, cleaning up and simplifying the code
+conditional compiles have been removed, cleaning up and simplifying the
+code
across the entire subsystem. DOS support is no longer needed since the
bootable Linux firmware kit is now available.
@@ -2922,17 +8114,22 @@ enable a clean subsystem restart, via the implementation of the
AcpiEvRemoveGlobalLockHandler function. (With assistance from Joel Bretz,
HP)
-Implemented enhancements to the multithreading support within the debugger
-to enable improved multithreading debugging and evaluation of the subsystem.
+Implemented enhancements to the multithreading support within the
+debugger
+to enable improved multithreading debugging and evaluation of the
+subsystem.
(Valery Podrezov)
-Debugger: Enhanced the Statistics/Memory command to emit the total (maximum)
-memory used during the execution, as well as the maximum memory consumed by
+Debugger: Enhanced the Statistics/Memory command to emit the total
+(maximum)
+memory used during the execution, as well as the maximum memory consumed
+by
each of the various object types. (Valery Podrezov)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -2953,30 +8150,36 @@ statistics upon subsystem/program termination. (Valery Podrezov)
1) ACPI CA Core Subsystem:
-Optimized the Load ASL operator in the case where the source operand is an
+Optimized the Load ASL operator in the case where the source operand is
+an
operation region. Simply map the operation region memory, instead of
performing a bytewise read. (Region must be of type SystemMemory, see
below.)
Fixed the Load ASL operator for the case where the source operand is a
-region field. A buffer object is also allowed as the source operand. BZ 480
+region field. A buffer object is also allowed as the source operand. BZ
+480
-Fixed a problem where the Load ASL operator allowed the source operand to be
+Fixed a problem where the Load ASL operator allowed the source operand to
+be
an operation region of any type. It is now restricted to regions of type
SystemMemory, as per the ACPI specification. BZ 481
Additional cleanup and optimizations for the new Table Manager code.
-AcpiEnable will now fail if all of the required ACPI tables are not loaded
+AcpiEnable will now fail if all of the required ACPI tables are not
+loaded
(FADT, FACS, DSDT). BZ 477
-Added #pragma pack(8/4) to acobject.h to ensure that the structures in this
+Added #pragma pack(8/4) to acobject.h to ensure that the structures in
+this
header are always compiled as aligned. The ACPI_OPERAND_OBJECT has been
manually optimized to be aligned and will not work if it is byte-packed.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3007,33 +8210,41 @@ interpreter performance by ~25%, reduces code size, and reduces CPU stack
use. (Valery Podrezov + interpreter changes in version 20051202 that
eliminated namespace loading during the pass one parse.)
-Implemented _CID support for PCI Root Bridge detection. If the _HID does not
-match the predefined PCI Root Bridge IDs, the _CID list (if present) is now
+Implemented _CID support for PCI Root Bridge detection. If the _HID does
+not
+match the predefined PCI Root Bridge IDs, the _CID list (if present) is
+now
obtained and also checked for an ID match.
-Implemented additional support for the PCI _ADR execution: upsearch until a
+Implemented additional support for the PCI _ADR execution: upsearch until
+a
device scope is found before executing _ADR. This allows PCI_Config
-operation regions to be declared locally within control methods underneath
+operation regions to be declared locally within control methods
+underneath
PCI device objects.
Fixed a problem with a possible race condition between threads executing
AcpiWalkNamespace and the AML interpreter. This condition was removed by
-modifying AcpiWalkNamespace to (by default) ignore all temporary namespace
+modifying AcpiWalkNamespace to (by default) ignore all temporary
+namespace
entries created during any concurrent control method execution. An
additional namespace race condition is known to exist between
AcpiWalkNamespace and the Load/Unload ASL operators and is still under
investigation.
Restructured the AML ParseLoop function, breaking it into several
-subfunctions in order to reduce CPU stack use and improve maintainability.
+subfunctions in order to reduce CPU stack use and improve
+maintainability.
(Mikhail Kouzmich)
-AcpiGetHandle: Fix for parameter validation to detect invalid combinations
+AcpiGetHandle: Fix for parameter validation to detect invalid
+combinations
of prefix handle and pathname. BZ 478
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3045,7 +8256,8 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Ported the -g option (get local ACPI tables) to the new ACPICA Table Manager
+Ported the -g option (get local ACPI tables) to the new ACPICA Table
+Manager
to restore original behavior.
----------------------------------------
@@ -3059,15 +8271,18 @@ level indication flag is not needed.
Fixed a problem with the Global Lock where the lock could appear to be
obtained before it is actually obtained. The global lock semaphore was
-inadvertently created with one unit instead of zero units. (BZ 464) Fiodor
+inadvertently created with one unit instead of zero units. (BZ 464)
+Fiodor
Suietov.
-Fixed a possible memory leak and fault in AcpiExResolveObjectToValue during
+Fixed a possible memory leak and fault in AcpiExResolveObjectToValue
+during
a read from a buffer or region field. (BZ 458) Fiodor Suietov.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3080,20 +8295,25 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Fixed a compilation problem with the pre-defined Resource Descriptor field
-names where an "object does not exist" error could be incorrectly generated
+Fixed a compilation problem with the pre-defined Resource Descriptor
+field
+names where an "object does not exist" error could be incorrectly
+generated
if the parent ResourceTemplate pathname places the template within a
different namespace scope than the current scope. (BZ 7212)
-Fixed a problem where the compiler could hang after syntax errors detected
+Fixed a problem where the compiler could hang after syntax errors
+detected
in an ElseIf construct. (BZ 453)
Fixed a problem with the AmlFilename parameter to the DefinitionBlock()
-operator. An incorrect output filename was produced when this parameter was
+operator. An incorrect output filename was produced when this parameter
+was
a null string (""). Now, the original input filename is used as the AML
output filename, with an ".aml" extension.
-Implemented a generic batch command mode for the AcpiExec utility (execute
+Implemented a generic batch command mode for the AcpiExec utility
+(execute
any AML debugger command) (Valery Podrezov).
----------------------------------------
@@ -3103,15 +8323,18 @@ any AML debugger command) (Valery Podrezov).
Enhanced the implementation of the "serialized mode" of the interpreter
(enabled via the AcpiGbl_AllMethodsSerialized flag.) When this mode is
-specified, instead of creating a serialization semaphore per control method,
+specified, instead of creating a serialization semaphore per control
+method,
the interpreter lock is simply no longer released before a blocking
operation during control method execution. This effectively makes the AML
Interpreter single-threaded. The overhead of a semaphore per-method is
eliminated.
-Fixed a regression where an error was no longer emitted if a control method
+Fixed a regression where an error was no longer emitted if a control
+method
attempts to create 2 objects of the same name. This once again returns
-AE_ALREADY_EXISTS. When this exception occurs, it invokes the mechanism that
+AE_ALREADY_EXISTS. When this exception occurs, it invokes the mechanism
+that
will dynamically serialize the control method to possible prevent future
errors. (BZ 440)
@@ -3122,13 +8345,18 @@ Moved all FADT-related functions to a new file, tbfadt.c. Eliminated the
AcpiHwInitialize function - the FADT registers are now validated when the
table is loaded.
-Added two new warnings during FADT verification - 1) if the FADT is larger
-than the largest known FADT version, and 2) if there is a mismatch between a
-32-bit block address and the 64-bit X counterpart (when both are non-zero.)
+Added two new warnings during FADT verification - 1) if the FADT is
+larger
+than the largest known FADT version, and 2) if there is a mismatch
+between
+a
+32-bit block address and the 64-bit X counterpart (when both are non-
+zero.)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3141,8 +8369,10 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Fixed a problem with the implementation of the Switch() operator where the
-temporary variable was declared too close to the actual Switch, instead of
+Fixed a problem with the implementation of the Switch() operator where
+the
+temporary variable was declared too close to the actual Switch, instead
+of
at method level. This could cause a problem if the Switch() operator is
within a while loop, causing an error on the second iteration. (BZ 460)
@@ -3152,17 +8382,20 @@ operator. Now, ignore it and continue.
Disassembly of an FADT now verifies the input FADT and reports any errors
found. Fix for proper disassembly of full-sized (ACPI 2.0) FADTs.
-Disassembly of raw data buffers with byte initialization data now prefixes
+Disassembly of raw data buffers with byte initialization data now
+prefixes
each output line with the current buffer offset.
Disassembly of ASF! table now includes all variable-length data fields at
the end of some of the subtables.
The disassembler now emits a comment if a buffer appears to be a
-ResourceTemplate, but cannot be disassembled as such because the EndTag does
+ResourceTemplate, but cannot be disassembled as such because the EndTag
+does
not appear at the very end of the buffer.
-AcpiExec - Added the "-t" command line option to enable the serialized mode
+AcpiExec - Added the "-t" command line option to enable the serialized
+mode
of the AML interpreter.
----------------------------------------
@@ -3178,11 +8411,13 @@ Miscellaneous fixes for the Table Manager:
- Additional parameter validation for AcpiGetTable, AcpiGetTableHeader,
AcpiGetTableByIndex
-Change for GPE support: when a "wake" GPE is received, all wake GPEs are now
+Change for GPE support: when a "wake" GPE is received, all wake GPEs are
+now
immediately disabled to prevent the waking GPE from firing again and to
prevent other wake GPEs from interrupting the wake process.
-Added the AcpiGpeCount global that tracks the number of processed GPEs, to
+Added the AcpiGpeCount global that tracks the number of processed GPEs,
+to
be used for debugging systems with a large number of ACPI interrupts.
Implemented support for the "DMAR" ACPI table (DMA Redirection Table) in
@@ -3190,7 +8425,8 @@ both the ACPICA headers and the disassembler.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3211,62 +8447,79 @@ Disassembler support for the DMAR ACPI table.
1) ACPI CA Core Subsystem:
The Table Manager component has been completely redesigned and
-reimplemented. The new design is much simpler, and reduces the overall code
-and data size of the kernel-resident ACPICA by approximately 5%. Also, it is
+reimplemented. The new design is much simpler, and reduces the overall
+code
+and data size of the kernel-resident ACPICA by approximately 5%. Also, it
+is
now possible to obtain the ACPI tables very early during kernel
initialization, even before dynamic memory management is initialized.
(Alexey Starikovskiy, Fiodor Suietov, Bob Moore)
Obsolete ACPICA interfaces:
-- AcpiGetFirmwareTable: Use AcpiGetTable instead (works at early kernel init
+- AcpiGetFirmwareTable: Use AcpiGetTable instead (works at early kernel
+init
time).
- AcpiLoadTable: Not needed.
- AcpiUnloadTable: Not needed.
New ACPICA interfaces:
-- AcpiInitializeTables: Must be called before the table manager can be used.
+- AcpiInitializeTables: Must be called before the table manager can be
+used.
- AcpiReallocateRootTable: Used to transfer the root table to dynamically
allocated memory after it becomes available.
-- AcpiGetTableByIndex: Allows the host to easily enumerate all ACPI tables
+- AcpiGetTableByIndex: Allows the host to easily enumerate all ACPI
+tables
in the RSDT/XSDT.
Other ACPICA changes:
-- AcpiGetTableHeader returns the actual mapped table header, not a copy. Use
+- AcpiGetTableHeader returns the actual mapped table header, not a copy.
+Use
AcpiOsUnmapMemory to free this mapping.
- AcpiGetTable returns the actual mapped table. The mapping is managed
internally and must not be deleted by the caller. Use of this interface
causes no additional dynamic memory allocation.
-- AcpiFindRootPointer: Support for physical addressing has been eliminated,
+- AcpiFindRootPointer: Support for physical addressing has been
+eliminated,
it appeared to be unused.
- The interface to AcpiOsMapMemory has changed to be consistent with the
other allocation interfaces.
-- The interface to AcpiOsGetRootPointer has changed to eliminate unnecessary
+- The interface to AcpiOsGetRootPointer has changed to eliminate
+unnecessary
parameters.
-- ACPI_PHYSICAL_ADDRESS is now 32 bits on 32-bit platforms, 64 bits on 64-
+- ACPI_PHYSICAL_ADDRESS is now 32 bits on 32-bit platforms, 64 bits on
+64-
bit platforms. Was previously 64 bits on all platforms.
-- The interface to the ACPI Global Lock acquire/release macros have changed
+- The interface to the ACPI Global Lock acquire/release macros have
+changed
slightly since ACPICA no longer keeps a local copy of the FACS with a
constructed pointer to the actual global lock.
Porting to the new table manager:
- AcpiInitializeTables: Must be called once, and can be called anytime
-during the OS initialization process. It allows the host to specify an area
+during the OS initialization process. It allows the host to specify an
+area
of memory to be used to store the internal version of the RSDT/XSDT (root
-table). This allows the host to access ACPI tables before memory management
+table). This allows the host to access ACPI tables before memory
+management
is initialized and running.
-- AcpiReallocateRootTable: Can be called after memory management is running
+- AcpiReallocateRootTable: Can be called after memory management is
+running
to copy the root table to a dynamically allocated array, freeing up the
scratch memory specified in the call to AcpiInitializeTables.
- AcpiSubsystemInitialize: This existing interface is independent of the
-Table Manager, and does not have to be called before the Table Manager can
+Table Manager, and does not have to be called before the Table Manager
+can
be used, it only must be called before the rest of ACPICA can be used.
-- ACPI Tables: Some changes have been made to the names and structure of the
-actbl.h and actbl1.h header files and may require changes to existing code.
-For example, bitfields have been completely removed because of their lack of
+- ACPI Tables: Some changes have been made to the names and structure of
+the
+actbl.h and actbl1.h header files and may require changes to existing
+code.
+For example, bitfields have been completely removed because of their lack
+of
portability across C compilers.
- Update interfaces to the Global Lock acquire/release macros if local
versions are used. (see acwin.h)
@@ -3277,7 +8530,8 @@ New files: tbfind.c
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3299,43 +8553,56 @@ No changes for this release.
The full source code for the ASL test suite used to validate the iASL
compiler and the ACPICA core subsystem is being released with the ACPICA
-source for the first time. The source is contained in a separate package and
-consists of over 1100 files that exercise all ASL/AML operators. The package
-should appear on the Intel/ACPI web site shortly. (Valery Podrezov, Fiodor
+source for the first time. The source is contained in a separate package
+and
+consists of over 1100 files that exercise all ASL/AML operators. The
+package
+should appear on the Intel/ACPI web site shortly. (Valery Podrezov,
+Fiodor
Suietov)
Completed a new design and implementation for support of the ACPI Global
Lock. On the OS side, the global lock is now treated as a standard AML
mutex. Previously, multiple OS threads could "acquire" the global lock
-simultaneously. However, this could cause the BIOS to be starved out of the
+simultaneously. However, this could cause the BIOS to be starved out of
+the
lock - especially in cases such as the Embedded Controller driver where
there is a tight coupling between the OS and the BIOS.
Implemented an optimization for the ACPI Global Lock interrupt mechanism.
The Global Lock interrupt handler no longer queues the execution of a
-separate thread to signal the global lock semaphore. Instead, the semaphore
+separate thread to signal the global lock semaphore. Instead, the
+semaphore
is signaled directly from the interrupt handler.
Implemented support within the AML interpreter for package objects that
-contain a larger AML length (package list length) than the package element
+contain a larger AML length (package list length) than the package
+element
count. In this case, the length of the package is truncated to match the
-package element count. Some BIOS code apparently modifies the package length
-on the fly, and this change supports this behavior. Provides compatibility
+package element count. Some BIOS code apparently modifies the package
+length
+on the fly, and this change supports this behavior. Provides
+compatibility
with the MS AML interpreter. (With assistance from Fiodor Suietov)
-Implemented a temporary fix for the BankValue parameter of a Bank Field to
+Implemented a temporary fix for the BankValue parameter of a Bank Field
+to
support all constant values, now including the Zero and One opcodes.
-Evaluation of this parameter must eventually be converted to a full TermArg
-evaluation. A not-implemented error is now returned (temporarily) for non-
+Evaluation of this parameter must eventually be converted to a full
+TermArg
+evaluation. A not-implemented error is now returned (temporarily) for
+non-
constant values for this parameter.
Fixed problem reports (Fiodor Suietov) integrated:
-- Fix for premature object deletion after CopyObject on Operation Region (BZ
+- Fix for premature object deletion after CopyObject on Operation Region
+(BZ
350)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3361,18 +8628,22 @@ structures - even though the hardware itself may support misaligned
transfers. Some of the debug data structures are packed by default to
minimize size.
-Added an error message for the case where AcpiOsGetThreadId() returns zero.
+Added an error message for the case where AcpiOsGetThreadId() returns
+zero.
A non-zero value is required by the core ACPICA code to ensure the proper
operation of AML mutexes and recursive control methods.
The DSDT is now the only ACPI table that determines whether the AML
-interpreter is in 32-bit or 64-bit mode. Not really a functional change, but
-the hooks for per-table 32/64 switching have been removed from the code. A
+interpreter is in 32-bit or 64-bit mode. Not really a functional change,
+but
+the hooks for per-table 32/64 switching have been removed from the code.
+A
clarification to the ACPI specification is forthcoming in ACPI 3.0B.
Fixed a possible leak of an OwnerID in the error path of
AcpiTbInitTableDescriptor (tbinstal.c), and migrated all table OwnerID
-deletion to a single place in AcpiTbUninstallTable to correct possible leaks
+deletion to a single place in AcpiTbUninstallTable to correct possible
+leaks
when using the AcpiTbDeleteTablesByType interface (with assistance from
Lance Ortiz.)
@@ -3380,7 +8651,8 @@ Fixed a problem with Serialized control methods where the semaphore
associated with the method could be over-signaled after multiple method
invocations.
-Fixed two issues with the locking of the internal namespace data structure.
+Fixed two issues with the locking of the internal namespace data
+structure.
Both the Unload() operator and AcpiUnloadTable interface now lock the
namespace during the namespace deletion associated with the table unload
(with assistance from Linn Crosetto.)
@@ -3391,19 +8663,23 @@ Fixed problem reports (Valery Podrezov) integrated:
Fixed problem reports (Fiodor Suietov) integrated:
- Incomplete cleanup branches in AcpiTbGetTableRsdt (BZ 369)
- On Address Space handler deletion, needless deactivation call (BZ 374)
-- AcpiRemoveAddressSpaceHandler: validate Device handle parameter (BZ 375)
-- Possible memory leak, Notify sub-objects of Processor, Power, ThermalZone
+- AcpiRemoveAddressSpaceHandler: validate Device handle parameter (BZ
+375)
+- Possible memory leak, Notify sub-objects of Processor, Power,
+ThermalZone
(BZ 376)
- AcpiRemoveAddressSpaceHandler: validate Handler parameter (BZ 378)
- Minimum Length of RSDT should be validated (BZ 379)
- AcpiRemoveNotifyHandler: return AE_NOT_EXIST if Processor Obj has no
Handler (BZ (380)
-- AcpiUnloadTable: return AE_NOT_EXIST if no table of specified type loaded
+- AcpiUnloadTable: return AE_NOT_EXIST if no table of specified type
+loaded
(BZ 381)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3429,39 +8705,53 @@ Implemented a new ACPI_SPINLOCK type for the OSL lock interfaces. This
allows the type to be customized to the host OS for improved efficiency
(since a spinlock is usually a very small object.)
-Implemented support for "ignored" bits in the ACPI registers. According to
+Implemented support for "ignored" bits in the ACPI registers. According
+to
the ACPI specification, these bits should be preserved when writing the
-registers via a read/modify/write cycle. There are 3 bits preserved in this
+registers via a read/modify/write cycle. There are 3 bits preserved in
+this
manner: PM1_CONTROL[0] (SCI_EN), PM1_CONTROL[9], and PM1_STATUS[11].
-Implemented the initial deployment of new OSL mutex interfaces. Since some
+Implemented the initial deployment of new OSL mutex interfaces. Since
+some
host operating systems have separate mutex and semaphore objects, this
feature was requested. The base code now uses mutexes (and the new mutex
interfaces) wherever a binary semaphore was used previously. However, for
-the current release, the mutex interfaces are defined as macros to map them
-to the existing semaphore interfaces. Therefore, no OSL changes are required
+the current release, the mutex interfaces are defined as macros to map
+them
+to the existing semaphore interfaces. Therefore, no OSL changes are
+required
at this time. (See acpiosxf.h)
Fixed several problems with the support for the control method SyncLevel
-parameter. The SyncLevel now works according to the ACPI specification and
-in concert with the Mutex SyncLevel parameter, since the current SyncLevel
-is a property of the executing thread. Mutual exclusion for control methods
+parameter. The SyncLevel now works according to the ACPI specification
+and
+in concert with the Mutex SyncLevel parameter, since the current
+SyncLevel
+is a property of the executing thread. Mutual exclusion for control
+methods
is now implemented with a mutex instead of a semaphore.
Fixed three instances of the use of the C shift operator in the bitfield
-support code (exfldio.c) to avoid the use of a shift value larger than the
-target data width. The behavior of C compilers is undefined in this case and
-can cause unpredictable results, and therefore the case must be detected and
+support code (exfldio.c) to avoid the use of a shift value larger than
+the
+target data width. The behavior of C compilers is undefined in this case
+and
+can cause unpredictable results, and therefore the case must be detected
+and
avoided. (Fiodor Suietov)
Added an info message whenever an SSDT or OEM table is loaded dynamically
-via the Load() or LoadTable() ASL operators. This should improve debugging
-capability since it will show exactly what tables have been loaded (beyond
+via the Load() or LoadTable() ASL operators. This should improve
+debugging
+capability since it will show exactly what tables have been loaded
+(beyond
the tables present in the RSDT/XSDT.)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3481,19 +8771,24 @@ No changes for this release.
1) ACPI CA Core Subsystem:
-Converted the locking mutex used for the ACPI hardware to a spinlock. This
+Converted the locking mutex used for the ACPI hardware to a spinlock.
+This
change should eliminate all problems caused by attempting to acquire a
semaphore at interrupt level, and it means that all ACPICA external
-interfaces that directly access the ACPI hardware can be safely called from
-interrupt level. OSL code that implements the semaphore interfaces should be
+interfaces that directly access the ACPI hardware can be safely called
+from
+interrupt level. OSL code that implements the semaphore interfaces should
+be
able to eliminate any workarounds for being called at interrupt level.
Fixed a regression introduced in 20060526 where the ACPI device
-initialization could be prematurely aborted with an AE_NOT_FOUND if a device
+initialization could be prematurely aborted with an AE_NOT_FOUND if a
+device
did not have an optional _INI method.
Fixed an IndexField issue where a write to the Data Register should be
-limited in size to the AccessSize (width) of the IndexField itself. (BZ 433,
+limited in size to the AccessSize (width) of the IndexField itself. (BZ
+433,
Fiodor Suietov)
Fixed problem reports (Valery Podrezov) integrated:
@@ -3507,7 +8802,8 @@ used.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3535,17 +8831,21 @@ Suietov)
1) ACPI CA Core Subsystem:
Restructured, flattened, and simplified the internal interfaces for
-namespace object evaluation - resulting in smaller code, less CPU stack use,
+namespace object evaluation - resulting in smaller code, less CPU stack
+use,
and fewer interfaces. (With assistance from Mikhail Kouzmich)
-Fixed a problem with the CopyObject operator where the first parameter was
-not typed correctly for the parser, interpreter, compiler, and disassembler.
+Fixed a problem with the CopyObject operator where the first parameter
+was
+not typed correctly for the parser, interpreter, compiler, and
+disassembler.
Caused various errors and unexpected behavior.
Fixed a problem where a ShiftLeft or ShiftRight of more than 64 bits
produced incorrect results with some C compilers. Since the behavior of C
compilers when the shift value is larger than the datatype width is
-apparently not well defined, the interpreter now detects this condition and
+apparently not well defined, the interpreter now detects this condition
+and
simply returns zero as expected in all such cases. (BZ 395)
Fixed problem reports (Valery Podrezov) integrated:
@@ -3553,21 +8853,25 @@ Fixed problem reports (Valery Podrezov) integrated:
- Allow interpreter to handle nested method declarations (BZ 5361)
Fixed problem reports (Fiodor Suietov) integrated:
-- AcpiTerminate doesn't free debug memory allocation list objects (BZ 355)
-- After Core Subsystem shutdown, AcpiSubsystemStatus returns AE_OK (BZ 356)
+- AcpiTerminate doesn't free debug memory allocation list objects (BZ
+355)
+- After Core Subsystem shutdown, AcpiSubsystemStatus returns AE_OK (BZ
+356)
- AcpiOsUnmapMemory for RSDP can be invoked inconsistently (BZ 357)
- Resource Manager should return AE_TYPE for non-device objects (BZ 358)
- Incomplete cleanup branch in AcpiNsEvaluateRelative (BZ 359)
- Use AcpiOsFree instead of ACPI_FREE in AcpiRsSetSrsMethodData (BZ 360)
- Incomplete cleanup branch in AcpiPsParseAml (BZ 361)
- Incomplete cleanup branch in AcpiDsDeleteWalkState (BZ 362)
-- AcpiGetTableHeader returns AE_NO_ACPI_TABLES until DSDT is loaded (BZ 365)
+- AcpiGetTableHeader returns AE_NO_ACPI_TABLES until DSDT is loaded (BZ
+365)
- Status of the Global Initialization Handler call not used (BZ 366)
- Incorrect object parameter to Global Initialization Handler (BZ 367)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3583,7 +8887,8 @@ a much larger code and data size.
Modified the parser to allow the names IO, DMA, and IRQ to be used as
namespace identifiers with no collision with existing resource descriptor
macro names. This provides compatibility with other ASL compilers and is
-most useful for disassembly/recompilation of existing tables without parse
+most useful for disassembly/recompilation of existing tables without
+parse
errors. (With assistance from Thomas Renninger)
Disassembler: fixed an incorrect disassembly problem with the
@@ -3596,27 +8901,37 @@ disassembly of some Alias operators.
1) ACPI CA Core Subsystem:
Replaced the AcpiOsQueueForExecution interface with a new interface named
-AcpiOsExecute. The major difference is that the new interface does not have
-a Priority parameter, this appeared to be useless and has been replaced by a
+AcpiOsExecute. The major difference is that the new interface does not
+have
+a Priority parameter, this appeared to be useless and has been replaced
+by
+a
Type parameter. The Type tells the host what type of execution is being
requested, such as global lock handler, notify handler, GPE handler, etc.
-This allows the host to queue and execute the request as appropriate for the
-request type, possibly using different work queues and different priorities
+This allows the host to queue and execute the request as appropriate for
+the
+request type, possibly using different work queues and different
+priorities
for the various request types. This enables fixes for multithreading
-deadlock problems such as BZ #5534, and will require changes to all existing
+deadlock problems such as BZ #5534, and will require changes to all
+existing
OS interface layers. (Alexey Starikovskiy and Bob Moore)
-Fixed a possible memory leak associated with the support for the so-called
+Fixed a possible memory leak associated with the support for the so-
+called
"implicit return" ACPI extension. Reported by FreeBSD, BZ #6514. (Fiodor
Suietov)
Fixed a problem with the Load() operator where a table load from an
-operation region could overwrite an internal table buffer by up to 7 bytes
-and cause alignment faults on IPF systems. (With assistance from Luming Yu)
+operation region could overwrite an internal table buffer by up to 7
+bytes
+and cause alignment faults on IPF systems. (With assistance from Luming
+Yu)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3630,16 +8945,22 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Disassembler: Implemented support to cross reference the internal namespace
-and automatically generate ASL External() statements for symbols not defined
+Disassembler: Implemented support to cross reference the internal
+namespace
+and automatically generate ASL External() statements for symbols not
+defined
within the current table being disassembled. This will simplify the
-disassembly and recompilation of interdependent tables such as SSDTs since
+disassembly and recompilation of interdependent tables such as SSDTs
+since
these statements will no longer have to be added manually.
Disassembler: Implemented experimental support to automatically detect
-invocations of external control methods and generate appropriate External()
-statements. This is problematic because the AML cannot be correctly parsed
-until the number of arguments for each control method is known. Currently,
+invocations of external control methods and generate appropriate
+External()
+statements. This is problematic because the AML cannot be correctly
+parsed
+until the number of arguments for each control method is known.
+Currently,
standalone method invocations and invocations as the source operand of a
Store() statement are supported.
@@ -3655,9 +8976,11 @@ more readable and likely closer to the original ASL source.
Removed a device initialization optimization introduced in 20051216 where
the _STA method was not run unless an _INI was also present for the same
-device. This optimization could cause problems because it could allow _INI
+device. This optimization could cause problems because it could allow
+_INI
methods to be run within a not-present device subtree. (If a not-present
-device had no _INI, _STA would not be run, the not-present status would not
+device had no _INI, _STA would not be run, the not-present status would
+not
be discovered, and the children of the device would be incorrectly
traversed.)
@@ -3667,7 +8990,8 @@ Selectively running _STA can significantly improve boot time on large
machines (with assistance from Len Brown.)
Implemented support for the device initialization case where the returned
-_STA flags indicate a device not-present but functioning. In this case, _INI
+_STA flags indicate a device not-present but functioning. In this case,
+_INI
is not run, but the device children are examined for presence, as per the
ACPI specification.
@@ -3680,7 +9004,8 @@ Defined and deployed a new OSL interface, AcpiOsValidateAddress. This
interface is called during the creation of all AML operation regions, and
allows the host OS to exert control over what addresses it will allow the
AML code to access. Operation Regions whose addresses are disallowed will
-cause a runtime exception when they are actually accessed (will not affect
+cause a runtime exception when they are actually accessed (will not
+affect
or abort table loading.) See oswinxf or osunixxf for an example
implementation.
@@ -3690,22 +9015,27 @@ interface/behavior strings for the _OSI predefined control method as
appropriate (with assistance from Bjorn Helgaas.) See oswinxf or osunixxf
for an example implementation.
-Restructured and corrected various problems in the exception handling code
+Restructured and corrected various problems in the exception handling
+code
paths within DsCallControlMethod and DsTerminateControlMethod in dsmethod
(with assistance from Takayoshi Kochi.)
-Modified the Linux source converter to ignore quoted string literals while
-converting identifiers from mixed to lower case. This will correct problems
+Modified the Linux source converter to ignore quoted string literals
+while
+converting identifiers from mixed to lower case. This will correct
+problems
with the disassembler and other areas where such strings must not be
modified.
The ACPI_FUNCTION_* macros no longer require quotes around the function
-name. This allows the Linux source converter to convert the names, now that
+name. This allows the Linux source converter to convert the names, now
+that
the converter ignores quoted strings.
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3719,21 +9049,28 @@ a much larger code and data size.
2) iASL Compiler/Disassembler and Tools:
-Implemented 3 new warnings for iASL, and implemented multiple warning levels
+Implemented 3 new warnings for iASL, and implemented multiple warning
+levels
(w2 flag).
-1) Ignored timeouts: If the TimeoutValue parameter to Wait or Acquire is not
+1) Ignored timeouts: If the TimeoutValue parameter to Wait or Acquire is
+not
WAIT_FOREVER (0xFFFF) and the code does not examine the return value to
check for the possible timeout, a warning is issued.
-2) Useless operators: If an ASL operator does not specify an optional target
+2) Useless operators: If an ASL operator does not specify an optional
+target
operand and it also does not use the function return value from the
-operator, a warning is issued since the operator effectively does nothing.
+operator, a warning is issued since the operator effectively does
+nothing.
3) Unreferenced objects: If a namespace object is created, but never
-referenced, a warning is issued. This is a warning level 2 since there are
-cases where this is ok, such as when a secondary table is loaded that uses
-the unreferenced objects. Even so, care is taken to only flag objects that
+referenced, a warning is issued. This is a warning level 2 since there
+are
+cases where this is ok, such as when a secondary table is loaded that
+uses
+the unreferenced objects. Even so, care is taken to only flag objects
+that
don't look like they will ever be used. For example, the reserved methods
(starting with an underscore) are usually not referenced because it is
expected that the OS will invoke them.
@@ -3744,19 +9081,24 @@ expected that the OS will invoke them.
1) ACPI CA Core Subsystem:
Implemented header file support for the following additional ACPI tables:
-ASF!, BOOT, CPEP, DBGP, MCFG, SPCR, SPMI, TCPA, and WDRT. With this support,
-all current and known ACPI tables are now defined in the ACPICA headers and
+ASF!, BOOT, CPEP, DBGP, MCFG, SPCR, SPMI, TCPA, and WDRT. With this
+support,
+all current and known ACPI tables are now defined in the ACPICA headers
+and
are available for use by device drivers and other software.
Implemented support to allow tables that contain ACPI names with invalid
characters to be loaded. Previously, this would cause the table load to
fail, but since there are several known cases of such tables on existing
-machines, this change was made to enable ACPI support for them. Also, this
+machines, this change was made to enable ACPI support for them. Also,
+this
matches the behavior of the Microsoft ACPI implementation.
-Fixed a couple regressions introduced during the memory optimization in the
+Fixed a couple regressions introduced during the memory optimization in
+the
20060317 release. The namespace node definition required additional
-reorganization and an internal datatype that had been changed to 8-bit was
+reorganization and an internal datatype that had been changed to 8-bit
+was
restored to 32-bit. (Valery Podrezov)
Fixed a problem where a null pointer passed to AcpiUtDeleteGenericState
@@ -3765,20 +9107,25 @@ null pointers are now trapped and ignored, matching the behavior of the
previous implementation before the deployment of AcpiOsReleaseObject.
(Valery Podrezov, Fiodor Suietov)
-Fixed a memory mapping leak during the deletion of a SystemMemory operation
+Fixed a memory mapping leak during the deletion of a SystemMemory
+operation
region where a cached memory mapping was not deleted. This became a
-noticeable problem for operation regions that are defined within frequently
+noticeable problem for operation regions that are defined within
+frequently
used control methods. (Dana Meyers)
Reorganized the ACPI table header files into two main files: one for the
-ACPI tables consumed by the ACPICA core, and another for the miscellaneous
-ACPI tables that are consumed by the drivers and other software. The various
+ACPI tables consumed by the ACPICA core, and another for the
+miscellaneous
+ACPI tables that are consumed by the drivers and other software. The
+various
FADT definitions were merged into one common section and three different
tables (ACPI 1.0, 1.0+, and 2.0)
Example Code and Data Size: These are the sizes for the OS-independent
acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. The
-debug version of the code includes the debug output trace mechanism and has
+debug version of the code includes the debug output trace mechanism and
+has
a much larger code and data size.
Previous Release:
@@ -3793,16 +9140,19 @@ a much larger code and data size.
Disassembler: Implemented support to decode and format all non-AML ACPI
tables (tables other than DSDTs and SSDTs.) This includes the new tables
-added to the ACPICA headers, therefore all current and known ACPI tables are
+added to the ACPICA headers, therefore all current and known ACPI tables
+are
supported.
Disassembler: The change to allow ACPI names with invalid characters also
-enables the disassembly of such tables. Invalid characters within names are
+enables the disassembly of such tables. Invalid characters within names
+are
changed to '*' to make the name printable; the iASL compiler will still
generate an error for such names, however, since this is an invalid ACPI
character.
-Implemented an option for AcpiXtract (-a) to extract all tables found in the
+Implemented an option for AcpiXtract (-a) to extract all tables found in
+the
input file. The default invocation extracts only the DSDTs and SSDTs.
Fixed a couple of gcc generation issues for iASL and AcpiExec and added a
@@ -3815,20 +9165,25 @@ makefile for the AcpiXtract utility.
Implemented the use of a cache object for all internal namespace nodes.
Since there are about 1000 static nodes in a typical system, this will
-decrease memory use for cache implementations that minimize per-allocation
+decrease memory use for cache implementations that minimize per-
+allocation
overhead (such as a slab allocator.)
-Removed the reference count mechanism for internal namespace nodes, since it
+Removed the reference count mechanism for internal namespace nodes, since
+it
was deemed unnecessary. This reduces the size of each namespace node by
-about 5%-10% on all platforms. Nodes are now 20 bytes for the 32-bit case,
+about 5%-10% on all platforms. Nodes are now 20 bytes for the 32-bit
+case,
and 32 bytes for the 64-bit case.
-Optimized several internal data structures to reduce object size on 64-bit
+Optimized several internal data structures to reduce object size on 64-
+bit
platforms by packing data within the 64-bit alignment. This includes the
frequently used ACPI_OPERAND_OBJECT, of which there can be ~1000 static
instances corresponding to the namespace objects.
-Added two new strings for the predefined _OSI method: "Windows 2001.1 SP1"
+Added two new strings for the predefined _OSI method: "Windows 2001.1
+SP1"
and "Windows 2006".
Split the allocation tracking mechanism out to a separate file, from
@@ -3836,13 +9191,17 @@ utalloc.c to uttrack.c. This mechanism appears to be only useful for
application-level code. Kernels may wish to not include uttrack.c in
distributions.
-Removed all remnants of the obsolete ACPI_REPORT_* macros and the associated
+Removed all remnants of the obsolete ACPI_REPORT_* macros and the
+associated
code. (These macros have been replaced by the ACPI_ERROR and ACPI_WARNING
macros.)
-Code and Data Size: These are the sizes for the acpica.lib produced by the
-Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any ACPI
-driver or OSPM code. The debug version of the code includes the debug output
+Code and Data Size: These are the sizes for the acpica.lib produced by
+the
+Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any
+ACPI
+driver or OSPM code. The debug version of the code includes the debug
+output
trace mechanism and has a much larger code and data size. Note that these
values will vary depending on the efficiency of the compiler and the
compiler options used during generation.
@@ -3857,7 +9216,8 @@ compiler options used during generation.
2) iASL Compiler/Disassembler and Tools:
-Implemented an ANSI C version of the acpixtract utility. This version will
+Implemented an ANSI C version of the acpixtract utility. This version
+will
automatically extract the DSDT and all SSDTs from the input acpidump text
file and dump the binary output to separate files. It can also display a
summary of the input file including the headers for each table found and
@@ -3870,32 +9230,41 @@ source/tools/acpixtract)
1) ACPI CA Core Subsystem:
Tagged all external interfaces to the subsystem with the new
-ACPI_EXPORT_SYMBOL macro. This macro can be defined as necessary to assist
+ACPI_EXPORT_SYMBOL macro. This macro can be defined as necessary to
+assist
kernel integration. For Linux, the macro resolves to the EXPORT_SYMBOL
macro. The default definition is NULL.
-Added the ACPI_THREAD_ID type for the return value from AcpiOsGetThreadId.
+Added the ACPI_THREAD_ID type for the return value from
+AcpiOsGetThreadId.
This allows the host to define this as necessary to simplify kernel
integration. The default definition is ACPI_NATIVE_UINT.
-Fixed two interpreter problems related to error processing, the deletion of
+Fixed two interpreter problems related to error processing, the deletion
+of
objects, and placing invalid pointers onto the internal operator result
stack. BZ 6028, 6151 (Valery Podrezov)
-Increased the reference count threshold where a warning is emitted for large
-reference counts in order to eliminate unnecessary warnings on systems with
+Increased the reference count threshold where a warning is emitted for
+large
+reference counts in order to eliminate unnecessary warnings on systems
+with
large namespaces (especially 64-bit.) Increased the value from 0x400 to
0x800.
-Due to universal disagreement as to the meaning of the 'c' in the calloc()
+Due to universal disagreement as to the meaning of the 'c' in the
+calloc()
function, the ACPI_MEM_CALLOCATE macro has been renamed to
ACPI_ALLOCATE_ZEROED so that the purpose of the interface is 'clear'.
ACPI_MEM_ALLOCATE and ACPI_MEM_FREE are renamed to ACPI_ALLOCATE and
ACPI_FREE.
-Code and Data Size: These are the sizes for the acpica.lib produced by the
-Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any ACPI
-driver or OSPM code. The debug version of the code includes the debug output
+Code and Data Size: These are the sizes for the acpica.lib produced by
+the
+Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any
+ACPI
+driver or OSPM code. The debug version of the code includes the debug
+output
trace mechanism and has a much larger code and data size. Note that these
values will vary depending on the efficiency of the compiler and the
compiler options used during generation.
@@ -3911,23 +9280,30 @@ compiler options used during generation.
2) iASL Compiler/Disassembler:
Disassembler: implemented support for symbolic resource descriptor
-references. If a CreateXxxxField operator references a fixed offset within a
-resource descriptor, a name is assigned to the descriptor and the offset is
+references. If a CreateXxxxField operator references a fixed offset
+within
+a
+resource descriptor, a name is assigned to the descriptor and the offset
+is
translated to the appropriate resource tag and pathname. The addition of
this support brings the disassembled code very close to the original ASL
-source code and helps eliminate run-time errors when the disassembled code
+source code and helps eliminate run-time errors when the disassembled
+code
is modified (and recompiled) in such a way as to invalidate the original
fixed offsets.
-Implemented support for a Descriptor Name as the last parameter to the ASL
+Implemented support for a Descriptor Name as the last parameter to the
+ASL
Register() macro. This parameter was inadvertently left out of the ACPI
specification, and will be added for ACPI 3.0b.
Fixed a problem where the use of the "_OSI" string (versus the full path
"\_OSI") caused an internal compiler error. ("No back ptr to op")
-Fixed a problem with the error message that occurs when an invalid string is
-used for a _HID object (such as one with an embedded asterisk: "*PNP010A".)
+Fixed a problem with the error message that occurs when an invalid string
+is
+used for a _HID object (such as one with an embedded asterisk:
+"*PNP010A".)
The correct message is now displayed.
----------------------------------------
@@ -3935,9 +9311,12 @@ The correct message is now displayed.
1) ACPI CA Core Subsystem:
-Implemented a change to the IndexField support to match the behavior of the
-Microsoft AML interpreter. The value written to the Index register is now a
-byte offset, no longer an index based upon the width of the Data register.
+Implemented a change to the IndexField support to match the behavior of
+the
+Microsoft AML interpreter. The value written to the Index register is now
+a
+byte offset, no longer an index based upon the width of the Data
+register.
This should fix IndexField problems seen on some machines where the Data
register is not exactly one byte wide. The ACPI specification will be
clarified on this point.
@@ -3947,12 +9326,16 @@ internal descriptor buffer due to size miscalculation: VendorShort,
VendorLong, and Interrupt. This was noticed on IA64 machines, but could
affect all platforms.
-Fixed a problem where individual resource descriptors were misaligned within
+Fixed a problem where individual resource descriptors were misaligned
+within
the internal buffer, causing alignment faults on IA64 platforms.
-Code and Data Size: These are the sizes for the acpica.lib produced by the
-Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any ACPI
-driver or OSPM code. The debug version of the code includes the debug output
+Code and Data Size: These are the sizes for the acpica.lib produced by
+the
+Microsoft Visual C++ 6.0 32-bit compiler. The values do not include any
+ACPI
+driver or OSPM code. The debug version of the code includes the debug
+output
trace mechanism and has a much larger code and data size. Note that these
values will vary depending on the efficiency of the compiler and the
compiler options used during generation.
@@ -3983,36 +9366,47 @@ Removed a couple of extraneous ACPI_ERROR messages that appeared during
normal execution. These became apparent after the conversion from
ACPI_DEBUG_PRINT.
-Fixed a problem where the CreateField operator could hang if the BitIndex or
+Fixed a problem where the CreateField operator could hang if the BitIndex
+or
NumBits parameter referred to a named object. (Valery Podrezov, BZ 5359)
Fixed a problem where a DeRefOf operation on a buffer object incorrectly
failed with an exception. This also fixes a couple of related RefOf and
DeRefOf issues. (Valery Podrezov, BZ 5360/5392/5387)
-Fixed a problem where the AE_BUFFER_LIMIT exception was returned instead of
-AE_STRING_LIMIT on an out-of-bounds Index() operation. (Valery Podrezov, BZ
+Fixed a problem where the AE_BUFFER_LIMIT exception was returned instead
+of
+AE_STRING_LIMIT on an out-of-bounds Index() operation. (Valery Podrezov,
+BZ
5480)
-Implemented a memory cleanup at the end of the execution of each iteration
-of an AML While() loop, preventing the accumulation of outstanding objects.
+Implemented a memory cleanup at the end of the execution of each
+iteration
+of an AML While() loop, preventing the accumulation of outstanding
+objects.
(Valery Podrezov, BZ 5427)
-Eliminated a chunk of duplicate code in the object resolution code. (Valery
+Eliminated a chunk of duplicate code in the object resolution code.
+(Valery
Podrezov, BZ 5336)
Fixed several warnings during the 64-bit code generation.
-The AcpiSrc source code conversion tool now inserts one line of whitespace
-after an if() statement that is followed immediately by a comment, improving
+The AcpiSrc source code conversion tool now inserts one line of
+whitespace
+after an if() statement that is followed immediately by a comment,
+improving
readability of the Linux code.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4025,7 +9419,8 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a problem with the disassembly of a BankField operator with a complex
+Fixed a problem with the disassembly of a BankField operator with a
+complex
expression for the BankValue parameter.
----------------------------------------
@@ -4033,11 +9428,14 @@ expression for the BankValue parameter.
1) ACPI CA Core Subsystem:
-Implemented support in the Resource Manager to allow unresolved namestring
-references within resource package objects for the _PRT method. This support
+Implemented support in the Resource Manager to allow unresolved
+namestring
+references within resource package objects for the _PRT method. This
+support
is in addition to the previously implemented unresolved reference support
within the AML parser. If the interpreter slack mode is enabled, these
-unresolved references will be passed through to the caller as a NULL package
+unresolved references will be passed through to the caller as a NULL
+package
entry.
Implemented and deployed new macros and functions for error and warning
@@ -4047,16 +9445,19 @@ ACPI_WARNING, and ACPI_INFO replace the ACPI_REPORT_* macros. The older
macros remain defined to allow ACPI drivers time to migrate to the new
macros.
-Implemented the ACPI_CPU_FLAGS type to simplify host OS integration of the
+Implemented the ACPI_CPU_FLAGS type to simplify host OS integration of
+the
Acquire/Release Lock OSL interfaces.
Fixed a problem where Alias ASL operators are sometimes not correctly
resolved, in both the interpreter and the iASL compiler.
-Fixed several problems with the implementation of the ConcatenateResTemplate
+Fixed several problems with the implementation of the
+ConcatenateResTemplate
ASL operator. As per the ACPI specification, zero length buffers are now
treated as a single EndTag. One-length buffers always cause a fatal
-exception. Non-zero length buffers that do not end with a full 2-byte EndTag
+exception. Non-zero length buffers that do not end with a full 2-byte
+EndTag
cause a fatal exception.
Fixed a possible structure overwrite in the AcpiGetObjectInfo external
@@ -4064,10 +9465,13 @@ interface. (With assistance from Thomas Renninger)
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4080,7 +9484,8 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed an internal error that was generated for any forward references to ASL
+Fixed an internal error that was generated for any forward references to
+ASL
Alias objects.
----------------------------------------
@@ -4092,18 +9497,24 @@ Added 2006 copyright to all module headers and signons. This affects
virtually every file in the ACPICA core subsystem, iASL compiler, and the
utilities.
-Enhanced the ACPICA error reporting in order to simplify user migration to
+Enhanced the ACPICA error reporting in order to simplify user migration
+to
the non-debug version of ACPICA. Replaced all instances of the
-ACPI_DEBUG_PRINT macro invoked at the ACPI_DB_ERROR and ACPI_DB_WARN debug
+ACPI_DEBUG_PRINT macro invoked at the ACPI_DB_ERROR and ACPI_DB_WARN
+debug
levels with the ACPI_REPORT_ERROR and ACPI_REPORT_WARNING macros,
-respectively. This preserves all error and warning messages in the non-debug
+respectively. This preserves all error and warning messages in the non-
+debug
version of the ACPICA code (this has been referred to as the "debug lite"
option.) Over 200 cases were converted to create a total of over 380
-error/warning messages across the ACPICA code. This increases the code and
-data size of the default non-debug version of the code somewhat (about 13K),
+error/warning messages across the ACPICA code. This increases the code
+and
+data size of the default non-debug version of the code somewhat (about
+13K),
but all error/warning reporting may be disabled if desired (and code
eliminated) by specifying the ACPI_NO_ERROR_MESSAGES compile-time
-configuration option. The size of the debug version of ACPICA remains about
+configuration option. The size of the debug version of ACPICA remains
+about
the same.
Fixed a memory leak within the AML Debugger "Set" command. One object was
@@ -4111,10 +9522,13 @@ not properly deleted for every successful invocation of the command.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4128,7 +9542,8 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
The compiler now officially supports the ACPI 3.0a specification that was
-released on December 30, 2005. (Specification is available at www.acpi.info)
+released on December 30, 2005. (Specification is available at
+www.acpi.info)
----------------------------------------
16 December 2005. Summary of changes for version 20051216:
@@ -4138,18 +9553,25 @@ released on December 30, 2005. (Specification is available at www.acpi.info)
Implemented optional support to allow unresolved names within ASL Package
objects. A null object is inserted in the package when a named reference
cannot be located in the current namespace. Enabled via the interpreter
-slack flag, this should eliminate AE_NOT_FOUND exceptions seen on machines
+slack flag, this should eliminate AE_NOT_FOUND exceptions seen on
+machines
that contain such code.
-Implemented an optimization to the initialization sequence that can improve
-boot time. During ACPI device initialization, the _STA method is now run if
-and only if the _INI method exists. The _STA method is used to determine if
-the device is present; An _INI can only be run if _STA returns present, but
+Implemented an optimization to the initialization sequence that can
+improve
+boot time. During ACPI device initialization, the _STA method is now run
+if
+and only if the _INI method exists. The _STA method is used to determine
+if
+the device is present; An _INI can only be run if _STA returns present,
+but
it is a waste of time to run the _STA method if the _INI does not exist.
(Prototype and assistance from Dong Wei)
-Implemented use of the C99 uintptr_t for the pointer casting macros if it is
-available in the current compiler. Otherwise, the default (void *) cast is
+Implemented use of the C99 uintptr_t for the pointer casting macros if it
+is
+available in the current compiler. Otherwise, the default (void *) cast
+is
used as before.
Fixed some possible memory leaks found within the execution path of the
@@ -4163,7 +9585,8 @@ Moved resource descriptor string constants that are used by both the AML
disassembler and AML debugger to the common utilities directory so that
these components are independent.
-Implemented support in the AcpiExec utility (-e switch) to globally ignore
+Implemented support in the AcpiExec utility (-e switch) to globally
+ignore
exceptions during control method execution (method is not aborted.)
Added the rsinfo.c source file to the AcpiExec makefile for Linux/Unix
@@ -4171,10 +9594,13 @@ generation.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4187,7 +9613,8 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a problem where a CPU stack overflow fault could occur if a recursive
+Fixed a problem where a CPU stack overflow fault could occur if a
+recursive
method call was made from within a Return statement.
----------------------------------------
@@ -4197,18 +9624,25 @@ method call was made from within a Return statement.
Modified the parsing of control methods to no longer create namespace
objects during the first pass of the parse. Objects are now created only
-during the execute phase, at the moment the namespace creation operator is
-encountered in the AML (Name, OperationRegion, CreateByteField, etc.) This
+during the execute phase, at the moment the namespace creation operator
+is
+encountered in the AML (Name, OperationRegion, CreateByteField, etc.)
+This
should eliminate ALREADY_EXISTS exceptions seen on some machines where
-reentrant control methods are protected by an AML mutex. The mutex will now
-correctly block multiple threads from attempting to create the same object
+reentrant control methods are protected by an AML mutex. The mutex will
+now
+correctly block multiple threads from attempting to create the same
+object
more than once.
Increased the number of available Owner Ids for namespace object tracking
-from 32 to 255. This should eliminate the OWNER_ID_LIMIT exceptions seen on
-some machines with a large number of ACPI tables (either static or dynamic).
+from 32 to 255. This should eliminate the OWNER_ID_LIMIT exceptions seen
+on
+some machines with a large number of ACPI tables (either static or
+dynamic).
-Fixed a problem with the AcpiExec utility where a fault could occur when the
+Fixed a problem with the AcpiExec utility where a fault could occur when
+the
-b switch (batch mode) is used.
Enhanced the namespace dump routine to output the owner ID for each
@@ -4216,10 +9650,13 @@ namespace object.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4232,8 +9669,10 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a parse error during compilation of certain Switch/Case constructs. To
-simplify the parse, the grammar now allows for multiple Default statements
+Fixed a parse error during compilation of certain Switch/Case constructs.
+To
+simplify the parse, the grammar now allows for multiple Default
+statements
and this error is now detected and flagged during the analysis phase.
Disassembler: The disassembly now includes the contents of the original
@@ -4246,32 +9685,43 @@ name and version of the original ASL compiler.
1) ACPI CA Core Subsystem:
Fixed a problem in the AML parser where the method thread count could be
-decremented below zero if any errors occurred during the method parse phase.
-This should eliminate AE_AML_METHOD_LIMIT exceptions seen on some machines.
+decremented below zero if any errors occurred during the method parse
+phase.
+This should eliminate AE_AML_METHOD_LIMIT exceptions seen on some
+machines.
This also fixed a related regression with the mechanism that detects and
corrects methods that cannot properly handle reentrancy (related to the
deployment of the new OwnerId mechanism.)
Eliminated the pre-parsing of control methods (to detect errors) during
-table load. Related to the problem above, this was causing unwind issues if
-any errors occurred during the parse, and it seemed to be overkill. A table
+table load. Related to the problem above, this was causing unwind issues
+if
+any errors occurred during the parse, and it seemed to be overkill. A
+table
load should not be aborted if there are problems with any single control
method, thus rendering this feature rather pointless.
-Fixed a problem with the new table-driven resource manager where an internal
+Fixed a problem with the new table-driven resource manager where an
+internal
buffer overflow could occur for small resource templates.
-Implemented a new external interface, AcpiGetVendorResource. This interface
-will find and return a vendor-defined resource descriptor within a _CRS or
-_PRS method via an ACPI 3.0 UUID match. With assistance from Bjorn Helgaas.
+Implemented a new external interface, AcpiGetVendorResource. This
+interface
+will find and return a vendor-defined resource descriptor within a _CRS
+or
+_PRS method via an ACPI 3.0 UUID match. With assistance from Bjorn
+Helgaas.
Removed the length limit (200) on string objects as per the upcoming ACPI
-3.0A specification. This affects the following areas of the interpreter: 1)
-any implicit conversion of a Buffer to a String, 2) a String object result
+3.0A specification. This affects the following areas of the interpreter:
+1)
+any implicit conversion of a Buffer to a String, 2) a String object
+result
of the ASL Concatentate operator, 3) the String object result of the ASL
ToString operator.
-Fixed a problem in the Windows OS interface layer (OSL) where a WAIT_FOREVER
+Fixed a problem in the Windows OS interface layer (OSL) where a
+WAIT_FOREVER
on a semaphore object would incorrectly timeout. This allows the
multithreading features of the AcpiExec utility to work properly under
Windows.
@@ -4281,10 +9731,13 @@ the recently added file named "utresrc.c".
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4298,15 +9751,18 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
Removed the limit (200) on string objects as per the upcoming ACPI 3.0A
-specification. For the iASL compiler, this means that string literals within
+specification. For the iASL compiler, this means that string literals
+within
the source ASL can be of any length.
Enhanced the listing output to dump the AML code for resource descriptors
-immediately after the ASL code for each descriptor, instead of in a block at
+immediately after the ASL code for each descriptor, instead of in a block
+at
the end of the entire resource template.
Enhanced the compiler debug output to dump the entire original parse tree
-constructed during the parse phase, before any transforms are applied to the
+constructed during the parse phase, before any transforms are applied to
+the
tree. The transformed tree is dumped also.
----------------------------------------
@@ -4314,34 +9770,45 @@ tree. The transformed tree is dumped also.
1) ACPI CA Core Subsystem:
-Modified the subsystem initialization sequence to improve GPE support. The
-GPE initialization has been split into two parts in order to defer execution
-of the _PRW methods (Power Resources for Wake) until after the hardware is
+Modified the subsystem initialization sequence to improve GPE support.
+The
+GPE initialization has been split into two parts in order to defer
+execution
+of the _PRW methods (Power Resources for Wake) until after the hardware
+is
fully initialized and the SCI handler is installed. This allows the _PRW
-methods to access fields protected by the Global Lock. This will fix systems
+methods to access fields protected by the Global Lock. This will fix
+systems
where a NO_GLOBAL_LOCK exception has been seen during initialization.
-Converted the ACPI internal object disassemble and display code within the
+Converted the ACPI internal object disassemble and display code within
+the
AML debugger to fully table-driven operation, reducing code size and
increasing maintainability.
-Fixed a regression with the ConcatenateResTemplate() ASL operator introduced
+Fixed a regression with the ConcatenateResTemplate() ASL operator
+introduced
in the 20051021 release.
Implemented support for "local" internal ACPI object types within the
debugger "Object" command and the AcpiWalkNamespace external interfaces.
-These local types include RegionFields, BankFields, IndexFields, Alias, and
+These local types include RegionFields, BankFields, IndexFields, Alias,
+and
reference objects.
-Moved common AML resource handling code into a new file, "utresrc.c". This
+Moved common AML resource handling code into a new file, "utresrc.c".
+This
code is shared by both the Resource Manager and the AML Debugger.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4354,20 +9821,26 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a problem with very large initializer lists (more than 4000 elements)
+Fixed a problem with very large initializer lists (more than 4000
+elements)
for both Buffer and Package objects where the parse stack could overflow.
-Enhanced the pre-compile source code scan for non-ASCII characters to ignore
-characters within comment fields. The scan is now always performed and is no
+Enhanced the pre-compile source code scan for non-ASCII characters to
+ignore
+characters within comment fields. The scan is now always performed and is
+no
longer optional, detecting invalid characters within a source file
immediately rather than during the parse phase or later.
-Enhanced the ASL grammar definition to force early reductions on all list-
+Enhanced the ASL grammar definition to force early reductions on all
+list-
style grammar elements so that the overall parse stack usage is greatly
-reduced. This should improve performance and reduce the possibility of parse
+reduced. This should improve performance and reduce the possibility of
+parse
stack overflow.
-Eliminated all reduce/reduce conflicts in the iASL parser generation. Also,
+Eliminated all reduce/reduce conflicts in the iASL parser generation.
+Also,
with the addition of a %expected statement, the compiler generates from
source with no warnings.
@@ -4387,27 +9860,34 @@ hardware support for non-aligned transfers.
Completed conversion of the Resource Manager to nearly full table-driven
operation. Specifically, the resource conversion code (convert AML to
internal format and the reverse) and the debug code to dump internal
-resource descriptors are fully table-driven, reducing code and data size and
+resource descriptors are fully table-driven, reducing code and data size
+and
improving maintainability.
-The OSL interfaces for Acquire and Release Lock now use a 64-bit flag word
-on 64-bit processors instead of a fixed 32-bit word. (With assistance from
+The OSL interfaces for Acquire and Release Lock now use a 64-bit flag
+word
+on 64-bit processors instead of a fixed 32-bit word. (With assistance
+from
Alexey Starikovskiy)
Implemented support within the resource conversion code for the Type-
Specific byte within the various ACPI 3.0 *WordSpace macros.
-Fixed some issues within the resource conversion code for the type-specific
+Fixed some issues within the resource conversion code for the type-
+specific
flags for both Memory and I/O address resource descriptors. For Memory,
implemented support for the MTP and TTP flags. For I/O, split the TRS and
TTP flags into two separate fields.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4421,23 +9901,29 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Relaxed a compiler restriction that disallowed a ResourceIndex byte if the
+Relaxed a compiler restriction that disallowed a ResourceIndex byte if
+the
corresponding ResourceSource string was not also present in a resource
descriptor declaration. This restriction caused problems with existing
-AML/ASL code that includes the Index byte without the string. When such AML
+AML/ASL code that includes the Index byte without the string. When such
+AML
was disassembled, it could not be compiled without modification. Further,
-the modified code created a resource template with a different size than the
-original, breaking code that used fixed offsets into the resource template
+the modified code created a resource template with a different size than
+the
+original, breaking code that used fixed offsets into the resource
+template
buffer.
-Removed a recent feature of the disassembler to ignore a lone ResourceIndex
+Removed a recent feature of the disassembler to ignore a lone
+ResourceIndex
byte. This byte is now emitted if present so that the exact AML can be
reproduced when the disassembled code is recompiled.
Improved comments and text alignment for the resource descriptor code
emitted by the disassembler.
-Implemented disassembler support for the ACPI 3.0 AccessSize field within a
+Implemented disassembler support for the ACPI 3.0 AccessSize field within
+a
Register() resource descriptor.
----------------------------------------
@@ -4446,10 +9932,13 @@ Register() resource descriptor.
1) ACPI CA Core Subsystem:
Completed a major overhaul of the Resource Manager code - specifically,
-optimizations in the area of the AML/internal resource conversion code. The
-code has been optimized to simplify and eliminate duplicated code, CPU stack
+optimizations in the area of the AML/internal resource conversion code.
+The
+code has been optimized to simplify and eliminate duplicated code, CPU
+stack
use has been decreased by optimizing function parameters and local
-variables, and naming conventions across the manager have been standardized
+variables, and naming conventions across the manager have been
+standardized
for clarity and ease of maintenance (this includes function, parameter,
variable, and struct/typedef names.) The update may force changes in some
driver code, depending on how resources are handled by the host OS.
@@ -4459,15 +9948,20 @@ single location for clarity and ease of maintenance. One new file was
created, named "rsinfo.c".
The ACPI return macros (return_ACPI_STATUS, etc.) have been modified to
-guarantee that the argument is not evaluated twice, making them less prone
+guarantee that the argument is not evaluated twice, making them less
+prone
to macro side-effects. However, since there exists the possibility of
-additional stack use if a particular compiler cannot optimize them (such as
-in the debug generation case), the original macros are optionally available.
+additional stack use if a particular compiler cannot optimize them (such
+as
+in the debug generation case), the original macros are optionally
+available.
Note that some invocations of the return_VALUE macro may now cause size
-mismatch warnings; the return_UINT8 and return_UINT32 macros are provided to
+mismatch warnings; the return_UINT8 and return_UINT32 macros are provided
+to
eliminate these. (From Randy Dunlap)
-Implemented a new mechanism to enable debug tracing for individual control
+Implemented a new mechanism to enable debug tracing for individual
+control
methods. A new external interface, AcpiDebugTrace, is provided to enable
this mechanism. The intent is to allow the host OS to easily enable and
disable tracing for problematic control methods. This interface can be
@@ -4480,10 +9974,13 @@ the behavior of AcpiUtAllocate.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4505,11 +10002,13 @@ buffer is zero. Previously, this was a warning.
1) ACPI CA Core Subsystem:
Fixed a problem within the Resource Manager where support for the Generic
-Register descriptor was not fully implemented. This descriptor is now fully
+Register descriptor was not fully implemented. This descriptor is now
+fully
recognized, parsed, disassembled, and displayed.
Completely restructured the Resource Manager code to utilize table-driven
-dispatch and lookup, eliminating many of the large switch() statements. This
+dispatch and lookup, eliminating many of the large switch() statements.
+This
reduces overall subsystem code size and code complexity. Affects the
resource parsing and construction, disassembly, and debug dump output.
@@ -4521,10 +10020,13 @@ optional ACPI_MUTEX_DEBUG code to fail compilation if specified.
Code and Data Size: The current and previous library sizes for the core
subsystem are shown below. These are the code and data sizes for the
-acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler. These
-values do not include any ACPI driver or OSPM code. The debug version of the
+acpica.lib produced by the Microsoft Visual C++ 6.0 32-bit compiler.
+These
+values do not include any ACPI driver or OSPM code. The debug version of
+the
code includes the debug output trace mechanism and has a much larger code
-and data size. Note that these values will vary depending on the efficiency
+and data size. Note that these values will vary depending on the
+efficiency
of the compiler and the compiler options used during generation.
Previous Release:
@@ -4537,12 +10039,14 @@ of the compiler and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Updated the disassembler to automatically insert an EndDependentFn() macro
+Updated the disassembler to automatically insert an EndDependentFn()
+macro
into the ASL stream if this macro is missing in the original AML code,
simplifying compilation of the resulting ASL module.
Fixed a problem in the disassembler where a disassembled ResourceSource
-string (within a large resource descriptor) was not surrounded by quotes and
+string (within a large resource descriptor) was not surrounded by quotes
+and
not followed by a comma, causing errors when the resulting ASL module was
compiled. Also, escape sequences within a ResourceSource string are now
handled correctly (especially "\\")
@@ -4559,15 +10063,18 @@ messages seen on some systems. Recursive method invocation depth is
currently limited to 255. (Alexey Starikovskiy)
Completely eliminated all vestiges of support for the "module-level
-executable code" until this support is fully implemented and debugged. This
+executable code" until this support is fully implemented and debugged.
+This
should eliminate the NO_RETURN_VALUE exceptions seen during table load on
some systems that invoke this support.
-Fixed a problem within the resource manager code where the transaction flags
+Fixed a problem within the resource manager code where the transaction
+flags
for a 64-bit address descriptor were handled incorrectly in the type-
specific flag byte.
-Consolidated duplicate code within the address descriptor resource manager
+Consolidated duplicate code within the address descriptor resource
+manager
code, reducing overall subsystem code size.
Fixed a fault when using the AML debugger "disassemble" command to
@@ -4579,9 +10086,12 @@ release package.
Code and Data Size: The current and previous core subsystem library sizes
are shown below. These are the code and data sizes for the acpica.lib
produced by the Microsoft Visual C++ 6.0 compiler. These values do not
-include any ACPI driver or OSPM code. The debug version of the code includes
-the debug output trace mechanism and has a much larger code and data size.
-Note that these values will vary depending on the efficiency of the compiler
+include any ACPI driver or OSPM code. The debug version of the code
+includes
+the debug output trace mechanism and has a much larger code and data
+size.
+Note that these values will vary depending on the efficiency of the
+compiler
and the compiler options used during generation.
Previous Release:
@@ -4594,11 +10104,13 @@ and the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Implemented an error check for illegal duplicate values in the interrupt and
+Implemented an error check for illegal duplicate values in the interrupt
+and
dma lists for the following ASL macros: Dma(), Irq(), IrqNoFlags(), and
Interrupt().
-Implemented error checking for the Irq() and IrqNoFlags() macros to detect
+Implemented error checking for the Irq() and IrqNoFlags() macros to
+detect
too many values in the interrupt list (16 max) and invalid values in the
list (range 0 - 15)
@@ -4615,20 +10127,26 @@ resource descriptor has already been used within the current scope.
1) ACPI CA Core Subsystem:
-Implemented a full bytewise compare to determine if a table load request is
-attempting to load a duplicate table. The compare is performed if the table
+Implemented a full bytewise compare to determine if a table load request
+is
+attempting to load a duplicate table. The compare is performed if the
+table
signatures and table lengths match. This will allow different tables with
-the same OEM Table ID and revision to be loaded - probably against the ACPI
+the same OEM Table ID and revision to be loaded - probably against the
+ACPI
specification, but discovered in the field nonetheless.
Added the changes.txt logfile to each of the zipped release packages.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4645,7 +10163,8 @@ Fixed a problem where incorrect AML code could be generated for Package
objects if optimization is disabled (via the -oa switch).
Fixed a problem with where incorrect AML code is generated for variable-
-length packages when the package length is not specified and the number of
+length packages when the package length is not specified and the number
+of
initializer values is greater than 255.
@@ -4654,37 +10173,48 @@ initializer values is greater than 255.
1) ACPI CA Core Subsystem:
-Implemented support to ignore an attempt to install/load a particular ACPI
+Implemented support to ignore an attempt to install/load a particular
+ACPI
table more than once. Apparently there exists BIOS code that repeatedly
attempts to load the same SSDT upon certain events. With assistance from
Venkatesh Pallipadi.
Restructured the main interface to the AML parser in order to correctly
-handle all exceptional conditions. This will prevent leakage of the OwnerId
-resource and should eliminate the AE_OWNER_ID_LIMIT exceptions seen on some
+handle all exceptional conditions. This will prevent leakage of the
+OwnerId
+resource and should eliminate the AE_OWNER_ID_LIMIT exceptions seen on
+some
machines. With assistance from Alexey Starikovskiy.
-Support for "module level code" has been disabled in this version due to a
-number of issues that have appeared on various machines. The support can be
+Support for "module level code" has been disabled in this version due to
+a
+number of issues that have appeared on various machines. The support can
+be
enabled by defining ACPI_ENABLE_MODULE_LEVEL_CODE during subsystem
-compilation. When the issues are fully resolved, the code will be enabled by
+compilation. When the issues are fully resolved, the code will be enabled
+by
default again.
Modified the internal functions for debug print support to define the
-FunctionName parameter as a (const char *) for compatibility with compiler
+FunctionName parameter as a (const char *) for compatibility with
+compiler
built-in macros such as __FUNCTION__, etc.
Linted the entire ACPICA source tree for both 32-bit and 64-bit.
-Implemented support to display an object count summary for the AML Debugger
+Implemented support to display an object count summary for the AML
+Debugger
commands Object and Methods.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4698,7 +10228,8 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
Fixed a regression that appeared in the 20050708 version of the compiler
-where an error message was inadvertently emitted for invocations of the _OSI
+where an error message was inadvertently emitted for invocations of the
+_OSI
reserved control method.
----------------------------------------
@@ -4710,45 +10241,60 @@ The use of the CPU stack in the debug version of the subsystem has been
considerably reduced. Previously, a debug structure was declared in every
function that used the debug macros. This structure has been removed in
favor of declaring the individual elements as parameters to the debug
-functions. This reduces the cumulative stack use during nested execution of
-ACPI function calls at the cost of a small increase in the code size of the
-debug version of the subsystem. With assistance from Alexey Starikovskiy and
+functions. This reduces the cumulative stack use during nested execution
+of
+ACPI function calls at the cost of a small increase in the code size of
+the
+debug version of the subsystem. With assistance from Alexey Starikovskiy
+and
Len Brown.
Added the ACPI_GET_FUNCTION_NAME macro to enable the compiler-dependent
headers to define a macro that will return the current function name at
-runtime (such as __FUNCTION__ or _func_, etc.) The function name is used by
+runtime (such as __FUNCTION__ or _func_, etc.) The function name is used
+by
the debug trace output. If ACPI_GET_FUNCTION_NAME is not defined in the
-compiler-dependent header, the function name is saved on the CPU stack (one
+compiler-dependent header, the function name is saved on the CPU stack
+(one
pointer per function.) This mechanism is used because apparently there
-exists no standard ANSI-C defined macro that that returns the function name.
+exists no standard ANSI-C defined macro that that returns the function
+name.
Redesigned and reimplemented the "Owner ID" mechanism used to track
namespace objects created/deleted by ACPI tables and control method
-execution. A bitmap is now used to allocate and free the IDs, thus solving
-the wraparound problem present in the previous implementation. The size of
+execution. A bitmap is now used to allocate and free the IDs, thus
+solving
+the wraparound problem present in the previous implementation. The size
+of
the namespace node descriptor was reduced by 2 bytes as a result (Alexey
Starikovskiy).
-Removed the UINT32_BIT and UINT16_BIT types that were used for the bitfield
+Removed the UINT32_BIT and UINT16_BIT types that were used for the
+bitfield
flag definitions within the headers for the predefined ACPI tables. These
-have been replaced by UINT8_BIT in order to increase the code portability of
+have been replaced by UINT8_BIT in order to increase the code portability
+of
the subsystem. If the use of UINT8 remains a problem, we may be forced to
eliminate bitfields entirely because of a lack of portability.
-Enhanced the performance of the AcpiUtUpdateObjectReference procedure. This
-is a frequently used function and this improvement increases the performance
+Enhanced the performance of the AcpiUtUpdateObjectReference procedure.
+This
+is a frequently used function and this improvement increases the
+performance
of the entire subsystem (Alexey Starikovskiy).
Fixed several possible memory leaks and the inverse - premature object
deletion (Alexey Starikovskiy).
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4764,7 +10310,8 @@ the compiler options used during generation.
1) ACPI CA Core Subsystem:
Modified the new OSL cache interfaces to use ACPI_CACHE_T as the type for
-the host-defined cache object. This allows the OSL implementation to define
+the host-defined cache object. This allows the OSL implementation to
+define
and type this object in any manner desired, simplifying the OSL
implementation. For example, ACPI_CACHE_T is defined as kmem_cache_t for
Linux, and should be defined in the OS-specific header file for other
@@ -4776,21 +10323,28 @@ change was made for performance reasons, since this is the purpose of the
interface in the first place. AcpiOsAcquireObject is now similar to the
AcpiOsAllocate interface.
-Implemented a new AML debugger command named Businfo. This command displays
-information about all devices that have an associate _PRT object. The _ADR,
+Implemented a new AML debugger command named Businfo. This command
+displays
+information about all devices that have an associate _PRT object. The
+_ADR,
_HID, _UID, and _CID are displayed for these devices.
-Modified the initialization sequence in AcpiInitializeSubsystem to call the
-OSL interface AcpiOslInitialize first, before any local initialization. This
+Modified the initialization sequence in AcpiInitializeSubsystem to call
+the
+OSL interface AcpiOslInitialize first, before any local initialization.
+This
change was required because the global initialization now calls OSL
interfaces.
-Enhanced the Dump command to display the entire contents of Package objects
+Enhanced the Dump command to display the entire contents of Package
+objects
(including all sub-objects and their values.)
Restructured the code base to split some files because of size and/or
-because the code logically belonged in a separate file. New files are listed
-below. All makefiles and project files included in the ACPI CA release have
+because the code logically belonged in a separate file. New files are
+listed
+below. All makefiles and project files included in the ACPI CA release
+have
been updated.
utilities/utcache.c /* Local cache interfaces */
utilities/utmutex.c /* Local mutex support */
@@ -4798,11 +10352,14 @@ been updated.
interpreter/parser/psloop.c /* Main AML parse loop */
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4815,7 +10372,8 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a regression introduced in version 20050513 where the use of a Package
+Fixed a regression introduced in version 20050513 where the use of a
+Package
object within a Case() statement caused a compile time exception. The
original behavior has been restored (a Match() operator is emitted.)
@@ -4824,10 +10382,13 @@ original behavior has been restored (a Match() operator is emitted.)
1) ACPI CA Core Subsystem:
-Moved the object cache operations into the OS interface layer (OSL) to allow
+Moved the object cache operations into the OS interface layer (OSL) to
+allow
the host OS to handle these operations if desired (for example, the Linux
-OSL will invoke the slab allocator). This support is optional; the compile
-time define ACPI_USE_LOCAL_CACHE may be used to utilize the original cache
+OSL will invoke the slab allocator). This support is optional; the
+compile
+time define ACPI_USE_LOCAL_CACHE may be used to utilize the original
+cache
code in the ACPI CA core. The new OSL interfaces are shown below. See
utalloc.c for an example implementation, and acpiosxf.h for the exact
interface definitions. With assistance from Alexey Starikovskiy.
@@ -4837,44 +10398,59 @@ interface definitions. With assistance from Alexey Starikovskiy.
AcpiOsAcquireObject
AcpiOsReleaseObject
-Modified the interfaces to AcpiOsAcquireLock and AcpiOsReleaseLock to return
+Modified the interfaces to AcpiOsAcquireLock and AcpiOsReleaseLock to
+return
and restore a flags parameter. This fits better with many OS lock models.
Note: the current execution state (interrupt handler or not) is no longer
-passed to these interfaces. If necessary, the OSL must determine this state
+passed to these interfaces. If necessary, the OSL must determine this
+state
by itself, a simple and fast operation. With assistance from Alexey
Starikovskiy.
Fixed a problem in the ACPI table handling where a valid XSDT was assumed
-present if the revision of the RSDP was 2 or greater. According to the ACPI
+present if the revision of the RSDP was 2 or greater. According to the
+ACPI
specification, the XSDT is optional in all cases, and the table manager
therefore now checks for both an RSDP >=2 and a valid XSDT pointer.
-Otherwise, the RSDT pointer is used. Some ACPI 2.0 compliant BIOSs contain
+Otherwise, the RSDT pointer is used. Some ACPI 2.0 compliant BIOSs
+contain
only the RSDT.
-Fixed an interpreter problem with the Mid() operator in the case of an input
-string where the resulting output string is of zero length. It now correctly
+Fixed an interpreter problem with the Mid() operator in the case of an
+input
+string where the resulting output string is of zero length. It now
+correctly
returns a valid, null terminated string object instead of a string object
with a null pointer.
-Fixed a problem with the control method argument handling to allow a store
-to an Arg object that already contains an object of type Device. The Device
+Fixed a problem with the control method argument handling to allow a
+store
+to an Arg object that already contains an object of type Device. The
+Device
object is now correctly overwritten. Previously, an error was returned.
-Enhanced the debugger Find command to emit object values in addition to the
-found object pathnames. The output format is the same as the dump namespace
+Enhanced the debugger Find command to emit object values in addition to
+the
+found object pathnames. The output format is the same as the dump
+namespace
command.
-Enhanced the debugger Set command. It now has the ability to set the value
-of any Named integer object in the namespace (Previously, only method locals
+Enhanced the debugger Set command. It now has the ability to set the
+value
+of any Named integer object in the namespace (Previously, only method
+locals
and args could be set.)
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4887,15 +10463,18 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a regression in the disassembler where if/else/while constructs were
+Fixed a regression in the disassembler where if/else/while constructs
+were
output incorrectly. This problem was introduced in the previous release
(20050526). This problem also affected the single-step disassembly in the
debugger.
-Fixed a problem where compiling the reserved _OSI method would randomly (but
+Fixed a problem where compiling the reserved _OSI method would randomly
+(but
rarely) produce compile errors.
-Enhanced the disassembler to emit compilable code in the face of incorrect
+Enhanced the disassembler to emit compilable code in the face of
+incorrect
AML resource descriptors. If the optional ResourceSourceIndex is present,
but the ResourceSource is not, do not emit the ResourceSourceIndex in the
disassembly. Otherwise, the resulting code cannot be compiled without
@@ -4907,17 +10486,23 @@ errors.
1) ACPI CA Core Subsystem:
Implemented support to execute Type 1 and Type 2 AML opcodes appearing at
-the module level (not within a control method.) These opcodes are executed
-exactly once at the time the table is loaded. This type of code was legal up
-until the release of ACPI 2.0B (2002) and is now supported within ACPI CA in
-order to provide backwards compatibility with earlier BIOS implementations.
+the module level (not within a control method.) These opcodes are
+executed
+exactly once at the time the table is loaded. This type of code was legal
+up
+until the release of ACPI 2.0B (2002) and is now supported within ACPI CA
+in
+order to provide backwards compatibility with earlier BIOS
+implementations.
This eliminates the "Encountered executable code at module level" warning
that was previously generated upon detection of such code.
Fixed a problem in the interpreter where an AE_NOT_FOUND exception could
inadvertently be generated during the lookup of namespace objects in the
-second pass parse of ACPI tables and control methods. It appears that this
-problem could occur during the resolution of forward references to namespace
+second pass parse of ACPI tables and control methods. It appears that
+this
+problem could occur during the resolution of forward references to
+namespace
objects.
Added the ACPI_MUTEX_DEBUG #ifdef to the AcpiUtReleaseMutex function,
@@ -4930,15 +10515,19 @@ Implemented a handful of miscellaneous fixes for possible memory leaks on
error conditions and error handling control paths. These fixes were
suggested by FreeBSD and the Coverity Prevent source code analysis tool.
-Added a check for a null RSDT pointer in AcpiGetFirmwareTable (tbxfroot.c)
+Added a check for a null RSDT pointer in AcpiGetFirmwareTable
+(tbxfroot.c)
to prevent a fault in this error case.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -4953,21 +10542,28 @@ the compiler options used during generation.
Implemented support to allow Type 1 and Type 2 ASL operators to appear at
the module level (not within a control method.) These operators will be
-executed once at the time the table is loaded. This type of code was legal
+executed once at the time the table is loaded. This type of code was
+legal
up until the release of ACPI 2.0B (2002) and is now supported by the iASL
-compiler in order to provide backwards compatibility with earlier BIOS ASL
+compiler in order to provide backwards compatibility with earlier BIOS
+ASL
code.
The ACPI integer width (specified via the table revision ID or the -r
-override, 32 or 64 bits) is now used internally during compile-time constant
+override, 32 or 64 bits) is now used internally during compile-time
+constant
folding to ensure that constants are truncated to 32 bits if necessary.
-Previously, the revision ID value was only emitted in the AML table header.
+Previously, the revision ID value was only emitted in the AML table
+header.
-An error message is now generated for the Mutex and Method operators if the
+An error message is now generated for the Mutex and Method operators if
+the
SyncLevel parameter is outside the legal range of 0 through 15.
-Fixed a problem with the Method operator ParameterTypes list handling (ACPI
-3.0). Previously, more than 2 types or 2 arguments generated a syntax error.
+Fixed a problem with the Method operator ParameterTypes list handling
+(ACPI
+3.0). Previously, more than 2 types or 2 arguments generated a syntax
+error.
The actual underlying implementation of method argument typechecking is
still under development, however.
@@ -4976,39 +10572,50 @@ still under development, however.
1) ACPI CA Core Subsystem:
-Implemented support for PCI Express root bridges -- added support for device
+Implemented support for PCI Express root bridges -- added support for
+device
PNP0A08 in the root bridge search within AcpiEvPciConfigRegionSetup.
-The interpreter now automatically truncates incoming 64-bit constants to 32
-bits if currently executing out of a 32-bit ACPI table (Revision < 2). This
+The interpreter now automatically truncates incoming 64-bit constants to
+32
+bits if currently executing out of a 32-bit ACPI table (Revision < 2).
+This
also affects the iASL compiler constant folding. (Note: as per below, the
iASL compiler no longer allows 64-bit constants within 32-bit tables.)
Fixed a problem where string and buffer objects with "static" pointers
(pointers to initialization data within an ACPI table) were not handled
-consistently. The internal object copy operation now always copies the data
+consistently. The internal object copy operation now always copies the
+data
to a newly allocated buffer, regardless of whether the source object is
static or not.
Fixed a problem with the FromBCD operator where an implicit result
-conversion was improperly performed while storing the result to the target
+conversion was improperly performed while storing the result to the
+target
operand. Since this is an "explicit conversion" operator, the implicit
conversion should never be performed on the output.
Fixed a problem with the CopyObject operator where a copy to an existing
-named object did not always completely overwrite the existing object stored
-at name. Specifically, a buffer-to-buffer copy did not delete the existing
+named object did not always completely overwrite the existing object
+stored
+at name. Specifically, a buffer-to-buffer copy did not delete the
+existing
buffer.
-Replaced "InterruptLevel" with "InterruptNumber" in all GPE interfaces and
+Replaced "InterruptLevel" with "InterruptNumber" in all GPE interfaces
+and
structs for consistency.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5021,17 +10628,21 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-The compiler now emits a warning if an attempt is made to generate a 64-bit
-integer constant from within a 32-bit ACPI table (Revision < 2). The integer
+The compiler now emits a warning if an attempt is made to generate a 64-
+bit
+integer constant from within a 32-bit ACPI table (Revision < 2). The
+integer
is truncated to 32 bits.
Fixed a problem with large package objects: if the static length of the
package is greater than 255, the "variable length package" opcode is
emitted. Previously, this caused an error. This requires an update to the
-ACPI spec, since it currently (incorrectly) states that packages larger than
+ACPI spec, since it currently (incorrectly) states that packages larger
+than
255 elements are not allowed.
-The disassembler now correctly handles variable length packages and packages
+The disassembler now correctly handles variable length packages and
+packages
larger than 255 elements.
----------------------------------------
@@ -5043,31 +10654,41 @@ Fixed three cases in the interpreter where an "index" argument to an ASL
function was still (internally) 32 bits instead of the required 64 bits.
This was the Index argument to the Index, Mid, and Match operators.
-The "strupr" function is now permanently local (AcpiUtStrupr), since this is
+The "strupr" function is now permanently local (AcpiUtStrupr), since this
+is
not a POSIX-defined function and not present in most kernel-level C
-libraries. All references to the C library strupr function have been removed
+libraries. All references to the C library strupr function have been
+removed
from the headers.
-Completed the deployment of static functions/prototypes. All prototypes with
-the static attribute have been moved from the headers to the owning C file.
+Completed the deployment of static functions/prototypes. All prototypes
+with
+the static attribute have been moved from the headers to the owning C
+file.
Implemented an extract option (-e) for the AcpiBin utility (AML binary
-utility). This option allows the utility to extract individual ACPI tables
+utility). This option allows the utility to extract individual ACPI
+tables
from the output of AcpiDmp. It provides the same functionality of the
acpixtract.pl perl script without the worry of setting the correct perl
-options. AcpiBin runs on Windows and has not yet been generated/validated in
+options. AcpiBin runs on Windows and has not yet been generated/validated
+in
the Linux/Unix environment (but should be soon).
Updated and fixed the table dump option for AcpiBin (-d). This option
-converts a single ACPI table to a hex/ascii file, similar to the output of
+converts a single ACPI table to a hex/ascii file, similar to the output
+of
AcpiDmp.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5080,8 +10701,10 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Disassembler fix: Added a check to ensure that the table length found in the
-ACPI table header within the input file is not longer than the actual input
+Disassembler fix: Added a check to ensure that the table length found in
+the
+ACPI table header within the input file is not longer than the actual
+input
file size. This indicates some kind of file or table corruption.
----------------------------------------
@@ -5089,24 +10712,28 @@ file size. This indicates some kind of file or table corruption.
1) ACPI CA Core Subsystem:
-An error is now generated if an attempt is made to create a Buffer Field of
+An error is now generated if an attempt is made to create a Buffer Field
+of
length zero (A CreateField with a length operand of zero.)
-The interpreter now issues a warning whenever executable code at the module
+The interpreter now issues a warning whenever executable code at the
+module
level is detected during ACPI table load. This will give some idea of the
prevalence of this type of code.
Implemented support for references to named objects (other than control
methods) within package objects.
-Enhanced package object output for the debug object. Package objects are now
+Enhanced package object output for the debug object. Package objects are
+now
completely dumped, showing all elements.
Enhanced miscellaneous object output for the debug object. Any object can
now be written to the debug object (for example, a device object can be
written, and the type of the object will be displayed.)
-The "static" qualifier has been added to all local functions across both the
+The "static" qualifier has been added to all local functions across both
+the
core subsystem and the iASL compiler.
The number of "long" lines (> 80 chars) within the source has been
@@ -5120,11 +10747,14 @@ Two new header files have been added, acopcode.h and acnames.h.
Removed several obsolete functions that were no longer used.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5139,10 +10769,12 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
Fixed a problem with the resource descriptor generation/support. For the
-ResourceSourceIndex and the ResourceSource fields, both must be present, or
+ResourceSourceIndex and the ResourceSource fields, both must be present,
+or
both must be not present - can't have one without the other.
-The compiler now returns non-zero from the main procedure if any errors have
+The compiler now returns non-zero from the main procedure if any errors
+have
occurred during the compilation.
@@ -5151,56 +10783,74 @@ occurred during the compilation.
1) ACPI CA Core Subsystem:
-The string-to-buffer implicit conversion code has been modified again after
-a change to the ACPI specification. In order to match the behavior of the
-other major ACPI implementation, the target buffer is no longer truncated if
+The string-to-buffer implicit conversion code has been modified again
+after
+a change to the ACPI specification. In order to match the behavior of
+the
+other major ACPI implementation, the target buffer is no longer truncated
+if
the source string is smaller than an existing target buffer. This change
requires an update to the ACPI spec, and should eliminate the recent
AE_AML_BUFFER_LIMIT issues.
-The "implicit return" support was rewritten to a new algorithm that solves
-the general case. Rather than attempt to determine when a method is about to
-exit, the result of every ASL operator is saved momentarily until the very
+The "implicit return" support was rewritten to a new algorithm that
+solves
+the general case. Rather than attempt to determine when a method is about
+to
+exit, the result of every ASL operator is saved momentarily until the
+very
next ASL operator is executed. Therefore, no matter how the method exits,
there will always be a saved implicit return value. This feature is only
-enabled with the AcpiGbl_EnableInterpreterSlack flag, and should eliminate
+enabled with the AcpiGbl_EnableInterpreterSlack flag, and should
+eliminate
AE_AML_NO_RETURN_VALUE errors when enabled.
-Implemented implicit conversion support for the predicate (operand) of the
-If, Else, and While operators. String and Buffer arguments are automatically
+Implemented implicit conversion support for the predicate (operand) of
+the
+If, Else, and While operators. String and Buffer arguments are
+automatically
converted to Integers.
Changed the string-to-integer conversion behavior to match the new ACPI
errata: "If no integer object exists, a new integer is created. The ASCII
string is interpreted as a hexadecimal constant. Each string character is
interpreted as a hexadecimal value ('0'-'9', 'A'-'F', 'a', 'f'), starting
-with the first character as the most significant digit, and ending with the
-first non-hexadecimal character or end-of-string." This means that the first
+with the first character as the most significant digit, and ending with
+the
+first non-hexadecimal character or end-of-string." This means that the
+first
non-hex character terminates the conversion and this is the code that was
changed.
-Fixed a problem where the ObjectType operator would fail (fault) when used
+Fixed a problem where the ObjectType operator would fail (fault) when
+used
on an Index of a Package which pointed to a null package element. The
operator now properly returns zero (Uninitialized) in this case.
Fixed a problem where the While operator used excessive memory by not
-properly popping the result stack during execution. There was no memory leak
+properly popping the result stack during execution. There was no memory
+leak
after execution, however. (Code provided by Valery Podrezov.)
-Fixed a problem where references to control methods within Package objects
+Fixed a problem where references to control methods within Package
+objects
caused the method to be invoked, instead of producing a reference object
pointing to the method.
-Restructured and simplified the pswalk.c module (AcpiPsDeleteParseTree) to
+Restructured and simplified the pswalk.c module (AcpiPsDeleteParseTree)
+to
improve performance and reduce code size. (Code provided by Alexey
Starikovskiy.)
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5214,25 +10864,29 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
Fixed a problem with the Return operator with no arguments. Since the AML
-grammar for the byte encoding requires an operand for the Return opcode, the
+grammar for the byte encoding requires an operand for the Return opcode,
+the
compiler now emits a Return(Zero) for this case. An ACPI specification
update has been written for this case.
For tables other than the DSDT, namepath optimization is automatically
-disabled. This is because SSDTs can be loaded anywhere in the namespace, the
+disabled. This is because SSDTs can be loaded anywhere in the namespace,
+the
compiler has no knowledge of where, and thus cannot optimize namepaths.
Added "ProcessorObj" to the ObjectTypeKeyword list. This object type was
inadvertently omitted from the ACPI specification, and will require an
update to the spec.
-The source file scan for ASCII characters is now optional (-a). This change
+The source file scan for ASCII characters is now optional (-a). This
+change
was made because some vendors place non-ascii characters within comments.
However, the scan is simply a brute-force byte compare to ensure all
characters in the file are in the range 0x00 to 0x7F.
Fixed a problem with the CondRefOf operator where the compiler was
-inappropriately checking for the existence of the target. Since the point of
+inappropriately checking for the existence of the target. Since the point
+of
the operator is to check for the existence of the target at run-time, the
compiler no longer checks for the target existence.
@@ -5240,17 +10894,22 @@ Fixed a problem where errors generated from the internal AML interpreter
during constant folding were not handled properly, causing a fault.
Fixed a problem with overly aggressive range checking for the Stall
-operator. The valid range (max 255) is now only checked if the operand is of
+operator. The valid range (max 255) is now only checked if the operand is
+of
type Integer. All other operand types cannot be statically checked.
-Fixed a problem where control method references within the RefOf, DeRefOf,
-and ObjectType operators were not treated properly. They are now treated as
+Fixed a problem where control method references within the RefOf,
+DeRefOf,
+and ObjectType operators were not treated properly. They are now treated
+as
actual references, not method invocations.
-Fixed and enhanced the "list namespace" option (-ln). This option was broken
+Fixed and enhanced the "list namespace" option (-ln). This option was
+broken
a number of releases ago.
-Improved error handling for the Field, IndexField, and BankField operators.
+Improved error handling for the Field, IndexField, and BankField
+operators.
The compiler now cleanly reports and recovers from errors in the field
component (FieldUnit) list.
@@ -5265,20 +10924,24 @@ Disassembler - Comments in output now use "//" instead of "/*"
1) ACPI CA Core Subsystem:
Fixed a problem where the result of an Index() operator (an object
-reference) must increment the reference count on the target object for the
+reference) must increment the reference count on the target object for
+the
life of the object reference.
Implemented AML Interpreter and Debugger support for the new ACPI 3.0
-Extended Address (IO, Memory, Space), QwordSpace, DwordSpace, and WordSpace
+Extended Address (IO, Memory, Space), QwordSpace, DwordSpace, and
+WordSpace
resource descriptors.
Implemented support in the _OSI method for the ACPI 3.0 "Extended Address
-Space Descriptor" string, indicating interpreter support for the descriptors
+Space Descriptor" string, indicating interpreter support for the
+descriptors
above.
Implemented header support for the new ACPI 3.0 FADT flag bits.
-Implemented header support for the new ACPI 3.0 PCI Express bits for the PM1
+Implemented header support for the new ACPI 3.0 PCI Express bits for the
+PM1
status/enable registers.
Updated header support for the MADT processor local Apic struct and MADT
@@ -5286,15 +10949,19 @@ platform interrupt source struct for new ACPI 3.0 fields.
Implemented header support for the SRAT and SLIT ACPI tables.
-Implemented the -s switch in AcpiExec to enable the "InterpreterSlack" flag
+Implemented the -s switch in AcpiExec to enable the "InterpreterSlack"
+flag
at runtime.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5307,15 +10974,18 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a problem with the internal 64-bit String-to-integer conversion with
+Fixed a problem with the internal 64-bit String-to-integer conversion
+with
strings less than two characters long.
Fixed a problem with constant folding where the result of the Index()
-operator can not be considered a constant. This means that Index() cannot be
+operator can not be considered a constant. This means that Index() cannot
+be
a type3 opcode and this will require an update to the ACPI specification.
Disassembler: Implemented support for the TTP, MTP, and TRS resource
-descriptor fields. These fields were inadvertently ignored and not output in
+descriptor fields. These fields were inadvertently ignored and not output
+in
the disassembly of the resource descriptor.
@@ -5325,29 +10995,40 @@ the disassembly of the resource descriptor.
1) ACPI CA Core Subsystem:
Implemented ACPI 3.0 support for implicit conversion within the Match()
-operator. MatchObjects can now be of type integer, buffer, or string instead
-of just type integer. Package elements are implicitly converted to the type
+operator. MatchObjects can now be of type integer, buffer, or string
+instead
+of just type integer. Package elements are implicitly converted to the
+type
of the MatchObject. This change aligns the behavior of Match() with the
-behavior of the other logical operators (LLess(), etc.) It also requires an
+behavior of the other logical operators (LLess(), etc.) It also requires
+an
errata change to the ACPI specification as this support was intended for
ACPI 3.0, but was inadvertently omitted.
-Fixed a problem with the internal implicit "to buffer" conversion. Strings
-that are converted to buffers will cause buffer truncation if the string is
-smaller than the target buffer. Integers that are converted to buffers will
+Fixed a problem with the internal implicit "to buffer" conversion.
+Strings
+that are converted to buffers will cause buffer truncation if the string
+is
+smaller than the target buffer. Integers that are converted to buffers
+will
not cause buffer truncation, only zero extension (both as per the ACPI
spec.) The problem was introduced when code was added to truncate the
-buffer, but this should not be performed in all cases, only the string case.
+buffer, but this should not be performed in all cases, only the string
+case.
-Fixed a problem with the Buffer and Package operators where the interpreter
+Fixed a problem with the Buffer and Package operators where the
+interpreter
would get confused if two such operators were used as operands to an ASL
operator (such as LLess(Buffer(1){0},Buffer(1){1}). The internal result
-stack was not being popped after the execution of these operators, resulting
+stack was not being popped after the execution of these operators,
+resulting
in an AE_NO_RETURN_VALUE exception.
Fixed a problem with constructs of the form Store(Index(...),...). The
-reference object returned from Index was inadvertently resolved to an actual
-value. This problem was introduced in version 20050114 when the behavior of
+reference object returned from Index was inadvertently resolved to an
+actual
+value. This problem was introduced in version 20050114 when the behavior
+of
Store() was modified to restrict the object types that can be used as the
source operand (to match the ACPI specification.)
@@ -5358,11 +11039,14 @@ Added a fix to aclinux.h to allow generation of AcpiExec on Linux.
Updated the AcpiSrc utility to add the FADT_DESCRIPTOR_REV2_MINUS struct.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5394,35 +11078,49 @@ Acquire() operation on _GL.
The local object cache is now optional, and is disabled by default. Both
AcpiExec and the iASL compiler enable the cache because they run in user
-mode and this enhances their performance. #define ACPI_ENABLE_OBJECT_CACHE
+mode and this enhances their performance. #define
+ACPI_ENABLE_OBJECT_CACHE
to enable the local cache.
-Fixed an issue in the internal function AcpiUtEvaluateObject concerning the
-optional "implicit return" support where an error was returned if no return
-object was expected, but one was implicitly returned. AE_OK is now returned
+Fixed an issue in the internal function AcpiUtEvaluateObject concerning
+the
+optional "implicit return" support where an error was returned if no
+return
+object was expected, but one was implicitly returned. AE_OK is now
+returned
in this case and the implicitly returned object is deleted.
-AcpiUtEvaluateObject is only occasionally used, and only to execute reserved
+AcpiUtEvaluateObject is only occasionally used, and only to execute
+reserved
methods such as _STA and _INI where the return type is known up front.
-Fixed a few issues with the internal convert-to-integer code. It now returns
+Fixed a few issues with the internal convert-to-integer code. It now
+returns
an error if an attempt is made to convert a null string, a string of only
-blanks/tabs, or a zero-length buffer. This affects both implicit conversion
+blanks/tabs, or a zero-length buffer. This affects both implicit
+conversion
and explicit conversion via the ToInteger() operator.
-The internal debug code in AcpiUtAcquireMutex has been commented out. It is
-not needed for normal operation and should increase the performance of the
-entire subsystem. The code remains in case it is needed for debug purposes
+The internal debug code in AcpiUtAcquireMutex has been commented out. It
+is
+not needed for normal operation and should increase the performance of
+the
+entire subsystem. The code remains in case it is needed for debug
+purposes
again.
-The AcpiExec source and makefile are included in the Unix/Linux package for
+The AcpiExec source and makefile are included in the Unix/Linux package
+for
the first time.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5434,24 +11132,32 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Switch/Case support: A warning is now issued if the type of the Switch value
+Switch/Case support: A warning is now issued if the type of the Switch
+value
cannot be determined at compile time. For example, Switch(Arg0) will
-generate the warning, and the type is assumed to be an integer. As per the
-ACPI spec, use a construct such as Switch(ToInteger(Arg0)) to eliminate the
+generate the warning, and the type is assumed to be an integer. As per
+the
+ACPI spec, use a construct such as Switch(ToInteger(Arg0)) to eliminate
+the
warning.
Switch/Case support: Implemented support for buffer and string objects as
the switch value. This is an ACPI 3.0 feature, now that LEqual supports
buffers and strings.
-Switch/Case support: The emitted code for the LEqual() comparisons now uses
-the switch value as the first operand, not the second. The case value is now
+Switch/Case support: The emitted code for the LEqual() comparisons now
+uses
+the switch value as the first operand, not the second. The case value is
+now
the second operand, and this allows the case value to be implicitly
converted to the type of the switch value, not the other way around.
-Switch/Case support: Temporary variables are now emitted immediately within
-the control method, not at the global level. This means that there are now
-36 temps available per-method, not 36 temps per-module as was the case with
+Switch/Case support: Temporary variables are now emitted immediately
+within
+the control method, not at the global level. This means that there are
+now
+36 temps available per-method, not 36 temps per-module as was the case
+with
the earlier implementation (_T_0 through _T_9 and _T_A through _T_Z.)
----------------------------------------
@@ -5463,51 +11169,66 @@ the core subsystem, iASL compiler, and the utilities.
1) ACPI CA Core Subsystem:
Fixed an issue with the String-to-Buffer conversion code where the string
-null terminator was not included in the buffer after conversion, but there
-is existing ASL that assumes the string null terminator is included. This is
+null terminator was not included in the buffer after conversion, but
+there
+is existing ASL that assumes the string null terminator is included. This
+is
the root of the ACPI_AML_BUFFER_LIMIT regression. This problem was
introduced in the previous version when the code was updated to correctly
-set the converted buffer size as per the ACPI specification. The ACPI spec
-is ambiguous and will be updated to specify that the null terminator must be
+set the converted buffer size as per the ACPI specification. The ACPI
+spec
+is ambiguous and will be updated to specify that the null terminator must
+be
included in the converted buffer. This also affects the ToBuffer() ASL
operator.
Fixed a problem with the Mid() ASL/AML operator where it did not work
-correctly on Buffer objects. Newly created sub-buffers were not being marked
+correctly on Buffer objects. Newly created sub-buffers were not being
+marked
as initialized.
Fixed a problem in AcpiTbFindTable where incorrect string compares were
-performed on the OemId and OemTableId table header fields. These fields are
+performed on the OemId and OemTableId table header fields. These fields
+are
not null terminated, so strncmp is now used instead of strcmp.
Implemented a restriction on the Store() ASL/AML operator to align the
-behavior with the ACPI specification. Previously, any object could be used
-as the source operand. Now, the only objects that may be used are Integers,
+behavior with the ACPI specification. Previously, any object could be
+used
+as the source operand. Now, the only objects that may be used are
+Integers,
Buffers, Strings, Packages, Object References, and DDB Handles. If
necessary, the original behavior can be restored by enabling the
EnableInterpreterSlack flag.
-Enhanced the optional "implicit return" support to allow an implicit return
+Enhanced the optional "implicit return" support to allow an implicit
+return
value from methods that are invoked externally via the AcpiEvaluateObject
interface. This enables implicit returns from the _STA and _INI methods,
for example.
-Changed the Revision() ASL/AML operator to return the current version of the
-AML interpreter, in the YYYYMMDD format. Previously, it incorrectly returned
+Changed the Revision() ASL/AML operator to return the current version of
+the
+AML interpreter, in the YYYYMMDD format. Previously, it incorrectly
+returned
the supported ACPI version (This is the function of the _REV method).
-Updated the _REV predefined method to return the currently supported version
+Updated the _REV predefined method to return the currently supported
+version
of ACPI, now 3.
Implemented batch mode option for the AcpiExec utility (-b).
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5525,23 +11246,29 @@ ACPI CA core subsystem.
1) ACPI CA Core Subsystem:
-Fixed a problem in the ToDecimalString operator where the resulting string
+Fixed a problem in the ToDecimalString operator where the resulting
+string
length was incorrectly calculated. The length is now calculated exactly,
eliminating incorrect AE_STRING_LIMIT exceptions.
-Fixed a problem in the ToHexString operator to allow a maximum 200 character
+Fixed a problem in the ToHexString operator to allow a maximum 200
+character
string to be produced.
-Fixed a problem in the internal string-to-buffer and buffer-to-buffer copy
+Fixed a problem in the internal string-to-buffer and buffer-to-buffer
+copy
routine where the length of the resulting buffer was not truncated to the
new size (if the target buffer already existed).
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5558,7 +11285,8 @@ Implemented the new ACPI 3.0 resource template macros - DWordSpace,
ExtendedIO, ExtendedMemory, ExtendedSpace, QWordSpace, and WordSpace.
Includes support in the disassembler.
-Implemented support for the new (ACPI 3.0) parameter to the Register macro,
+Implemented support for the new (ACPI 3.0) parameter to the Register
+macro,
AccessSize.
Fixed a problem where the _HE resource name for the Interrupt macro was
@@ -5571,29 +11299,34 @@ incorrect AML code was generated if the offset within the resource buffer
was 0 or 1. The optimizer shortened the AML code to a single byte opcode
but did not update the surrounding package lengths.
-Changes to the Dma macro: All channels within the channel list must be in
+Changes to the Dma macro: All channels within the channel list must be
+in
the range 0-7. Maximum 8 channels can be specified. BusMaster operand is
optional (default is BusMaster).
Implemented check for maximum 7 data bytes for the VendorShort macro.
-The ReadWrite parameter is now optional for the Memory32 and similar macros.
+The ReadWrite parameter is now optional for the Memory32 and similar
+macros.
----------------------------------------
03 December 2004. Summary of changes for version 20041203:
1) ACPI CA Core Subsystem:
-The low-level field insertion/extraction code (exfldio) has been completely
+The low-level field insertion/extraction code (exfldio) has been
+completely
rewritten to eliminate unnecessary complexity, bugs, and boundary
conditions.
-Fixed a problem in the ToInteger, ToBuffer, ToHexString, and ToDecimalString
+Fixed a problem in the ToInteger, ToBuffer, ToHexString, and
+ToDecimalString
operators where the input operand could be inadvertently deleted if no
conversion was necessary (e.g., if the input to ToInteger was an Integer
object.)
-Fixed a problem with the ToDecimalString and ToHexString where an incorrect
+Fixed a problem with the ToDecimalString and ToHexString where an
+incorrect
exception code was returned if the resulting string would be > 200 chars.
AE_STRING_LIMIT is now returned.
@@ -5604,11 +11337,14 @@ Fixed a problem in oswinxf (used by AcpiExec and iASL) to allow > 128
semaphores to be allocated.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5624,7 +11360,8 @@ the compiler options used during generation.
Fixed typechecking for the ObjectType and SizeOf operators. Problem was
recently introduced in 20041119.
-Fixed a problem with the ToUUID macro where the upper nybble of each buffer
+Fixed a problem with the ToUUID macro where the upper nybble of each
+buffer
byte was inadvertently set to zero.
----------------------------------------
@@ -5632,31 +11369,42 @@ byte was inadvertently set to zero.
1) ACPI CA Core Subsystem:
-Fixed a problem in the internal ConvertToInteger routine where new integers
-were not truncated to 32 bits for 32-bit ACPI tables. This routine converts
+Fixed a problem in the internal ConvertToInteger routine where new
+integers
+were not truncated to 32 bits for 32-bit ACPI tables. This routine
+converts
buffers and strings to integers.
-Implemented support to store a value to an Index() on a String object. This
+Implemented support to store a value to an Index() on a String object.
+This
is an ACPI 2.0 feature that had not yet been implemented.
-Implemented new behavior for storing objects to individual package elements
-(via the Index() operator). The previous behavior was to invoke the implicit
+Implemented new behavior for storing objects to individual package
+elements
+(via the Index() operator). The previous behavior was to invoke the
+implicit
conversion rules if an object was already present at the index. The new
-behavior is to simply delete any existing object and directly store the new
-object. Although the ACPI specification seems unclear on this subject, other
+behavior is to simply delete any existing object and directly store the
+new
+object. Although the ACPI specification seems unclear on this subject,
+other
ACPI implementations behave in this manner. (This is the root of the
AE_BAD_HEX_CONSTANT issue.)
-Modified the RSDP memory scan mechanism to support the extended checksum for
+Modified the RSDP memory scan mechanism to support the extended checksum
+for
ACPI 2.0 (and above) RSDPs. Note that the search continues until a valid
RSDP signature is found with a valid checksum.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5676,30 +11424,38 @@ Fixed a missing semicolon in the aslcompiler.y file.
1) ACPI CA Core Subsystem:
-Implemented support for FADT revision 2. This was an interim table (between
+Implemented support for FADT revision 2. This was an interim table
+(between
ACPI 1.0 and ACPI 2.0) that adds support for the FADT reset register.
Implemented optional support to allow uninitialized LocalX and ArgX
-variables in a control method. The variables are initialized to an Integer
+variables in a control method. The variables are initialized to an
+Integer
object with a value of zero. This support is enabled by setting the
AcpiGbl_EnableInterpreterSlack flag to TRUE.
-Implemented support for Integer objects for the SizeOf operator. Either 4
-or 8 is returned, depending on the current integer size (32-bit or 64-bit,
+Implemented support for Integer objects for the SizeOf operator. Either
+4
+or 8 is returned, depending on the current integer size (32-bit or 64-
+bit,
depending on the parent table revision).
-Fixed a problem in the implementation of the SizeOf and ObjectType operators
+Fixed a problem in the implementation of the SizeOf and ObjectType
+operators
where the operand was resolved to a value too early, causing incorrect
return values for some objects.
Fixed some possible memory leaks during exceptional conditions.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5741,8 +11497,10 @@ the number of bug fixes in the next few months.
1) ACPI CA Core Subsystem:
-Fixed two alignment issues on 64-bit platforms - within debug statements in
-AcpiEvGpeDetect and AcpiEvCreateGpeBlock. Removed references to the Address
+Fixed two alignment issues on 64-bit platforms - within debug statements
+in
+AcpiEvGpeDetect and AcpiEvCreateGpeBlock. Removed references to the
+Address
field within the non-aligned ACPI generic address structure.
Fixed a problem in the Increment and Decrement operators where incorrect
@@ -5750,14 +11508,18 @@ operand resolution could result in the inadvertent modification of the
original integer when the integer is passed into another method as an
argument and the arg is then incremented/decremented.
-Fixed a problem in the FromBCD operator where the upper 32-bits of a 64-bit
+Fixed a problem in the FromBCD operator where the upper 32-bits of a 64-
+bit
BCD number were truncated during conversion.
-Fixed a problem in the ToDecimal operator where the length of the resulting
-string could be set incorrectly too long if the input operand was a Buffer
+Fixed a problem in the ToDecimal operator where the length of the
+resulting
+string could be set incorrectly too long if the input operand was a
+Buffer
object.
-Fixed a problem in the Logical operators (LLess, etc.) where a NULL byte (0)
+Fixed a problem in the Logical operators (LLess, etc.) where a NULL byte
+(0)
within a buffer would prematurely terminate a compare between buffer
objects.
@@ -5765,11 +11527,14 @@ Added a check for string overflow (>200 characters as per the ACPI
specification) during the Concatenate operator with two string operands.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5786,11 +11551,14 @@ the compiler options used during generation.
Allow the use of the ObjectType operator on uninitialized Locals and Args
(returns 0 as per the ACPI specification).
-Fixed a problem where the compiler would fault if there was a syntax error
+Fixed a problem where the compiler would fault if there was a syntax
+error
in the FieldName of all of the various CreateXXXField operators.
-Disallow the use of lower case letters within the EISAID macro, as per the
-ACPI specification. All EISAID strings must be of the form "UUUNNNN" Where
+Disallow the use of lower case letters within the EISAID macro, as per
+the
+ACPI specification. All EISAID strings must be of the form "UUUNNNN"
+Where
U is an uppercase letter and N is a hex digit.
@@ -5803,41 +11571,52 @@ Implemented support for the ACPI 3.0 Timer operator. This ASL function
implements a 64-bit timer with 100 nanosecond granularity.
Defined a new OSL interface, AcpiOsGetTimer. This interface is used to
-implement the ACPI 3.0 Timer operator. This allows the host OS to implement
-the timer with the best clock available. Also, it keeps the core subsystem
+implement the ACPI 3.0 Timer operator. This allows the host OS to
+implement
+the timer with the best clock available. Also, it keeps the core
+subsystem
out of the clock handling business, since the host OS (usually) performs
this function.
Fixed an alignment issue on 64-bit platforms. The HwLowLevelRead(Write)
functions use a 64-bit address which is part of the packed ACPI Generic
-Address Structure. Since the structure is non-aligned, the alignment macros
+Address Structure. Since the structure is non-aligned, the alignment
+macros
are now used to extract the address to a local variable before use.
-Fixed a problem where the ToInteger operator assumed all input strings were
-hexadecimal. The operator now handles both decimal strings and hex strings
+Fixed a problem where the ToInteger operator assumed all input strings
+were
+hexadecimal. The operator now handles both decimal strings and hex
+strings
(prefixed with "0x").
Fixed a problem where the string length in the string object created as a
result of the internal ConvertToString procedure could be incorrect. This
-potentially affected all implicit conversions and also the ToDecimalString
+potentially affected all implicit conversions and also the
+ToDecimalString
and ToHexString operators.
Fixed two problems in the ToString operator. If the length parameter was
zero, an incorrect string object was created and the value of the input
length parameter was inadvertently changed from zero to Ones.
-Fixed a problem where the optional ResourceSource string in the ExtendedIRQ
+Fixed a problem where the optional ResourceSource string in the
+ExtendedIRQ
resource macro was ignored.
-Simplified the interfaces to the internal division functions, reducing code
+Simplified the interfaces to the internal division functions, reducing
+code
size and complexity.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5852,11 +11631,13 @@ the compiler options used during generation.
Implemented support for the ACPI 3.0 Timer operator.
-Fixed a problem where the Default() operator was inadvertently ignored in a
+Fixed a problem where the Default() operator was inadvertently ignored in
+a
Switch/Case block. This was a problem in the translation of the Switch
statement to If...Else pairs.
-Added support to allow a standalone Return operator, with no parentheses (or
+Added support to allow a standalone Return operator, with no parentheses
+(or
operands).
Fixed a problem with code generation for the ElseIf operator where the
@@ -5868,12 +11649,16 @@ loss of some code.
1) ACPI CA Core Subsystem:
-Fixed a problem with the implementation of the LNot() operator where "Ones"
-was not returned for the TRUE case. Changed the code to return Ones instead
-of (!Arg) which was usually 1. This change affects iASL constant folding for
+Fixed a problem with the implementation of the LNot() operator where
+"Ones"
+was not returned for the TRUE case. Changed the code to return Ones
+instead
+of (!Arg) which was usually 1. This change affects iASL constant folding
+for
this operator also.
-Fixed a problem in AcpiUtInitializeBuffer where an existing buffer was not
+Fixed a problem in AcpiUtInitializeBuffer where an existing buffer was
+not
initialized properly -- Now zero the entire buffer in this case where the
buffer already exists.
@@ -5882,19 +11667,24 @@ Milliseconds) to simply (ACPI_INTEGER Milliseconds). This simplifies all
related code considerably. This will require changes/updates to all OS
interface layers (OSLs.)
-Implemented a new external interface, AcpiInstallExceptionHandler, to allow
-a system exception handler to be installed. This handler is invoked upon any
+Implemented a new external interface, AcpiInstallExceptionHandler, to
+allow
+a system exception handler to be installed. This handler is invoked upon
+any
run-time exception that occurs during control method execution.
Added support for the DSDT in AcpiTbFindTable. This allows the
DataTableRegion() operator to access the local copy of the DSDT.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5908,16 +11698,20 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
Fixed a problem with constant folding and the LNot operator. LNot was
-returning 1 in the TRUE case, not Ones as per the ACPI specification. This
+returning 1 in the TRUE case, not Ones as per the ACPI specification.
+This
could result in the generation of an incorrect folded/reduced constant.
End-Of-File is now allowed within a "//"-style comment. A parse error no
-longer occurs if such a comment is at the very end of the input ASL source
+longer occurs if such a comment is at the very end of the input ASL
+source
file.
Implemented the "-r" option to override the Revision in the table header.
-The initial use of this option will be to simplify the evaluation of the AML
-interpreter by allowing a single ASL source module to be compiled for either
+The initial use of this option will be to simplify the evaluation of the
+AML
+interpreter by allowing a single ASL source module to be compiled for
+either
32-bit or 64-bit integers.
@@ -5927,41 +11721,55 @@ interpreter by allowing a single ASL source module to be compiled for either
1) ACPI CA Core Subsystem:
- Implemented support for implicit object conversion in the non-numeric
-logical operators (LEqual, LGreater, LGreaterEqual, LLess, LLessEqual, and
+logical operators (LEqual, LGreater, LGreaterEqual, LLess, LLessEqual,
+and
LNotEqual.) Any combination of Integers/Strings/Buffers may now be used;
-the second operand is implicitly converted on the fly to match the type of
+the second operand is implicitly converted on the fly to match the type
+of
the first operand. For example:
LEqual (Source1, Source2)
-Source1 and Source2 must each evaluate to an integer, a string, or a buffer.
-The data type of Source1 dictates the required type of Source2. Source2 is
+Source1 and Source2 must each evaluate to an integer, a string, or a
+buffer.
+The data type of Source1 dictates the required type of Source2. Source2
+is
implicitly converted if necessary to match the type of Source1.
-- Updated and corrected the behavior of the string conversion support. The
+- Updated and corrected the behavior of the string conversion support.
+The
rules concerning conversion of buffers to strings (according to the ACPI
specification) are as follows:
ToDecimalString - explicit byte-wise conversion of buffer to string of
-decimal values (0-255) separated by commas. ToHexString - explicit byte-wise
+decimal values (0-255) separated by commas. ToHexString - explicit byte-
+wise
conversion of buffer to string of hex values (0-FF) separated by commas.
-ToString - explicit byte-wise conversion of buffer to string. Byte-by-byte
-copy with no transform except NULL terminated. Any other implicit buffer-to-
-string conversion - byte-wise conversion of buffer to string of hex values
+ToString - explicit byte-wise conversion of buffer to string. Byte-by-
+byte
+copy with no transform except NULL terminated. Any other implicit buffer-
+to-
+string conversion - byte-wise conversion of buffer to string of hex
+values
(0-FF) separated by spaces.
- Fixed typo in definition of AcpiGbl_EnableInterpreterSlack.
-- Fixed a problem in AcpiNsGetPathnameLength where the returned length was
+- Fixed a problem in AcpiNsGetPathnameLength where the returned length
+was
one byte too short in the case of a node in the root scope. This could
cause a fault during debug output.
-- Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+- Code and Data Size: Current and previous core subsystem library sizes
+are
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -5983,25 +11791,33 @@ the compiler options used during generation.
1) ACPI CA Core Subsystem:
Designed and implemented support within the AML interpreter for the so-
-called "implicit return". This support returns the result of the last ASL
+called "implicit return". This support returns the result of the last
+ASL
operation within a control method, in the absence of an explicit Return()
operator. A few machines depend on this behavior, even though it is not
-explicitly supported by the ASL language. It is optional support that can
+explicitly supported by the ASL language. It is optional support that
+can
be enabled at runtime via the AcpiGbl_EnableInterpreterSlack flag.
-Removed support for the PCI_Config address space from the internal low level
+Removed support for the PCI_Config address space from the internal low
+level
hardware interfaces (AcpiHwLowLevelRead and AcpiHwLowLevelWrite). This
-support was not used internally, and would not work correctly anyway because
+support was not used internally, and would not work correctly anyway
+because
the PCI bus number and segment number were not supported. There are
-separate interfaces for PCI configuration space access because of the unique
+separate interfaces for PCI configuration space access because of the
+unique
interface.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6024,30 +11840,39 @@ generation. This problem was introduced in the 20040715 release.
1) ACPI CA Core Subsystem:
-Restructured the internal HW GPE interfaces to pass/track the current state
+Restructured the internal HW GPE interfaces to pass/track the current
+state
of interrupts (enabled/disabled) in order to avoid possible deadlock and
increase flexibility of the interfaces.
-Implemented a "lexicographical compare" for String and Buffer objects within
-the logical operators -- LGreater, LLess, LGreaterEqual, and LLessEqual --
-as per further clarification to the ACPI specification. Behavior is similar
+Implemented a "lexicographical compare" for String and Buffer objects
+within
+the logical operators -- LGreater, LLess, LGreaterEqual, and LLessEqual -
+-
+as per further clarification to the ACPI specification. Behavior is
+similar
to C library "strcmp".
Completed a major reduction in CPU stack use for the AcpiGetFirmwareTable
external function. In the 32-bit non-debug case, the stack use has been
reduced from 168 bytes to 32 bytes.
-Deployed a new run-time configuration flag, AcpiGbl_EnableInterpreterSlack,
+Deployed a new run-time configuration flag,
+AcpiGbl_EnableInterpreterSlack,
whose purpose is to allow the AML interpreter to forgive certain bad AML
constructs. Default setting is FALSE.
-Implemented the first use of AcpiGbl_EnableInterpreterSlack in the Field IO
-support code. If enabled, it allows field access to go beyond the end of a
-region definition if the field is within the region length rounded up to the
+Implemented the first use of AcpiGbl_EnableInterpreterSlack in the Field
+IO
+support code. If enabled, it allows field access to go beyond the end of
+a
+region definition if the field is within the region length rounded up to
+the
next access width boundary (a common coding error.)
Renamed OSD_HANDLER to ACPI_OSD_HANDLER, and OSD_EXECUTION_CALLBACK to
-ACPI_OSD_EXEC_CALLBACK for consistency with other ACPI symbols. Also, these
+ACPI_OSD_EXEC_CALLBACK for consistency with other ACPI symbols. Also,
+these
symbols are lowercased by the latest version of the AcpiSrc tool.
The prototypes for the PCI interfaces in acpiosxf.h have been updated to
@@ -6055,11 +11880,14 @@ rename "Register" to simply "Reg" to prevent certain compilers from
complaining.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6074,11 +11902,13 @@ the compiler options used during generation.
Implemented full support for Package objects within the Case() operator.
Note: The Break() operator is currently not supported within Case blocks
-(TermLists) as there is some question about backward compatibility with ACPI
+(TermLists) as there is some question about backward compatibility with
+ACPI
1.0 interpreters.
-Fixed a problem where complex terms were not supported properly within the
+Fixed a problem where complex terms were not supported properly within
+the
Switch() operator.
Eliminated extraneous warning for compiler-emitted reserved names of the
@@ -6093,22 +11923,27 @@ within the DefinitionBlock operator.
1) ACPI CA Core Subsystem:
-Implemented support for Buffer and String objects (as per ACPI 2.0) for the
+Implemented support for Buffer and String objects (as per ACPI 2.0) for
+the
following ASL operators: LEqual, LGreater, LLess, LGreaterEqual, and
LLessEqual.
All directory names in the entire source package are lower case, as they
were in earlier releases.
-Implemented "Disassemble" command in the AML debugger that will disassemble
+Implemented "Disassemble" command in the AML debugger that will
+disassemble
a single control method.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6122,7 +11957,8 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Implemented support for Buffer and String objects (as per ACPI 2.0) for the
+Implemented support for Buffer and String objects (as per ACPI 2.0) for
+the
following ASL operators: LEqual, LGreater, LLess, LGreaterEqual, and
LLessEqual.
@@ -6132,14 +11968,17 @@ were in earlier releases.
Fixed a fault when using the -g or -d<nofilename> options if the FADT was
not found.
-Fixed an issue with the Windows version of the compiler where later versions
+Fixed an issue with the Windows version of the compiler where later
+versions
of Windows place the FADT in the registry under the name "FADT" and not
"FACP" as earlier versions did. This applies when using the -g or -
d<nofilename> options. The compiler now looks for both strings as
necessary.
-Fixed a problem with compiler namepath optimization where a namepath within
-the Scope() operator could not be optimized if the namepath was a subpath of
+Fixed a problem with compiler namepath optimization where a namepath
+within
+the Scope() operator could not be optimized if the namepath was a subpath
+of
the current scope path.
----------------------------------------
@@ -6147,11 +11986,15 @@ the current scope path.
1) ACPI CA Core Subsystem:
-Completed a new design and implementation for EBDA (Extended BIOS Data Area)
-support in the RSDP scan code. The original code improperly scanned for the
-EBDA by simply scanning from memory location 0 to 0x400. The correct method
+Completed a new design and implementation for EBDA (Extended BIOS Data
+Area)
+support in the RSDP scan code. The original code improperly scanned for
+the
+EBDA by simply scanning from memory location 0 to 0x400. The correct
+method
is to first obtain the EBDA pointer from within the BIOS data area, then
-scan 1K of memory starting at the EBDA pointer. There appear to be few if
+scan 1K of memory starting at the EBDA pointer. There appear to be few
+if
any machines that place the RSDP in the EBDA, however.
Integrated a fix for a possible fault during evaluation of BufferField
@@ -6166,11 +12009,14 @@ Rolled in a couple of changes to the FreeBSD-specific header.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6183,7 +12029,8 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-Fixed a generation warning produced by some overly-verbose compilers for a
+Fixed a generation warning produced by some overly-verbose compilers for
+a
64-bit constant.
----------------------------------------
@@ -6197,18 +12044,22 @@ during and after GPE method execution. Result of 04/27 changes.
Removed extra "clear all GPEs" when sleeping/waking.
Removed AcpiHwEnableGpe and AcpiHwDisableGpe, replaced by the single
-AcpiHwWriteGpeEnableReg. Changed a couple of calls to the functions above to
+AcpiHwWriteGpeEnableReg. Changed a couple of calls to the functions above
+to
the new AcpiEv* calls as appropriate.
-ACPI_OS_NAME was removed from the OS-specific headers. The default name is
-now "Microsoft Windows NT" for maximum compatibility. However this can be
+ACPI_OS_NAME was removed from the OS-specific headers. The default name
+is
+now "Microsoft Windows NT" for maximum compatibility. However this can
+be
changed by modifying the acconfig.h file.
Allow a single invocation of AcpiInstallNotifyHandler for a handler that
traps both types of notifies (System, Device). Use ACPI_ALL_NOTIFY flag.
Run _INI methods on ThermalZone objects. This is against the ACPI
-specification, but there is apparently ASL code in the field that has these
+specification, but there is apparently ASL code in the field that has
+these
_INI methods, and apparently "other" AML interpreters execute them.
Performed a full 16/32/64 bit lint that resulted in some small changes.
@@ -6216,11 +12067,14 @@ Performed a full 16/32/64 bit lint that resulted in some small changes.
Added a sleep simulation command to the AML debugger to test sleep code.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6236,26 +12090,36 @@ the compiler options used during generation.
1) ACPI CA Core Subsystem:
Completed a major overhaul of the GPE handling within ACPI CA. There are
-now three types of GPEs: wake-only, runtime-only, and combination wake/run.
+now three types of GPEs: wake-only, runtime-only, and combination
+wake/run.
The only GPEs allowed to be combination wake/run are for button-style
-devices such as a control-method power button, control-method sleep button,
-or a notebook lid switch. GPEs that have an _Lxx or _Exx method and are not
+devices such as a control-method power button, control-method sleep
+button,
+or a notebook lid switch. GPEs that have an _Lxx or _Exx method and are
+not
referenced by any _PRW methods are marked for "runtime" and hardware
-enabled. Any GPE that is referenced by a _PRW method is marked for "wake"
+enabled. Any GPE that is referenced by a _PRW method is marked for
+"wake"
(and disabled at runtime). However, at sleep time, only those GPEs that
-have been specifically enabled for wake via the AcpiEnableGpe interface will
+have been specifically enabled for wake via the AcpiEnableGpe interface
+will
actually be hardware enabled.
-A new external interface has been added, AcpiSetGpeType(), that is meant to
-be used by device drivers to force a GPE to a particular type. It will be
+A new external interface has been added, AcpiSetGpeType(), that is meant
+to
+be used by device drivers to force a GPE to a particular type. It will
+be
especially useful for the drivers for the button devices mentioned above.
Completed restructuring of the ACPI CA initialization sequence so that
-default operation region handlers are installed before GPEs are initialized
-and the _PRW methods are executed. This will prevent errors when the _PRW
+default operation region handlers are installed before GPEs are
+initialized
+and the _PRW methods are executed. This will prevent errors when the
+_PRW
methods attempt to access system memory or I/O space.
-GPE enable/disable no longer reads the GPE enable register. We now keep the
+GPE enable/disable no longer reads the GPE enable register. We now keep
+the
enable info for runtime and wake separate and in the GPE_EVENT_INFO. We
thus no longer depend on the hardware to maintain these bits.
@@ -6265,12 +12129,16 @@ for state S5.
Improved the AML debugger output for displaying the GPE blocks and their
current status.
-Added new strings for the _OSI method, of the form "Windows 2001 SPx" where
+Added new strings for the _OSI method, of the form "Windows 2001 SPx"
+where
x = 0,1,2,3,4.
-Fixed a problem where the physical address was incorrectly calculated when
-the Load() operator was used to directly load from an Operation Region (vs.
-loading from a Field object.) Also added check for minimum table length for
+Fixed a problem where the physical address was incorrectly calculated
+when
+the Load() operator was used to directly load from an Operation Region
+(vs.
+loading from a Field object.) Also added check for minimum table length
+for
this case.
Fix for multiple mutex acquisition. Restore original thread SyncLevel on
@@ -6283,21 +12151,27 @@ Shrunk the ACPI_GPE_EVENT_INFO structure by 40%. There is one such
structure for each GPE in the system, so the size of this structure is
important.
-CPU stack requirement reduction: Cleaned up the method execution and object
+CPU stack requirement reduction: Cleaned up the method execution and
+object
evaluation paths so that now a parameter structure is passed, instead of
copying the various method parameters over and over again.
In evregion.c: Correctly exit and reenter the interpreter region if and
-only if dispatching an operation region request to a user-installed handler.
+only if dispatching an operation region request to a user-installed
+handler.
Do not exit/reenter when dispatching to a default handler (e.g., default
system memory or I/O handlers)
-Notes for updating drivers for the new GPE support. The following changes
-must be made to ACPI-related device drivers that are attached to one or more
-GPEs: (This information will be added to the ACPI CA Programmer Reference.)
+Notes for updating drivers for the new GPE support. The following
+changes
+must be made to ACPI-related device drivers that are attached to one or
+more
+GPEs: (This information will be added to the ACPI CA Programmer
+Reference.)
-1) AcpiInstallGpeHandler no longer automatically enables the GPE, you must
+1) AcpiInstallGpeHandler no longer automatically enables the GPE, you
+must
explicitly call AcpiEnableGpe.
2) There is a new interface called AcpiSetGpeType. This should be called
before enabling the GPE. Also, this interface will automatically disable
@@ -6318,18 +12192,24 @@ If _PRW exists: /* This is a control-method button */
AcpiSetGpeType (GpeDevice, GpeNum, ACPI_GPE_TYPE_WAKE_RUN);
AcpiEnableGpe (GpeDevice, GpeNum, ACPI_NOT_ISR);
-For all other devices that have _PRWs, we automatically set the GPE type to
-ACPI_GPE_TYPE_WAKE, but the GPE is NOT automatically (wake) enabled. This
-must be done on a selective basis, usually requiring some kind of user app
+For all other devices that have _PRWs, we automatically set the GPE type
+to
+ACPI_GPE_TYPE_WAKE, but the GPE is NOT automatically (wake) enabled.
+This
+must be done on a selective basis, usually requiring some kind of user
+app
to allow the user to pick the wake devices.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6349,7 +12229,8 @@ the compiler options used during generation.
Fixed an interpreter problem where an indirect store through an ArgX
parameter was incorrectly applying the "implicit conversion rules" during
-the store. From the ACPI specification: "If the target is a method local or
+the store. From the ACPI specification: "If the target is a method local
+or
argument (LocalX or ArgX), no conversion is performed and the result is
stored directly to the target". The new behavior is to disable implicit
conversion during ALL stores to an ArgX.
@@ -6358,16 +12239,20 @@ Changed the behavior of the _PRW method scan to ignore any and all errors
returned by a given _PRW. This prevents the scan from aborting from the
failure of any single _PRW.
-Moved the runtime configuration parameters from the global init procedure to
+Moved the runtime configuration parameters from the global init procedure
+to
static variables in acglobal.h. This will allow the host to override the
default values easily.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6380,16 +12265,20 @@ the compiler options used during generation.
2) iASL Compiler/Disassembler:
-iASL now fully disassembles SSDTs. However, External() statements are not
+iASL now fully disassembles SSDTs. However, External() statements are
+not
generated automatically for unresolved symbols at this time. This is a
planned feature for future implementation.
-Fixed a scoping problem in the disassembler that occurs when the type of the
+Fixed a scoping problem in the disassembler that occurs when the type of
+the
target of a Scope() operator is overridden. This problem caused an
incorrectly nested internal namespace to be constructed.
-Any warnings or errors that are emitted during disassembly are now commented
-out automatically so that the resulting file can be recompiled without any
+Any warnings or errors that are emitted during disassembly are now
+commented
+out automatically so that the resulting file can be recompiled without
+any
hand editing.
----------------------------------------
@@ -6400,8 +12289,10 @@ hand editing.
Implemented support for "wake" GPEs via interaction between GPEs and the
_PRW methods. Every GPE that is pointed to by one or more _PRWs is
identified as a WAKE GPE and by default will no longer be enabled at
-runtime. Previously, we were blindly enabling all GPEs with a corresponding
-_Lxx or _Exx method - but most of these turn out to be WAKE GPEs anyway. We
+runtime. Previously, we were blindly enabling all GPEs with a
+corresponding
+_Lxx or _Exx method - but most of these turn out to be WAKE GPEs anyway.
+We
believe this has been the cause of thousands of "spurious" GPEs on some
systems.
@@ -6413,14 +12304,17 @@ properly. The proper scope within the namespace was not initialized
(transferred to the target of the aliased method) before executing the
target method.
-Fixed a potential race condition on internal object deletion on the return
+Fixed a potential race condition on internal object deletion on the
+return
object in AcpiEvaluateObject.
Integrated a fix for resource descriptors where both _MEM and _MTP were
being extracted instead of just _MEM. (i.e. bitmask was incorrectly too
wide, 0x0F instead of 0x03.)
-Added a special case for ACPI_ROOT_OBJECT in AcpiUtGetNodeName, preventing a
+Added a special case for ACPI_ROOT_OBJECT in AcpiUtGetNodeName,
+preventing
+a
fault in some cases.
Updated Notify() values for debug statements in evmisc.c
@@ -6428,11 +12322,14 @@ Updated Notify() values for debug statements in evmisc.c
Return proper status from AcpiUtMutexInitialize, not just simply AE_OK.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6453,21 +12350,27 @@ method execution did not abort cleanly. For example, objects created and
installed in the namespace were not deleted. This caused all subsequent
invocations of the method to return the AE_ALREADY_EXISTS exception.
-Implemented a mechanism to force a control method to "Serialized" execution
+Implemented a mechanism to force a control method to "Serialized"
+execution
if the method attempts to create namespace objects. (The root of the
AE_ALREADY_EXISTS problem.)
Implemented support for the predefined _OSI "internal" control method.
-Initial supported strings are "Linux", "Windows 2000", "Windows 2001", and
-"Windows 2001.1", and can be easily upgraded for new strings as necessary.
+Initial supported strings are "Linux", "Windows 2000", "Windows 2001",
+and
+"Windows 2001.1", and can be easily upgraded for new strings as
+necessary.
This feature will allow "other" operating systems to execute the fully
tested, "Windows" code path through the ASL code
Global Lock Support: Now allows multiple acquires and releases with any
-internal thread. Removed concept of "owning thread" for this special mutex.
+internal thread. Removed concept of "owning thread" for this special
+mutex.
-Fixed two functions that were inappropriately declaring large objects on the
-CPU stack: PsParseLoop, NsEvaluateRelative. Reduces the stack usage during
+Fixed two functions that were inappropriately declaring large objects on
+the
+CPU stack: PsParseLoop, NsEvaluateRelative. Reduces the stack usage
+during
method execution considerably.
Fixed a problem in the ACPI 2.0 FACS descriptor (actbl2.h) where the
@@ -6476,16 +12379,21 @@ S4Bios_f field was incorrectly defined as UINT32 instead of UINT32_BIT.
Fixed a problem where AcpiEvGpeDetect would fault if there were no GPEs
defined on the machine.
-Implemented two runtime options: One to force all control method execution
-to "Serialized" to mimic Windows behavior, another to disable _OSI support
+Implemented two runtime options: One to force all control method
+execution
+to "Serialized" to mimic Windows behavior, another to disable _OSI
+support
if it causes problems on a given machine.
Code and Data Size: Current and previous core subsystem library sizes are
-shown below. These are the code and data sizes for the acpica.lib produced
+shown below. These are the code and data sizes for the acpica.lib
+produced
by the Microsoft Visual C++ 6.0 compiler, and these values do not include
any ACPI driver or OSPM code. The debug version of the code includes the
-debug output trace mechanism and has a much larger code and data size. Note
-that these values will vary depending on the efficiency of the compiler and
+debug output trace mechanism and has a much larger code and data size.
+Note
+that these values will vary depending on the efficiency of the compiler
+and
the compiler options used during generation.
Previous Release:
@@ -6520,7 +12428,8 @@ structures to the beginning of the file.
After wake, clear GPE status register(s) before enabling GPEs.
-After wake, clear/enable power button. (Perhaps we should clear/enable all
+After wake, clear/enable power button. (Perhaps we should clear/enable
+all
fixed events upon wake.)
Fixed a couple of possible memory leaks in the Namespace manager.
@@ -6540,15 +12449,18 @@ Fixed a problem where a store of an object into an indexed package could
fail if the store occurs within a different method than the method that
created the package.
-Fixed a problem where the ToDecimal operator could return incorrect results.
+Fixed a problem where the ToDecimal operator could return incorrect
+results.
-Fixed a problem where the CopyObject operator could fail on some of the more
+Fixed a problem where the CopyObject operator could fail on some of the
+more
obscure objects (e.g., Reference objects.)
Improved the output of the Debug object to display buffer, package, and
index objects.
-Fixed a problem where constructs of the form "RefOf (ArgX)" did not return
+Fixed a problem where constructs of the form "RefOf (ArgX)" did not
+return
the expected result.
Added permanent ACPI_REPORT_ERROR macros for all instances of the
@@ -6565,7 +12477,8 @@ functional changes, however.
1) ACPI CA Core Subsystem:
-Improved error messages when there is a problem finding one or more of the
+Improved error messages when there is a problem finding one or more of
+the
required base ACPI tables
Reintroduced the definition of APIC_HEADER in actbl.h
@@ -6576,7 +12489,8 @@ Removed extraneous reference to NewObj in dsmthdat.c
2) iASL compiler
-Fixed a problem introduced in December that disabled the correct disassembly
+Fixed a problem introduced in December that disabled the correct
+disassembly
of Resource Templates
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbcmds.c b/usr/src/uts/intel/io/acpica/debugger/dbcmds.c
deleted file mode 100644
index 73387b872c..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbcmds.c
+++ /dev/null
@@ -1,822 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbcmds - Miscellaneous debug commands and output routines
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acevents.h"
-#include "acdebug.h"
-#include "acresrc.h"
-#include "actables.h"
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbcmds")
-
-
-/* Local prototypes */
-
-static void
-AcpiDmCompareAmlResources (
- UINT8 *Aml1Buffer,
- ACPI_RSDESC_SIZE Aml1BufferLength,
- UINT8 *Aml2Buffer,
- ACPI_RSDESC_SIZE Aml2BufferLength);
-
-static ACPI_STATUS
-AcpiDmTestResourceConversion (
- ACPI_NAMESPACE_NODE *Node,
- char *Name);
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbConvertToNode
- *
- * PARAMETERS: InString - String to convert
- *
- * RETURN: Pointer to a NS node
- *
- * DESCRIPTION: Convert a string to a valid NS pointer. Handles numeric or
- * alpha strings.
- *
- ******************************************************************************/
-
-ACPI_NAMESPACE_NODE *
-AcpiDbConvertToNode (
- char *InString)
-{
- ACPI_NAMESPACE_NODE *Node;
-
-
- if ((*InString >= 0x30) && (*InString <= 0x39))
- {
- /* Numeric argument, convert */
-
- Node = ACPI_TO_POINTER (ACPI_STRTOUL (InString, NULL, 16));
- if (!AcpiOsReadable (Node, sizeof (ACPI_NAMESPACE_NODE)))
- {
- AcpiOsPrintf ("Address %p is invalid in this address space\n",
- Node);
- return (NULL);
- }
-
- /* Make sure pointer is valid NS node */
-
- if (ACPI_GET_DESCRIPTOR_TYPE (Node) != ACPI_DESC_TYPE_NAMED)
- {
- AcpiOsPrintf ("Address %p is not a valid NS node [%s]\n",
- Node, AcpiUtGetDescriptorName (Node));
- return (NULL);
- }
- }
- else
- {
- /* Alpha argument */
- /* The parameter is a name string that must be resolved to a
- * Named obj
- */
- Node = AcpiDbLocalNsLookup (InString);
- if (!Node)
- {
- Node = AcpiGbl_RootNode;
- }
- }
-
- return (Node);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSleep
- *
- * PARAMETERS: ObjectArg - Desired sleep state (0-5)
- *
- * RETURN: Status
- *
- * DESCRIPTION: Simulate a sleep/wake sequence
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbSleep (
- char *ObjectArg)
-{
- ACPI_STATUS Status;
- UINT8 SleepState;
-
-
- SleepState = (UINT8) ACPI_STRTOUL (ObjectArg, NULL, 0);
-
- AcpiOsPrintf ("**** Prepare to sleep ****\n");
- Status = AcpiEnterSleepStatePrep (SleepState);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- AcpiOsPrintf ("**** Going to sleep ****\n");
- Status = AcpiEnterSleepState (SleepState);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- AcpiOsPrintf ("**** returning from sleep ****\n");
- Status = AcpiLeaveSleepState (SleepState);
-
- return (Status);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayLocks
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display information about internal mutexes.
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayLocks (
- void)
-{
- UINT32 i;
-
-
- for (i = 0; i < ACPI_MAX_MUTEX; i++)
- {
- AcpiOsPrintf ("%26s : %s\n", AcpiUtGetMutexName (i),
- AcpiGbl_MutexInfo[i].ThreadId == ACPI_MUTEX_NOT_ACQUIRED
- ? "Locked" : "Unlocked");
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayTableInfo
- *
- * PARAMETERS: TableArg - String with name of table to be displayed
- *
- * RETURN: None
- *
- * DESCRIPTION: Display information about loaded tables. Current
- * implementation displays all loaded tables.
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayTableInfo (
- char *TableArg)
-{
- UINT32 i;
- ACPI_TABLE_DESC *TableDesc;
- ACPI_STATUS Status;
-
-
- /* Walk the entire root table list */
-
- for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
- {
- TableDesc = &AcpiGbl_RootTableList.Tables[i];
- AcpiOsPrintf ("%u ", i);
-
- /* Make sure that the table is mapped */
-
- Status = AcpiTbVerifyTable (TableDesc);
- if (ACPI_FAILURE (Status))
- {
- return;
- }
-
- /* Dump the table header */
-
- if (TableDesc->Pointer)
- {
- AcpiTbPrintTableHeader (TableDesc->Address, TableDesc->Pointer);
- }
- else
- {
- /* If the pointer is null, the table has been unloaded */
-
- ACPI_INFO ((AE_INFO, "%4.4s - Table has been unloaded",
- TableDesc->Signature.Ascii));
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbUnloadAcpiTable
- *
- * PARAMETERS: TableArg - Name of the table to be unloaded
- * InstanceArg - Which instance of the table to unload (if
- * there are multiple tables of the same type)
- *
- * RETURN: Nonde
- *
- * DESCRIPTION: Unload an ACPI table.
- * Instance is not implemented
- *
- ******************************************************************************/
-
-void
-AcpiDbUnloadAcpiTable (
- char *TableArg,
- char *InstanceArg)
-{
-/* TBD: Need to reimplement for new data structures */
-
-#if 0
- UINT32 i;
- ACPI_STATUS Status;
-
-
- /* Search all tables for the target type */
-
- for (i = 0; i < (ACPI_TABLE_ID_MAX+1); i++)
- {
- if (!ACPI_STRNCMP (TableArg, AcpiGbl_TableData[i].Signature,
- AcpiGbl_TableData[i].SigLength))
- {
- /* Found the table, unload it */
-
- Status = AcpiUnloadTable (i);
- if (ACPI_SUCCESS (Status))
- {
- AcpiOsPrintf ("[%s] unloaded and uninstalled\n", TableArg);
- }
- else
- {
- AcpiOsPrintf ("%s, while unloading [%s]\n",
- AcpiFormatException (Status), TableArg);
- }
-
- return;
- }
- }
-
- AcpiOsPrintf ("Unknown table type [%s]\n", TableArg);
-#endif
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSendNotify
- *
- * PARAMETERS: Name - Name of ACPI object to send the notify to
- * Value - Value of the notify to send.
- *
- * RETURN: None
- *
- * DESCRIPTION: Send an ACPI notification. The value specified is sent to the
- * named object as an ACPI notify.
- *
- ******************************************************************************/
-
-void
-AcpiDbSendNotify (
- char *Name,
- UINT32 Value)
-{
- ACPI_NAMESPACE_NODE *Node;
- ACPI_STATUS Status;
-
-
- /* Translate name to an Named object */
-
- Node = AcpiDbConvertToNode (Name);
- if (!Node)
- {
- return;
- }
-
- /* Decode Named object type */
-
- switch (Node->Type)
- {
- case ACPI_TYPE_DEVICE:
- case ACPI_TYPE_THERMAL:
-
- /* Send the notify */
-
- Status = AcpiEvQueueNotifyRequest (Node, Value);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not queue notify\n");
- }
- break;
-
- default:
- AcpiOsPrintf ("Named object is not a device or a thermal object\n");
- break;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayInterfaces
- *
- * PARAMETERS: ActionArg - Null, "install", or "remove"
- * InterfaceNameArg - Name for install/remove options
- *
- * RETURN: None
- *
- * DESCRIPTION: Display or modify the global _OSI interface list
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayInterfaces (
- char *ActionArg,
- char *InterfaceNameArg)
-{
- ACPI_INTERFACE_INFO *NextInterface;
- char *SubString;
- ACPI_STATUS Status;
-
-
- /* If no arguments, just display current interface list */
-
- if (!ActionArg)
- {
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex,
- ACPI_WAIT_FOREVER);
-
- NextInterface = AcpiGbl_SupportedInterfaces;
-
- while (NextInterface)
- {
- if (!(NextInterface->Flags & ACPI_OSI_INVALID))
- {
- AcpiOsPrintf ("%s\n", NextInterface->Name);
- }
- NextInterface = NextInterface->Next;
- }
-
- AcpiOsReleaseMutex (AcpiGbl_OsiMutex);
- return;
- }
-
- /* If ActionArg exists, so must InterfaceNameArg */
-
- if (!InterfaceNameArg)
- {
- AcpiOsPrintf ("Missing Interface Name argument\n");
- return;
- }
-
- /* Uppercase the action for match below */
-
- AcpiUtStrupr (ActionArg);
-
- /* Install - install an interface */
-
- SubString = ACPI_STRSTR ("INSTALL", ActionArg);
- if (SubString)
- {
- Status = AcpiInstallInterface (InterfaceNameArg);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("%s, while installing \"%s\"\n",
- AcpiFormatException (Status), InterfaceNameArg);
- }
- return;
- }
-
- /* Remove - remove an interface */
-
- SubString = ACPI_STRSTR ("REMOVE", ActionArg);
- if (SubString)
- {
- Status = AcpiRemoveInterface (InterfaceNameArg);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("%s, while removing \"%s\"\n",
- AcpiFormatException (Status), InterfaceNameArg);
- }
- return;
- }
-
- /* Invalid ActionArg */
-
- AcpiOsPrintf ("Invalid action argument: %s\n", ActionArg);
- return;
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmCompareAmlResources
- *
- * PARAMETERS: Aml1Buffer - Contains first resource list
- * Aml1BufferLength - Length of first resource list
- * Aml2Buffer - Contains second resource list
- * Aml2BufferLength - Length of second resource list
- *
- * RETURN: None
- *
- * DESCRIPTION: Compare two AML resource lists, descriptor by descriptor (in
- * order to isolate a miscompare to an individual resource)
- *
- ******************************************************************************/
-
-static void
-AcpiDmCompareAmlResources (
- UINT8 *Aml1Buffer,
- ACPI_RSDESC_SIZE Aml1BufferLength,
- UINT8 *Aml2Buffer,
- ACPI_RSDESC_SIZE Aml2BufferLength)
-{
- UINT8 *Aml1;
- UINT8 *Aml2;
- ACPI_RSDESC_SIZE Aml1Length;
- ACPI_RSDESC_SIZE Aml2Length;
- ACPI_RSDESC_SIZE Offset = 0;
- UINT8 ResourceType;
- UINT32 Count = 0;
-
-
- /* Compare overall buffer sizes (may be different due to size rounding) */
-
- if (Aml1BufferLength != Aml2BufferLength)
- {
- AcpiOsPrintf (
- "**** Buffer length mismatch in converted AML: original %X new %X ****\n",
- Aml1BufferLength, Aml2BufferLength);
- }
-
- Aml1 = Aml1Buffer;
- Aml2 = Aml2Buffer;
-
- /* Walk the descriptor lists, comparing each descriptor */
-
- while (Aml1 < (Aml1Buffer + Aml1BufferLength))
- {
- /* Get the lengths of each descriptor */
-
- Aml1Length = AcpiUtGetDescriptorLength (Aml1);
- Aml2Length = AcpiUtGetDescriptorLength (Aml2);
- ResourceType = AcpiUtGetResourceType (Aml1);
-
- /* Check for descriptor length match */
-
- if (Aml1Length != Aml2Length)
- {
- AcpiOsPrintf (
- "**** Length mismatch in descriptor [%.2X] type %2.2X, Offset %8.8X L1 %X L2 %X ****\n",
- Count, ResourceType, Offset, Aml1Length, Aml2Length);
- }
-
- /* Check for descriptor byte match */
-
- else if (ACPI_MEMCMP (Aml1, Aml2, Aml1Length))
- {
- AcpiOsPrintf (
- "**** Data mismatch in descriptor [%.2X] type %2.2X, Offset %8.8X ****\n",
- Count, ResourceType, Offset);
- }
-
- /* Exit on EndTag descriptor */
-
- if (ResourceType == ACPI_RESOURCE_NAME_END_TAG)
- {
- return;
- }
-
- /* Point to next descriptor in each buffer */
-
- Count++;
- Offset += Aml1Length;
- Aml1 += Aml1Length;
- Aml2 += Aml2Length;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmTestResourceConversion
- *
- * PARAMETERS: Node - Parent device node
- * Name - resource method name (_CRS)
- *
- * RETURN: Status
- *
- * DESCRIPTION: Compare the original AML with a conversion of the AML to
- * internal resource list, then back to AML.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDmTestResourceConversion (
- ACPI_NAMESPACE_NODE *Node,
- char *Name)
-{
- ACPI_STATUS Status;
- ACPI_BUFFER ReturnObj;
- ACPI_BUFFER ResourceObj;
- ACPI_BUFFER NewAml;
- ACPI_OBJECT *OriginalAml;
-
-
- AcpiOsPrintf ("Resource Conversion Comparison:\n");
-
- NewAml.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- ReturnObj.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- ResourceObj.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
-
- /* Get the original _CRS AML resource template */
-
- Status = AcpiEvaluateObject (Node, Name, NULL, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not obtain %s: %s\n",
- Name, AcpiFormatException (Status));
- return (Status);
- }
-
- /* Get the AML resource template, converted to internal resource structs */
-
- Status = AcpiGetCurrentResources (Node, &ResourceObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("AcpiGetCurrentResources failed: %s\n",
- AcpiFormatException (Status));
- goto Exit1;
- }
-
- /* Convert internal resource list to external AML resource template */
-
- Status = AcpiRsCreateAmlResources (ResourceObj.Pointer, &NewAml);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("AcpiRsCreateAmlResources failed: %s\n",
- AcpiFormatException (Status));
- goto Exit2;
- }
-
- /* Compare original AML to the newly created AML resource list */
-
- OriginalAml = ReturnObj.Pointer;
-
- AcpiDmCompareAmlResources (
- OriginalAml->Buffer.Pointer, (ACPI_RSDESC_SIZE) OriginalAml->Buffer.Length,
- NewAml.Pointer, (ACPI_RSDESC_SIZE) NewAml.Length);
-
- /* Cleanup and exit */
-
- ACPI_FREE (NewAml.Pointer);
-Exit2:
- ACPI_FREE (ResourceObj.Pointer);
-Exit1:
- ACPI_FREE (ReturnObj.Pointer);
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayResources
- *
- * PARAMETERS: ObjectArg - String with hex value of the object
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the resource objects associated with a device.
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayResources (
- char *ObjectArg)
-{
- ACPI_NAMESPACE_NODE *Node;
- ACPI_STATUS Status;
- ACPI_BUFFER ReturnObj;
-
-
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
- AcpiDbgLevel |= ACPI_LV_RESOURCES;
-
- /* Convert string to object pointer */
-
- Node = AcpiDbConvertToNode (ObjectArg);
- if (!Node)
- {
- return;
- }
-
- /* Prepare for a return object of arbitrary size */
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- /* _PRT */
-
- AcpiOsPrintf ("Evaluating _PRT\n");
-
- /* Check if _PRT exists */
-
- Status = AcpiEvaluateObject (Node, METHOD_NAME__PRT, NULL, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not obtain _PRT: %s\n",
- AcpiFormatException (Status));
- goto GetCrs;
- }
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- Status = AcpiGetIrqRoutingTable (Node, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("GetIrqRoutingTable failed: %s\n",
- AcpiFormatException (Status));
- goto GetCrs;
- }
-
- AcpiRsDumpIrqList (ACPI_CAST_PTR (UINT8, AcpiGbl_DbBuffer));
-
-
- /* _CRS */
-
-GetCrs:
- AcpiOsPrintf ("Evaluating _CRS\n");
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- /* Check if _CRS exists */
-
- Status = AcpiEvaluateObject (Node, METHOD_NAME__CRS, NULL, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not obtain _CRS: %s\n",
- AcpiFormatException (Status));
- goto GetPrs;
- }
-
- /* Get the _CRS resource list */
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- Status = AcpiGetCurrentResources (Node, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("AcpiGetCurrentResources failed: %s\n",
- AcpiFormatException (Status));
- goto GetPrs;
- }
-
- /* Dump the _CRS resource list */
-
- AcpiRsDumpResourceList (ACPI_CAST_PTR (ACPI_RESOURCE,
- ReturnObj.Pointer));
-
- /*
- * Perform comparison of original AML to newly created AML. This tests both
- * the AML->Resource conversion and the Resource->Aml conversion.
- */
- Status = AcpiDmTestResourceConversion (Node, METHOD_NAME__CRS);
-
- /* Execute _SRS with the resource list */
-
- Status = AcpiSetCurrentResources (Node, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("AcpiSetCurrentResources failed: %s\n",
- AcpiFormatException (Status));
- goto GetPrs;
- }
-
-
- /* _PRS */
-
-GetPrs:
- AcpiOsPrintf ("Evaluating _PRS\n");
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- /* Check if _PRS exists */
-
- Status = AcpiEvaluateObject (Node, METHOD_NAME__PRS, NULL, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not obtain _PRS: %s\n",
- AcpiFormatException (Status));
- goto Cleanup;
- }
-
- ReturnObj.Pointer = AcpiGbl_DbBuffer;
- ReturnObj.Length = ACPI_DEBUG_BUFFER_SIZE;
-
- Status = AcpiGetPossibleResources (Node, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("AcpiGetPossibleResources failed: %s\n",
- AcpiFormatException (Status));
- goto Cleanup;
- }
-
- AcpiRsDumpResourceList (ACPI_CAST_PTR (ACPI_RESOURCE, AcpiGbl_DbBuffer));
-
-Cleanup:
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
- return;
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGenerateGpe
- *
- * PARAMETERS: GpeArg - Raw GPE number, ascii string
- * BlockArg - GPE block number, ascii string
- * 0 or 1 for FADT GPE blocks
- *
- * RETURN: None
- *
- * DESCRIPTION: Generate a GPE
- *
- ******************************************************************************/
-
-void
-AcpiDbGenerateGpe (
- char *GpeArg,
- char *BlockArg)
-{
- UINT32 BlockNumber;
- UINT32 GpeNumber;
- ACPI_GPE_EVENT_INFO *GpeEventInfo;
-
-
- GpeNumber = ACPI_STRTOUL (GpeArg, NULL, 0);
- BlockNumber = ACPI_STRTOUL (BlockArg, NULL, 0);
-
-
- GpeEventInfo = AcpiEvGetGpeEventInfo (ACPI_TO_POINTER (BlockNumber),
- GpeNumber);
- if (!GpeEventInfo)
- {
- AcpiOsPrintf ("Invalid GPE\n");
- return;
- }
-
- (void) AcpiEvGpeDispatch (NULL, GpeEventInfo, GpeNumber);
-}
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbdisply.c b/usr/src/uts/intel/io/acpica/debugger/dbdisply.c
deleted file mode 100644
index 61e837e751..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbdisply.c
+++ /dev/null
@@ -1,1031 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbdisply - debug display commands
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "amlcode.h"
-#include "acdispat.h"
-#include "acnamesp.h"
-#include "acparser.h"
-#include "acinterp.h"
-#include "acdebug.h"
-#include "acdisasm.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbdisply")
-
-/* Local prototypes */
-
-static void
-AcpiDbDumpParserDescriptor (
- ACPI_PARSE_OBJECT *Op);
-
-static void *
-AcpiDbGetPointer (
- void *Target);
-
-
-/*
- * System handler information.
- * Used for Handlers command, in AcpiDbDisplayHandlers.
- */
-#define ACPI_PREDEFINED_PREFIX "%25s (%.2X) : "
-#define ACPI_HANDLER_NAME_STRING "%30s : "
-#define ACPI_HANDLER_PRESENT_STRING "%-9s (%p)\n"
-#define ACPI_HANDLER_NOT_PRESENT_STRING "%-9s\n"
-
-/* All predefined Address Space IDs */
-
-static ACPI_ADR_SPACE_TYPE AcpiGbl_SpaceIdList[] =
-{
- ACPI_ADR_SPACE_SYSTEM_MEMORY,
- ACPI_ADR_SPACE_SYSTEM_IO,
- ACPI_ADR_SPACE_PCI_CONFIG,
- ACPI_ADR_SPACE_EC,
- ACPI_ADR_SPACE_SMBUS,
- ACPI_ADR_SPACE_CMOS,
- ACPI_ADR_SPACE_PCI_BAR_TARGET,
- ACPI_ADR_SPACE_IPMI,
- ACPI_ADR_SPACE_DATA_TABLE,
- ACPI_ADR_SPACE_FIXED_HARDWARE
-};
-
-/* Global handler information */
-
-typedef struct acpi_handler_info
-{
- void *Handler;
- char *Name;
-
-} ACPI_HANDLER_INFO;
-
-static ACPI_HANDLER_INFO AcpiGbl_HandlerList[] =
-{
- {&AcpiGbl_SystemNotify.Handler, "System Notifications"},
- {&AcpiGbl_DeviceNotify.Handler, "Device Notifications"},
- {&AcpiGbl_TableHandler, "ACPI Table Events"},
- {&AcpiGbl_ExceptionHandler, "Control Method Exceptions"},
- {&AcpiGbl_InterfaceHandler, "OSI Invocations"}
-};
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetPointer
- *
- * PARAMETERS: Target - Pointer to string to be converted
- *
- * RETURN: Converted pointer
- *
- * DESCRIPTION: Convert an ascii pointer value to a real value
- *
- ******************************************************************************/
-
-static void *
-AcpiDbGetPointer (
- void *Target)
-{
- void *ObjPtr;
-
-
- ObjPtr = ACPI_TO_POINTER (ACPI_STRTOUL (Target, NULL, 16));
- return (ObjPtr);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDumpParserDescriptor
- *
- * PARAMETERS: Op - A parser Op descriptor
- *
- * RETURN: None
- *
- * DESCRIPTION: Display a formatted parser object
- *
- ******************************************************************************/
-
-static void
-AcpiDbDumpParserDescriptor (
- ACPI_PARSE_OBJECT *Op)
-{
- const ACPI_OPCODE_INFO *Info;
-
-
- Info = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
-
- AcpiOsPrintf ("Parser Op Descriptor:\n");
- AcpiOsPrintf ("%20.20s : %4.4X\n", "Opcode", Op->Common.AmlOpcode);
-
- ACPI_DEBUG_ONLY_MEMBERS (AcpiOsPrintf ("%20.20s : %s\n", "Opcode Name",
- Info->Name));
-
- AcpiOsPrintf ("%20.20s : %p\n", "Value/ArgList", Op->Common.Value.Arg);
- AcpiOsPrintf ("%20.20s : %p\n", "Parent", Op->Common.Parent);
- AcpiOsPrintf ("%20.20s : %p\n", "NextOp", Op->Common.Next);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDecodeAndDisplayObject
- *
- * PARAMETERS: Target - String with object to be displayed. Names
- * and hex pointers are supported.
- * OutputType - Byte, Word, Dword, or Qword (B|W|D|Q)
- *
- * RETURN: None
- *
- * DESCRIPTION: Display a formatted ACPI object
- *
- ******************************************************************************/
-
-void
-AcpiDbDecodeAndDisplayObject (
- char *Target,
- char *OutputType)
-{
- void *ObjPtr;
- ACPI_NAMESPACE_NODE *Node;
- ACPI_OPERAND_OBJECT *ObjDesc;
- UINT32 Display = DB_BYTE_DISPLAY;
- char Buffer[80];
- ACPI_BUFFER RetBuf;
- ACPI_STATUS Status;
- UINT32 Size;
-
-
- if (!Target)
- {
- return;
- }
-
- /* Decode the output type */
-
- if (OutputType)
- {
- AcpiUtStrupr (OutputType);
- if (OutputType[0] == 'W')
- {
- Display = DB_WORD_DISPLAY;
- }
- else if (OutputType[0] == 'D')
- {
- Display = DB_DWORD_DISPLAY;
- }
- else if (OutputType[0] == 'Q')
- {
- Display = DB_QWORD_DISPLAY;
- }
- }
-
- RetBuf.Length = sizeof (Buffer);
- RetBuf.Pointer = Buffer;
-
- /* Differentiate between a number and a name */
-
- if ((Target[0] >= 0x30) && (Target[0] <= 0x39))
- {
- ObjPtr = AcpiDbGetPointer (Target);
- if (!AcpiOsReadable (ObjPtr, 16))
- {
- AcpiOsPrintf ("Address %p is invalid in this address space\n",
- ObjPtr);
- return;
- }
-
- /* Decode the object type */
-
- switch (ACPI_GET_DESCRIPTOR_TYPE (ObjPtr))
- {
- case ACPI_DESC_TYPE_NAMED:
-
- /* This is a namespace Node */
-
- if (!AcpiOsReadable (ObjPtr, sizeof (ACPI_NAMESPACE_NODE)))
- {
- AcpiOsPrintf (
- "Cannot read entire Named object at address %p\n", ObjPtr);
- return;
- }
-
- Node = ObjPtr;
- goto DumpNode;
-
-
- case ACPI_DESC_TYPE_OPERAND:
-
- /* This is a ACPI OPERAND OBJECT */
-
- if (!AcpiOsReadable (ObjPtr, sizeof (ACPI_OPERAND_OBJECT)))
- {
- AcpiOsPrintf ("Cannot read entire ACPI object at address %p\n",
- ObjPtr);
- return;
- }
-
- AcpiUtDumpBuffer (ObjPtr, sizeof (ACPI_OPERAND_OBJECT), Display,
- ACPI_UINT32_MAX);
- AcpiExDumpObjectDescriptor (ObjPtr, 1);
- break;
-
-
- case ACPI_DESC_TYPE_PARSER:
-
- /* This is a Parser Op object */
-
- if (!AcpiOsReadable (ObjPtr, sizeof (ACPI_PARSE_OBJECT)))
- {
- AcpiOsPrintf (
- "Cannot read entire Parser object at address %p\n", ObjPtr);
- return;
- }
-
- AcpiUtDumpBuffer (ObjPtr, sizeof (ACPI_PARSE_OBJECT), Display,
- ACPI_UINT32_MAX);
- AcpiDbDumpParserDescriptor ((ACPI_PARSE_OBJECT *) ObjPtr);
- break;
-
-
- default:
-
- /* Is not a recognizeable object */
-
- Size = 16;
- if (AcpiOsReadable (ObjPtr, 64))
- {
- Size = 64;
- }
-
- /* Just dump some memory */
-
- AcpiUtDumpBuffer (ObjPtr, Size, Display, ACPI_UINT32_MAX);
- break;
- }
-
- return;
- }
-
- /* The parameter is a name string that must be resolved to a Named obj */
-
- Node = AcpiDbLocalNsLookup (Target);
- if (!Node)
- {
- return;
- }
-
-
-DumpNode:
- /* Now dump the NS node */
-
- Status = AcpiGetName (Node, ACPI_FULL_PATHNAME, &RetBuf);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not convert name to pathname\n");
- }
-
- else
- {
- AcpiOsPrintf ("Object (%p) Pathname: %s\n",
- Node, (char *) RetBuf.Pointer);
- }
-
- if (!AcpiOsReadable (Node, sizeof (ACPI_NAMESPACE_NODE)))
- {
- AcpiOsPrintf ("Invalid Named object at address %p\n", Node);
- return;
- }
-
- AcpiUtDumpBuffer ((void *) Node, sizeof (ACPI_NAMESPACE_NODE),
- Display, ACPI_UINT32_MAX);
- AcpiExDumpNamespaceNode (Node, 1);
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc)
- {
- AcpiOsPrintf ("\nAttached Object (%p):\n", ObjDesc);
- if (!AcpiOsReadable (ObjDesc, sizeof (ACPI_OPERAND_OBJECT)))
- {
- AcpiOsPrintf ("Invalid internal ACPI Object at address %p\n",
- ObjDesc);
- return;
- }
-
- AcpiUtDumpBuffer ((void *) ObjDesc, sizeof (ACPI_OPERAND_OBJECT),
- Display, ACPI_UINT32_MAX);
- AcpiExDumpObjectDescriptor (ObjDesc, 1);
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayMethodInfo
- *
- * PARAMETERS: StartOp - Root of the control method parse tree
- *
- * RETURN: None
- *
- * DESCRIPTION: Display information about the current method
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayMethodInfo (
- ACPI_PARSE_OBJECT *StartOp)
-{
- ACPI_WALK_STATE *WalkState;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Node;
- ACPI_PARSE_OBJECT *RootOp;
- ACPI_PARSE_OBJECT *Op;
- const ACPI_OPCODE_INFO *OpInfo;
- UINT32 NumOps = 0;
- UINT32 NumOperands = 0;
- UINT32 NumOperators = 0;
- UINT32 NumRemainingOps = 0;
- UINT32 NumRemainingOperands = 0;
- UINT32 NumRemainingOperators = 0;
- BOOLEAN CountRemaining = FALSE;
-
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- ObjDesc = WalkState->MethodDesc;
- Node = WalkState->MethodNode;
-
- AcpiOsPrintf ("Currently executing control method is [%4.4s]\n",
- AcpiUtGetNodeName (Node));
- AcpiOsPrintf ("%X Arguments, SyncLevel = %X\n",
- (UINT32) ObjDesc->Method.ParamCount,
- (UINT32) ObjDesc->Method.SyncLevel);
-
-
- RootOp = StartOp;
- while (RootOp->Common.Parent)
- {
- RootOp = RootOp->Common.Parent;
- }
-
- Op = RootOp;
-
- while (Op)
- {
- if (Op == StartOp)
- {
- CountRemaining = TRUE;
- }
-
- NumOps++;
- if (CountRemaining)
- {
- NumRemainingOps++;
- }
-
- /* Decode the opcode */
-
- OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
- switch (OpInfo->Class)
- {
- case AML_CLASS_ARGUMENT:
- if (CountRemaining)
- {
- NumRemainingOperands++;
- }
-
- NumOperands++;
- break;
-
- case AML_CLASS_UNKNOWN:
- /* Bad opcode or ASCII character */
-
- continue;
-
- default:
- if (CountRemaining)
- {
- NumRemainingOperators++;
- }
-
- NumOperators++;
- break;
- }
-
- Op = AcpiPsGetDepthNext (StartOp, Op);
- }
-
- AcpiOsPrintf (
- "Method contains: %X AML Opcodes - %X Operators, %X Operands\n",
- NumOps, NumOperators, NumOperands);
-
- AcpiOsPrintf (
- "Remaining to execute: %X AML Opcodes - %X Operators, %X Operands\n",
- NumRemainingOps, NumRemainingOperators, NumRemainingOperands);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayLocals
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display all locals for the currently running control method
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayLocals (
- void)
-{
- ACPI_WALK_STATE *WalkState;
-
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- AcpiDmDisplayLocals (WalkState);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayArguments
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display all arguments for the currently running control method
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayArguments (
- void)
-{
- ACPI_WALK_STATE *WalkState;
-
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- AcpiDmDisplayArguments (WalkState);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayResults
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display current contents of a method result stack
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayResults (
- void)
-{
- UINT32 i;
- ACPI_WALK_STATE *WalkState;
- ACPI_OPERAND_OBJECT *ObjDesc;
- UINT32 ResultCount = 0;
- ACPI_NAMESPACE_NODE *Node;
- ACPI_GENERIC_STATE *Frame;
- UINT32 Index; /* Index onto current frame */
-
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- ObjDesc = WalkState->MethodDesc;
- Node = WalkState->MethodNode;
-
- if (WalkState->Results)
- {
- ResultCount = WalkState->ResultCount;
- }
-
- AcpiOsPrintf ("Method [%4.4s] has %X stacked result objects\n",
- AcpiUtGetNodeName (Node), ResultCount);
-
- /* From the top element of result stack */
-
- Frame = WalkState->Results;
- Index = (ResultCount - 1) % ACPI_RESULTS_FRAME_OBJ_NUM;
-
- for (i = 0; i < ResultCount; i++)
- {
- ObjDesc = Frame->Results.ObjDesc[Index];
- AcpiOsPrintf ("Result%u: ", i);
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- if (Index == 0)
- {
- Frame = Frame->Results.Next;
- Index = ACPI_RESULTS_FRAME_OBJ_NUM;
- }
- Index--;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayCallingTree
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display current calling tree of nested control methods
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayCallingTree (
- void)
-{
- ACPI_WALK_STATE *WalkState;
- ACPI_NAMESPACE_NODE *Node;
-
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- Node = WalkState->MethodNode;
- AcpiOsPrintf ("Current Control Method Call Tree\n");
-
- while (WalkState)
- {
- Node = WalkState->MethodNode;
-
- AcpiOsPrintf (" [%4.4s]\n", AcpiUtGetNodeName (Node));
-
- WalkState = WalkState->Next;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayObjectType
- *
- * PARAMETERS: ObjectArg - User entered NS node handle
- *
- * RETURN: None
- *
- * DESCRIPTION: Display type of an arbitrary NS node
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayObjectType (
- char *ObjectArg)
-{
- ACPI_HANDLE Handle;
- ACPI_DEVICE_INFO *Info;
- ACPI_STATUS Status;
- UINT32 i;
-
-
- Handle = ACPI_TO_POINTER (ACPI_STRTOUL (ObjectArg, NULL, 16));
-
- Status = AcpiGetObjectInfo (Handle, &Info);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not get object info, %s\n",
- AcpiFormatException (Status));
- return;
- }
-
- AcpiOsPrintf ("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n",
- ACPI_FORMAT_UINT64 (Info->Address),
- Info->CurrentStatus, Info->Flags);
-
- AcpiOsPrintf ("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n",
- Info->HighestDstates[0], Info->HighestDstates[1],
- Info->HighestDstates[2], Info->HighestDstates[3]);
-
- AcpiOsPrintf ("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n",
- Info->LowestDstates[0], Info->LowestDstates[1],
- Info->LowestDstates[2], Info->LowestDstates[3],
- Info->LowestDstates[4]);
-
- if (Info->Valid & ACPI_VALID_HID)
- {
- AcpiOsPrintf ("HID: %s\n", Info->HardwareId.String);
- }
- if (Info->Valid & ACPI_VALID_UID)
- {
- AcpiOsPrintf ("UID: %s\n", Info->UniqueId.String);
- }
- if (Info->Valid & ACPI_VALID_CID)
- {
- for (i = 0; i < Info->CompatibleIdList.Count; i++)
- {
- AcpiOsPrintf ("CID %u: %s\n", i,
- Info->CompatibleIdList.Ids[i].String);
- }
- }
-
- ACPI_FREE (Info);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayResultObject
- *
- * PARAMETERS: ObjDesc - Object to be displayed
- * WalkState - Current walk state
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the result of an AML opcode
- *
- * Note: Curently only displays the result object if we are single stepping.
- * However, this output may be useful in other contexts and could be enabled
- * to do so if needed.
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayResultObject (
- ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState)
-{
-
- /* Only display if single stepping */
-
- if (!AcpiGbl_CmSingleStep)
- {
- return;
- }
-
- AcpiOsPrintf ("ResultObj: ");
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- AcpiOsPrintf ("\n");
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayArgumentObject
- *
- * PARAMETERS: ObjDesc - Object to be displayed
- * WalkState - Current walk state
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the result of an AML opcode
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayArgumentObject (
- ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState)
-{
-
- if (!AcpiGbl_CmSingleStep)
- {
- return;
- }
-
- AcpiOsPrintf ("ArgObj: ");
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayGpes
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the current GPE structures
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayGpes (
- void)
-{
- ACPI_GPE_BLOCK_INFO *GpeBlock;
- ACPI_GPE_XRUPT_INFO *GpeXruptInfo;
- ACPI_GPE_EVENT_INFO *GpeEventInfo;
- ACPI_GPE_REGISTER_INFO *GpeRegisterInfo;
- char *GpeType;
- UINT32 GpeIndex;
- UINT32 Block = 0;
- UINT32 i;
- UINT32 j;
- char Buffer[80];
- ACPI_BUFFER RetBuf;
- ACPI_STATUS Status;
-
-
- RetBuf.Length = sizeof (Buffer);
- RetBuf.Pointer = Buffer;
-
- Block = 0;
-
- /* Walk the GPE lists */
-
- GpeXruptInfo = AcpiGbl_GpeXruptListHead;
- while (GpeXruptInfo)
- {
- GpeBlock = GpeXruptInfo->GpeBlockListHead;
- while (GpeBlock)
- {
- Status = AcpiGetName (GpeBlock->Node, ACPI_FULL_PATHNAME, &RetBuf);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not convert name to pathname\n");
- }
-
- if (GpeBlock->Node == AcpiGbl_FadtGpeDevice)
- {
- GpeType = "FADT-defined GPE block";
- }
- else
- {
- GpeType = "GPE Block Device";
- }
-
- AcpiOsPrintf ("\nBlock %u - Info %p DeviceNode %p [%s] - %s\n",
- Block, GpeBlock, GpeBlock->Node, Buffer, GpeType);
-
- AcpiOsPrintf (" Registers: %u (%u GPEs)\n",
- GpeBlock->RegisterCount, GpeBlock->GpeCount);
-
- AcpiOsPrintf (" GPE range: 0x%X to 0x%X on interrupt %u\n",
- GpeBlock->BlockBaseNumber,
- GpeBlock->BlockBaseNumber + (GpeBlock->GpeCount - 1),
- GpeXruptInfo->InterruptNumber);
-
- AcpiOsPrintf (
- " RegisterInfo: %p Status %8.8X%8.8X Enable %8.8X%8.8X\n",
- GpeBlock->RegisterInfo,
- ACPI_FORMAT_UINT64 (GpeBlock->RegisterInfo->StatusAddress.Address),
- ACPI_FORMAT_UINT64 (GpeBlock->RegisterInfo->EnableAddress.Address));
-
- AcpiOsPrintf (" EventInfo: %p\n", GpeBlock->EventInfo);
-
- /* Examine each GPE Register within the block */
-
- for (i = 0; i < GpeBlock->RegisterCount; i++)
- {
- GpeRegisterInfo = &GpeBlock->RegisterInfo[i];
-
- AcpiOsPrintf (
- " Reg %u: (GPE %.2X-%.2X) RunEnable %2.2X WakeEnable %2.2X"
- " Status %8.8X%8.8X Enable %8.8X%8.8X\n",
- i, GpeRegisterInfo->BaseGpeNumber,
- GpeRegisterInfo->BaseGpeNumber + (ACPI_GPE_REGISTER_WIDTH - 1),
- GpeRegisterInfo->EnableForRun,
- GpeRegisterInfo->EnableForWake,
- ACPI_FORMAT_UINT64 (GpeRegisterInfo->StatusAddress.Address),
- ACPI_FORMAT_UINT64 (GpeRegisterInfo->EnableAddress.Address));
-
- /* Now look at the individual GPEs in this byte register */
-
- for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
- {
- GpeIndex = (i * ACPI_GPE_REGISTER_WIDTH) + j;
- GpeEventInfo = &GpeBlock->EventInfo[GpeIndex];
-
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_NONE)
- {
- /* This GPE is not used (no method or handler), ignore it */
-
- continue;
- }
-
- AcpiOsPrintf (
- " GPE %.2X: %p RunRefs %2.2X Flags %2.2X (",
- GpeBlock->BlockBaseNumber + GpeIndex, GpeEventInfo,
- GpeEventInfo->RuntimeCount, GpeEventInfo->Flags);
-
- /* Decode the flags byte */
-
- if (GpeEventInfo->Flags & ACPI_GPE_LEVEL_TRIGGERED)
- {
- AcpiOsPrintf ("Level, ");
- }
- else
- {
- AcpiOsPrintf ("Edge, ");
- }
-
- if (GpeEventInfo->Flags & ACPI_GPE_CAN_WAKE)
- {
- AcpiOsPrintf ("CanWake, ");
- }
- else
- {
- AcpiOsPrintf ("RunOnly, ");
- }
-
- switch (GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK)
- {
- case ACPI_GPE_DISPATCH_NONE:
- AcpiOsPrintf ("NotUsed");
- break;
- case ACPI_GPE_DISPATCH_METHOD:
- AcpiOsPrintf ("Method");
- break;
- case ACPI_GPE_DISPATCH_HANDLER:
- AcpiOsPrintf ("Handler");
- break;
- case ACPI_GPE_DISPATCH_NOTIFY:
- AcpiOsPrintf ("Notify");
- break;
- default:
- AcpiOsPrintf ("UNKNOWN: %X",
- GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK);
- break;
- }
-
- AcpiOsPrintf (")\n");
- }
- }
- Block++;
- GpeBlock = GpeBlock->Next;
- }
- GpeXruptInfo = GpeXruptInfo->Next;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayHandlers
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the currently installed global handlers
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayHandlers (
- void)
-{
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT *HandlerObj;
- ACPI_ADR_SPACE_TYPE SpaceId;
- UINT32 i;
-
-
- /* Operation region handlers */
-
- AcpiOsPrintf ("\nOperation Region Handlers:\n");
-
- ObjDesc = AcpiNsGetAttachedObject (AcpiGbl_RootNode);
- if (ObjDesc)
- {
- for (i = 0; i < ACPI_ARRAY_LENGTH (AcpiGbl_SpaceIdList); i++)
- {
- SpaceId = AcpiGbl_SpaceIdList[i];
- HandlerObj = ObjDesc->Device.Handler;
-
- AcpiOsPrintf (ACPI_PREDEFINED_PREFIX,
- AcpiUtGetRegionName ((UINT8) SpaceId), SpaceId);
-
- while (HandlerObj)
- {
- if (i == HandlerObj->AddressSpace.SpaceId)
- {
- AcpiOsPrintf (ACPI_HANDLER_PRESENT_STRING,
- (HandlerObj->AddressSpace.HandlerFlags &
- ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) ? "Default" : "User",
- HandlerObj->AddressSpace.Handler);
- goto FoundHandler;
- }
-
- HandlerObj = HandlerObj->AddressSpace.Next;
- }
-
- /* There is no handler for this SpaceId */
-
- AcpiOsPrintf ("None\n");
-
- FoundHandler:;
- }
- }
-
- /* Fixed event handlers */
-
- AcpiOsPrintf ("\nFixed Event Handlers:\n");
-
- for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++)
- {
- AcpiOsPrintf (ACPI_PREDEFINED_PREFIX, AcpiUtGetEventName (i), i);
- if (AcpiGbl_FixedEventHandlers[i].Handler)
- {
- AcpiOsPrintf (ACPI_HANDLER_PRESENT_STRING, "User",
- AcpiGbl_FixedEventHandlers[i].Handler);
- }
- else
- {
- AcpiOsPrintf (ACPI_HANDLER_NOT_PRESENT_STRING, "None");
- }
- }
-
- /* Miscellaneous global handlers */
-
- AcpiOsPrintf ("\nMiscellaneous Global Handlers:\n");
-
- for (i = 0; i < ACPI_ARRAY_LENGTH (AcpiGbl_HandlerList); i++)
- {
- AcpiOsPrintf (ACPI_HANDLER_NAME_STRING, AcpiGbl_HandlerList[i].Name);
- if (AcpiGbl_HandlerList[i].Handler)
- {
- AcpiOsPrintf (ACPI_HANDLER_PRESENT_STRING, "User",
- AcpiGbl_HandlerList[i].Handler);
- }
- else
- {
- AcpiOsPrintf (ACPI_HANDLER_NOT_PRESENT_STRING, "None");
- }
- }
-}
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbexec.c b/usr/src/uts/intel/io/acpica/debugger/dbexec.c
deleted file mode 100644
index bcbc7a8daf..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbexec.c
+++ /dev/null
@@ -1,1105 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbexec - debugger control method execution
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdebug.h"
-#include "acnamesp.h"
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbexec")
-
-
-static ACPI_DB_METHOD_INFO AcpiGbl_DbMethodInfo;
-#define DB_DEFAULT_PKG_ELEMENTS 33
-
-/* Local prototypes */
-
-static ACPI_STATUS
-AcpiDbExecuteMethod (
- ACPI_DB_METHOD_INFO *Info,
- ACPI_BUFFER *ReturnObj);
-
-static void
-AcpiDbExecuteSetup (
- ACPI_DB_METHOD_INFO *Info);
-
-static UINT32
-AcpiDbGetOutstandingAllocations (
- void);
-
-static void ACPI_SYSTEM_XFACE
-AcpiDbMethodThread (
- void *Context);
-
-static ACPI_STATUS
-AcpiDbExecutionWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbHexCharToValue (
- int HexChar,
- UINT8 *ReturnValue);
-
-static ACPI_STATUS
-AcpiDbConvertToPackage (
- char *String,
- ACPI_OBJECT *Object);
-
-static ACPI_STATUS
-AcpiDbConvertToObject (
- ACPI_OBJECT_TYPE Type,
- char *String,
- ACPI_OBJECT *Object);
-
-static void
-AcpiDbDeleteObjects (
- UINT32 Count,
- ACPI_OBJECT *Objects);
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbHexCharToValue
- *
- * PARAMETERS: HexChar - Ascii Hex digit, 0-9|a-f|A-F
- * ReturnValue - Where the converted value is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Convert a single hex character to a 4-bit number (0-16).
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbHexCharToValue (
- int HexChar,
- UINT8 *ReturnValue)
-{
- UINT8 Value;
-
-
- /* Digit must be ascii [0-9a-fA-F] */
-
- if (!ACPI_IS_XDIGIT (HexChar))
- {
- return (AE_BAD_HEX_CONSTANT);
- }
-
- if (HexChar <= 0x39)
- {
- Value = (UINT8) (HexChar - 0x30);
- }
- else
- {
- Value = (UINT8) (ACPI_TOUPPER (HexChar) - 0x37);
- }
-
- *ReturnValue = Value;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbHexByteToBinary
- *
- * PARAMETERS: HexByte - Double hex digit (0x00 - 0xFF) in format:
- * HiByte then LoByte.
- * ReturnValue - Where the converted value is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Convert two hex characters to an 8 bit number (0 - 255).
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbHexByteToBinary (
- char *HexByte,
- UINT8 *ReturnValue)
-{
- UINT8 Local0;
- UINT8 Local1;
- ACPI_STATUS Status;
-
-
- /* High byte */
-
- Status = AcpiDbHexCharToValue (HexByte[0], &Local0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /* Low byte */
-
- Status = AcpiDbHexCharToValue (HexByte[1], &Local1);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- *ReturnValue = (UINT8) ((Local0 << 4) | Local1);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbConvertToBuffer
- *
- * PARAMETERS: String - Input string to be converted
- * Object - Where the buffer object is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Convert a string to a buffer object. String is treated a list
- * of buffer elements, each separated by a space or comma.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbConvertToBuffer (
- char *String,
- ACPI_OBJECT *Object)
-{
- UINT32 i;
- UINT32 j;
- UINT32 Length;
- UINT8 *Buffer;
- ACPI_STATUS Status;
-
-
- /* Generate the final buffer length */
-
- for (i = 0, Length = 0; String[i];)
- {
- i+=2;
- Length++;
-
- while (String[i] &&
- ((String[i] == ',') || (String[i] == ' ')))
- {
- i++;
- }
- }
-
- Buffer = ACPI_ALLOCATE (Length);
- if (!Buffer)
- {
- return (AE_NO_MEMORY);
- }
-
- /* Convert the command line bytes to the buffer */
-
- for (i = 0, j = 0; String[i];)
- {
- Status = AcpiDbHexByteToBinary (&String[i], &Buffer[j]);
- if (ACPI_FAILURE (Status))
- {
- ACPI_FREE (Buffer);
- return (Status);
- }
-
- j++;
- i+=2;
- while (String[i] &&
- ((String[i] == ',') || (String[i] == ' ')))
- {
- i++;
- }
- }
-
- Object->Type = ACPI_TYPE_BUFFER;
- Object->Buffer.Pointer = Buffer;
- Object->Buffer.Length = Length;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbConvertToPackage
- *
- * PARAMETERS: String - Input string to be converted
- * Object - Where the package object is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Convert a string to a package object. Handles nested packages
- * via recursion with AcpiDbConvertToObject.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbConvertToPackage (
- char *String,
- ACPI_OBJECT *Object)
-{
- char *This;
- char *Next;
- UINT32 i;
- ACPI_OBJECT_TYPE Type;
- ACPI_OBJECT *Elements;
- ACPI_STATUS Status;
-
-
- Elements = ACPI_ALLOCATE_ZEROED (
- DB_DEFAULT_PKG_ELEMENTS * sizeof (ACPI_OBJECT));
-
- This = String;
- for (i = 0; i < (DB_DEFAULT_PKG_ELEMENTS - 1); i++)
- {
- This = AcpiDbGetNextToken (This, &Next, &Type);
- if (!This)
- {
- break;
- }
-
- /* Recursive call to convert each package element */
-
- Status = AcpiDbConvertToObject (Type, This, &Elements[i]);
- if (ACPI_FAILURE (Status))
- {
- AcpiDbDeleteObjects (i + 1, Elements);
- ACPI_FREE (Elements);
- return (Status);
- }
-
- This = Next;
- }
-
- Object->Type = ACPI_TYPE_PACKAGE;
- Object->Package.Count = i;
- Object->Package.Elements = Elements;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbConvertToObject
- *
- * PARAMETERS: Type - Object type as determined by parser
- * String - Input string to be converted
- * Object - Where the new object is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Convert a typed and tokenized string to an ACPI_OBJECT. Typing:
- * 1) String objects were surrounded by quotes.
- * 2) Buffer objects were surrounded by parentheses.
- * 3) Package objects were surrounded by brackets "[]".
- * 4) All standalone tokens are treated as integers.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbConvertToObject (
- ACPI_OBJECT_TYPE Type,
- char *String,
- ACPI_OBJECT *Object)
-{
- ACPI_STATUS Status = AE_OK;
-
-
- switch (Type)
- {
- case ACPI_TYPE_STRING:
- Object->Type = ACPI_TYPE_STRING;
- Object->String.Pointer = String;
- Object->String.Length = (UINT32) ACPI_STRLEN (String);
- break;
-
- case ACPI_TYPE_BUFFER:
- Status = AcpiDbConvertToBuffer (String, Object);
- break;
-
- case ACPI_TYPE_PACKAGE:
- Status = AcpiDbConvertToPackage (String, Object);
- break;
-
- default:
- Object->Type = ACPI_TYPE_INTEGER;
- Status = AcpiUtStrtoul64 (String, 16, &Object->Integer.Value);
- break;
- }
-
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDeleteObjects
- *
- * PARAMETERS: Count - Count of objects in the list
- * Objects - Array of ACPI_OBJECTs to be deleted
- *
- * RETURN: None
- *
- * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
- * packages via recursion.
- *
- ******************************************************************************/
-
-static void
-AcpiDbDeleteObjects (
- UINT32 Count,
- ACPI_OBJECT *Objects)
-{
- UINT32 i;
-
-
- for (i = 0; i < Count; i++)
- {
- switch (Objects[i].Type)
- {
- case ACPI_TYPE_BUFFER:
- ACPI_FREE (Objects[i].Buffer.Pointer);
- break;
-
- case ACPI_TYPE_PACKAGE:
-
- /* Recursive call to delete package elements */
-
- AcpiDbDeleteObjects (Objects[i].Package.Count,
- Objects[i].Package.Elements);
-
- /* Free the elements array */
-
- ACPI_FREE (Objects[i].Package.Elements);
- break;
-
- default:
- break;
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbExecuteMethod
- *
- * PARAMETERS: Info - Valid info segment
- * ReturnObj - Where to put return object
- *
- * RETURN: Status
- *
- * DESCRIPTION: Execute a control method.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbExecuteMethod (
- ACPI_DB_METHOD_INFO *Info,
- ACPI_BUFFER *ReturnObj)
-{
- ACPI_STATUS Status;
- ACPI_OBJECT_LIST ParamObjects;
- ACPI_OBJECT Params[ACPI_METHOD_NUM_ARGS];
- ACPI_HANDLE Handle;
- ACPI_DEVICE_INFO *ObjInfo;
- UINT32 i;
-
-
- ACPI_FUNCTION_TRACE (DbExecuteMethod);
-
-
- if (AcpiGbl_DbOutputToFile && !AcpiDbgLevel)
- {
- AcpiOsPrintf ("Warning: debug output is not enabled!\n");
- }
-
- /* Get the NS node, determines existence also */
-
- Status = AcpiGetHandle (NULL, Info->Pathname, &Handle);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Get the object info for number of method parameters */
-
- Status = AcpiGetObjectInfo (Handle, &ObjInfo);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- ParamObjects.Pointer = NULL;
- ParamObjects.Count = 0;
-
- if (ObjInfo->Type == ACPI_TYPE_METHOD)
- {
- /* Are there arguments to the method? */
-
- i = 0;
- if (Info->Args && Info->Args[0])
- {
- /* Get arguments passed on the command line */
-
- for (; Info->Args[i] &&
- (i < ACPI_METHOD_NUM_ARGS) &&
- (i < ObjInfo->ParamCount);
- i++)
- {
- /* Convert input string (token) to an actual ACPI_OBJECT */
-
- Status = AcpiDbConvertToObject (Info->Types[i],
- Info->Args[i], &Params[i]);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "While parsing method arguments"));
- goto Cleanup;
- }
- }
- }
-
- /* Create additional "default" parameters as needed */
-
- if (i < ObjInfo->ParamCount)
- {
- AcpiOsPrintf ("Adding %u arguments containing default values\n",
- ObjInfo->ParamCount - i);
-
- for (; i < ObjInfo->ParamCount; i++)
- {
- switch (i)
- {
- case 0:
-
- Params[0].Type = ACPI_TYPE_INTEGER;
- Params[0].Integer.Value = 0x01020304;
- break;
-
- case 1:
-
- Params[1].Type = ACPI_TYPE_STRING;
- Params[1].String.Length = 12;
- Params[1].String.Pointer = "AML Debugger";
- break;
-
- default:
-
- Params[i].Type = ACPI_TYPE_INTEGER;
- Params[i].Integer.Value = i * (UINT64) 0x1000;
- break;
- }
- }
- }
-
- ParamObjects.Count = ObjInfo->ParamCount;
- ParamObjects.Pointer = Params;
- }
-
- /* Prepare for a return object of arbitrary size */
-
- ReturnObj->Pointer = AcpiGbl_DbBuffer;
- ReturnObj->Length = ACPI_DEBUG_BUFFER_SIZE;
-
- /* Do the actual method execution */
-
- AcpiGbl_MethodExecuting = TRUE;
- Status = AcpiEvaluateObject (NULL,
- Info->Pathname, &ParamObjects, ReturnObj);
-
- AcpiGbl_CmSingleStep = FALSE;
- AcpiGbl_MethodExecuting = FALSE;
-
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "while executing %s from debugger", Info->Pathname));
-
- if (Status == AE_BUFFER_OVERFLOW)
- {
- ACPI_ERROR ((AE_INFO,
- "Possible overflow of internal debugger buffer (size 0x%X needed 0x%X)",
- ACPI_DEBUG_BUFFER_SIZE, (UINT32) ReturnObj->Length));
- }
- }
-
-Cleanup:
- AcpiDbDeleteObjects (ObjInfo->ParamCount, Params);
- ACPI_FREE (ObjInfo);
-
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbExecuteSetup
- *
- * PARAMETERS: Info - Valid method info
- *
- * RETURN: None
- *
- * DESCRIPTION: Setup info segment prior to method execution
- *
- ******************************************************************************/
-
-static void
-AcpiDbExecuteSetup (
- ACPI_DB_METHOD_INFO *Info)
-{
-
- /* Catenate the current scope to the supplied name */
-
- Info->Pathname[0] = 0;
- if ((Info->Name[0] != '\\') &&
- (Info->Name[0] != '/'))
- {
- ACPI_STRCAT (Info->Pathname, AcpiGbl_DbScopeBuf);
- }
-
- ACPI_STRCAT (Info->Pathname, Info->Name);
- AcpiDbPrepNamestring (Info->Pathname);
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
- AcpiOsPrintf ("Executing %s\n", Info->Pathname);
-
- if (Info->Flags & EX_SINGLE_STEP)
- {
- AcpiGbl_CmSingleStep = TRUE;
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
- }
-
- else
- {
- /* No single step, allow redirection to a file */
-
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
- }
-}
-
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
-UINT32
-AcpiDbGetCacheInfo (
- ACPI_MEMORY_LIST *Cache)
-{
-
- return (Cache->TotalAllocated - Cache->TotalFreed - Cache->CurrentDepth);
-}
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetOutstandingAllocations
- *
- * PARAMETERS: None
- *
- * RETURN: Current global allocation count minus cache entries
- *
- * DESCRIPTION: Determine the current number of "outstanding" allocations --
- * those allocations that have not been freed and also are not
- * in one of the various object caches.
- *
- ******************************************************************************/
-
-static UINT32
-AcpiDbGetOutstandingAllocations (
- void)
-{
- UINT32 Outstanding = 0;
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
-
- Outstanding += AcpiDbGetCacheInfo (AcpiGbl_StateCache);
- Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeCache);
- Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeExtCache);
- Outstanding += AcpiDbGetCacheInfo (AcpiGbl_OperandCache);
-#endif
-
- return (Outstanding);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbExecutionWalk
- *
- * PARAMETERS: WALK_CALLBACK
- *
- * RETURN: Status
- *
- * DESCRIPTION: Execute a control method. Name is relative to the current
- * scope.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbExecutionWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ACPI_BUFFER ReturnObj;
- ACPI_STATUS Status;
-
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc->Method.ParamCount)
- {
- return (AE_OK);
- }
-
- ReturnObj.Pointer = NULL;
- ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
-
- AcpiNsPrintNodePathname (Node, "Execute");
-
- /* Do the actual method execution */
-
- AcpiOsPrintf ("\n");
- AcpiGbl_MethodExecuting = TRUE;
-
- Status = AcpiEvaluateObject (Node, NULL, NULL, &ReturnObj);
-
- AcpiOsPrintf ("[%4.4s] returned %s\n", AcpiUtGetNodeName (Node),
- AcpiFormatException (Status));
- AcpiGbl_MethodExecuting = FALSE;
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbExecute
- *
- * PARAMETERS: Name - Name of method to execute
- * Args - Parameters to the method
- * Flags - single step/no single step
- *
- * RETURN: None
- *
- * DESCRIPTION: Execute a control method. Name is relative to the current
- * scope.
- *
- ******************************************************************************/
-
-void
-AcpiDbExecute (
- char *Name,
- char **Args,
- ACPI_OBJECT_TYPE *Types,
- UINT32 Flags)
-{
- ACPI_STATUS Status;
- ACPI_BUFFER ReturnObj;
- char *NameString;
-
-
-#ifdef ACPI_DEBUG_OUTPUT
- UINT32 PreviousAllocations;
- UINT32 Allocations;
-
-
- /* Memory allocation tracking */
-
- PreviousAllocations = AcpiDbGetOutstandingAllocations ();
-#endif
-
- if (*Name == '*')
- {
- (void) AcpiWalkNamespace (ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, AcpiDbExecutionWalk, NULL, NULL, NULL);
- return;
- }
- else
- {
- NameString = ACPI_ALLOCATE (ACPI_STRLEN (Name) + 1);
- if (!NameString)
- {
- return;
- }
-
- ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
-
- ACPI_STRCPY (NameString, Name);
- AcpiUtStrupr (NameString);
- AcpiGbl_DbMethodInfo.Name = NameString;
- AcpiGbl_DbMethodInfo.Args = Args;
- AcpiGbl_DbMethodInfo.Types = Types;
- AcpiGbl_DbMethodInfo.Flags = Flags;
-
- ReturnObj.Pointer = NULL;
- ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
-
- AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
- Status = AcpiDbExecuteMethod (&AcpiGbl_DbMethodInfo, &ReturnObj);
- ACPI_FREE (NameString);
- }
-
- /*
- * Allow any handlers in separate threads to complete.
- * (Such as Notify handlers invoked from AML executed above).
- */
- AcpiOsSleep ((UINT64) 10);
-
-
-#ifdef ACPI_DEBUG_OUTPUT
-
- /* Memory allocation tracking */
-
- Allocations = AcpiDbGetOutstandingAllocations () - PreviousAllocations;
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
-
- if (Allocations > 0)
- {
- AcpiOsPrintf ("Outstanding: 0x%X allocations after execution\n",
- Allocations);
- }
-#endif
-
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Execution of %s failed with status %s\n",
- AcpiGbl_DbMethodInfo.Pathname, AcpiFormatException (Status));
- }
- else
- {
- /* Display a return object, if any */
-
- if (ReturnObj.Length)
- {
- AcpiOsPrintf ("Execution of %s returned object %p Buflen %X\n",
- AcpiGbl_DbMethodInfo.Pathname, ReturnObj.Pointer,
- (UINT32) ReturnObj.Length);
- AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
- }
- else
- {
- AcpiOsPrintf ("No return object from execution of %s\n",
- AcpiGbl_DbMethodInfo.Pathname);
- }
- }
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbMethodThread
- *
- * PARAMETERS: Context - Execution info segment
- *
- * RETURN: None
- *
- * DESCRIPTION: Debugger execute thread. Waits for a command line, then
- * simply dispatches it.
- *
- ******************************************************************************/
-
-static void ACPI_SYSTEM_XFACE
-AcpiDbMethodThread (
- void *Context)
-{
- ACPI_STATUS Status;
- ACPI_DB_METHOD_INFO *Info = Context;
- ACPI_DB_METHOD_INFO LocalInfo;
- UINT32 i;
- UINT8 Allow;
- ACPI_BUFFER ReturnObj;
-
-
- /*
- * AcpiGbl_DbMethodInfo.Arguments will be passed as method arguments.
- * Prevent AcpiGbl_DbMethodInfo from being modified by multiple threads
- * concurrently.
- *
- * Note: The arguments we are passing are used by the ASL test suite
- * (aslts). Do not change them without updating the tests.
- */
- (void) AcpiOsWaitSemaphore (Info->InfoGate, 1, ACPI_WAIT_FOREVER);
-
- if (Info->InitArgs)
- {
- AcpiDbUInt32ToHexString (Info->NumCreated, Info->IndexOfThreadStr);
- AcpiDbUInt32ToHexString ((UINT32) AcpiOsGetThreadId (), Info->IdOfThreadStr);
- }
-
- if (Info->Threads && (Info->NumCreated < Info->NumThreads))
- {
- Info->Threads[Info->NumCreated++] = AcpiOsGetThreadId();
- }
-
- LocalInfo = *Info;
- LocalInfo.Args = LocalInfo.Arguments;
- LocalInfo.Arguments[0] = LocalInfo.NumThreadsStr;
- LocalInfo.Arguments[1] = LocalInfo.IdOfThreadStr;
- LocalInfo.Arguments[2] = LocalInfo.IndexOfThreadStr;
- LocalInfo.Arguments[3] = NULL;
-
- LocalInfo.Types = LocalInfo.ArgTypes;
-
- (void) AcpiOsSignalSemaphore (Info->InfoGate, 1);
-
- for (i = 0; i < Info->NumLoops; i++)
- {
- Status = AcpiDbExecuteMethod (&LocalInfo, &ReturnObj);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("%s During execution of %s at iteration %X\n",
- AcpiFormatException (Status), Info->Pathname, i);
- if (Status == AE_ABORT_METHOD)
- {
- break;
- }
- }
-
-#if 0
- if ((i % 100) == 0)
- {
- AcpiOsPrintf ("%u executions, Thread 0x%x\n", i, AcpiOsGetThreadId ());
- }
-
- if (ReturnObj.Length)
- {
- AcpiOsPrintf ("Execution of %s returned object %p Buflen %X\n",
- Info->Pathname, ReturnObj.Pointer, (UINT32) ReturnObj.Length);
- AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
- }
-#endif
- }
-
- /* Signal our completion */
-
- Allow = 0;
- (void) AcpiOsWaitSemaphore (Info->ThreadCompleteGate, 1, ACPI_WAIT_FOREVER);
- Info->NumCompleted++;
-
- if (Info->NumCompleted == Info->NumThreads)
- {
- /* Do signal for main thread once only */
- Allow = 1;
- }
-
- (void) AcpiOsSignalSemaphore (Info->ThreadCompleteGate, 1);
-
- if (Allow)
- {
- Status = AcpiOsSignalSemaphore (Info->MainThreadGate, 1);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not signal debugger thread sync semaphore, %s\n",
- AcpiFormatException (Status));
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCreateExecutionThreads
- *
- * PARAMETERS: NumThreadsArg - Number of threads to create
- * NumLoopsArg - Loop count for the thread(s)
- * MethodNameArg - Control method to execute
- *
- * RETURN: None
- *
- * DESCRIPTION: Create threads to execute method(s)
- *
- ******************************************************************************/
-
-void
-AcpiDbCreateExecutionThreads (
- char *NumThreadsArg,
- char *NumLoopsArg,
- char *MethodNameArg)
-{
- ACPI_STATUS Status;
- UINT32 NumThreads;
- UINT32 NumLoops;
- UINT32 i;
- UINT32 Size;
- ACPI_MUTEX MainThreadGate;
- ACPI_MUTEX ThreadCompleteGate;
- ACPI_MUTEX InfoGate;
-
-
- /* Get the arguments */
-
- NumThreads = ACPI_STRTOUL (NumThreadsArg, NULL, 0);
- NumLoops = ACPI_STRTOUL (NumLoopsArg, NULL, 0);
-
- if (!NumThreads || !NumLoops)
- {
- AcpiOsPrintf ("Bad argument: Threads %X, Loops %X\n",
- NumThreads, NumLoops);
- return;
- }
-
- /*
- * Create the semaphore for synchronization of
- * the created threads with the main thread.
- */
- Status = AcpiOsCreateSemaphore (1, 0, &MainThreadGate);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not create semaphore for synchronization with the main thread, %s\n",
- AcpiFormatException (Status));
- return;
- }
-
- /*
- * Create the semaphore for synchronization
- * between the created threads.
- */
- Status = AcpiOsCreateSemaphore (1, 1, &ThreadCompleteGate);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not create semaphore for synchronization between the created threads, %s\n",
- AcpiFormatException (Status));
- (void) AcpiOsDeleteSemaphore (MainThreadGate);
- return;
- }
-
- Status = AcpiOsCreateSemaphore (1, 1, &InfoGate);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not create semaphore for synchronization of AcpiGbl_DbMethodInfo, %s\n",
- AcpiFormatException (Status));
- (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
- (void) AcpiOsDeleteSemaphore (MainThreadGate);
- return;
- }
-
- ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
-
- /* Array to store IDs of threads */
-
- AcpiGbl_DbMethodInfo.NumThreads = NumThreads;
- Size = sizeof (ACPI_THREAD_ID) * AcpiGbl_DbMethodInfo.NumThreads;
- AcpiGbl_DbMethodInfo.Threads = AcpiOsAllocate (Size);
- if (AcpiGbl_DbMethodInfo.Threads == NULL)
- {
- AcpiOsPrintf ("No memory for thread IDs array\n");
- (void) AcpiOsDeleteSemaphore (MainThreadGate);
- (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
- (void) AcpiOsDeleteSemaphore (InfoGate);
- return;
- }
- ACPI_MEMSET (AcpiGbl_DbMethodInfo.Threads, 0, Size);
-
- /* Setup the context to be passed to each thread */
-
- AcpiGbl_DbMethodInfo.Name = MethodNameArg;
- AcpiGbl_DbMethodInfo.Flags = 0;
- AcpiGbl_DbMethodInfo.NumLoops = NumLoops;
- AcpiGbl_DbMethodInfo.MainThreadGate = MainThreadGate;
- AcpiGbl_DbMethodInfo.ThreadCompleteGate = ThreadCompleteGate;
- AcpiGbl_DbMethodInfo.InfoGate = InfoGate;
-
- /* Init arguments to be passed to method */
-
- AcpiGbl_DbMethodInfo.InitArgs = 1;
- AcpiGbl_DbMethodInfo.Args = AcpiGbl_DbMethodInfo.Arguments;
- AcpiGbl_DbMethodInfo.Arguments[0] = AcpiGbl_DbMethodInfo.NumThreadsStr;
- AcpiGbl_DbMethodInfo.Arguments[1] = AcpiGbl_DbMethodInfo.IdOfThreadStr;
- AcpiGbl_DbMethodInfo.Arguments[2] = AcpiGbl_DbMethodInfo.IndexOfThreadStr;
- AcpiGbl_DbMethodInfo.Arguments[3] = NULL;
-
- AcpiGbl_DbMethodInfo.Types = AcpiGbl_DbMethodInfo.ArgTypes;
- AcpiGbl_DbMethodInfo.ArgTypes[0] = ACPI_TYPE_INTEGER;
- AcpiGbl_DbMethodInfo.ArgTypes[1] = ACPI_TYPE_INTEGER;
- AcpiGbl_DbMethodInfo.ArgTypes[2] = ACPI_TYPE_INTEGER;
-
- AcpiDbUInt32ToHexString (NumThreads, AcpiGbl_DbMethodInfo.NumThreadsStr);
-
- AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
-
- /* Create the threads */
-
- AcpiOsPrintf ("Creating %X threads to execute %X times each\n",
- NumThreads, NumLoops);
-
- for (i = 0; i < (NumThreads); i++)
- {
- Status = AcpiOsExecute (OSL_DEBUGGER_THREAD, AcpiDbMethodThread,
- &AcpiGbl_DbMethodInfo);
- if (ACPI_FAILURE (Status))
- {
- break;
- }
- }
-
- /* Wait for all threads to complete */
-
- (void) AcpiOsWaitSemaphore (MainThreadGate, 1, ACPI_WAIT_FOREVER);
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
- AcpiOsPrintf ("All threads (%X) have completed\n", NumThreads);
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-
- /* Cleanup and exit */
-
- (void) AcpiOsDeleteSemaphore (MainThreadGate);
- (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
- (void) AcpiOsDeleteSemaphore (InfoGate);
-
- AcpiOsFree (AcpiGbl_DbMethodInfo.Threads);
- AcpiGbl_DbMethodInfo.Threads = NULL;
-}
-
-#endif /* ACPI_DEBUGGER */
-
-
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbfileio.c b/usr/src/uts/intel/io/acpica/debugger/dbfileio.c
deleted file mode 100644
index 8d0ef0aaa8..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbfileio.c
+++ /dev/null
@@ -1,577 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbfileio - Debugger file I/O commands. These can't usually
- * be used when running the debugger in Ring 0 (Kernel mode)
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdebug.h"
-
-#ifdef ACPI_APPLICATION
-#include "actables.h"
-#endif
-
-#if (defined ACPI_DEBUGGER || defined ACPI_DISASSEMBLER)
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbfileio")
-
-/*
- * NOTE: this is here for lack of a better place. It is used in all
- * flavors of the debugger, need LCD file
- */
-#ifdef ACPI_APPLICATION
-#include <stdio.h>
-FILE *AcpiGbl_DebugFile = NULL;
-#endif
-
-
-#ifdef ACPI_DEBUGGER
-
-/* Local prototypes */
-
-#ifdef ACPI_APPLICATION
-
-static ACPI_STATUS
-AcpiDbCheckTextModeCorruption (
- UINT8 *Table,
- UINT32 TableLength,
- UINT32 FileLength);
-
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCloseDebugFile
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: If open, close the current debug output file
- *
- ******************************************************************************/
-
-void
-AcpiDbCloseDebugFile (
- void)
-{
-
-#ifdef ACPI_APPLICATION
-
- if (AcpiGbl_DebugFile)
- {
- fclose (AcpiGbl_DebugFile);
- AcpiGbl_DebugFile = NULL;
- AcpiGbl_DbOutputToFile = FALSE;
- AcpiOsPrintf ("Debug output file %s closed\n", AcpiGbl_DbDebugFilename);
- }
-#endif
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbOpenDebugFile
- *
- * PARAMETERS: Name - Filename to open
- *
- * RETURN: None
- *
- * DESCRIPTION: Open a file where debug output will be directed.
- *
- ******************************************************************************/
-
-void
-AcpiDbOpenDebugFile (
- char *Name)
-{
-
-#ifdef ACPI_APPLICATION
-
- AcpiDbCloseDebugFile ();
- AcpiGbl_DebugFile = fopen (Name, "w+");
- if (AcpiGbl_DebugFile)
- {
- AcpiOsPrintf ("Debug output file %s opened\n", Name);
- ACPI_STRCPY (AcpiGbl_DbDebugFilename, Name);
- AcpiGbl_DbOutputToFile = TRUE;
- }
- else
- {
- AcpiOsPrintf ("Could not open debug file %s\n", Name);
- }
-
-#endif
-}
-#endif
-
-
-#ifdef ACPI_APPLICATION
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCheckTextModeCorruption
- *
- * PARAMETERS: Table - Table buffer
- * TableLength - Length of table from the table header
- * FileLength - Length of the file that contains the table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Check table for text mode file corruption where all linefeed
- * characters (LF) have been replaced by carriage return linefeed
- * pairs (CR/LF).
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbCheckTextModeCorruption (
- UINT8 *Table,
- UINT32 TableLength,
- UINT32 FileLength)
-{
- UINT32 i;
- UINT32 Pairs = 0;
-
-
- if (TableLength != FileLength)
- {
- ACPI_WARNING ((AE_INFO,
- "File length (0x%X) is not the same as the table length (0x%X)",
- FileLength, TableLength));
- }
-
- /* Scan entire table to determine if each LF has been prefixed with a CR */
-
- for (i = 1; i < FileLength; i++)
- {
- if (Table[i] == 0x0A)
- {
- if (Table[i - 1] != 0x0D)
- {
- /* The LF does not have a preceding CR, table not corrupted */
-
- return (AE_OK);
- }
- else
- {
- /* Found a CR/LF pair */
-
- Pairs++;
- }
- i++;
- }
- }
-
- if (!Pairs)
- {
- return (AE_OK);
- }
-
- /*
- * Entire table scanned, each CR is part of a CR/LF pair --
- * meaning that the table was treated as a text file somewhere.
- *
- * NOTE: We can't "fix" the table, because any existing CR/LF pairs in the
- * original table are left untouched by the text conversion process --
- * meaning that we cannot simply replace CR/LF pairs with LFs.
- */
- AcpiOsPrintf ("Table has been corrupted by text mode conversion\n");
- AcpiOsPrintf ("All LFs (%u) were changed to CR/LF pairs\n", Pairs);
- AcpiOsPrintf ("Table cannot be repaired!\n");
- return (AE_BAD_VALUE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbReadTable
- *
- * PARAMETERS: fp - File that contains table
- * Table - Return value, buffer with table
- * TableLength - Return value, length of table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Load the DSDT from the file pointer
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbReadTable (
- FILE *fp,
- ACPI_TABLE_HEADER **Table,
- UINT32 *TableLength)
-{
- ACPI_TABLE_HEADER TableHeader;
- UINT32 Actual;
- ACPI_STATUS Status;
- UINT32 FileSize;
- BOOLEAN StandardHeader = TRUE;
-
-
- /* Get the file size */
-
- fseek (fp, 0, SEEK_END);
- FileSize = (UINT32) ftell (fp);
- fseek (fp, 0, SEEK_SET);
-
- if (FileSize < 4)
- {
- return (AE_BAD_HEADER);
- }
-
- /* Read the signature */
-
- if (fread (&TableHeader, 1, 4, fp) != 4)
- {
- AcpiOsPrintf ("Could not read the table signature\n");
- return (AE_BAD_HEADER);
- }
-
- fseek (fp, 0, SEEK_SET);
-
- /* The RSDT and FACS tables do not have standard ACPI headers */
-
- if (ACPI_COMPARE_NAME (TableHeader.Signature, "RSD ") ||
- ACPI_COMPARE_NAME (TableHeader.Signature, "FACS"))
- {
- *TableLength = FileSize;
- StandardHeader = FALSE;
- }
- else
- {
- /* Read the table header */
-
- if (fread (&TableHeader, 1, sizeof (TableHeader), fp) !=
- sizeof (ACPI_TABLE_HEADER))
- {
- AcpiOsPrintf ("Could not read the table header\n");
- return (AE_BAD_HEADER);
- }
-
-#if 0
- /* Validate the table header/length */
-
- Status = AcpiTbValidateTableHeader (&TableHeader);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Table header is invalid!\n");
- return (Status);
- }
-#endif
-
- /* File size must be at least as long as the Header-specified length */
-
- if (TableHeader.Length > FileSize)
- {
- AcpiOsPrintf (
- "TableHeader length [0x%X] greater than the input file size [0x%X]\n",
- TableHeader.Length, FileSize);
- return (AE_BAD_HEADER);
- }
-
-#ifdef ACPI_OBSOLETE_CODE
- /* We only support a limited number of table types */
-
- if (ACPI_STRNCMP ((char *) TableHeader.Signature, DSDT_SIG, 4) &&
- ACPI_STRNCMP ((char *) TableHeader.Signature, PSDT_SIG, 4) &&
- ACPI_STRNCMP ((char *) TableHeader.Signature, SSDT_SIG, 4))
- {
- AcpiOsPrintf ("Table signature [%4.4s] is invalid or not supported\n",
- (char *) TableHeader.Signature);
- ACPI_DUMP_BUFFER (&TableHeader, sizeof (ACPI_TABLE_HEADER));
- return (AE_ERROR);
- }
-#endif
-
- *TableLength = TableHeader.Length;
- }
-
- /* Allocate a buffer for the table */
-
- *Table = AcpiOsAllocate ((size_t) FileSize);
- if (!*Table)
- {
- AcpiOsPrintf (
- "Could not allocate memory for ACPI table %4.4s (size=0x%X)\n",
- TableHeader.Signature, *TableLength);
- return (AE_NO_MEMORY);
- }
-
- /* Get the rest of the table */
-
- fseek (fp, 0, SEEK_SET);
- Actual = fread (*Table, 1, (size_t) FileSize, fp);
- if (Actual == FileSize)
- {
- if (StandardHeader)
- {
- /* Now validate the checksum */
-
- Status = AcpiTbVerifyChecksum ((void *) *Table,
- ACPI_CAST_PTR (ACPI_TABLE_HEADER, *Table)->Length);
-
- if (Status == AE_BAD_CHECKSUM)
- {
- Status = AcpiDbCheckTextModeCorruption ((UINT8 *) *Table,
- FileSize, (*Table)->Length);
- return (Status);
- }
- }
- return (AE_OK);
- }
-
- if (Actual > 0)
- {
- AcpiOsPrintf ("Warning - reading table, asked for %X got %X\n",
- FileSize, Actual);
- return (AE_OK);
- }
-
- AcpiOsPrintf ("Error - could not read the table file\n");
- AcpiOsFree (*Table);
- *Table = NULL;
- *TableLength = 0;
-
- return (AE_ERROR);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AeLocalLoadTable
- *
- * PARAMETERS: Table - pointer to a buffer containing the entire
- * table to be loaded
- *
- * RETURN: Status
- *
- * DESCRIPTION: This function is called to load a table from the caller's
- * buffer. The buffer must contain an entire ACPI Table including
- * a valid header. The header fields will be verified, and if it
- * is determined that the table is invalid, the call will fail.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AeLocalLoadTable (
- ACPI_TABLE_HEADER *Table)
-{
- ACPI_STATUS Status = AE_OK;
-/* ACPI_TABLE_DESC TableInfo; */
-
-
- ACPI_FUNCTION_TRACE (AeLocalLoadTable);
-#if 0
-
-
- if (!Table)
- {
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
-
- TableInfo.Pointer = Table;
- Status = AcpiTbRecognizeTable (&TableInfo, ACPI_TABLE_ALL);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Install the new table into the local data structures */
-
- Status = AcpiTbInstallTable (&TableInfo);
- if (ACPI_FAILURE (Status))
- {
- if (Status == AE_ALREADY_EXISTS)
- {
- /* Table already exists, no error */
-
- Status = AE_OK;
- }
-
- /* Free table allocated by AcpiTbGetTable */
-
- AcpiTbDeleteSingleTable (&TableInfo);
- return_ACPI_STATUS (Status);
- }
-
-#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY))
-
- Status = AcpiNsLoadTable (TableInfo.InstalledDesc, AcpiGbl_RootNode);
- if (ACPI_FAILURE (Status))
- {
- /* Uninstall table and free the buffer */
-
- AcpiTbDeleteTablesByType (ACPI_TABLE_ID_DSDT);
- return_ACPI_STATUS (Status);
- }
-#endif
-#endif
-
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbReadTableFromFile
- *
- * PARAMETERS: Filename - File where table is located
- * Table - Where a pointer to the table is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Get an ACPI table from a file
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbReadTableFromFile (
- char *Filename,
- ACPI_TABLE_HEADER **Table)
-{
- FILE *fp;
- UINT32 TableLength;
- ACPI_STATUS Status;
-
-
- /* Open the file */
-
- fp = fopen (Filename, "rb");
- if (!fp)
- {
- AcpiOsPrintf ("Could not open input file %s\n", Filename);
- return (AE_ERROR);
- }
-
- /* Get the entire file */
-
- fprintf (stderr, "Loading Acpi table from file %s\n", Filename);
- Status = AcpiDbReadTable (fp, Table, &TableLength);
- fclose(fp);
-
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not get table from the file\n");
- return (Status);
- }
-
- return (AE_OK);
- }
-#endif
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetTableFromFile
- *
- * PARAMETERS: Filename - File where table is located
- * ReturnTable - Where a pointer to the table is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Load an ACPI table from a file
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbGetTableFromFile (
- char *Filename,
- ACPI_TABLE_HEADER **ReturnTable)
-{
-#ifdef ACPI_APPLICATION
- ACPI_STATUS Status;
- ACPI_TABLE_HEADER *Table;
- BOOLEAN IsAmlTable = TRUE;
-
-
- Status = AcpiDbReadTableFromFile (Filename, &Table);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
-#ifdef ACPI_DATA_TABLE_DISASSEMBLY
- IsAmlTable = AcpiUtIsAmlTable (Table);
-#endif
-
- if (IsAmlTable)
- {
- /* Attempt to recognize and install the table */
-
- Status = AeLocalLoadTable (Table);
- if (ACPI_FAILURE (Status))
- {
- if (Status == AE_ALREADY_EXISTS)
- {
- AcpiOsPrintf ("Table %4.4s is already installed\n",
- Table->Signature);
- }
- else
- {
- AcpiOsPrintf ("Could not install table, %s\n",
- AcpiFormatException (Status));
- }
-
- return (Status);
- }
-
- fprintf (stderr,
- "Acpi table [%4.4s] successfully installed and loaded\n",
- Table->Signature);
- }
-
- AcpiGbl_AcpiHardwarePresent = FALSE;
- if (ReturnTable)
- {
- *ReturnTable = Table;
- }
-
-
-#endif /* ACPI_APPLICATION */
- return (AE_OK);
-}
-
-#endif /* ACPI_DEBUGGER */
-
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbhistry.c b/usr/src/uts/intel/io/acpica/debugger/dbhistry.c
deleted file mode 100644
index 855d6b956f..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbhistry.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/******************************************************************************
- *
- * Module Name: dbhistry - debugger HISTORY command
- *
- *****************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdebug.h"
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbhistry")
-
-
-#define HI_NO_HISTORY 0
-#define HI_RECORD_HISTORY 1
-#define HISTORY_SIZE 20
-
-
-typedef struct HistoryInfo
-{
- char Command[80];
- UINT32 CmdNum;
-
-} HISTORY_INFO;
-
-
-static HISTORY_INFO AcpiGbl_HistoryBuffer[HISTORY_SIZE];
-static UINT16 AcpiGbl_LoHistory = 0;
-static UINT16 AcpiGbl_NumHistory = 0;
-static UINT16 AcpiGbl_NextHistoryIndex = 0;
-static UINT32 AcpiGbl_NextCmdNum = 1;
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbAddToHistory
- *
- * PARAMETERS: CommandLine - Command to add
- *
- * RETURN: None
- *
- * DESCRIPTION: Add a command line to the history buffer.
- *
- ******************************************************************************/
-
-void
-AcpiDbAddToHistory (
- char *CommandLine)
-{
-
- /* Put command into the next available slot */
-
- ACPI_STRCPY (AcpiGbl_HistoryBuffer[AcpiGbl_NextHistoryIndex].Command,
- CommandLine);
-
- AcpiGbl_HistoryBuffer[AcpiGbl_NextHistoryIndex].CmdNum = AcpiGbl_NextCmdNum;
-
- /* Adjust indexes */
-
- if ((AcpiGbl_NumHistory == HISTORY_SIZE) &&
- (AcpiGbl_NextHistoryIndex == AcpiGbl_LoHistory))
- {
- AcpiGbl_LoHistory++;
- if (AcpiGbl_LoHistory >= HISTORY_SIZE)
- {
- AcpiGbl_LoHistory = 0;
- }
- }
-
- AcpiGbl_NextHistoryIndex++;
- if (AcpiGbl_NextHistoryIndex >= HISTORY_SIZE)
- {
- AcpiGbl_NextHistoryIndex = 0;
- }
-
- AcpiGbl_NextCmdNum++;
- if (AcpiGbl_NumHistory < HISTORY_SIZE)
- {
- AcpiGbl_NumHistory++;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayHistory
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display the contents of the history buffer
- *
- ******************************************************************************/
-
-void
-AcpiDbDisplayHistory (
- void)
-{
- UINT32 i;
- UINT16 HistoryIndex;
-
-
- HistoryIndex = AcpiGbl_LoHistory;
-
- /* Dump entire history buffer */
-
- for (i = 0; i < AcpiGbl_NumHistory; i++)
- {
- AcpiOsPrintf ("%ld %s\n", AcpiGbl_HistoryBuffer[HistoryIndex].CmdNum,
- AcpiGbl_HistoryBuffer[HistoryIndex].Command);
-
- HistoryIndex++;
- if (HistoryIndex >= HISTORY_SIZE)
- {
- HistoryIndex = 0;
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetFromHistory
- *
- * PARAMETERS: CommandNumArg - String containing the number of the
- * command to be retrieved
- *
- * RETURN: Pointer to the retrieved command. Null on error.
- *
- * DESCRIPTION: Get a command from the history buffer
- *
- ******************************************************************************/
-
-char *
-AcpiDbGetFromHistory (
- char *CommandNumArg)
-{
- UINT32 i;
- UINT16 HistoryIndex;
- UINT32 CmdNum;
-
-
- if (CommandNumArg == NULL)
- {
- CmdNum = AcpiGbl_NextCmdNum - 1;
- }
-
- else
- {
- CmdNum = ACPI_STRTOUL (CommandNumArg, NULL, 0);
- }
-
- /* Search history buffer */
-
- HistoryIndex = AcpiGbl_LoHistory;
- for (i = 0; i < AcpiGbl_NumHistory; i++)
- {
- if (AcpiGbl_HistoryBuffer[HistoryIndex].CmdNum == CmdNum)
- {
- /* Found the commnad, return it */
-
- return (AcpiGbl_HistoryBuffer[HistoryIndex].Command);
- }
-
-
- HistoryIndex++;
- if (HistoryIndex >= HISTORY_SIZE)
- {
- HistoryIndex = 0;
- }
- }
-
- AcpiOsPrintf ("Invalid history number: %u\n", HistoryIndex);
- return (NULL);
-}
-
-#endif /* ACPI_DEBUGGER */
-
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbinput.c b/usr/src/uts/intel/io/acpica/debugger/dbinput.c
deleted file mode 100644
index 1d716cfb1b..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbinput.c
+++ /dev/null
@@ -1,1081 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbinput - user front-end to the AML debugger
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdebug.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbinput")
-
-/* Local prototypes */
-
-static UINT32
-AcpiDbGetLine (
- char *InputBuffer);
-
-static UINT32
-AcpiDbMatchCommand (
- char *UserCommand);
-
-static void
-AcpiDbSingleThread (
- void);
-
-static void
-AcpiDbDisplayHelp (
- void);
-
-
-/*
- * Top-level debugger commands.
- *
- * This list of commands must match the string table below it
- */
-enum AcpiExDebuggerCommands
-{
- CMD_NOT_FOUND = 0,
- CMD_NULL,
- CMD_ALLOCATIONS,
- CMD_ARGS,
- CMD_ARGUMENTS,
- CMD_BATCH,
- CMD_BREAKPOINT,
- CMD_BUSINFO,
- CMD_CALL,
- CMD_CLOSE,
- CMD_DEBUG,
- CMD_DISASSEMBLE,
- CMD_DUMP,
- CMD_ENABLEACPI,
- CMD_EVENT,
- CMD_EXECUTE,
- CMD_EXIT,
- CMD_FIND,
- CMD_GO,
- CMD_GPE,
- CMD_GPES,
- CMD_HANDLERS,
- CMD_HELP,
- CMD_HELP2,
- CMD_HISTORY,
- CMD_HISTORY_EXE,
- CMD_HISTORY_LAST,
- CMD_INFORMATION,
- CMD_INTEGRITY,
- CMD_INTO,
- CMD_LEVEL,
- CMD_LIST,
- CMD_LOAD,
- CMD_LOCALS,
- CMD_LOCKS,
- CMD_METHODS,
- CMD_NAMESPACE,
- CMD_NOTIFY,
- CMD_OBJECT,
- CMD_OPEN,
- CMD_OSI,
- CMD_OWNER,
- CMD_PREDEFINED,
- CMD_PREFIX,
- CMD_QUIT,
- CMD_REFERENCES,
- CMD_RESOURCES,
- CMD_RESULTS,
- CMD_SET,
- CMD_SLEEP,
- CMD_STATS,
- CMD_STOP,
- CMD_TABLES,
- CMD_TERMINATE,
- CMD_THREADS,
- CMD_TRACE,
- CMD_TREE,
- CMD_TYPE,
- CMD_UNLOAD
-};
-
-#define CMD_FIRST_VALID 2
-
-
-/* Second parameter is the required argument count */
-
-static const COMMAND_INFO AcpiGbl_DbCommands[] =
-{
- {"<NOT FOUND>", 0},
- {"<NULL>", 0},
- {"ALLOCATIONS", 0},
- {"ARGS", 0},
- {"ARGUMENTS", 0},
- {"BATCH", 0},
- {"BREAKPOINT", 1},
- {"BUSINFO", 0},
- {"CALL", 0},
- {"CLOSE", 0},
- {"DEBUG", 1},
- {"DISASSEMBLE", 1},
- {"DUMP", 1},
- {"ENABLEACPI", 0},
- {"EVENT", 1},
- {"EXECUTE", 1},
- {"EXIT", 0},
- {"FIND", 1},
- {"GO", 0},
- {"GPE", 2},
- {"GPES", 0},
- {"HANDLERS", 0},
- {"HELP", 0},
- {"?", 0},
- {"HISTORY", 0},
- {"!", 1},
- {"!!", 0},
- {"INFORMATION", 0},
- {"INTEGRITY", 0},
- {"INTO", 0},
- {"LEVEL", 0},
- {"LIST", 0},
- {"LOAD", 1},
- {"LOCALS", 0},
- {"LOCKS", 0},
- {"METHODS", 0},
- {"NAMESPACE", 0},
- {"NOTIFY", 2},
- {"OBJECT", 1},
- {"OPEN", 1},
- {"OSI", 0},
- {"OWNER", 1},
- {"PREDEFINED", 0},
- {"PREFIX", 0},
- {"QUIT", 0},
- {"REFERENCES", 1},
- {"RESOURCES", 1},
- {"RESULTS", 0},
- {"SET", 3},
- {"SLEEP", 1},
- {"STATS", 0},
- {"STOP", 0},
- {"TABLES", 0},
- {"TERMINATE", 0},
- {"THREADS", 3},
- {"TRACE", 1},
- {"TREE", 0},
- {"TYPE", 1},
- {"UNLOAD", 1},
- {NULL, 0}
-};
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayHelp
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Print a usage message.
- *
- ******************************************************************************/
-
-static void
-AcpiDbDisplayHelp (
- void)
-{
-
- AcpiOsPrintf ("\nGeneral-Purpose Commands:\n");
- AcpiOsPrintf (" Allocations Display list of current memory allocations\n");
- AcpiOsPrintf (" Dump <Address>|<Namepath>\n");
- AcpiOsPrintf (" [Byte|Word|Dword|Qword] Display ACPI objects or memory\n");
- AcpiOsPrintf (" EnableAcpi Enable ACPI (hardware) mode\n");
- AcpiOsPrintf (" Handlers Info about global handlers\n");
- AcpiOsPrintf (" Help This help screen\n");
- AcpiOsPrintf (" History Display command history buffer\n");
- AcpiOsPrintf (" Level [<DebugLevel>] [console] Get/Set debug level for file or console\n");
- AcpiOsPrintf (" Locks Current status of internal mutexes\n");
- AcpiOsPrintf (" Osi [Install|Remove <name>] Display or modify global _OSI list\n");
- AcpiOsPrintf (" Quit or Exit Exit this command\n");
- AcpiOsPrintf (" Stats [Allocations|Memory|Misc|\n");
- AcpiOsPrintf (" Objects|Sizes|Stack|Tables] Display namespace and memory statistics\n");
- AcpiOsPrintf (" Allocations Display list of current memory allocations\n");
- AcpiOsPrintf (" Memory Dump internal memory lists\n");
- AcpiOsPrintf (" Misc Namespace search and mutex stats\n");
- AcpiOsPrintf (" Objects Summary of namespace objects\n");
- AcpiOsPrintf (" Sizes Sizes for each of the internal objects\n");
- AcpiOsPrintf (" Stack Display CPU stack usage\n");
- AcpiOsPrintf (" Tables Info about current ACPI table(s)\n");
- AcpiOsPrintf (" Tables Display info about loaded ACPI tables\n");
- AcpiOsPrintf (" Unload <TableSig> [Instance] Unload an ACPI table\n");
- AcpiOsPrintf (" ! <CommandNumber> Execute command from history buffer\n");
- AcpiOsPrintf (" !! Execute last command again\n");
-
- AcpiOsPrintf ("\nNamespace Access Commands:\n");
- AcpiOsPrintf (" Businfo Display system bus info\n");
- AcpiOsPrintf (" Disassemble <Method> Disassemble a control method\n");
- AcpiOsPrintf (" Event <F|G> <Value> Generate AcpiEvent (Fixed/GPE)\n");
- AcpiOsPrintf (" Find <AcpiName> (? is wildcard) Find ACPI name(s) with wildcards\n");
- AcpiOsPrintf (" Gpe <GpeNum> <GpeBlock> Simulate a GPE\n");
- AcpiOsPrintf (" Gpes Display info on all GPEs\n");
- AcpiOsPrintf (" Integrity Validate namespace integrity\n");
- AcpiOsPrintf (" Methods Display list of loaded control methods\n");
- AcpiOsPrintf (" Namespace [Object] [Depth] Display loaded namespace tree/subtree\n");
- AcpiOsPrintf (" Notify <Object> <Value> Send a notification on Object\n");
- AcpiOsPrintf (" Objects <ObjectType> Display all objects of the given type\n");
- AcpiOsPrintf (" Owner <OwnerId> [Depth] Display loaded namespace by object owner\n");
- AcpiOsPrintf (" Predefined Check all predefined names\n");
- AcpiOsPrintf (" Prefix [<NamePath>] Set or Get current execution prefix\n");
- AcpiOsPrintf (" References <Addr> Find all references to object at addr\n");
- AcpiOsPrintf (" Resources <Device> Get and display Device resources\n");
- AcpiOsPrintf (" Set N <NamedObject> <Value> Set value for named integer\n");
- AcpiOsPrintf (" Sleep <SleepState> Simulate sleep/wake sequence\n");
- AcpiOsPrintf (" Terminate Delete namespace and all internal objects\n");
- AcpiOsPrintf (" Type <Object> Display object type\n");
-
- AcpiOsPrintf ("\nControl Method Execution Commands:\n");
- AcpiOsPrintf (" Arguments (or Args) Display method arguments\n");
- AcpiOsPrintf (" Breakpoint <AmlOffset> Set an AML execution breakpoint\n");
- AcpiOsPrintf (" Call Run to next control method invocation\n");
- AcpiOsPrintf (" Debug <Namepath> [Arguments] Single Step a control method\n");
- AcpiOsPrintf (" Execute <Namepath> [Arguments] Execute control method\n");
- AcpiOsPrintf (" Hex Integer Integer method argument\n");
- AcpiOsPrintf (" \"Ascii String\" String method argument\n");
- AcpiOsPrintf (" (Byte List) Buffer method argument\n");
- AcpiOsPrintf (" [Package Element List] Package method argument\n");
- AcpiOsPrintf (" Go Allow method to run to completion\n");
- AcpiOsPrintf (" Information Display info about the current method\n");
- AcpiOsPrintf (" Into Step into (not over) a method call\n");
- AcpiOsPrintf (" List [# of Aml Opcodes] Display method ASL statements\n");
- AcpiOsPrintf (" Locals Display method local variables\n");
- AcpiOsPrintf (" Results Display method result stack\n");
- AcpiOsPrintf (" Set <A|L> <#> <Value> Set method data (Arguments/Locals)\n");
- AcpiOsPrintf (" Stop Terminate control method\n");
- AcpiOsPrintf (" Thread <Threads><Loops><NamePath> Spawn threads to execute method(s)\n");
- AcpiOsPrintf (" Trace <method name> Trace method execution\n");
- AcpiOsPrintf (" Tree Display control method calling tree\n");
- AcpiOsPrintf (" <Enter> Single step next AML opcode (over calls)\n");
-
- AcpiOsPrintf ("\nFile I/O Commands:\n");
- AcpiOsPrintf (" Close Close debug output file\n");
- AcpiOsPrintf (" Load <Input Filename> Load ACPI table from a file\n");
- AcpiOsPrintf (" Open <Output Filename> Open a file for debug output\n");
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetNextToken
- *
- * PARAMETERS: String - Command buffer
- * Next - Return value, end of next token
- *
- * RETURN: Pointer to the start of the next token.
- *
- * DESCRIPTION: Command line parsing. Get the next token on the command line
- *
- ******************************************************************************/
-
-char *
-AcpiDbGetNextToken (
- char *String,
- char **Next,
- ACPI_OBJECT_TYPE *ReturnType)
-{
- char *Start;
- UINT32 Depth;
- ACPI_OBJECT_TYPE Type = ACPI_TYPE_INTEGER;
-
-
- /* At end of buffer? */
-
- if (!String || !(*String))
- {
- return (NULL);
- }
-
- /* Remove any spaces at the beginning */
-
- if (*String == ' ')
- {
- while (*String && (*String == ' '))
- {
- String++;
- }
-
- if (!(*String))
- {
- return (NULL);
- }
- }
-
- switch (*String)
- {
- case '"':
-
- /* This is a quoted string, scan until closing quote */
-
- String++;
- Start = String;
- Type = ACPI_TYPE_STRING;
-
- /* Find end of string */
-
- while (*String && (*String != '"'))
- {
- String++;
- }
- break;
-
- case '(':
-
- /* This is the start of a buffer, scan until closing paren */
-
- String++;
- Start = String;
- Type = ACPI_TYPE_BUFFER;
-
- /* Find end of buffer */
-
- while (*String && (*String != ')'))
- {
- String++;
- }
- break;
-
- case '[':
-
- /* This is the start of a package, scan until closing bracket */
-
- String++;
- Depth = 1;
- Start = String;
- Type = ACPI_TYPE_PACKAGE;
-
- /* Find end of package (closing bracket) */
-
- while (*String)
- {
- /* Handle String package elements */
-
- if (*String == '"')
- {
- /* Find end of string */
-
- String++;
- while (*String && (*String != '"'))
- {
- String++;
- }
- if (!(*String))
- {
- break;
- }
- }
- else if (*String == '[')
- {
- Depth++; /* A nested package declaration */
- }
- else if (*String == ']')
- {
- Depth--;
- if (Depth == 0) /* Found final package closing bracket */
- {
- break;
- }
- }
-
- String++;
- }
- break;
-
- default:
-
- Start = String;
-
- /* Find end of token */
-
- while (*String && (*String != ' '))
- {
- String++;
- }
- break;
- }
-
- if (!(*String))
- {
- *Next = NULL;
- }
- else
- {
- *String = 0;
- *Next = String + 1;
- }
-
- *ReturnType = Type;
- return (Start);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetLine
- *
- * PARAMETERS: InputBuffer - Command line buffer
- *
- * RETURN: Count of arguments to the command
- *
- * DESCRIPTION: Get the next command line from the user. Gets entire line
- * up to the next newline
- *
- ******************************************************************************/
-
-static UINT32
-AcpiDbGetLine (
- char *InputBuffer)
-{
- UINT32 i;
- UINT32 Count;
- char *Next;
- char *This;
-
-
- ACPI_STRCPY (AcpiGbl_DbParsedBuf, InputBuffer);
-
- This = AcpiGbl_DbParsedBuf;
- for (i = 0; i < ACPI_DEBUGGER_MAX_ARGS; i++)
- {
- AcpiGbl_DbArgs[i] = AcpiDbGetNextToken (This, &Next,
- &AcpiGbl_DbArgTypes[i]);
- if (!AcpiGbl_DbArgs[i])
- {
- break;
- }
-
- This = Next;
- }
-
- /* Uppercase the actual command */
-
- if (AcpiGbl_DbArgs[0])
- {
- AcpiUtStrupr (AcpiGbl_DbArgs[0]);
- }
-
- Count = i;
- if (Count)
- {
- Count--; /* Number of args only */
- }
-
- return (Count);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbMatchCommand
- *
- * PARAMETERS: UserCommand - User command line
- *
- * RETURN: Index into command array, -1 if not found
- *
- * DESCRIPTION: Search command array for a command match
- *
- ******************************************************************************/
-
-static UINT32
-AcpiDbMatchCommand (
- char *UserCommand)
-{
- UINT32 i;
-
-
- if (!UserCommand || UserCommand[0] == 0)
- {
- return (CMD_NULL);
- }
-
- for (i = CMD_FIRST_VALID; AcpiGbl_DbCommands[i].Name; i++)
- {
- if (ACPI_STRSTR (AcpiGbl_DbCommands[i].Name, UserCommand) ==
- AcpiGbl_DbCommands[i].Name)
- {
- return (i);
- }
- }
-
- /* Command not recognized */
-
- return (CMD_NOT_FOUND);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCommandDispatch
- *
- * PARAMETERS: InputBuffer - Command line buffer
- * WalkState - Current walk
- * Op - Current (executing) parse op
- *
- * RETURN: Status
- *
- * DESCRIPTION: Command dispatcher.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbCommandDispatch (
- char *InputBuffer,
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op)
-{
- UINT32 Temp;
- UINT32 CommandIndex;
- UINT32 ParamCount;
- char *CommandLine;
- ACPI_STATUS Status = AE_CTRL_TRUE;
-
-
- /* If AcpiTerminate has been called, terminate this thread */
-
- if (AcpiGbl_DbTerminateThreads)
- {
- return (AE_CTRL_TERMINATE);
- }
-
- ParamCount = AcpiDbGetLine (InputBuffer);
- CommandIndex = AcpiDbMatchCommand (AcpiGbl_DbArgs[0]);
- Temp = 0;
-
- /* Verify that we have the minimum number of params */
-
- if (ParamCount < AcpiGbl_DbCommands[CommandIndex].MinArgs)
- {
- AcpiOsPrintf ("%u parameters entered, [%s] requires %u parameters\n",
- ParamCount, AcpiGbl_DbCommands[CommandIndex].Name,
- AcpiGbl_DbCommands[CommandIndex].MinArgs);
-
- return (AE_CTRL_TRUE);
- }
-
- /* Decode and dispatch the command */
-
- switch (CommandIndex)
- {
- case CMD_NULL:
- if (Op)
- {
- return (AE_OK);
- }
- break;
-
- case CMD_ALLOCATIONS:
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- AcpiUtDumpAllocations ((UINT32) -1, NULL);
-#endif
- break;
-
- case CMD_ARGS:
- case CMD_ARGUMENTS:
- AcpiDbDisplayArguments ();
- break;
-
- case CMD_BATCH:
- AcpiDbBatchExecute (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_BREAKPOINT:
- AcpiDbSetMethodBreakpoint (AcpiGbl_DbArgs[1], WalkState, Op);
- break;
-
- case CMD_BUSINFO:
- AcpiDbGetBusInfo ();
- break;
-
- case CMD_CALL:
- AcpiDbSetMethodCallBreakpoint (Op);
- Status = AE_OK;
- break;
-
- case CMD_CLOSE:
- AcpiDbCloseDebugFile ();
- break;
-
- case CMD_DEBUG:
- AcpiDbExecute (AcpiGbl_DbArgs[1],
- &AcpiGbl_DbArgs[2], &AcpiGbl_DbArgTypes[2], EX_SINGLE_STEP);
- break;
-
- case CMD_DISASSEMBLE:
- (void) AcpiDbDisassembleMethod (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_DUMP:
- AcpiDbDecodeAndDisplayObject (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_ENABLEACPI:
- Status = AcpiEnable();
- if (ACPI_FAILURE(Status))
- {
- AcpiOsPrintf("AcpiEnable failed (Status=%X)\n", Status);
- return (Status);
- }
- break;
-
- case CMD_EVENT:
- AcpiOsPrintf ("Event command not implemented\n");
- break;
-
- case CMD_EXECUTE:
- AcpiDbExecute (AcpiGbl_DbArgs[1],
- &AcpiGbl_DbArgs[2], &AcpiGbl_DbArgTypes[2], EX_NO_SINGLE_STEP);
- break;
-
- case CMD_FIND:
- Status = AcpiDbFindNameInNamespace (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_GO:
- AcpiGbl_CmSingleStep = FALSE;
- return (AE_OK);
-
- case CMD_GPE:
- AcpiDbGenerateGpe (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_GPES:
- AcpiDbDisplayGpes ();
- break;
-
- case CMD_HANDLERS:
- AcpiDbDisplayHandlers ();
- break;
-
- case CMD_HELP:
- case CMD_HELP2:
- AcpiDbDisplayHelp ();
- break;
-
- case CMD_HISTORY:
- AcpiDbDisplayHistory ();
- break;
-
- case CMD_HISTORY_EXE:
- CommandLine = AcpiDbGetFromHistory (AcpiGbl_DbArgs[1]);
- if (!CommandLine)
- {
- return (AE_CTRL_TRUE);
- }
-
- Status = AcpiDbCommandDispatch (CommandLine, WalkState, Op);
- return (Status);
-
- case CMD_HISTORY_LAST:
- CommandLine = AcpiDbGetFromHistory (NULL);
- if (!CommandLine)
- {
- return (AE_CTRL_TRUE);
- }
-
- Status = AcpiDbCommandDispatch (CommandLine, WalkState, Op);
- return (Status);
-
- case CMD_INFORMATION:
- AcpiDbDisplayMethodInfo (Op);
- break;
-
- case CMD_INTEGRITY:
- AcpiDbCheckIntegrity ();
- break;
-
- case CMD_INTO:
- if (Op)
- {
- AcpiGbl_CmSingleStep = TRUE;
- return (AE_OK);
- }
- break;
-
- case CMD_LEVEL:
- if (ParamCount == 0)
- {
- AcpiOsPrintf ("Current debug level for file output is: %8.8lX\n",
- AcpiGbl_DbDebugLevel);
- AcpiOsPrintf ("Current debug level for console output is: %8.8lX\n",
- AcpiGbl_DbConsoleDebugLevel);
- }
- else if (ParamCount == 2)
- {
- Temp = AcpiGbl_DbConsoleDebugLevel;
- AcpiGbl_DbConsoleDebugLevel = ACPI_STRTOUL (AcpiGbl_DbArgs[1],
- NULL, 16);
- AcpiOsPrintf (
- "Debug Level for console output was %8.8lX, now %8.8lX\n",
- Temp, AcpiGbl_DbConsoleDebugLevel);
- }
- else
- {
- Temp = AcpiGbl_DbDebugLevel;
- AcpiGbl_DbDebugLevel = ACPI_STRTOUL (AcpiGbl_DbArgs[1], NULL, 16);
- AcpiOsPrintf (
- "Debug Level for file output was %8.8lX, now %8.8lX\n",
- Temp, AcpiGbl_DbDebugLevel);
- }
- break;
-
- case CMD_LIST:
- AcpiDbDisassembleAml (AcpiGbl_DbArgs[1], Op);
- break;
-
- case CMD_LOAD:
- Status = AcpiDbGetTableFromFile (AcpiGbl_DbArgs[1], NULL);
- break;
-
- case CMD_LOCKS:
- AcpiDbDisplayLocks ();
- break;
-
- case CMD_LOCALS:
- AcpiDbDisplayLocals ();
- break;
-
- case CMD_METHODS:
- Status = AcpiDbDisplayObjects ("METHOD", AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_NAMESPACE:
- AcpiDbDumpNamespace (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_NOTIFY:
- Temp = ACPI_STRTOUL (AcpiGbl_DbArgs[2], NULL, 0);
- AcpiDbSendNotify (AcpiGbl_DbArgs[1], Temp);
- break;
-
- case CMD_OBJECT:
- AcpiUtStrupr (AcpiGbl_DbArgs[1]);
- Status = AcpiDbDisplayObjects (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_OPEN:
- AcpiDbOpenDebugFile (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_OSI:
- AcpiDbDisplayInterfaces (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_OWNER:
- AcpiDbDumpNamespaceByOwner (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_PREDEFINED:
- AcpiDbCheckPredefinedNames ();
- break;
-
- case CMD_PREFIX:
- AcpiDbSetScope (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_REFERENCES:
- AcpiDbFindReferences (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_RESOURCES:
- AcpiDbDisplayResources (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_RESULTS:
- AcpiDbDisplayResults ();
- break;
-
- case CMD_SET:
- AcpiDbSetMethodData (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2],
- AcpiGbl_DbArgs[3]);
- break;
-
- case CMD_SLEEP:
- Status = AcpiDbSleep (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_STATS:
- Status = AcpiDbDisplayStatistics (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_STOP:
- return (AE_NOT_IMPLEMENTED);
-
- case CMD_TABLES:
- AcpiDbDisplayTableInfo (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_TERMINATE:
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
- AcpiUtSubsystemShutdown ();
-
- /*
- * TBD: [Restructure] Need some way to re-initialize without
- * re-creating the semaphores!
- */
-
- /* AcpiInitialize (NULL); */
- break;
-
- case CMD_THREADS:
- AcpiDbCreateExecutionThreads (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2],
- AcpiGbl_DbArgs[3]);
- break;
-
- case CMD_TRACE:
- (void) AcpiDebugTrace (AcpiGbl_DbArgs[1],0,0,1);
- break;
-
- case CMD_TREE:
- AcpiDbDisplayCallingTree ();
- break;
-
- case CMD_TYPE:
- AcpiDbDisplayObjectType (AcpiGbl_DbArgs[1]);
- break;
-
- case CMD_UNLOAD:
- AcpiDbUnloadAcpiTable (AcpiGbl_DbArgs[1], AcpiGbl_DbArgs[2]);
- break;
-
- case CMD_EXIT:
- case CMD_QUIT:
- if (Op)
- {
- AcpiOsPrintf ("Method execution terminated\n");
- return (AE_CTRL_TERMINATE);
- }
-
- if (!AcpiGbl_DbOutputToFile)
- {
- AcpiDbgLevel = ACPI_DEBUG_DEFAULT;
- }
-
- AcpiDbCloseDebugFile ();
- AcpiGbl_DbTerminateThreads = TRUE;
- return (AE_CTRL_TERMINATE);
-
- case CMD_NOT_FOUND:
- default:
- AcpiOsPrintf ("Unknown Command\n");
- return (AE_CTRL_TRUE);
- }
-
- if (ACPI_SUCCESS (Status))
- {
- Status = AE_CTRL_TRUE;
- }
-
- /* Add all commands that come here to the history buffer */
-
- AcpiDbAddToHistory (InputBuffer);
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbExecuteThread
- *
- * PARAMETERS: Context - Not used
- *
- * RETURN: None
- *
- * DESCRIPTION: Debugger execute thread. Waits for a command line, then
- * simply dispatches it.
- *
- ******************************************************************************/
-
-void ACPI_SYSTEM_XFACE
-AcpiDbExecuteThread (
- void *Context)
-{
- ACPI_STATUS Status = AE_OK;
- ACPI_STATUS MStatus;
-
-
- while (Status != AE_CTRL_TERMINATE)
- {
- AcpiGbl_MethodExecuting = FALSE;
- AcpiGbl_StepToNextCall = FALSE;
-
- MStatus = AcpiUtAcquireMutex (ACPI_MTX_DEBUG_CMD_READY);
- if (ACPI_FAILURE (MStatus))
- {
- return;
- }
-
- Status = AcpiDbCommandDispatch (AcpiGbl_DbLineBuf, NULL, NULL);
-
- MStatus = AcpiUtReleaseMutex (ACPI_MTX_DEBUG_CMD_COMPLETE);
- if (ACPI_FAILURE (MStatus))
- {
- return;
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSingleThread
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Debugger execute thread. Waits for a command line, then
- * simply dispatches it.
- *
- ******************************************************************************/
-
-static void
-AcpiDbSingleThread (
- void)
-{
-
- AcpiGbl_MethodExecuting = FALSE;
- AcpiGbl_StepToNextCall = FALSE;
-
- (void) AcpiDbCommandDispatch (AcpiGbl_DbLineBuf, NULL, NULL);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbUserCommands
- *
- * PARAMETERS: Prompt - User prompt (depends on mode)
- * Op - Current executing parse op
- *
- * RETURN: None
- *
- * DESCRIPTION: Command line execution for the AML debugger. Commands are
- * matched and dispatched here.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbUserCommands (
- char Prompt,
- ACPI_PARSE_OBJECT *Op)
-{
- ACPI_STATUS Status = AE_OK;
-
-
- /* TBD: [Restructure] Need a separate command line buffer for step mode */
-
- while (!AcpiGbl_DbTerminateThreads)
- {
- /* Force output to console until a command is entered */
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-
- /* Different prompt if method is executing */
-
- if (!AcpiGbl_MethodExecuting)
- {
- AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- }
- else
- {
- AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
-
- /* Get the user input line */
-
- Status = AcpiOsGetLine (AcpiGbl_DbLineBuf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "While parsing command line"));
- return (Status);
- }
-
- /* Check for single or multithreaded debug */
-
- if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED)
- {
- /*
- * Signal the debug thread that we have a command to execute,
- * and wait for the command to complete.
- */
- Status = AcpiUtReleaseMutex (ACPI_MTX_DEBUG_CMD_READY);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_DEBUG_CMD_COMPLETE);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- else
- {
- /* Just call to the command line interpreter */
-
- AcpiDbSingleThread ();
- }
- }
-
- /*
- * Only this thread (the original thread) should actually terminate the
- * subsystem, because all the semaphores are deleted during termination
- */
- Status = AcpiTerminate ();
- return (Status);
-}
-
-#endif /* ACPI_DEBUGGER */
-
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbmethod.c b/usr/src/uts/intel/io/acpica/debugger/dbmethod.c
deleted file mode 100644
index f0b88ebdb3..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbmethod.c
+++ /dev/null
@@ -1,525 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbmethod - Debug commands for control methods
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdispat.h"
-#include "acnamesp.h"
-#include "acdebug.h"
-#include "acdisasm.h"
-#include "acparser.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbmethod")
-
-
-/* Local prototypes */
-
-static ACPI_STATUS
-AcpiDbWalkForExecute (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSetMethodBreakpoint
- *
- * PARAMETERS: Location - AML offset of breakpoint
- * WalkState - Current walk info
- * Op - Current Op (from parse walk)
- *
- * RETURN: None
- *
- * DESCRIPTION: Set a breakpoint in a control method at the specified
- * AML offset
- *
- ******************************************************************************/
-
-void
-AcpiDbSetMethodBreakpoint (
- char *Location,
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op)
-{
- UINT32 Address;
-
-
- if (!Op)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- /* Get and verify the breakpoint address */
-
- Address = ACPI_STRTOUL (Location, NULL, 16);
- if (Address <= Op->Common.AmlOffset)
- {
- AcpiOsPrintf ("Breakpoint %X is beyond current address %X\n",
- Address, Op->Common.AmlOffset);
- }
-
- /* Save breakpoint in current walk */
-
- WalkState->UserBreakpoint = Address;
- AcpiOsPrintf ("Breakpoint set at AML offset %X\n", Address);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSetMethodCallBreakpoint
- *
- * PARAMETERS: Op - Current Op (from parse walk)
- *
- * RETURN: None
- *
- * DESCRIPTION: Set a breakpoint in a control method at the specified
- * AML offset
- *
- ******************************************************************************/
-
-void
-AcpiDbSetMethodCallBreakpoint (
- ACPI_PARSE_OBJECT *Op)
-{
-
-
- if (!Op)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- AcpiGbl_StepToNextCall = TRUE;
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSetMethodData
- *
- * PARAMETERS: TypeArg - L for local, A for argument
- * IndexArg - which one
- * ValueArg - Value to set.
- *
- * RETURN: None
- *
- * DESCRIPTION: Set a local or argument for the running control method.
- * NOTE: only object supported is Number.
- *
- ******************************************************************************/
-
-void
-AcpiDbSetMethodData (
- char *TypeArg,
- char *IndexArg,
- char *ValueArg)
-{
- char Type;
- UINT32 Index;
- UINT32 Value;
- ACPI_WALK_STATE *WalkState;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *Node;
-
-
- /* Validate TypeArg */
-
- AcpiUtStrupr (TypeArg);
- Type = TypeArg[0];
- if ((Type != 'L') &&
- (Type != 'A') &&
- (Type != 'N'))
- {
- AcpiOsPrintf ("Invalid SET operand: %s\n", TypeArg);
- return;
- }
-
- Value = ACPI_STRTOUL (ValueArg, NULL, 16);
-
- if (Type == 'N')
- {
- Node = AcpiDbConvertToNode (IndexArg);
- if (Node->Type != ACPI_TYPE_INTEGER)
- {
- AcpiOsPrintf ("Can only set Integer nodes\n");
- return;
- }
- ObjDesc = Node->Object;
- ObjDesc->Integer.Value = Value;
- return;
- }
-
- /* Get the index and value */
-
- Index = ACPI_STRTOUL (IndexArg, NULL, 16);
-
- WalkState = AcpiDsGetCurrentWalkState (AcpiGbl_CurrentWalkList);
- if (!WalkState)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- /* Create and initialize the new object */
-
- ObjDesc = AcpiUtCreateIntegerObject ((UINT64) Value);
- if (!ObjDesc)
- {
- AcpiOsPrintf ("Could not create an internal object\n");
- return;
- }
-
- /* Store the new object into the target */
-
- switch (Type)
- {
- case 'A':
-
- /* Set a method argument */
-
- if (Index > ACPI_METHOD_MAX_ARG)
- {
- AcpiOsPrintf ("Arg%u - Invalid argument name\n", Index);
- goto Cleanup;
- }
-
- Status = AcpiDsStoreObjectToLocal (ACPI_REFCLASS_ARG, Index, ObjDesc,
- WalkState);
- if (ACPI_FAILURE (Status))
- {
- goto Cleanup;
- }
-
- ObjDesc = WalkState->Arguments[Index].Object;
-
- AcpiOsPrintf ("Arg%u: ", Index);
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- break;
-
- case 'L':
-
- /* Set a method local */
-
- if (Index > ACPI_METHOD_MAX_LOCAL)
- {
- AcpiOsPrintf ("Local%u - Invalid local variable name\n", Index);
- goto Cleanup;
- }
-
- Status = AcpiDsStoreObjectToLocal (ACPI_REFCLASS_LOCAL, Index, ObjDesc,
- WalkState);
- if (ACPI_FAILURE (Status))
- {
- goto Cleanup;
- }
-
- ObjDesc = WalkState->LocalVariables[Index].Object;
-
- AcpiOsPrintf ("Local%u: ", Index);
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- break;
-
- default:
- break;
- }
-
-Cleanup:
- AcpiUtRemoveReference (ObjDesc);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisassembleAml
- *
- * PARAMETERS: Statements - Number of statements to disassemble
- * Op - Current Op (from parse walk)
- *
- * RETURN: None
- *
- * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number
- * of statements specified.
- *
- ******************************************************************************/
-
-void
-AcpiDbDisassembleAml (
- char *Statements,
- ACPI_PARSE_OBJECT *Op)
-{
- UINT32 NumStatements = 8;
-
-
- if (!Op)
- {
- AcpiOsPrintf ("There is no method currently executing\n");
- return;
- }
-
- if (Statements)
- {
- NumStatements = ACPI_STRTOUL (Statements, NULL, 0);
- }
-
- AcpiDmDisassemble (NULL, Op, NumStatements);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisassembleMethod
- *
- * PARAMETERS: Name - Name of control method
- *
- * RETURN: None
- *
- * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number
- * of statements specified.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbDisassembleMethod (
- char *Name)
-{
- ACPI_STATUS Status;
- ACPI_PARSE_OBJECT *Op;
- ACPI_WALK_STATE *WalkState;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Method;
-
-
- Method = AcpiDbConvertToNode (Name);
- if (!Method)
- {
- return (AE_BAD_PARAMETER);
- }
-
- ObjDesc = Method->Object;
-
- Op = AcpiPsCreateScopeOp ();
- if (!Op)
- {
- return (AE_NO_MEMORY);
- }
-
- /* Create and initialize a new walk state */
-
- WalkState = AcpiDsCreateWalkState (0, Op, NULL, NULL);
- if (!WalkState)
- {
- return (AE_NO_MEMORY);
- }
-
- Status = AcpiDsInitAmlWalk (WalkState, Op, NULL,
- ObjDesc->Method.AmlStart,
- ObjDesc->Method.AmlLength, NULL, ACPI_IMODE_LOAD_PASS1);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /* Parse the AML */
-
- WalkState->ParseFlags &= ~ACPI_PARSE_DELETE_TREE;
- WalkState->ParseFlags |= ACPI_PARSE_DISASSEMBLE;
- Status = AcpiPsParseAml (WalkState);
-
- AcpiDmDisassemble (NULL, Op, 0);
- AcpiPsDeleteParseTree (Op);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbWalkForExecute
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Batch execution module. Currently only executes predefined
- * ACPI names.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbWalkForExecute (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ACPI_EXECUTE_WALK *Info = (ACPI_EXECUTE_WALK *) Context;
- ACPI_BUFFER ReturnObj;
- ACPI_STATUS Status;
- char *Pathname;
- UINT32 i;
- ACPI_DEVICE_INFO *ObjInfo;
- ACPI_OBJECT_LIST ParamObjects;
- ACPI_OBJECT Params[ACPI_METHOD_NUM_ARGS];
- const ACPI_PREDEFINED_INFO *Predefined;
-
-
- Predefined = AcpiNsCheckForPredefinedName (Node);
- if (!Predefined)
- {
- return (AE_OK);
- }
-
- if (Node->Type == ACPI_TYPE_LOCAL_SCOPE)
- {
- return (AE_OK);
- }
-
- Pathname = AcpiNsGetExternalPathname (Node);
- if (!Pathname)
- {
- return (AE_OK);
- }
-
- /* Get the object info for number of method parameters */
-
- Status = AcpiGetObjectInfo (ObjHandle, &ObjInfo);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- ParamObjects.Pointer = NULL;
- ParamObjects.Count = 0;
-
- if (ObjInfo->Type == ACPI_TYPE_METHOD)
- {
- /* Setup default parameters */
-
- for (i = 0; i < ObjInfo->ParamCount; i++)
- {
- Params[i].Type = ACPI_TYPE_INTEGER;
- Params[i].Integer.Value = 1;
- }
-
- ParamObjects.Pointer = Params;
- ParamObjects.Count = ObjInfo->ParamCount;
- }
-
- ACPI_FREE (ObjInfo);
- ReturnObj.Pointer = NULL;
- ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
-
- /* Do the actual method execution */
-
- AcpiGbl_MethodExecuting = TRUE;
-
- Status = AcpiEvaluateObject (Node, NULL, &ParamObjects, &ReturnObj);
-
- AcpiOsPrintf ("%-32s returned %s\n", Pathname, AcpiFormatException (Status));
- AcpiGbl_MethodExecuting = FALSE;
- ACPI_FREE (Pathname);
-
- /* Ignore status from method execution */
-
- Status = AE_OK;
-
- /* Update count, check if we have executed enough methods */
-
- Info->Count++;
- if (Info->Count >= Info->MaxCount)
- {
- Status = AE_CTRL_TERMINATE;
- }
-
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbBatchExecute
- *
- * PARAMETERS: CountArg - Max number of methods to execute
- *
- * RETURN: None
- *
- * DESCRIPTION: Namespace batch execution. Execute predefined names in the
- * namespace, up to the max count, if specified.
- *
- ******************************************************************************/
-
-void
-AcpiDbBatchExecute (
- char *CountArg)
-{
- ACPI_EXECUTE_WALK Info;
-
-
- Info.Count = 0;
- Info.MaxCount = ACPI_UINT32_MAX;
-
- if (CountArg)
- {
- Info.MaxCount = ACPI_STRTOUL (CountArg, NULL, 0);
- }
-
-
- /* Search all nodes in namespace */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbWalkForExecute, NULL, (void *) &Info, NULL);
-
- AcpiOsPrintf ("Executed %u predefined names in the namespace\n", Info.Count);
-}
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbnames.c b/usr/src/uts/intel/io/acpica/debugger/dbnames.c
deleted file mode 100644
index e4e4280f29..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbnames.c
+++ /dev/null
@@ -1,934 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbnames - Debugger commands for the acpi namespace
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acnamesp.h"
-#include "acdebug.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbnames")
-
-
-/* Local prototypes */
-
-static ACPI_STATUS
-AcpiDbWalkAndMatchName (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbWalkForPredefinedNames (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbWalkForSpecificObjects (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbIntegrityWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbWalkForReferences (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-static ACPI_STATUS
-AcpiDbBusWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-/*
- * Arguments for the Objects command
- * These object types map directly to the ACPI_TYPES
- */
-static ARGUMENT_INFO AcpiDbObjectTypes [] =
-{
- {"ANY"},
- {"INTEGERS"},
- {"STRINGS"},
- {"BUFFERS"},
- {"PACKAGES"},
- {"FIELDS"},
- {"DEVICES"},
- {"EVENTS"},
- {"METHODS"},
- {"MUTEXES"},
- {"REGIONS"},
- {"POWERRESOURCES"},
- {"PROCESSORS"},
- {"THERMALZONES"},
- {"BUFFERFIELDS"},
- {"DDBHANDLES"},
- {"DEBUG"},
- {"REGIONFIELDS"},
- {"BANKFIELDS"},
- {"INDEXFIELDS"},
- {"REFERENCES"},
- {"ALIAS"},
- {NULL} /* Must be null terminated */
-};
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSetScope
- *
- * PARAMETERS: Name - New scope path
- *
- * RETURN: Status
- *
- * DESCRIPTION: Set the "current scope" as maintained by this utility.
- * The scope is used as a prefix to ACPI paths.
- *
- ******************************************************************************/
-
-void
-AcpiDbSetScope (
- char *Name)
-{
- ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *Node;
-
-
- if (!Name || Name[0] == 0)
- {
- AcpiOsPrintf ("Current scope: %s\n", AcpiGbl_DbScopeBuf);
- return;
- }
-
- AcpiDbPrepNamestring (Name);
-
- if (Name[0] == '\\')
- {
- /* Validate new scope from the root */
-
- Status = AcpiNsGetNode (AcpiGbl_RootNode, Name, ACPI_NS_NO_UPSEARCH,
- &Node);
- if (ACPI_FAILURE (Status))
- {
- goto ErrorExit;
- }
-
- ACPI_STRCPY (AcpiGbl_DbScopeBuf, Name);
- ACPI_STRCAT (AcpiGbl_DbScopeBuf, "\\");
- }
- else
- {
- /* Validate new scope relative to old scope */
-
- Status = AcpiNsGetNode (AcpiGbl_DbScopeNode, Name, ACPI_NS_NO_UPSEARCH,
- &Node);
- if (ACPI_FAILURE (Status))
- {
- goto ErrorExit;
- }
-
- ACPI_STRCAT (AcpiGbl_DbScopeBuf, Name);
- ACPI_STRCAT (AcpiGbl_DbScopeBuf, "\\");
- }
-
- AcpiGbl_DbScopeNode = Node;
- AcpiOsPrintf ("New scope: %s\n", AcpiGbl_DbScopeBuf);
- return;
-
-ErrorExit:
-
- AcpiOsPrintf ("Could not attach scope: %s, %s\n",
- Name, AcpiFormatException (Status));
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDumpNamespace
- *
- * PARAMETERS: StartArg - Node to begin namespace dump
- * DepthArg - Maximum tree depth to be dumped
- *
- * RETURN: None
- *
- * DESCRIPTION: Dump entire namespace or a subtree. Each node is displayed
- * with type and other information.
- *
- ******************************************************************************/
-
-void
-AcpiDbDumpNamespace (
- char *StartArg,
- char *DepthArg)
-{
- ACPI_HANDLE SubtreeEntry = AcpiGbl_RootNode;
- UINT32 MaxDepth = ACPI_UINT32_MAX;
-
-
- /* No argument given, just start at the root and dump entire namespace */
-
- if (StartArg)
- {
- SubtreeEntry = AcpiDbConvertToNode (StartArg);
- if (!SubtreeEntry)
- {
- return;
- }
-
- /* Now we can check for the depth argument */
-
- if (DepthArg)
- {
- MaxDepth = ACPI_STRTOUL (DepthArg, NULL, 0);
- }
- }
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
- AcpiOsPrintf ("ACPI Namespace (from %4.4s (%p) subtree):\n",
- ((ACPI_NAMESPACE_NODE *) SubtreeEntry)->Name.Ascii, SubtreeEntry);
-
- /* Display the subtree */
-
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
- AcpiNsDumpObjects (ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, MaxDepth,
- ACPI_OWNER_ID_MAX, SubtreeEntry);
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDumpNamespaceByOwner
- *
- * PARAMETERS: OwnerArg - Owner ID whose nodes will be displayed
- * DepthArg - Maximum tree depth to be dumped
- *
- * RETURN: None
- *
- * DESCRIPTION: Dump elements of the namespace that are owned by the OwnerId.
- *
- ******************************************************************************/
-
-void
-AcpiDbDumpNamespaceByOwner (
- char *OwnerArg,
- char *DepthArg)
-{
- ACPI_HANDLE SubtreeEntry = AcpiGbl_RootNode;
- UINT32 MaxDepth = ACPI_UINT32_MAX;
- ACPI_OWNER_ID OwnerId;
-
-
- OwnerId = (ACPI_OWNER_ID) ACPI_STRTOUL (OwnerArg, NULL, 0);
-
- /* Now we can check for the depth argument */
-
- if (DepthArg)
- {
- MaxDepth = ACPI_STRTOUL (DepthArg, NULL, 0);
- }
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
- AcpiOsPrintf ("ACPI Namespace by owner %X:\n", OwnerId);
-
- /* Display the subtree */
-
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
- AcpiNsDumpObjects (ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, MaxDepth, OwnerId,
- SubtreeEntry);
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbWalkAndMatchName
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Find a particular name/names within the namespace. Wildcards
- * are supported -- '?' matches any character.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbWalkAndMatchName (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_STATUS Status;
- char *RequestedName = (char *) Context;
- UINT32 i;
- ACPI_BUFFER Buffer;
- ACPI_WALK_INFO Info;
-
-
- /* Check for a name match */
-
- for (i = 0; i < 4; i++)
- {
- /* Wildcard support */
-
- if ((RequestedName[i] != '?') &&
- (RequestedName[i] != ((ACPI_NAMESPACE_NODE *) ObjHandle)->Name.Ascii[i]))
- {
- /* No match, just exit */
-
- return (AE_OK);
- }
- }
-
- /* Get the full pathname to this object */
-
- Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (ObjHandle, &Buffer);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could Not get pathname for object %p\n", ObjHandle);
- }
- else
- {
- Info.OwnerId = ACPI_OWNER_ID_MAX;
- Info.DebugLevel = ACPI_UINT32_MAX;
- Info.DisplayType = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT;
-
- AcpiOsPrintf ("%32s", (char *) Buffer.Pointer);
- (void) AcpiNsDumpOneObject (ObjHandle, NestingLevel, &Info, NULL);
- ACPI_FREE (Buffer.Pointer);
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbFindNameInNamespace
- *
- * PARAMETERS: NameArg - The 4-character ACPI name to find.
- * wildcards are supported.
- *
- * RETURN: None
- *
- * DESCRIPTION: Search the namespace for a given name (with wildcards)
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbFindNameInNamespace (
- char *NameArg)
-{
- char AcpiName[5] = "____";
- char *AcpiNamePtr = AcpiName;
-
-
- if (ACPI_STRLEN (NameArg) > 4)
- {
- AcpiOsPrintf ("Name must be no longer than 4 characters\n");
- return (AE_OK);
- }
-
- /* Pad out name with underscores as necessary to create a 4-char name */
-
- AcpiUtStrupr (NameArg);
- while (*NameArg)
- {
- *AcpiNamePtr = *NameArg;
- AcpiNamePtr++;
- NameArg++;
- }
-
- /* Walk the namespace from the root */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbWalkAndMatchName, NULL, AcpiName, NULL);
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbWalkForPredefinedNames
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Detect and display predefined ACPI names (names that start with
- * an underscore)
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbWalkForPredefinedNames (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- UINT32 *Count = (UINT32 *) Context;
- const ACPI_PREDEFINED_INFO *Predefined;
- const ACPI_PREDEFINED_INFO *Package = NULL;
- char *Pathname;
-
-
- Predefined = AcpiNsCheckForPredefinedName (Node);
- if (!Predefined)
- {
- return (AE_OK);
- }
-
- Pathname = AcpiNsGetExternalPathname (Node);
- if (!Pathname)
- {
- return (AE_OK);
- }
-
- /* If method returns a package, the info is in the next table entry */
-
- if (Predefined->Info.ExpectedBtypes & ACPI_BTYPE_PACKAGE)
- {
- Package = Predefined + 1;
- }
-
- AcpiOsPrintf ("%-32s arg %X ret %2.2X", Pathname,
- Predefined->Info.ParamCount, Predefined->Info.ExpectedBtypes);
-
- if (Package)
- {
- AcpiOsPrintf (" PkgType %2.2X ObjType %2.2X Count %2.2X",
- Package->RetInfo.Type, Package->RetInfo.ObjectType1,
- Package->RetInfo.Count1);
- }
-
- AcpiOsPrintf("\n");
-
- AcpiNsCheckParameterCount (Pathname, Node, ACPI_UINT32_MAX, Predefined);
- ACPI_FREE (Pathname);
- (*Count)++;
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCheckPredefinedNames
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Validate all predefined names in the namespace
- *
- ******************************************************************************/
-
-void
-AcpiDbCheckPredefinedNames (
- void)
-{
- UINT32 Count = 0;
-
-
- /* Search all nodes in namespace */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbWalkForPredefinedNames, NULL, (void *) &Count, NULL);
-
- AcpiOsPrintf ("Found %u predefined names in the namespace\n", Count);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbWalkForSpecificObjects
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Display short info about objects in the namespace
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbWalkForSpecificObjects (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_WALK_INFO *Info = (ACPI_WALK_INFO *) Context;
- ACPI_BUFFER Buffer;
- ACPI_STATUS Status;
-
-
- Info->Count++;
-
- /* Get and display the full pathname to this object */
-
- Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (ObjHandle, &Buffer);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could Not get pathname for object %p\n", ObjHandle);
- return (AE_OK);
- }
-
- AcpiOsPrintf ("%32s", (char *) Buffer.Pointer);
- ACPI_FREE (Buffer.Pointer);
-
- /* Dump short info about the object */
-
- (void) AcpiNsDumpOneObject (ObjHandle, NestingLevel, Info, NULL);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayObjects
- *
- * PARAMETERS: ObjTypeArg - Type of object to display
- * DisplayCountArg - Max depth to display
- *
- * RETURN: None
- *
- * DESCRIPTION: Display objects in the namespace of the requested type
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbDisplayObjects (
- char *ObjTypeArg,
- char *DisplayCountArg)
-{
- ACPI_WALK_INFO Info;
- ACPI_OBJECT_TYPE Type;
-
-
- /* Get the object type */
-
- Type = AcpiDbMatchArgument (ObjTypeArg, AcpiDbObjectTypes);
- if (Type == ACPI_TYPE_NOT_FOUND)
- {
- AcpiOsPrintf ("Invalid or unsupported argument\n");
- return (AE_OK);
- }
-
- AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
- AcpiOsPrintf (
- "Objects of type [%s] defined in the current ACPI Namespace:\n",
- AcpiUtGetTypeName (Type));
-
- AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
-
- Info.Count = 0;
- Info.OwnerId = ACPI_OWNER_ID_MAX;
- Info.DebugLevel = ACPI_UINT32_MAX;
- Info.DisplayType = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT;
-
- /* Walk the namespace from the root */
-
- (void) AcpiWalkNamespace (Type, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbWalkForSpecificObjects, NULL, (void *) &Info, NULL);
-
- AcpiOsPrintf (
- "\nFound %u objects of type [%s] in the current ACPI Namespace\n",
- Info.Count, AcpiUtGetTypeName (Type));
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbIntegrityWalk
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Examine one NS node for valid values.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbIntegrityWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_INTEGRITY_INFO *Info = (ACPI_INTEGRITY_INFO *) Context;
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ACPI_OPERAND_OBJECT *Object;
- BOOLEAN Alias = TRUE;
-
-
- Info->Nodes++;
-
- /* Verify the NS node, and dereference aliases */
-
- while (Alias)
- {
- if (ACPI_GET_DESCRIPTOR_TYPE (Node) != ACPI_DESC_TYPE_NAMED)
- {
- AcpiOsPrintf ("Invalid Descriptor Type for Node %p [%s] - is %2.2X should be %2.2X\n",
- Node, AcpiUtGetDescriptorName (Node), ACPI_GET_DESCRIPTOR_TYPE (Node),
- ACPI_DESC_TYPE_NAMED);
- return (AE_OK);
- }
-
- if ((Node->Type == ACPI_TYPE_LOCAL_ALIAS) ||
- (Node->Type == ACPI_TYPE_LOCAL_METHOD_ALIAS))
- {
- Node = (ACPI_NAMESPACE_NODE *) Node->Object;
- }
- else
- {
- Alias = FALSE;
- }
- }
-
- if (Node->Type > ACPI_TYPE_LOCAL_MAX)
- {
- AcpiOsPrintf ("Invalid Object Type for Node %p, Type = %X\n",
- Node, Node->Type);
- return (AE_OK);
- }
-
- if (!AcpiUtValidAcpiName (Node->Name.Integer))
- {
- AcpiOsPrintf ("Invalid AcpiName for Node %p\n", Node);
- return (AE_OK);
- }
-
- Object = AcpiNsGetAttachedObject (Node);
- if (Object)
- {
- Info->Objects++;
- if (ACPI_GET_DESCRIPTOR_TYPE (Object) != ACPI_DESC_TYPE_OPERAND)
- {
- AcpiOsPrintf ("Invalid Descriptor Type for Object %p [%s]\n",
- Object, AcpiUtGetDescriptorName (Object));
- }
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCheckIntegrity
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Check entire namespace for data structure integrity
- *
- ******************************************************************************/
-
-void
-AcpiDbCheckIntegrity (
- void)
-{
- ACPI_INTEGRITY_INFO Info = {0,0};
-
- /* Search all nodes in namespace */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbIntegrityWalk, NULL, (void *) &Info, NULL);
-
- AcpiOsPrintf ("Verified %u namespace nodes with %u Objects\n",
- Info.Nodes, Info.Objects);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbWalkForReferences
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Check if this namespace object refers to the target object
- * that is passed in as the context value.
- *
- * Note: Currently doesn't check subobjects within the Node's object
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbWalkForReferences (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_OPERAND_OBJECT *ObjDesc = (ACPI_OPERAND_OBJECT *) Context;
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
-
-
- /* Check for match against the namespace node itself */
-
- if (Node == (void *) ObjDesc)
- {
- AcpiOsPrintf ("Object is a Node [%4.4s]\n",
- AcpiUtGetNodeName (Node));
- }
-
- /* Check for match against the object attached to the node */
-
- if (AcpiNsGetAttachedObject (Node) == ObjDesc)
- {
- AcpiOsPrintf ("Reference at Node->Object %p [%4.4s]\n",
- Node, AcpiUtGetNodeName (Node));
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbFindReferences
- *
- * PARAMETERS: ObjectArg - String with hex value of the object
- *
- * RETURN: None
- *
- * DESCRIPTION: Search namespace for all references to the input object
- *
- ******************************************************************************/
-
-void
-AcpiDbFindReferences (
- char *ObjectArg)
-{
- ACPI_OPERAND_OBJECT *ObjDesc;
-
-
- /* Convert string to object pointer */
-
- ObjDesc = ACPI_TO_POINTER (ACPI_STRTOUL (ObjectArg, NULL, 16));
-
- /* Search all nodes in namespace */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbWalkForReferences, NULL, (void *) ObjDesc, NULL);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbBusWalk
- *
- * PARAMETERS: Callback from WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Display info about device objects that have a corresponding
- * _PRT method.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbBusWalk (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ACPI_STATUS Status;
- ACPI_BUFFER Buffer;
- ACPI_NAMESPACE_NODE *TempNode;
- ACPI_DEVICE_INFO *Info;
- UINT32 i;
-
-
- if ((Node->Type != ACPI_TYPE_DEVICE) &&
- (Node->Type != ACPI_TYPE_PROCESSOR))
- {
- return (AE_OK);
- }
-
- /* Exit if there is no _PRT under this device */
-
- Status = AcpiGetHandle (Node, METHOD_NAME__PRT,
- ACPI_CAST_PTR (ACPI_HANDLE, &TempNode));
- if (ACPI_FAILURE (Status))
- {
- return (AE_OK);
- }
-
- /* Get the full path to this device object */
-
- Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (ObjHandle, &Buffer);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could Not get pathname for object %p\n", ObjHandle);
- return (AE_OK);
- }
-
- Status = AcpiGetObjectInfo (ObjHandle, &Info);
- if (ACPI_FAILURE (Status))
- {
- return (AE_OK);
- }
-
- /* Display the full path */
-
- AcpiOsPrintf ("%-32s Type %X", (char *) Buffer.Pointer, Node->Type);
- ACPI_FREE (Buffer.Pointer);
-
- if (Info->Flags & ACPI_PCI_ROOT_BRIDGE)
- {
- AcpiOsPrintf (" - Is PCI Root Bridge");
- }
- AcpiOsPrintf ("\n");
-
- /* _PRT info */
-
- AcpiOsPrintf ("_PRT: %p\n", TempNode);
-
- /* Dump _ADR, _HID, _UID, _CID */
-
- if (Info->Valid & ACPI_VALID_ADR)
- {
- AcpiOsPrintf ("_ADR: %8.8X%8.8X\n", ACPI_FORMAT_UINT64 (Info->Address));
- }
- else
- {
- AcpiOsPrintf ("_ADR: <Not Present>\n");
- }
-
- if (Info->Valid & ACPI_VALID_HID)
- {
- AcpiOsPrintf ("_HID: %s\n", Info->HardwareId.String);
- }
- else
- {
- AcpiOsPrintf ("_HID: <Not Present>\n");
- }
-
- if (Info->Valid & ACPI_VALID_UID)
- {
- AcpiOsPrintf ("_UID: %s\n", Info->UniqueId.String);
- }
- else
- {
- AcpiOsPrintf ("_UID: <Not Present>\n");
- }
-
- if (Info->Valid & ACPI_VALID_CID)
- {
- for (i = 0; i < Info->CompatibleIdList.Count; i++)
- {
- AcpiOsPrintf ("_CID: %s\n",
- Info->CompatibleIdList.Ids[i].String);
- }
- }
- else
- {
- AcpiOsPrintf ("_CID: <Not Present>\n");
- }
-
- ACPI_FREE (Info);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbGetBusInfo
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Display info about system busses.
- *
- ******************************************************************************/
-
-void
-AcpiDbGetBusInfo (
- void)
-{
- /* Search all nodes in namespace */
-
- (void) AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
- AcpiDbBusWalk, NULL, NULL, NULL);
-}
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbstats.c b/usr/src/uts/intel/io/acpica/debugger/dbstats.c
deleted file mode 100644
index bf17c63932..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbstats.c
+++ /dev/null
@@ -1,549 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbstats - Generation and display of ACPI table statistics
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acdebug.h"
-#include "acnamesp.h"
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbstats")
-
-/* Local prototypes */
-
-static void
-AcpiDbCountNamespaceObjects (
- void);
-
-static void
-AcpiDbEnumerateObject (
- ACPI_OPERAND_OBJECT *ObjDesc);
-
-static ACPI_STATUS
-AcpiDbClassifyOneObject (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue);
-
-#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE
-static void
-AcpiDbListInfo (
- ACPI_MEMORY_LIST *List);
-#endif
-
-
-/*
- * Statistics subcommands
- */
-static ARGUMENT_INFO AcpiDbStatTypes [] =
-{
- {"ALLOCATIONS"},
- {"OBJECTS"},
- {"MEMORY"},
- {"MISC"},
- {"TABLES"},
- {"SIZES"},
- {"STACK"},
- {NULL} /* Must be null terminated */
-};
-
-#define CMD_STAT_ALLOCATIONS 0
-#define CMD_STAT_OBJECTS 1
-#define CMD_STAT_MEMORY 2
-#define CMD_STAT_MISC 3
-#define CMD_STAT_TABLES 4
-#define CMD_STAT_SIZES 5
-#define CMD_STAT_STACK 6
-
-
-#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbListInfo
- *
- * PARAMETERS: List - Memory list/cache to be displayed
- *
- * RETURN: None
- *
- * DESCRIPTION: Display information about the input memory list or cache.
- *
- ******************************************************************************/
-
-static void
-AcpiDbListInfo (
- ACPI_MEMORY_LIST *List)
-{
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- UINT32 Outstanding;
-#endif
-
- AcpiOsPrintf ("\n%s\n", List->ListName);
-
- /* MaxDepth > 0 indicates a cache object */
-
- if (List->MaxDepth > 0)
- {
- AcpiOsPrintf (
- " Cache: [Depth MaxD Avail Size] %8.2X %8.2X %8.2X %8.2X\n",
- List->CurrentDepth,
- List->MaxDepth,
- List->MaxDepth - List->CurrentDepth,
- (List->CurrentDepth * List->ObjectSize));
- }
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- if (List->MaxDepth > 0)
- {
- AcpiOsPrintf (
- " Cache: [Requests Hits Misses ObjSize] %8.2X %8.2X %8.2X %8.2X\n",
- List->Requests,
- List->Hits,
- List->Requests - List->Hits,
- List->ObjectSize);
- }
-
- Outstanding = AcpiDbGetCacheInfo (List);
-
- if (List->ObjectSize)
- {
- AcpiOsPrintf (
- " Mem: [Alloc Free Max CurSize Outstanding] %8.2X %8.2X %8.2X %8.2X %8.2X\n",
- List->TotalAllocated,
- List->TotalFreed,
- List->MaxOccupied,
- Outstanding * List->ObjectSize,
- Outstanding);
- }
- else
- {
- AcpiOsPrintf (
- " Mem: [Alloc Free Max CurSize Outstanding Total] %8.2X %8.2X %8.2X %8.2X %8.2X %8.2X\n",
- List->TotalAllocated,
- List->TotalFreed,
- List->MaxOccupied,
- List->CurrentTotalSize,
- Outstanding,
- List->TotalSize);
- }
-#endif
-}
-#endif
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbEnumerateObject
- *
- * PARAMETERS: ObjDesc - Object to be counted
- *
- * RETURN: None
- *
- * DESCRIPTION: Add this object to the global counts, by object type.
- * Limited recursion handles subobjects and packages, and this
- * is probably acceptable within the AML debugger only.
- *
- ******************************************************************************/
-
-static void
-AcpiDbEnumerateObject (
- ACPI_OPERAND_OBJECT *ObjDesc)
-{
- UINT32 i;
-
-
- if (!ObjDesc)
- {
- return;
- }
-
- /* Enumerate this object first */
-
- AcpiGbl_NumObjects++;
-
- if (ObjDesc->Common.Type > ACPI_TYPE_NS_NODE_MAX)
- {
- AcpiGbl_ObjTypeCountMisc++;
- }
- else
- {
- AcpiGbl_ObjTypeCount [ObjDesc->Common.Type]++;
- }
-
- /* Count the sub-objects */
-
- switch (ObjDesc->Common.Type)
- {
- case ACPI_TYPE_PACKAGE:
-
- for (i = 0; i < ObjDesc->Package.Count; i++)
- {
- AcpiDbEnumerateObject (ObjDesc->Package.Elements[i]);
- }
- break;
-
- case ACPI_TYPE_DEVICE:
-
- AcpiDbEnumerateObject (ObjDesc->Device.SystemNotify);
- AcpiDbEnumerateObject (ObjDesc->Device.DeviceNotify);
- AcpiDbEnumerateObject (ObjDesc->Device.Handler);
- break;
-
- case ACPI_TYPE_BUFFER_FIELD:
-
- if (AcpiNsGetSecondaryObject (ObjDesc))
- {
- AcpiGbl_ObjTypeCount [ACPI_TYPE_BUFFER_FIELD]++;
- }
- break;
-
- case ACPI_TYPE_REGION:
-
- AcpiGbl_ObjTypeCount [ACPI_TYPE_LOCAL_REGION_FIELD ]++;
- AcpiDbEnumerateObject (ObjDesc->Region.Handler);
- break;
-
- case ACPI_TYPE_POWER:
-
- AcpiDbEnumerateObject (ObjDesc->PowerResource.SystemNotify);
- AcpiDbEnumerateObject (ObjDesc->PowerResource.DeviceNotify);
- break;
-
- case ACPI_TYPE_PROCESSOR:
-
- AcpiDbEnumerateObject (ObjDesc->Processor.SystemNotify);
- AcpiDbEnumerateObject (ObjDesc->Processor.DeviceNotify);
- AcpiDbEnumerateObject (ObjDesc->Processor.Handler);
- break;
-
- case ACPI_TYPE_THERMAL:
-
- AcpiDbEnumerateObject (ObjDesc->ThermalZone.SystemNotify);
- AcpiDbEnumerateObject (ObjDesc->ThermalZone.DeviceNotify);
- AcpiDbEnumerateObject (ObjDesc->ThermalZone.Handler);
- break;
-
- default:
- break;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbClassifyOneObject
- *
- * PARAMETERS: Callback for WalkNamespace
- *
- * RETURN: Status
- *
- * DESCRIPTION: Enumerate both the object descriptor (including subobjects) and
- * the parent namespace node.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbClassifyOneObject (
- ACPI_HANDLE ObjHandle,
- UINT32 NestingLevel,
- void *Context,
- void **ReturnValue)
-{
- ACPI_NAMESPACE_NODE *Node;
- ACPI_OPERAND_OBJECT *ObjDesc;
- UINT32 Type;
-
-
- AcpiGbl_NumNodes++;
-
- Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ObjDesc = AcpiNsGetAttachedObject (Node);
-
- AcpiDbEnumerateObject (ObjDesc);
-
- Type = Node->Type;
- if (Type > ACPI_TYPE_NS_NODE_MAX)
- {
- AcpiGbl_NodeTypeCountMisc++;
- }
- else
- {
- AcpiGbl_NodeTypeCount [Type]++;
- }
-
- return AE_OK;
-
-
-#ifdef ACPI_FUTURE_IMPLEMENTATION
-
- /* TBD: These need to be counted during the initial parsing phase */
-
- if (AcpiPsIsNamedOp (Op->Opcode))
- {
- NumNodes++;
- }
-
- if (IsMethod)
- {
- NumMethodElements++;
- }
-
- NumGrammarElements++;
- Op = AcpiPsGetDepthNext (Root, Op);
-
- SizeOfParseTree = (NumGrammarElements - NumMethodElements) *
- (UINT32) sizeof (ACPI_PARSE_OBJECT);
- SizeOfMethodTrees = NumMethodElements * (UINT32) sizeof (ACPI_PARSE_OBJECT);
- SizeOfNodeEntries = NumNodes * (UINT32) sizeof (ACPI_NAMESPACE_NODE);
- SizeOfAcpiObjects = NumNodes * (UINT32) sizeof (ACPI_OPERAND_OBJECT);
-#endif
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbCountNamespaceObjects
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Count and classify the entire namespace, including all
- * namespace nodes and attached objects.
- *
- ******************************************************************************/
-
-static void
-AcpiDbCountNamespaceObjects (
- void)
-{
- UINT32 i;
-
-
- AcpiGbl_NumNodes = 0;
- AcpiGbl_NumObjects = 0;
-
- AcpiGbl_ObjTypeCountMisc = 0;
- for (i = 0; i < (ACPI_TYPE_NS_NODE_MAX -1); i++)
- {
- AcpiGbl_ObjTypeCount [i] = 0;
- AcpiGbl_NodeTypeCount [i] = 0;
- }
-
- (void) AcpiNsWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, FALSE, AcpiDbClassifyOneObject, NULL, NULL, NULL);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDisplayStatistics
- *
- * PARAMETERS: TypeArg - Subcommand
- *
- * RETURN: Status
- *
- * DESCRIPTION: Display various statistics
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbDisplayStatistics (
- char *TypeArg)
-{
- UINT32 i;
- UINT32 Temp;
-
-
- if (!TypeArg)
- {
- AcpiOsPrintf ("The following subcommands are available:\n ALLOCATIONS, OBJECTS, MEMORY, MISC, SIZES, TABLES\n");
- return (AE_OK);
- }
-
- AcpiUtStrupr (TypeArg);
- Temp = AcpiDbMatchArgument (TypeArg, AcpiDbStatTypes);
- if (Temp == (UINT32) -1)
- {
- AcpiOsPrintf ("Invalid or unsupported argument\n");
- return (AE_OK);
- }
-
-
- switch (Temp)
- {
- case CMD_STAT_ALLOCATIONS:
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- AcpiUtDumpAllocationInfo ();
-#endif
- break;
-
- case CMD_STAT_TABLES:
-
- AcpiOsPrintf ("ACPI Table Information (not implemented):\n\n");
- break;
-
- case CMD_STAT_OBJECTS:
-
- AcpiDbCountNamespaceObjects ();
-
- AcpiOsPrintf ("\nObjects defined in the current namespace:\n\n");
-
- AcpiOsPrintf ("%16.16s %10.10s %10.10s\n",
- "ACPI_TYPE", "NODES", "OBJECTS");
-
- for (i = 0; i < ACPI_TYPE_NS_NODE_MAX; i++)
- {
- AcpiOsPrintf ("%16.16s % 10ld% 10ld\n", AcpiUtGetTypeName (i),
- AcpiGbl_NodeTypeCount [i], AcpiGbl_ObjTypeCount [i]);
- }
- AcpiOsPrintf ("%16.16s % 10ld% 10ld\n", "Misc/Unknown",
- AcpiGbl_NodeTypeCountMisc, AcpiGbl_ObjTypeCountMisc);
-
- AcpiOsPrintf ("%16.16s % 10ld% 10ld\n", "TOTALS:",
- AcpiGbl_NumNodes, AcpiGbl_NumObjects);
- break;
-
- case CMD_STAT_MEMORY:
-
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- AcpiOsPrintf ("\n----Object Statistics (all in hex)---------\n");
-
- AcpiDbListInfo (AcpiGbl_GlobalList);
- AcpiDbListInfo (AcpiGbl_NsNodeList);
-#endif
-
-#ifdef ACPI_USE_LOCAL_CACHE
- AcpiOsPrintf ("\n----Cache Statistics (all in hex)---------\n");
- AcpiDbListInfo (AcpiGbl_OperandCache);
- AcpiDbListInfo (AcpiGbl_PsNodeCache);
- AcpiDbListInfo (AcpiGbl_PsNodeExtCache);
- AcpiDbListInfo (AcpiGbl_StateCache);
-#endif
-
- break;
-
- case CMD_STAT_MISC:
-
- AcpiOsPrintf ("\nMiscellaneous Statistics:\n\n");
- AcpiOsPrintf ("Calls to AcpiPsFind:.. ........% 7ld\n",
- AcpiGbl_PsFindCount);
- AcpiOsPrintf ("Calls to AcpiNsLookup:..........% 7ld\n",
- AcpiGbl_NsLookupCount);
-
- AcpiOsPrintf ("\n");
-
- AcpiOsPrintf ("Mutex usage:\n\n");
- for (i = 0; i < ACPI_NUM_MUTEX; i++)
- {
- AcpiOsPrintf ("%-28s: % 7ld\n",
- AcpiUtGetMutexName (i), AcpiGbl_MutexInfo[i].UseCount);
- }
- break;
-
-
- case CMD_STAT_SIZES:
-
- AcpiOsPrintf ("\nInternal object sizes:\n\n");
-
- AcpiOsPrintf ("Common %3d\n", sizeof (ACPI_OBJECT_COMMON));
- AcpiOsPrintf ("Number %3d\n", sizeof (ACPI_OBJECT_INTEGER));
- AcpiOsPrintf ("String %3d\n", sizeof (ACPI_OBJECT_STRING));
- AcpiOsPrintf ("Buffer %3d\n", sizeof (ACPI_OBJECT_BUFFER));
- AcpiOsPrintf ("Package %3d\n", sizeof (ACPI_OBJECT_PACKAGE));
- AcpiOsPrintf ("BufferField %3d\n", sizeof (ACPI_OBJECT_BUFFER_FIELD));
- AcpiOsPrintf ("Device %3d\n", sizeof (ACPI_OBJECT_DEVICE));
- AcpiOsPrintf ("Event %3d\n", sizeof (ACPI_OBJECT_EVENT));
- AcpiOsPrintf ("Method %3d\n", sizeof (ACPI_OBJECT_METHOD));
- AcpiOsPrintf ("Mutex %3d\n", sizeof (ACPI_OBJECT_MUTEX));
- AcpiOsPrintf ("Region %3d\n", sizeof (ACPI_OBJECT_REGION));
- AcpiOsPrintf ("PowerResource %3d\n", sizeof (ACPI_OBJECT_POWER_RESOURCE));
- AcpiOsPrintf ("Processor %3d\n", sizeof (ACPI_OBJECT_PROCESSOR));
- AcpiOsPrintf ("ThermalZone %3d\n", sizeof (ACPI_OBJECT_THERMAL_ZONE));
- AcpiOsPrintf ("RegionField %3d\n", sizeof (ACPI_OBJECT_REGION_FIELD));
- AcpiOsPrintf ("BankField %3d\n", sizeof (ACPI_OBJECT_BANK_FIELD));
- AcpiOsPrintf ("IndexField %3d\n", sizeof (ACPI_OBJECT_INDEX_FIELD));
- AcpiOsPrintf ("Reference %3d\n", sizeof (ACPI_OBJECT_REFERENCE));
- AcpiOsPrintf ("Notify %3d\n", sizeof (ACPI_OBJECT_NOTIFY_HANDLER));
- AcpiOsPrintf ("AddressSpace %3d\n", sizeof (ACPI_OBJECT_ADDR_HANDLER));
- AcpiOsPrintf ("Extra %3d\n", sizeof (ACPI_OBJECT_EXTRA));
- AcpiOsPrintf ("Data %3d\n", sizeof (ACPI_OBJECT_DATA));
-
- AcpiOsPrintf ("\n");
-
- AcpiOsPrintf ("ParseObject %3d\n", sizeof (ACPI_PARSE_OBJ_COMMON));
- AcpiOsPrintf ("ParseObjectNamed %3d\n", sizeof (ACPI_PARSE_OBJ_NAMED));
- AcpiOsPrintf ("ParseObjectAsl %3d\n", sizeof (ACPI_PARSE_OBJ_ASL));
- AcpiOsPrintf ("OperandObject %3d\n", sizeof (ACPI_OPERAND_OBJECT));
- AcpiOsPrintf ("NamespaceNode %3d\n", sizeof (ACPI_NAMESPACE_NODE));
- AcpiOsPrintf ("AcpiObject %3d\n", sizeof (ACPI_OBJECT));
-
- break;
-
-
- case CMD_STAT_STACK:
-#if defined(ACPI_DEBUG_OUTPUT)
-
- Temp = (UINT32) ACPI_PTR_DIFF (AcpiGbl_EntryStackPointer, AcpiGbl_LowestStackPointer);
-
- AcpiOsPrintf ("\nSubsystem Stack Usage:\n\n");
- AcpiOsPrintf ("Entry Stack Pointer %p\n", AcpiGbl_EntryStackPointer);
- AcpiOsPrintf ("Lowest Stack Pointer %p\n", AcpiGbl_LowestStackPointer);
- AcpiOsPrintf ("Stack Use %X (%u)\n", Temp, Temp);
- AcpiOsPrintf ("Deepest Procedure Nesting %u\n", AcpiGbl_DeepestNesting);
-#endif
- break;
-
- default:
- break;
- }
-
- AcpiOsPrintf ("\n");
- return (AE_OK);
-}
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbutils.c b/usr/src/uts/intel/io/acpica/debugger/dbutils.c
deleted file mode 100644
index 5b071e87b6..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbutils.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbutils - AML debugger utilities
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acnamesp.h"
-#include "acdebug.h"
-#include "acdisasm.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbutils")
-
-/* Local prototypes */
-
-#ifdef ACPI_OBSOLETE_FUNCTIONS
-ACPI_STATUS
-AcpiDbSecondPassParse (
- ACPI_PARSE_OBJECT *Root);
-
-void
-AcpiDbDumpBuffer (
- UINT32 Address);
-#endif
-
-static char *Converter = "0123456789ABCDEF";
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbMatchArgument
- *
- * PARAMETERS: UserArgument - User command line
- * Arguments - Array of commands to match against
- *
- * RETURN: Index into command array or ACPI_TYPE_NOT_FOUND if not found
- *
- * DESCRIPTION: Search command array for a command match
- *
- ******************************************************************************/
-
-ACPI_OBJECT_TYPE
-AcpiDbMatchArgument (
- char *UserArgument,
- ARGUMENT_INFO *Arguments)
-{
- UINT32 i;
-
-
- if (!UserArgument || UserArgument[0] == 0)
- {
- return (ACPI_TYPE_NOT_FOUND);
- }
-
- for (i = 0; Arguments[i].Name; i++)
- {
- if (ACPI_STRSTR (Arguments[i].Name, UserArgument) == Arguments[i].Name)
- {
- return (i);
- }
- }
-
- /* Argument not recognized */
-
- return (ACPI_TYPE_NOT_FOUND);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSetOutputDestination
- *
- * PARAMETERS: OutputFlags - Current flags word
- *
- * RETURN: None
- *
- * DESCRIPTION: Set the current destination for debugger output. Also sets
- * the debug output level accordingly.
- *
- ******************************************************************************/
-
-void
-AcpiDbSetOutputDestination (
- UINT32 OutputFlags)
-{
-
- AcpiGbl_DbOutputFlags = (UINT8) OutputFlags;
-
- if ((OutputFlags & ACPI_DB_REDIRECTABLE_OUTPUT) && AcpiGbl_DbOutputToFile)
- {
- AcpiDbgLevel = AcpiGbl_DbDebugLevel;
- }
- else
- {
- AcpiDbgLevel = AcpiGbl_DbConsoleDebugLevel;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDumpExternalObject
- *
- * PARAMETERS: ObjDesc - External ACPI object to dump
- * Level - Nesting level.
- *
- * RETURN: None
- *
- * DESCRIPTION: Dump the contents of an ACPI external object
- *
- ******************************************************************************/
-
-void
-AcpiDbDumpExternalObject (
- ACPI_OBJECT *ObjDesc,
- UINT32 Level)
-{
- UINT32 i;
-
-
- if (!ObjDesc)
- {
- AcpiOsPrintf ("[Null Object]\n");
- return;
- }
-
- for (i = 0; i < Level; i++)
- {
- AcpiOsPrintf (" ");
- }
-
- switch (ObjDesc->Type)
- {
- case ACPI_TYPE_ANY:
-
- AcpiOsPrintf ("[Null Object] (Type=0)\n");
- break;
-
-
- case ACPI_TYPE_INTEGER:
-
- AcpiOsPrintf ("[Integer] = %8.8X%8.8X\n",
- ACPI_FORMAT_UINT64 (ObjDesc->Integer.Value));
- break;
-
-
- case ACPI_TYPE_STRING:
-
- AcpiOsPrintf ("[String] Length %.2X = ", ObjDesc->String.Length);
- for (i = 0; i < ObjDesc->String.Length; i++)
- {
- AcpiOsPrintf ("%c", ObjDesc->String.Pointer[i]);
- }
- AcpiOsPrintf ("\n");
- break;
-
-
- case ACPI_TYPE_BUFFER:
-
- AcpiOsPrintf ("[Buffer] Length %.2X = ", ObjDesc->Buffer.Length);
- if (ObjDesc->Buffer.Length)
- {
- if (ObjDesc->Buffer.Length > 16)
- {
- AcpiOsPrintf ("\n");
- }
- AcpiUtDumpBuffer (ACPI_CAST_PTR (UINT8, ObjDesc->Buffer.Pointer),
- ObjDesc->Buffer.Length, DB_DWORD_DISPLAY, _COMPONENT);
- }
- else
- {
- AcpiOsPrintf ("\n");
- }
- break;
-
-
- case ACPI_TYPE_PACKAGE:
-
- AcpiOsPrintf ("[Package] Contains %u Elements:\n",
- ObjDesc->Package.Count);
-
- for (i = 0; i < ObjDesc->Package.Count; i++)
- {
- AcpiDbDumpExternalObject (&ObjDesc->Package.Elements[i], Level+1);
- }
- break;
-
-
- case ACPI_TYPE_LOCAL_REFERENCE:
-
- AcpiOsPrintf ("[Object Reference] = ");
- AcpiDmDisplayInternalObject (ObjDesc->Reference.Handle, NULL);
- break;
-
-
- case ACPI_TYPE_PROCESSOR:
-
- AcpiOsPrintf ("[Processor]\n");
- break;
-
-
- case ACPI_TYPE_POWER:
-
- AcpiOsPrintf ("[Power Resource]\n");
- break;
-
-
- default:
-
- AcpiOsPrintf ("[Unknown Type] %X\n", ObjDesc->Type);
- break;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbPrepNamestring
- *
- * PARAMETERS: Name - String to prepare
- *
- * RETURN: None
- *
- * DESCRIPTION: Translate all forward slashes and dots to backslashes.
- *
- ******************************************************************************/
-
-void
-AcpiDbPrepNamestring (
- char *Name)
-{
-
- if (!Name)
- {
- return;
- }
-
- AcpiUtStrupr (Name);
-
- /* Convert a leading forward slash to a backslash */
-
- if (*Name == '/')
- {
- *Name = '\\';
- }
-
- /* Ignore a leading backslash, this is the root prefix */
-
- if (*Name == '\\')
- {
- Name++;
- }
-
- /* Convert all slash path separators to dots */
-
- while (*Name)
- {
- if ((*Name == '/') ||
- (*Name == '\\'))
- {
- *Name = '.';
- }
-
- Name++;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbLocalNsLookup
- *
- * PARAMETERS: Name - Name to lookup
- *
- * RETURN: Pointer to a namespace node, null on failure
- *
- * DESCRIPTION: Lookup a name in the ACPI namespace
- *
- * Note: Currently begins search from the root. Could be enhanced to use
- * the current prefix (scope) node as the search beginning point.
- *
- ******************************************************************************/
-
-ACPI_NAMESPACE_NODE *
-AcpiDbLocalNsLookup (
- char *Name)
-{
- char *InternalPath;
- ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *Node = NULL;
-
-
- AcpiDbPrepNamestring (Name);
-
- /* Build an internal namestring */
-
- Status = AcpiNsInternalizeName (Name, &InternalPath);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Invalid namestring: %s\n", Name);
- return (NULL);
- }
-
- /*
- * Lookup the name.
- * (Uses root node as the search starting point)
- */
- Status = AcpiNsLookup (NULL, InternalPath, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE, NULL, &Node);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not locate name: %s, %s\n",
- Name, AcpiFormatException (Status));
- }
-
- ACPI_FREE (InternalPath);
- return (Node);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbUInt32ToHexString
- *
- * PARAMETERS: Value - The value to be converted to string
- * Buffer - Buffer for result (not less than 11 bytes)
- *
- * RETURN: None
- *
- * DESCRIPTION: Convert the unsigned 32-bit value to the hexadecimal image
- *
- * NOTE: It is the caller's responsibility to ensure that the length of buffer
- * is sufficient.
- *
- ******************************************************************************/
-
-void
-AcpiDbUInt32ToHexString (
- UINT32 Value,
- char *Buffer)
-{
- int i;
-
-
- if (Value == 0)
- {
- ACPI_STRCPY (Buffer, "0");
- return;
- }
-
- Buffer[8] = '\0';
-
- for (i = 7; i >= 0; i--)
- {
- Buffer[i] = Converter [Value & 0x0F];
- Value = Value >> 4;
- }
-}
-
-
-#ifdef ACPI_OBSOLETE_FUNCTIONS
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSecondPassParse
- *
- * PARAMETERS: Root - Root of the parse tree
- *
- * RETURN: Status
- *
- * DESCRIPTION: Second pass parse of the ACPI tables. We need to wait until
- * second pass to parse the control methods
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbSecondPassParse (
- ACPI_PARSE_OBJECT *Root)
-{
- ACPI_PARSE_OBJECT *Op = Root;
- ACPI_PARSE_OBJECT *Method;
- ACPI_PARSE_OBJECT *SearchOp;
- ACPI_PARSE_OBJECT *StartOp;
- ACPI_STATUS Status = AE_OK;
- UINT32 BaseAmlOffset;
- ACPI_WALK_STATE *WalkState;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- AcpiOsPrintf ("Pass two parse ....\n");
-
- while (Op)
- {
- if (Op->Common.AmlOpcode == AML_METHOD_OP)
- {
- Method = Op;
-
- /* Create a new walk state for the parse */
-
- WalkState = AcpiDsCreateWalkState (0, NULL, NULL, NULL);
- if (!WalkState)
- {
- return (AE_NO_MEMORY);
- }
-
- /* Init the Walk State */
-
- WalkState->ParserState.Aml =
- WalkState->ParserState.AmlStart = Method->Named.Data;
- WalkState->ParserState.AmlEnd =
- WalkState->ParserState.PkgEnd = Method->Named.Data +
- Method->Named.Length;
- WalkState->ParserState.StartScope = Op;
-
- WalkState->DescendingCallback = AcpiDsLoad1BeginOp;
- WalkState->AscendingCallback = AcpiDsLoad1EndOp;
-
- /* Perform the AML parse */
-
- Status = AcpiPsParseAml (WalkState);
-
- BaseAmlOffset = (Method->Common.Value.Arg)->Common.AmlOffset + 1;
- StartOp = (Method->Common.Value.Arg)->Common.Next;
- SearchOp = StartOp;
-
- while (SearchOp)
- {
- SearchOp->Common.AmlOffset += BaseAmlOffset;
- SearchOp = AcpiPsGetDepthNext (StartOp, SearchOp);
- }
- }
-
- if (Op->Common.AmlOpcode == AML_REGION_OP)
- {
- /* TBD: [Investigate] this isn't quite the right thing to do! */
- /*
- *
- * Method = (ACPI_DEFERRED_OP *) Op;
- * Status = AcpiPsParseAml (Op, Method->Body, Method->BodyLength);
- */
- }
-
- if (ACPI_FAILURE (Status))
- {
- break;
- }
-
- Op = AcpiPsGetDepthNext (Root, Op);
- }
-
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbDumpBuffer
- *
- * PARAMETERS: Address - Pointer to the buffer
- *
- * RETURN: None
- *
- * DESCRIPTION: Print a portion of a buffer
- *
- ******************************************************************************/
-
-void
-AcpiDbDumpBuffer (
- UINT32 Address)
-{
-
- AcpiOsPrintf ("\nLocation %X:\n", Address);
-
- AcpiDbgLevel |= ACPI_LV_TABLES;
- AcpiUtDumpBuffer (ACPI_TO_POINTER (Address), 64, DB_BYTE_DISPLAY,
- ACPI_UINT32_MAX);
-}
-#endif
-
-#endif /* ACPI_DEBUGGER */
-
-
diff --git a/usr/src/uts/intel/io/acpica/debugger/dbxface.c b/usr/src/uts/intel/io/acpica/debugger/dbxface.c
deleted file mode 100644
index e638733c22..0000000000
--- a/usr/src/uts/intel/io/acpica/debugger/dbxface.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dbxface - AML Debugger external interfaces
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "amlcode.h"
-#include "acdebug.h"
-#include "acdisasm.h"
-
-
-#ifdef ACPI_DEBUGGER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dbxface")
-
-
-/* Local prototypes */
-
-static ACPI_STATUS
-AcpiDbStartCommand (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op);
-
-#ifdef ACPI_OBSOLETE_FUNCTIONS
-void
-AcpiDbMethodEnd (
- ACPI_WALK_STATE *WalkState);
-#endif
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbStartCommand
- *
- * PARAMETERS: WalkState - Current walk
- * Op - Current executing Op, from AML interpreter
- *
- * RETURN: Status
- *
- * DESCRIPTION: Enter debugger command loop
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiDbStartCommand (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op)
-{
- ACPI_STATUS Status;
-
-
- /* TBD: [Investigate] are there namespace locking issues here? */
-
- /* AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); */
-
- /* Go into the command loop and await next user command */
-
-
- AcpiGbl_MethodExecuting = TRUE;
- Status = AE_CTRL_TRUE;
- while (Status == AE_CTRL_TRUE)
- {
- if (AcpiGbl_DebuggerConfiguration == DEBUGGER_MULTI_THREADED)
- {
- /* Handshake with the front-end that gets user command lines */
-
- Status = AcpiUtReleaseMutex (ACPI_MTX_DEBUG_CMD_COMPLETE);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- Status = AcpiUtAcquireMutex (ACPI_MTX_DEBUG_CMD_READY);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- else
- {
- /* Single threaded, we must get a command line ourselves */
-
- /* Force output to console until a command is entered */
-
- AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
-
- /* Different prompt if method is executing */
-
- if (!AcpiGbl_MethodExecuting)
- {
- AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- }
- else
- {
- AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
-
- /* Get the user input line */
-
- Status = AcpiOsGetLine (AcpiGbl_DbLineBuf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "While parsing command line"));
- return (Status);
- }
- }
-
- Status = AcpiDbCommandDispatch (AcpiGbl_DbLineBuf, WalkState, Op);
- }
-
- /* AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); */
-
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbSingleStep
- *
- * PARAMETERS: WalkState - Current walk
- * Op - Current executing op (from aml interpreter)
- * OpcodeClass - Class of the current AML Opcode
- *
- * RETURN: Status
- *
- * DESCRIPTION: Called just before execution of an AML opcode.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbSingleStep (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op,
- UINT32 OpcodeClass)
-{
- ACPI_PARSE_OBJECT *Next;
- ACPI_STATUS Status = AE_OK;
- UINT32 OriginalDebugLevel;
- ACPI_PARSE_OBJECT *DisplayOp;
- ACPI_PARSE_OBJECT *ParentOp;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- /* Check the abort flag */
-
- if (AcpiGbl_AbortMethod)
- {
- AcpiGbl_AbortMethod = FALSE;
- return (AE_ABORT_METHOD);
- }
-
- /* Check for single-step breakpoint */
-
- if (WalkState->MethodBreakpoint &&
- (WalkState->MethodBreakpoint <= Op->Common.AmlOffset))
- {
- /* Check if the breakpoint has been reached or passed */
- /* Hit the breakpoint, resume single step, reset breakpoint */
-
- AcpiOsPrintf ("***Break*** at AML offset %X\n", Op->Common.AmlOffset);
- AcpiGbl_CmSingleStep = TRUE;
- AcpiGbl_StepToNextCall = FALSE;
- WalkState->MethodBreakpoint = 0;
- }
-
- /* Check for user breakpoint (Must be on exact Aml offset) */
-
- else if (WalkState->UserBreakpoint &&
- (WalkState->UserBreakpoint == Op->Common.AmlOffset))
- {
- AcpiOsPrintf ("***UserBreakpoint*** at AML offset %X\n",
- Op->Common.AmlOffset);
- AcpiGbl_CmSingleStep = TRUE;
- AcpiGbl_StepToNextCall = FALSE;
- WalkState->MethodBreakpoint = 0;
- }
-
- /*
- * Check if this is an opcode that we are interested in --
- * namely, opcodes that have arguments
- */
- if (Op->Common.AmlOpcode == AML_INT_NAMEDFIELD_OP)
- {
- return (AE_OK);
- }
-
- switch (OpcodeClass)
- {
- case AML_CLASS_UNKNOWN:
- case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */
- return (AE_OK);
-
- default:
- /* All other opcodes -- continue */
- break;
- }
-
- /*
- * Under certain debug conditions, display this opcode and its operands
- */
- if ((AcpiGbl_DbOutputToFile) ||
- (AcpiGbl_CmSingleStep) ||
- (AcpiDbgLevel & ACPI_LV_PARSE))
- {
- if ((AcpiGbl_DbOutputToFile) ||
- (AcpiDbgLevel & ACPI_LV_PARSE))
- {
- AcpiOsPrintf ("\n[AmlDebug] Next AML Opcode to execute:\n");
- }
-
- /*
- * Display this op (and only this op - zero out the NEXT field
- * temporarily, and disable parser trace output for the duration of
- * the display because we don't want the extraneous debug output)
- */
- OriginalDebugLevel = AcpiDbgLevel;
- AcpiDbgLevel &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS);
- Next = Op->Common.Next;
- Op->Common.Next = NULL;
-
-
- DisplayOp = Op;
- ParentOp = Op->Common.Parent;
- if (ParentOp)
- {
- if ((WalkState->ControlState) &&
- (WalkState->ControlState->Common.State ==
- ACPI_CONTROL_PREDICATE_EXECUTING))
- {
- /*
- * We are executing the predicate of an IF or WHILE statement
- * Search upwards for the containing IF or WHILE so that the
- * entire predicate can be displayed.
- */
- while (ParentOp)
- {
- if ((ParentOp->Common.AmlOpcode == AML_IF_OP) ||
- (ParentOp->Common.AmlOpcode == AML_WHILE_OP))
- {
- DisplayOp = ParentOp;
- break;
- }
- ParentOp = ParentOp->Common.Parent;
- }
- }
- else
- {
- while (ParentOp)
- {
- if ((ParentOp->Common.AmlOpcode == AML_IF_OP) ||
- (ParentOp->Common.AmlOpcode == AML_ELSE_OP) ||
- (ParentOp->Common.AmlOpcode == AML_SCOPE_OP) ||
- (ParentOp->Common.AmlOpcode == AML_METHOD_OP) ||
- (ParentOp->Common.AmlOpcode == AML_WHILE_OP))
- {
- break;
- }
- DisplayOp = ParentOp;
- ParentOp = ParentOp->Common.Parent;
- }
- }
- }
-
- /* Now we can display it */
-
- AcpiDmDisassemble (WalkState, DisplayOp, ACPI_UINT32_MAX);
-
- if ((Op->Common.AmlOpcode == AML_IF_OP) ||
- (Op->Common.AmlOpcode == AML_WHILE_OP))
- {
- if (WalkState->ControlState->Common.Value)
- {
- AcpiOsPrintf ("Predicate = [True], IF block was executed\n");
- }
- else
- {
- AcpiOsPrintf ("Predicate = [False], Skipping IF block\n");
- }
- }
- else if (Op->Common.AmlOpcode == AML_ELSE_OP)
- {
- AcpiOsPrintf ("Predicate = [False], ELSE block was executed\n");
- }
-
- /* Restore everything */
-
- Op->Common.Next = Next;
- AcpiOsPrintf ("\n");
- if ((AcpiGbl_DbOutputToFile) ||
- (AcpiDbgLevel & ACPI_LV_PARSE))
- {
- AcpiOsPrintf ("\n");
- }
- AcpiDbgLevel = OriginalDebugLevel;
- }
-
- /* If we are not single stepping, just continue executing the method */
-
- if (!AcpiGbl_CmSingleStep)
- {
- return (AE_OK);
- }
-
- /*
- * If we are executing a step-to-call command,
- * Check if this is a method call.
- */
- if (AcpiGbl_StepToNextCall)
- {
- if (Op->Common.AmlOpcode != AML_INT_METHODCALL_OP)
- {
- /* Not a method call, just keep executing */
-
- return (AE_OK);
- }
-
- /* Found a method call, stop executing */
-
- AcpiGbl_StepToNextCall = FALSE;
- }
-
- /*
- * If the next opcode is a method call, we will "step over" it
- * by default.
- */
- if (Op->Common.AmlOpcode == AML_INT_METHODCALL_OP)
- {
- /* Force no more single stepping while executing called method */
-
- AcpiGbl_CmSingleStep = FALSE;
-
- /*
- * Set the breakpoint on/before the call, it will stop execution
- * as soon as we return
- */
- WalkState->MethodBreakpoint = 1; /* Must be non-zero! */
- }
-
-
- Status = AcpiDbStartCommand (WalkState, Op);
-
- /* User commands complete, continue execution of the interrupted method */
-
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbInitialize
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Init and start debugger
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiDbInitialize (
- void)
-{
- ACPI_STATUS Status;
-
-
- /* Init globals */
-
- AcpiGbl_DbBuffer = NULL;
- AcpiGbl_DbFilename = NULL;
- AcpiGbl_DbOutputToFile = FALSE;
-
- AcpiGbl_DbDebugLevel = ACPI_LV_VERBOSITY2;
- AcpiGbl_DbConsoleDebugLevel = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES;
- AcpiGbl_DbOutputFlags = ACPI_DB_CONSOLE_OUTPUT;
-
- AcpiGbl_DbOpt_tables = FALSE;
- AcpiGbl_DbOpt_disasm = FALSE;
- AcpiGbl_DbOpt_stats = FALSE;
- AcpiGbl_DbOpt_verbose = TRUE;
- AcpiGbl_DbOpt_ini_methods = TRUE;
-
- AcpiGbl_DbBuffer = AcpiOsAllocate (ACPI_DEBUG_BUFFER_SIZE);
- if (!AcpiGbl_DbBuffer)
- {
- return (AE_NO_MEMORY);
- }
- ACPI_MEMSET (AcpiGbl_DbBuffer, 0, ACPI_DEBUG_BUFFER_SIZE);
-
- /* Initial scope is the root */
-
- AcpiGbl_DbScopeBuf [0] = '\\';
- AcpiGbl_DbScopeBuf [1] = 0;
- AcpiGbl_DbScopeNode = AcpiGbl_RootNode;
-
- /*
- * If configured for multi-thread support, the debug executor runs in
- * a separate thread so that the front end can be in another address
- * space, environment, or even another machine.
- */
- if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED)
- {
- /* These were created with one unit, grab it */
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_DEBUG_CMD_COMPLETE);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not get debugger mutex\n");
- return (Status);
- }
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_DEBUG_CMD_READY);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not get debugger mutex\n");
- return (Status);
- }
-
- /* Create the debug execution thread to execute commands */
-
- Status = AcpiOsExecute (OSL_DEBUGGER_THREAD, AcpiDbExecuteThread, NULL);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("Could not start debugger thread\n");
- return (Status);
- }
- }
-
- if (!AcpiGbl_DbOpt_verbose)
- {
- AcpiGbl_DbOpt_disasm = TRUE;
- AcpiGbl_DbOpt_stats = FALSE;
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbTerminate
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Stop debugger
- *
- ******************************************************************************/
-
-void
-AcpiDbTerminate (
- void)
-{
-
- if (AcpiGbl_DbBuffer)
- {
- AcpiOsFree (AcpiGbl_DbBuffer);
- }
-}
-
-
-#ifdef ACPI_OBSOLETE_FUNCTIONS
-/*******************************************************************************
- *
- * FUNCTION: AcpiDbMethodEnd
- *
- * PARAMETERS: WalkState - Current walk
- *
- * RETURN: Status
- *
- * DESCRIPTION: Called at method termination
- *
- ******************************************************************************/
-
-void
-AcpiDbMethodEnd (
- ACPI_WALK_STATE *WalkState)
-{
-
- if (!AcpiGbl_CmSingleStep)
- {
- return;
- }
-
- AcpiOsPrintf ("<Method Terminating>\n");
-
- AcpiDbStartCommand (WalkState, NULL);
-}
-#endif
-
-#endif /* ACPI_DEBUGGER */
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c b/usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c
index f223f89f43..923a4850d4 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmbuffer.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,29 +41,46 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
+#include "acutils.h"
#include "acdisasm.h"
#include "acparser.h"
#include "amlcode.h"
+#include "acinterp.h"
-#ifdef ACPI_DISASSEMBLER
-
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dmbuffer")
/* Local prototypes */
static void
+AcpiDmUuid (
+ ACPI_PARSE_OBJECT *Op);
+
+static void
AcpiDmUnicode (
ACPI_PARSE_OBJECT *Op);
static void
-AcpiDmIsEisaIdElement (
+AcpiDmGetHardwareIdType (
ACPI_PARSE_OBJECT *Op);
+static void
+AcpiDmPldBuffer (
+ UINT32 Level,
+ UINT8 *ByteData,
+ UINT32 ByteCount);
+
+static const char *
+AcpiDmFindNameByIndex (
+ UINT64 Index,
+ const char **List);
+
+
+#define ACPI_BUFFER_BYTES_PER_LINE 8
+
/*******************************************************************************
*
@@ -87,6 +104,9 @@ AcpiDmDisasmByteList (
UINT32 ByteCount)
{
UINT32 i;
+ UINT32 j;
+ UINT32 CurrentIndex;
+ UINT8 BufChar;
if (!ByteCount)
@@ -94,39 +114,68 @@ AcpiDmDisasmByteList (
return;
}
- /* Dump the byte list */
-
- for (i = 0; i < ByteCount; i++)
+ for (i = 0; i < ByteCount; i += ACPI_BUFFER_BYTES_PER_LINE)
{
- /* New line every 8 bytes */
+ /* Line indent and offset prefix for each new line */
- if (((i % 8) == 0) && (i < ByteCount))
+ AcpiDmIndent (Level);
+ if (ByteCount > ACPI_BUFFER_BYTES_PER_LINE)
{
- if (i > 0)
+ AcpiOsPrintf ("/* %04X */ ", i);
+ }
+
+ /* Dump the actual hex values */
+
+ for (j = 0; j < ACPI_BUFFER_BYTES_PER_LINE; j++)
+ {
+ CurrentIndex = i + j;
+ if (CurrentIndex >= ByteCount)
{
- AcpiOsPrintf ("\n");
+ /* Dump fill spaces */
+
+ AcpiOsPrintf (" ");
+ continue;
}
- AcpiDmIndent (Level);
- if (ByteCount > 7)
+ AcpiOsPrintf (" 0x%2.2X", ByteData[CurrentIndex]);
+
+ /* Add comma if there are more bytes to display */
+
+ if (CurrentIndex < (ByteCount - 1))
+ {
+ AcpiOsPrintf (",");
+ }
+ else
{
- AcpiOsPrintf ("/* %04X */ ", i);
+ AcpiOsPrintf (" ");
}
}
- AcpiOsPrintf ("0x%2.2X", (UINT32) ByteData[i]);
+ /* Dump the ASCII equivalents within a comment */
- /* Add comma if there are more bytes to display */
-
- if (i < (ByteCount -1))
+ AcpiOsPrintf (" /* ");
+ for (j = 0; j < ACPI_BUFFER_BYTES_PER_LINE; j++)
{
- AcpiOsPrintf (", ");
+ CurrentIndex = i + j;
+ if (CurrentIndex >= ByteCount)
+ {
+ break;
+ }
+
+ BufChar = ByteData[CurrentIndex];
+ if (isprint (BufChar))
+ {
+ AcpiOsPrintf ("%c", BufChar);
+ }
+ else
+ {
+ AcpiOsPrintf (".");
+ }
}
- }
- if (Level)
- {
- AcpiOsPrintf ("\n");
+ /* Finished with this line */
+
+ AcpiOsPrintf (" */\n");
}
}
@@ -165,24 +214,36 @@ AcpiDmByteList (
{
case ACPI_DASM_RESOURCE:
- AcpiDmResourceTemplate (Info, Op->Common.Parent, ByteData, ByteCount);
+ AcpiDmResourceTemplate (
+ Info, Op->Common.Parent, ByteData, ByteCount);
break;
case ACPI_DASM_STRING:
AcpiDmIndent (Info->Level);
- AcpiUtPrintString ((char *) ByteData, ACPI_UINT8_MAX);
+ AcpiUtPrintString ((char *) ByteData, ACPI_UINT16_MAX);
AcpiOsPrintf ("\n");
break;
+ case ACPI_DASM_UUID:
+
+ AcpiDmUuid (Op);
+ break;
+
case ACPI_DASM_UNICODE:
AcpiDmUnicode (Op);
break;
+ case ACPI_DASM_PLD_METHOD:
+#if 0
+ AcpiDmDisasmByteList (Info->Level, ByteData, ByteCount);
+#endif
+ AcpiDmPldBuffer (Info->Level, ByteData, ByteCount);
+ break;
+
case ACPI_DASM_BUFFER:
default:
-
/*
* Not a resource, string, or unicode string.
* Just dump the buffer
@@ -195,6 +256,139 @@ AcpiDmByteList (
/*******************************************************************************
*
+ * FUNCTION: AcpiDmIsUuidBuffer
+ *
+ * PARAMETERS: Op - Buffer Object to be examined
+ *
+ * RETURN: TRUE if buffer contains a UUID
+ *
+ * DESCRIPTION: Determine if a buffer Op contains a UUID
+ *
+ * To help determine whether the buffer is a UUID versus a raw data buffer,
+ * there a are a couple bytes we can look at:
+ *
+ * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
+ *
+ * The variant covered by the UUID specification is indicated by the two most
+ * significant bits of N being 1 0 (i.e., the hexadecimal N will always be
+ * 8, 9, A, or B).
+ *
+ * The variant covered by the UUID specification has five versions. For this
+ * variant, the four bits of M indicates the UUID version (i.e., the
+ * hexadecimal M will be either 1, 2, 3, 4, or 5).
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiDmIsUuidBuffer (
+ ACPI_PARSE_OBJECT *Op)
+{
+ UINT8 *ByteData;
+ UINT32 ByteCount;
+ ACPI_PARSE_OBJECT *SizeOp;
+ ACPI_PARSE_OBJECT *NextOp;
+
+
+ /* Buffer size is the buffer argument */
+
+ SizeOp = Op->Common.Value.Arg;
+
+ /* Next, the initializer byte list to examine */
+
+ NextOp = SizeOp->Common.Next;
+ if (!NextOp)
+ {
+ return (FALSE);
+ }
+
+ /* Extract the byte list info */
+
+ ByteData = NextOp->Named.Data;
+ ByteCount = (UINT32) NextOp->Common.Value.Integer;
+
+ /* Byte count must be exactly 16 */
+
+ if (ByteCount != UUID_BUFFER_LENGTH)
+ {
+ return (FALSE);
+ }
+
+ /* Check for valid "M" and "N" values (see function header above) */
+
+ if (((ByteData[7] & 0xF0) == 0x00) || /* M={1,2,3,4,5} */
+ ((ByteData[7] & 0xF0) > 0x50) ||
+ ((ByteData[8] & 0xF0) < 0x80) || /* N={8,9,A,B} */
+ ((ByteData[8] & 0xF0) > 0xB0))
+ {
+ return (FALSE);
+ }
+
+ /* Ignore the Size argument in the disassembly of this buffer op */
+
+ SizeOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmUuid
+ *
+ * PARAMETERS: Op - Byte List op containing a UUID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump a buffer containing a UUID as a standard ASCII string.
+ *
+ * Output Format:
+ * In its canonical form, the UUID is represented by a string containing 32
+ * lowercase hexadecimal digits, displayed in 5 groups separated by hyphens.
+ * The complete form is 8-4-4-4-12 for a total of 36 characters (32
+ * alphanumeric characters representing hex digits and 4 hyphens). In bytes,
+ * 4-2-2-2-6. Example:
+ *
+ * ToUUID ("107ededd-d381-4fd7-8da9-08e9a6c79644")
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmUuid (
+ ACPI_PARSE_OBJECT *Op)
+{
+ UINT8 *Data;
+ const char *Description;
+
+
+ Data = ACPI_CAST_PTR (UINT8, Op->Named.Data);
+
+ /* Emit the 36-byte UUID string in the proper format/order */
+
+ AcpiOsPrintf (
+ "\"%2.2x%2.2x%2.2x%2.2x-"
+ "%2.2x%2.2x-"
+ "%2.2x%2.2x-"
+ "%2.2x%2.2x-"
+ "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\")",
+ Data[3], Data[2], Data[1], Data[0],
+ Data[5], Data[4],
+ Data[7], Data[6],
+ Data[8], Data[9],
+ Data[10], Data[11], Data[12], Data[13], Data[14], Data[15]);
+
+#ifdef ACPI_APPLICATION
+ /* Dump the UUID description string if available */
+
+ Description = AcpiAhMatchUuid (Data);
+ if (Description)
+ {
+ AcpiOsPrintf (" /* %s */", Description);
+ }
+#endif
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiDmIsUnicodeBuffer
*
* PARAMETERS: Op - Buffer Object to be examined
@@ -247,11 +441,12 @@ AcpiDmIsUnicodeBuffer (
return (FALSE);
}
- /* For each word, 1st byte must be ascii, 2nd byte must be zero */
+ /* For each word, 1st byte must be ascii (1-0x7F), 2nd byte must be zero */
for (i = 0; i < (ByteCount - 2); i += 2)
{
- if ((!ACPI_IS_PRINT (ByteData[i])) ||
+ if ((ByteData[i] == 0) ||
+ (ByteData[i] > 0x7F) ||
(ByteData[(ACPI_SIZE) i + 1] != 0))
{
return (FALSE);
@@ -320,7 +515,7 @@ AcpiDmIsStringBuffer (
* they will be handled in the string output routine
*/
- if (!ACPI_IS_PRINT (ByteData[i]))
+ if (!isprint (ByteData[i]))
{
return (FALSE);
}
@@ -332,13 +527,244 @@ AcpiDmIsStringBuffer (
/*******************************************************************************
*
+ * FUNCTION: AcpiDmIsPldBuffer
+ *
+ * PARAMETERS: Op - Buffer Object to be examined
+ *
+ * RETURN: TRUE if buffer contains a ASCII string, FALSE otherwise
+ *
+ * DESCRIPTION: Determine if a buffer Op contains a _PLD structure
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiDmIsPldBuffer (
+ ACPI_PARSE_OBJECT *Op)
+{
+ ACPI_NAMESPACE_NODE *Node;
+ ACPI_PARSE_OBJECT *SizeOp;
+ ACPI_PARSE_OBJECT *ParentOp;
+
+
+ /* Buffer size is the buffer argument */
+
+ SizeOp = Op->Common.Value.Arg;
+
+ ParentOp = Op->Common.Parent;
+ if (!ParentOp)
+ {
+ return (FALSE);
+ }
+
+ /* Check for form: Name(_PLD, Buffer() {}). Not legal, however */
+
+ if (ParentOp->Common.AmlOpcode == AML_NAME_OP)
+ {
+ Node = ParentOp->Common.Node;
+
+ if (ACPI_COMPARE_NAME (Node->Name.Ascii, METHOD_NAME__PLD))
+ {
+ /* Ignore the Size argument in the disassembly of this buffer op */
+
+ SizeOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ return (TRUE);
+ }
+
+ return (FALSE);
+ }
+
+ /* Check for proper form: Name(_PLD, Package() {Buffer() {}}) */
+
+ if (ParentOp->Common.AmlOpcode == AML_PACKAGE_OP)
+ {
+ ParentOp = ParentOp->Common.Parent;
+ if (!ParentOp)
+ {
+ return (FALSE);
+ }
+
+ if (ParentOp->Common.AmlOpcode == AML_NAME_OP)
+ {
+ Node = ParentOp->Common.Node;
+
+ if (ACPI_COMPARE_NAME (Node->Name.Ascii, METHOD_NAME__PLD))
+ {
+ /* Ignore the Size argument in the disassembly of this buffer op */
+
+ SizeOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ return (TRUE);
+ }
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmFindNameByIndex
+ *
+ * PARAMETERS: Index - Index of array to check
+ * List - Array to reference
+ *
+ * RETURN: String from List or empty string
+ *
+ * DESCRIPTION: Finds and returns the char string located at the given index
+ * position in List.
+ *
+ ******************************************************************************/
+
+static const char *
+AcpiDmFindNameByIndex (
+ UINT64 Index,
+ const char **List)
+{
+ const char *NameString;
+ UINT32 i;
+
+
+ /* Bounds check */
+
+ NameString = List[0];
+ i = 0;
+
+ while (NameString)
+ {
+ i++;
+ NameString = List[i];
+ }
+
+ if (Index >= i)
+ {
+ /* TBD: Add error msg */
+
+ return ("");
+ }
+
+ return (List[Index]);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmPldBuffer
+ *
+ * PARAMETERS: Level - Current source code indentation level
+ * ByteData - Pointer to the byte list
+ * ByteCount - Length of the byte list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump and format the contents of a _PLD buffer object
+ *
+ ******************************************************************************/
+
+#define ACPI_PLD_OUTPUT08 "%*.s%-22s = 0x%X,\n", ACPI_MUL_4 (Level), " "
+#define ACPI_PLD_OUTPUT08P "%*.s%-22s = 0x%X)\n", ACPI_MUL_4 (Level), " "
+#define ACPI_PLD_OUTPUT16 "%*.s%-22s = 0x%X,\n", ACPI_MUL_4 (Level), " "
+#define ACPI_PLD_OUTPUT16P "%*.s%-22s = 0x%X)\n", ACPI_MUL_4 (Level), " "
+#define ACPI_PLD_OUTPUT24 "%*.s%-22s = 0x%X,\n", ACPI_MUL_4 (Level), " "
+#define ACPI_PLD_OUTPUTSTR "%*.s%-22s = \"%s\",\n", ACPI_MUL_4 (Level), " "
+
+static void
+AcpiDmPldBuffer (
+ UINT32 Level,
+ UINT8 *ByteData,
+ UINT32 ByteCount)
+{
+ ACPI_PLD_INFO *PldInfo;
+ ACPI_STATUS Status;
+
+
+ /* Check for valid byte count */
+
+ if (ByteCount < ACPI_PLD_REV1_BUFFER_SIZE)
+ {
+ return;
+ }
+
+ /* Convert _PLD buffer to local _PLD struct */
+
+ Status = AcpiDecodePldBuffer (ByteData, ByteCount, &PldInfo);
+ if (ACPI_FAILURE (Status))
+ {
+ return;
+ }
+
+ AcpiOsPrintf ("\n");
+
+ /* First 32-bit dword */
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Revision", PldInfo->Revision);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_IgnoreColor", PldInfo->IgnoreColor);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Red", PldInfo->Red);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Green", PldInfo->Green);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Blue", PldInfo->Blue);
+
+ /* Second 32-bit dword */
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT16, "PLD_Width", PldInfo->Width);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT16, "PLD_Height", PldInfo->Height);
+
+ /* Third 32-bit dword */
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_UserVisible", PldInfo->UserVisible);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Dock", PldInfo->Dock);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Lid", PldInfo->Lid);
+ AcpiOsPrintf (ACPI_PLD_OUTPUTSTR, "PLD_Panel",
+ AcpiDmFindNameByIndex(PldInfo->Panel, AcpiGbl_PldPanelList));
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUTSTR, "PLD_VerticalPosition",
+ AcpiDmFindNameByIndex(PldInfo->VerticalPosition, AcpiGbl_PldVerticalPositionList));
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUTSTR, "PLD_HorizontalPosition",
+ AcpiDmFindNameByIndex(PldInfo->HorizontalPosition, AcpiGbl_PldHorizontalPositionList));
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUTSTR, "PLD_Shape",
+ AcpiDmFindNameByIndex(PldInfo->Shape, AcpiGbl_PldShapeList));
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_GroupOrientation", PldInfo->GroupOrientation);
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_GroupToken", PldInfo->GroupToken);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_GroupPosition", PldInfo->GroupPosition);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Bay", PldInfo->Bay);
+
+ /* Fourth 32-bit dword */
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Ejectable", PldInfo->Ejectable);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_EjectRequired", PldInfo->OspmEjectRequired);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_CabinetNumber", PldInfo->CabinetNumber);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_CardCageNumber", PldInfo->CardCageNumber);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Reference", PldInfo->Reference);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Rotation", PldInfo->Rotation);
+
+ if (ByteCount >= ACPI_PLD_REV2_BUFFER_SIZE)
+ {
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08, "PLD_Order", PldInfo->Order);
+
+ /* Fifth 32-bit dword */
+
+ AcpiOsPrintf (ACPI_PLD_OUTPUT16, "PLD_VerticalOffset", PldInfo->VerticalOffset);
+ AcpiOsPrintf (ACPI_PLD_OUTPUT16P, "PLD_HorizontalOffset", PldInfo->HorizontalOffset);
+ }
+ else /* Rev 1 buffer */
+ {
+ AcpiOsPrintf (ACPI_PLD_OUTPUT08P, "PLD_Order", PldInfo->Order);
+ }
+
+ ACPI_FREE (PldInfo);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiDmUnicode
*
* PARAMETERS: Op - Byte List op containing Unicode string
*
* RETURN: None
*
- * DESCRIPTION: Dump Unicode string as a standard ASCII string. (Remove
+ * DESCRIPTION: Dump Unicode string as a standard ASCII string. (Remove
* the extra zero bytes).
*
******************************************************************************/
@@ -350,6 +776,7 @@ AcpiDmUnicode (
UINT16 *WordData;
UINT32 WordCount;
UINT32 i;
+ int OutputValue;
/* Extract the buffer info as a WORD buffer */
@@ -357,14 +784,28 @@ AcpiDmUnicode (
WordData = ACPI_CAST_PTR (UINT16, Op->Named.Data);
WordCount = ACPI_DIV_2 (((UINT32) Op->Common.Value.Integer));
-
- AcpiOsPrintf ("\"");
-
/* Write every other byte as an ASCII character */
+ AcpiOsPrintf ("\"");
for (i = 0; i < (WordCount - 1); i++)
{
- AcpiOsPrintf ("%c", (int) WordData[i]);
+ OutputValue = (int) WordData[i];
+
+ /* Handle values that must be escaped */
+
+ if ((OutputValue == '\"') ||
+ (OutputValue == '\\'))
+ {
+ AcpiOsPrintf ("\\%c", OutputValue);
+ }
+ else if (!isprint (OutputValue))
+ {
+ AcpiOsPrintf ("\\x%2.2X", OutputValue);
+ }
+ else
+ {
+ AcpiOsPrintf ("%c", OutputValue);
+ }
}
AcpiOsPrintf ("\")");
@@ -373,19 +814,20 @@ AcpiDmUnicode (
/*******************************************************************************
*
- * FUNCTION: AcpiDmIsEisaIdElement
+ * FUNCTION: AcpiDmGetHardwareIdType
*
* PARAMETERS: Op - Op to be examined
*
* RETURN: None
*
- * DESCRIPTION: Determine if an Op (argument to _HID or _CID) can be converted
- * to an EISA ID.
+ * DESCRIPTION: Determine the type of the argument to a _HID or _CID
+ * 1) Strings are allowed
+ * 2) If Integer, determine if it is a valid EISAID
*
******************************************************************************/
static void
-AcpiDmIsEisaIdElement (
+AcpiDmGetHardwareIdType (
ACPI_PARSE_OBJECT *Op)
{
UINT32 BigEndianId;
@@ -393,55 +835,66 @@ AcpiDmIsEisaIdElement (
UINT32 i;
- /* The parameter must be either a word or a dword */
-
- if ((Op->Common.AmlOpcode != AML_DWORD_OP) &&
- (Op->Common.AmlOpcode != AML_WORD_OP))
+ switch (Op->Common.AmlOpcode)
{
- return;
- }
+ case AML_STRING_OP:
+
+ /* Mark this string as an _HID/_CID string */
- /* Swap from little-endian to big-endian to simplify conversion */
+ Op->Common.DisasmOpcode = ACPI_DASM_HID_STRING;
+ break;
- BigEndianId = AcpiUtDwordByteSwap ((UINT32) Op->Common.Value.Integer);
+ case AML_WORD_OP:
+ case AML_DWORD_OP:
- /* Create the 3 leading ASCII letters */
+ /* Determine if a Word/Dword is a valid encoded EISAID */
- Prefix[0] = ((BigEndianId >> 26) & 0x1F) + 0x40;
- Prefix[1] = ((BigEndianId >> 21) & 0x1F) + 0x40;
- Prefix[2] = ((BigEndianId >> 16) & 0x1F) + 0x40;
+ /* Swap from little-endian to big-endian to simplify conversion */
- /* Verify that all 3 are ascii and alpha */
+ BigEndianId = AcpiUtDwordByteSwap ((UINT32) Op->Common.Value.Integer);
- for (i = 0; i < 3; i++)
- {
- if (!ACPI_IS_ASCII (Prefix[i]) ||
- !ACPI_IS_ALPHA (Prefix[i]))
+ /* Create the 3 leading ASCII letters */
+
+ Prefix[0] = ((BigEndianId >> 26) & 0x1F) + 0x40;
+ Prefix[1] = ((BigEndianId >> 21) & 0x1F) + 0x40;
+ Prefix[2] = ((BigEndianId >> 16) & 0x1F) + 0x40;
+
+ /* Verify that all 3 are ascii and alpha */
+
+ for (i = 0; i < 3; i++)
{
- return;
+ if (!ACPI_IS_ASCII (Prefix[i]) ||
+ !isalpha (Prefix[i]))
+ {
+ return;
+ }
}
- }
- /* OK - mark this node as convertable to an EISA ID */
+ /* Mark this node as convertable to an EISA ID string */
+
+ Op->Common.DisasmOpcode = ACPI_DASM_EISAID;
+ break;
- Op->Common.DisasmOpcode = ACPI_DASM_EISAID;
+ default:
+ break;
+ }
}
/*******************************************************************************
*
- * FUNCTION: AcpiDmIsEisaId
+ * FUNCTION: AcpiDmCheckForHardwareId
*
* PARAMETERS: Op - Op to be examined
*
* RETURN: None
*
- * DESCRIPTION: Determine if a Name() Op can be converted to an EisaId.
+ * DESCRIPTION: Determine if a Name() Op is a _HID/_CID.
*
******************************************************************************/
void
-AcpiDmIsEisaId (
+AcpiDmCheckForHardwareId (
ACPI_PARSE_OBJECT *Op)
{
UINT32 Name;
@@ -466,7 +919,7 @@ AcpiDmIsEisaId (
if (ACPI_COMPARE_NAME (&Name, METHOD_NAME__HID))
{
- AcpiDmIsEisaIdElement (NextOp);
+ AcpiDmGetHardwareIdType (NextOp);
return;
}
@@ -481,20 +934,24 @@ AcpiDmIsEisaId (
if (NextOp->Common.AmlOpcode != AML_PACKAGE_OP)
{
- AcpiDmIsEisaIdElement (NextOp);
+ AcpiDmGetHardwareIdType (NextOp);
return;
}
- /* _CID with Package: get the package length */
+ /* _CID with Package: get the package length, check all elements */
NextOp = AcpiPsGetDepthNext (NULL, NextOp);
+ if (!NextOp)
+ {
+ return;
+ }
/* Don't need to use the length, just walk the peer list */
NextOp = NextOp->Common.Next;
while (NextOp)
{
- AcpiDmIsEisaIdElement (NextOp);
+ AcpiDmGetHardwareIdType (NextOp);
NextOp = NextOp->Common.Next;
}
}
@@ -502,41 +959,36 @@ AcpiDmIsEisaId (
/*******************************************************************************
*
- * FUNCTION: AcpiDmEisaId
+ * FUNCTION: AcpiDmDecompressEisaId
*
* PARAMETERS: EncodedId - Raw encoded EISA ID.
*
* RETURN: None
*
- * DESCRIPTION: Convert an encoded EISAID back to the original ASCII String.
+ * DESCRIPTION: Convert an encoded EISAID back to the original ASCII String
+ * and emit the correct ASL statement. If the ID is known, emit
+ * a description of the ID as a comment.
*
******************************************************************************/
void
-AcpiDmEisaId (
+AcpiDmDecompressEisaId (
UINT32 EncodedId)
{
- UINT32 BigEndianId;
+ char IdBuffer[ACPI_EISAID_STRING_SIZE];
+ const AH_DEVICE_ID *Info;
- /* Swap from little-endian to big-endian to simplify conversion */
+ /* Convert EISAID to a string an emit the statement */
- BigEndianId = AcpiUtDwordByteSwap (EncodedId);
+ AcpiExEisaIdToString (IdBuffer, EncodedId);
+ AcpiOsPrintf ("EisaId (\"%s\")", IdBuffer);
+ /* If we know about the ID, emit the description */
- /* Split to form "AAANNNN" string */
-
- AcpiOsPrintf ("EisaId (\"%c%c%c%4.4X\")",
-
- /* Three Alpha characters (AAA), 5 bits each */
-
- (int) ((BigEndianId >> 26) & 0x1F) + 0x40,
- (int) ((BigEndianId >> 21) & 0x1F) + 0x40,
- (int) ((BigEndianId >> 16) & 0x1F) + 0x40,
-
- /* Numeric part (NNNN) is simply the lower 16 bits */
-
- (UINT32) (BigEndianId & 0xFFFF));
+ Info = AcpiAhMatchHardwareId (IdBuffer);
+ if (Info)
+ {
+ AcpiOsPrintf (" /* %s */", Info->Description);
+ }
}
-
-#endif
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmcstyle.c b/usr/src/uts/intel/io/acpica/disassembler/dmcstyle.c
new file mode 100644
index 0000000000..2f61dea208
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmcstyle.c
@@ -0,0 +1,877 @@
+/*******************************************************************************
+ *
+ * Module Name: dmcstyle - Support for C-style operator disassembly
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdebug.h"
+
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ ACPI_MODULE_NAME ("dmcstyle")
+
+
+/* Local prototypes */
+
+static char *
+AcpiDmGetCompoundSymbol (
+ UINT16 AslOpcode);
+
+static void
+AcpiDmPromoteTarget (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_PARSE_OBJECT *Target);
+
+static BOOLEAN
+AcpiDmIsValidTarget (
+ ACPI_PARSE_OBJECT *Op);
+
+static BOOLEAN
+AcpiDmIsTargetAnOperand (
+ ACPI_PARSE_OBJECT *Target,
+ ACPI_PARSE_OBJECT *Operand,
+ BOOLEAN TopLevel);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmCheckForSymbolicOpcode
+ *
+ * PARAMETERS: Op - Current parse object
+ * Walk - Current parse tree walk info
+ *
+ * RETURN: TRUE if opcode can be converted to symbolic, FALSE otherwise
+ *
+ * DESCRIPTION: This is the main code that implements disassembly of AML code
+ * to C-style operators. Called during descending phase of the
+ * parse tree walk.
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiDmCheckForSymbolicOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_OP_WALK_INFO *Info)
+{
+ char *OperatorSymbol = NULL;
+ ACPI_PARSE_OBJECT *Child1;
+ ACPI_PARSE_OBJECT *Child2;
+ ACPI_PARSE_OBJECT *Target;
+
+
+ /* Exit immediately if ASL+ not enabled */
+
+ if (!AcpiGbl_CstyleDisassembly)
+ {
+ return (FALSE);
+ }
+
+ /* Get the first operand */
+
+ Child1 = AcpiPsGetArg (Op, 0);
+ if (!Child1)
+ {
+ return (FALSE);
+ }
+
+ /* Get the second operand */
+
+ Child2 = Child1->Common.Next;
+
+ /* Setup the operator string for this opcode */
+
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_ADD_OP:
+ OperatorSymbol = " + ";
+ break;
+
+ case AML_SUBTRACT_OP:
+ OperatorSymbol = " - ";
+ break;
+
+ case AML_MULTIPLY_OP:
+ OperatorSymbol = " * ";
+ break;
+
+ case AML_DIVIDE_OP:
+ OperatorSymbol = " / ";
+ break;
+
+ case AML_MOD_OP:
+ OperatorSymbol = " % ";
+ break;
+
+ case AML_SHIFT_LEFT_OP:
+ OperatorSymbol = " << ";
+ break;
+
+ case AML_SHIFT_RIGHT_OP:
+ OperatorSymbol = " >> ";
+ break;
+
+ case AML_BIT_AND_OP:
+ OperatorSymbol = " & ";
+ break;
+
+ case AML_BIT_OR_OP:
+ OperatorSymbol = " | ";
+ break;
+
+ case AML_BIT_XOR_OP:
+ OperatorSymbol = " ^ ";
+ break;
+
+ /* Logical operators, no target */
+
+ case AML_LAND_OP:
+ OperatorSymbol = " && ";
+ break;
+
+ case AML_LEQUAL_OP:
+ OperatorSymbol = " == ";
+ break;
+
+ case AML_LGREATER_OP:
+ OperatorSymbol = " > ";
+ break;
+
+ case AML_LLESS_OP:
+ OperatorSymbol = " < ";
+ break;
+
+ case AML_LOR_OP:
+ OperatorSymbol = " || ";
+ break;
+
+ case AML_LNOT_OP:
+ /*
+ * Check for the LNOT sub-opcodes. These correspond to
+ * LNotEqual, LLessEqual, and LGreaterEqual. There are
+ * no actual AML opcodes for these operators.
+ */
+ switch (Child1->Common.AmlOpcode)
+ {
+ case AML_LEQUAL_OP:
+ OperatorSymbol = " != ";
+ break;
+
+ case AML_LGREATER_OP:
+ OperatorSymbol = " <= ";
+ break;
+
+ case AML_LLESS_OP:
+ OperatorSymbol = " >= ";
+ break;
+
+ default:
+
+ /* Unary LNOT case, emit "!" immediately */
+
+ AcpiOsPrintf ("!");
+ return (TRUE);
+ }
+
+ Child1->Common.DisasmOpcode = ACPI_DASM_LNOT_SUFFIX;
+ Op->Common.DisasmOpcode = ACPI_DASM_LNOT_PREFIX;
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND_ASSIGNMENT;
+
+ /* Save symbol string in the next child (not peer) */
+
+ Child2 = AcpiPsGetArg (Child1, 0);
+ if (!Child2)
+ {
+ return (FALSE);
+ }
+
+ Child2->Common.OperatorSymbol = OperatorSymbol;
+ return (TRUE);
+
+ case AML_INDEX_OP:
+ /*
+ * Check for constant source operand. Note: although technically
+ * legal syntax, the iASL compiler does not support this with
+ * the symbolic operators for Index(). It doesn't make sense to
+ * use Index() with a constant anyway.
+ */
+ if ((Child1->Common.AmlOpcode == AML_STRING_OP) ||
+ (Child1->Common.AmlOpcode == AML_BUFFER_OP) ||
+ (Child1->Common.AmlOpcode == AML_PACKAGE_OP) ||
+ (Child1->Common.AmlOpcode == AML_VAR_PACKAGE_OP))
+ {
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_CLOSING_PAREN;
+ return (FALSE);
+ }
+
+ /* Index operator is [] */
+
+ Child1->Common.OperatorSymbol = " [";
+ Child2->Common.OperatorSymbol = "]";
+ break;
+
+ /* Unary operators */
+
+ case AML_DECREMENT_OP:
+ OperatorSymbol = "--";
+ break;
+
+ case AML_INCREMENT_OP:
+ OperatorSymbol = "++";
+ break;
+
+ case AML_BIT_NOT_OP:
+ case AML_STORE_OP:
+ OperatorSymbol = NULL;
+ break;
+
+ default:
+ return (FALSE);
+ }
+
+ if (Child1->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX)
+ {
+ return (TRUE);
+ }
+
+ /*
+ * This is the key to how the disassembly of the C-style operators
+ * works. We save the operator symbol in the first child, thus
+ * deferring symbol output until after the first operand has been
+ * emitted.
+ */
+ if (!Child1->Common.OperatorSymbol)
+ {
+ Child1->Common.OperatorSymbol = OperatorSymbol;
+ }
+
+ /*
+ * Check for a valid target as the 3rd (or sometimes 2nd) operand
+ *
+ * Compound assignment operator support:
+ * Attempt to optimize constructs of the form:
+ * Add (Local1, 0xFF, Local1)
+ * to:
+ * Local1 += 0xFF
+ *
+ * Only the math operators and Store() have a target.
+ * Logicals have no target.
+ */
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_ADD_OP:
+ case AML_SUBTRACT_OP:
+ case AML_MULTIPLY_OP:
+ case AML_DIVIDE_OP:
+ case AML_MOD_OP:
+ case AML_SHIFT_LEFT_OP:
+ case AML_SHIFT_RIGHT_OP:
+ case AML_BIT_AND_OP:
+ case AML_BIT_OR_OP:
+ case AML_BIT_XOR_OP:
+
+ /* Target is 3rd operand */
+
+ Target = Child2->Common.Next;
+ if (Op->Common.AmlOpcode == AML_DIVIDE_OP)
+ {
+ /*
+ * Divide has an extra target operand (Remainder).
+ * If this extra target is specified, it cannot be converted
+ * to a C-style operator
+ */
+ if (AcpiDmIsValidTarget (Target))
+ {
+ Child1->Common.OperatorSymbol = NULL;
+ return (FALSE);
+ }
+
+ Target->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ Target = Target->Common.Next;
+ }
+
+ /* Parser should ensure there is at least a placeholder target */
+
+ if (!Target)
+ {
+ return (FALSE);
+ }
+
+ if (!AcpiDmIsValidTarget (Target))
+ {
+ /* Not a valid target (placeholder only, from parser) */
+ break;
+ }
+
+ /*
+ * Promote the target up to the first child in the parse
+ * tree. This is done because the target will be output
+ * first, in the form:
+ * <Target> = Operands...
+ */
+ AcpiDmPromoteTarget (Op, Target);
+
+ /* Check operands for conversion to a "Compound Assignment" */
+
+ switch (Op->Common.AmlOpcode)
+ {
+ /* Commutative operators */
+
+ case AML_ADD_OP:
+ case AML_MULTIPLY_OP:
+ case AML_BIT_AND_OP:
+ case AML_BIT_OR_OP:
+ case AML_BIT_XOR_OP:
+ /*
+ * For the commutative operators, we can convert to a
+ * compound statement only if at least one (either) operand
+ * is the same as the target.
+ *
+ * Add (A, B, A) --> A += B
+ * Add (B, A, A) --> A += B
+ * Add (B, C, A) --> A = (B + C)
+ */
+ if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)) ||
+ (AcpiDmIsTargetAnOperand (Target, Child2, TRUE)))
+ {
+ Target->Common.OperatorSymbol =
+ AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
+
+ /* Convert operator to compound assignment */
+
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND_ASSIGNMENT;
+ Child1->Common.OperatorSymbol = NULL;
+ return (TRUE);
+ }
+ break;
+
+ /* Non-commutative operators */
+
+ case AML_SUBTRACT_OP:
+ case AML_DIVIDE_OP:
+ case AML_MOD_OP:
+ case AML_SHIFT_LEFT_OP:
+ case AML_SHIFT_RIGHT_OP:
+ /*
+ * For the non-commutative operators, we can convert to a
+ * compound statement only if the target is the same as the
+ * first operand.
+ *
+ * Subtract (A, B, A) --> A -= B
+ * Subtract (B, A, A) --> A = (B - A)
+ */
+ if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)))
+ {
+ Target->Common.OperatorSymbol =
+ AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
+
+ /* Convert operator to compound assignment */
+
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND_ASSIGNMENT;
+ Child1->Common.OperatorSymbol = NULL;
+ return (TRUE);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * If we are within a C-style expression, emit an extra open
+ * paren. Implemented by examining the parent op.
+ */
+ switch (Op->Common.Parent->Common.AmlOpcode)
+ {
+ case AML_ADD_OP:
+ case AML_SUBTRACT_OP:
+ case AML_MULTIPLY_OP:
+ case AML_DIVIDE_OP:
+ case AML_MOD_OP:
+ case AML_SHIFT_LEFT_OP:
+ case AML_SHIFT_RIGHT_OP:
+ case AML_BIT_AND_OP:
+ case AML_BIT_OR_OP:
+ case AML_BIT_XOR_OP:
+ case AML_LAND_OP:
+ case AML_LEQUAL_OP:
+ case AML_LGREATER_OP:
+ case AML_LLESS_OP:
+ case AML_LOR_OP:
+
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_ASSIGNMENT;
+ AcpiOsPrintf ("(");
+ break;
+
+ default:
+ break;
+ }
+
+ /* Normal output for ASL/AML operators with a target operand */
+
+ Target->Common.OperatorSymbol = " = (";
+ return (TRUE);
+
+ /* Binary operators, no parens */
+
+ case AML_DECREMENT_OP:
+ case AML_INCREMENT_OP:
+ return (TRUE);
+
+ case AML_INDEX_OP:
+
+ /* Target is optional, 3rd operand */
+
+ Target = Child2->Common.Next;
+ if (AcpiDmIsValidTarget (Target))
+ {
+ AcpiDmPromoteTarget (Op, Target);
+
+ if (!Target->Common.OperatorSymbol)
+ {
+ Target->Common.OperatorSymbol = " = ";
+ }
+ }
+ return (TRUE);
+
+ case AML_STORE_OP:
+ /*
+ * Target is the 2nd operand.
+ * We know the target is valid, it is not optional.
+ * In the parse tree, simply swap the target with the
+ * source so that the target is processed first.
+ */
+ Target = Child1->Common.Next;
+ if (!Target)
+ {
+ return (FALSE);
+ }
+
+ AcpiDmPromoteTarget (Op, Target);
+ if (!Target->Common.OperatorSymbol)
+ {
+ Target->Common.OperatorSymbol = " = ";
+ }
+ return (TRUE);
+
+ case AML_BIT_NOT_OP:
+
+ /* Target is optional, 2nd operand */
+
+ Target = Child1->Common.Next;
+ if (!Target)
+ {
+ return (FALSE);
+ }
+
+ if (AcpiDmIsValidTarget (Target))
+ {
+ /* Valid target, not a placeholder */
+
+ AcpiDmPromoteTarget (Op, Target);
+ Target->Common.OperatorSymbol = " = ~";
+ }
+ else
+ {
+ /* No target. Emit this prefix operator immediately */
+
+ AcpiOsPrintf ("~");
+ }
+ return (TRUE);
+
+ default:
+ break;
+ }
+
+ /*
+ * Nodes marked with ACPI_PARSEOP_PARAMLIST don't need a parens
+ * output here. We also need to check the parent to see if this op
+ * is part of a compound test (!=, >=, <=).
+ */
+ if ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) ||
+ ((Op->Common.Parent->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) &&
+ (Op->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX)))
+ {
+ /* Do Nothing. Paren already generated */
+ return (TRUE);
+ }
+
+ /* All other operators, emit an open paren */
+
+ AcpiOsPrintf ("(");
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmCloseOperator
+ *
+ * PARAMETERS: Op - Current parse object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Closes an operator by adding a closing parentheses if and
+ * when necessary. Called during ascending phase of the
+ * parse tree walk.
+ *
+ ******************************************************************************/
+
+void
+AcpiDmCloseOperator (
+ ACPI_PARSE_OBJECT *Op)
+{
+ BOOLEAN IsCStyleOp = FALSE;
+
+ /* Always emit paren if ASL+ disassembly disabled */
+
+ if (!AcpiGbl_CstyleDisassembly)
+ {
+ AcpiOsPrintf (")");
+ return;
+ }
+
+ /* Check if we need to add an additional closing paren */
+
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_ADD_OP:
+ case AML_SUBTRACT_OP:
+ case AML_MULTIPLY_OP:
+ case AML_DIVIDE_OP:
+ case AML_MOD_OP:
+ case AML_SHIFT_LEFT_OP:
+ case AML_SHIFT_RIGHT_OP:
+ case AML_BIT_AND_OP:
+ case AML_BIT_OR_OP:
+ case AML_BIT_XOR_OP:
+ case AML_LAND_OP:
+ case AML_LEQUAL_OP:
+ case AML_LGREATER_OP:
+ case AML_LLESS_OP:
+ case AML_LOR_OP:
+
+ /* Emit paren only if this is not a compound assignment */
+
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_COMPOUND_ASSIGNMENT)
+ {
+ return;
+ }
+
+ /* Emit extra close paren for assignment within an expression */
+
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_ASSIGNMENT)
+ {
+ AcpiOsPrintf (")");
+ }
+
+ IsCStyleOp = TRUE;
+ break;
+
+ case AML_INDEX_OP:
+
+ /* This is case for unsupported Index() source constants */
+
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_CLOSING_PAREN)
+ {
+ AcpiOsPrintf (")");
+ }
+ return;
+
+ /* No need for parens for these */
+
+ case AML_DECREMENT_OP:
+ case AML_INCREMENT_OP:
+ case AML_LNOT_OP:
+ case AML_BIT_NOT_OP:
+ case AML_STORE_OP:
+ return;
+
+ default:
+
+ /* Always emit paren for non-ASL+ operators */
+ break;
+ }
+
+ /*
+ * Nodes marked with ACPI_PARSEOP_PARAMLIST don't need a parens
+ * output here. We also need to check the parent to see if this op
+ * is part of a compound test (!=, >=, <=).
+ */
+ if (IsCStyleOp &&
+ ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) ||
+ ((Op->Common.Parent->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) &&
+ (Op->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX))))
+ {
+ return;
+ }
+
+ AcpiOsPrintf (")");
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmGetCompoundSymbol
+ *
+ * PARAMETERS: AslOpcode
+ *
+ * RETURN: String containing the compound assignment symbol
+ *
+ * DESCRIPTION: Detect opcodes that can be converted to compound assignment,
+ * return the appropriate operator string.
+ *
+ ******************************************************************************/
+
+static char *
+AcpiDmGetCompoundSymbol (
+ UINT16 AmlOpcode)
+{
+ char *Symbol;
+
+
+ switch (AmlOpcode)
+ {
+ case AML_ADD_OP:
+ Symbol = " += ";
+ break;
+
+ case AML_SUBTRACT_OP:
+ Symbol = " -= ";
+ break;
+
+ case AML_MULTIPLY_OP:
+ Symbol = " *= ";
+ break;
+
+ case AML_DIVIDE_OP:
+ Symbol = " /= ";
+ break;
+
+ case AML_MOD_OP:
+ Symbol = " %= ";
+ break;
+
+ case AML_SHIFT_LEFT_OP:
+ Symbol = " <<= ";
+ break;
+
+ case AML_SHIFT_RIGHT_OP:
+ Symbol = " >>= ";
+ break;
+
+ case AML_BIT_AND_OP:
+ Symbol = " &= ";
+ break;
+
+ case AML_BIT_OR_OP:
+ Symbol = " |= ";
+ break;
+
+ case AML_BIT_XOR_OP:
+ Symbol = " ^= ";
+ break;
+
+ default:
+
+ /* No operator string for all other opcodes */
+
+ return (NULL);
+ }
+
+ return (Symbol);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmPromoteTarget
+ *
+ * PARAMETERS: Op - Operator parse object
+ * Target - Target associate with the Op
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Transform the parse tree by moving the target up to the first
+ * child of the Op.
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmPromoteTarget (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_PARSE_OBJECT *Target)
+{
+ ACPI_PARSE_OBJECT *Child;
+
+
+ /* Link target directly to the Op as first child */
+
+ Child = Op->Common.Value.Arg;
+ Op->Common.Value.Arg = Target;
+ Target->Common.Next = Child;
+
+ /* Find the last peer, it is linked to the target. Unlink it. */
+
+ while (Child->Common.Next != Target)
+ {
+ Child = Child->Common.Next;
+ }
+
+ Child->Common.Next = NULL;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmIsValidTarget
+ *
+ * PARAMETERS: Target - Target Op from the parse tree
+ *
+ * RETURN: TRUE if the Target is real. FALSE if it is just a placeholder
+ * Op that was inserted by the parser.
+ *
+ * DESCRIPTION: Determine if a Target Op is a placeholder Op or a real Target.
+ * In other words, determine if the optional target is used or
+ * not. Note: If Target is NULL, something is seriously wrong,
+ * probably with the parse tree.
+ *
+ ******************************************************************************/
+
+static BOOLEAN
+AcpiDmIsValidTarget (
+ ACPI_PARSE_OBJECT *Target)
+{
+
+ if (!Target)
+ {
+ return (FALSE);
+ }
+
+ if ((Target->Common.AmlOpcode == AML_INT_NAMEPATH_OP) &&
+ (Target->Common.Value.Arg == NULL))
+ {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmIsTargetAnOperand
+ *
+ * PARAMETERS: Target - Target associated with the expression
+ * Operand - An operand associated with expression
+ *
+ * RETURN: TRUE if expression can be converted to a compound assignment.
+ * FALSE otherwise.
+ *
+ * DESCRIPTION: Determine if the Target duplicates the operand, in order to
+ * detect if the expression can be converted to a compound
+ * assigment. (+=, *=, etc.)
+ *
+ ******************************************************************************/
+
+static BOOLEAN
+AcpiDmIsTargetAnOperand (
+ ACPI_PARSE_OBJECT *Target,
+ ACPI_PARSE_OBJECT *Operand,
+ BOOLEAN TopLevel)
+{
+ const ACPI_OPCODE_INFO *OpInfo;
+ BOOLEAN Same;
+
+
+ /*
+ * Opcodes must match. Note: ignoring the difference between nameseg
+ * and namepath for now. May be needed later.
+ */
+ if (Target->Common.AmlOpcode != Operand->Common.AmlOpcode)
+ {
+ return (FALSE);
+ }
+
+ /* Nodes should match, even if they are NULL */
+
+ if (Target->Common.Node != Operand->Common.Node)
+ {
+ return (FALSE);
+ }
+
+ /* Determine if a child exists */
+
+ OpInfo = AcpiPsGetOpcodeInfo (Operand->Common.AmlOpcode);
+ if (OpInfo->Flags & AML_HAS_ARGS)
+ {
+ Same = AcpiDmIsTargetAnOperand (Target->Common.Value.Arg,
+ Operand->Common.Value.Arg, FALSE);
+ if (!Same)
+ {
+ return (FALSE);
+ }
+ }
+
+ /* Check the next peer, as long as we are not at the top level */
+
+ if ((!TopLevel) &&
+ Target->Common.Next)
+ {
+ Same = AcpiDmIsTargetAnOperand (Target->Common.Next,
+ Operand->Common.Next, FALSE);
+ if (!Same)
+ {
+ return (FALSE);
+ }
+ }
+
+ /* Supress the duplicate operand at the top-level */
+
+ if (TopLevel)
+ {
+ Operand->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ }
+ return (TRUE);
+}
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmdeferred.c b/usr/src/uts/intel/io/acpica/disassembler/dmdeferred.c
new file mode 100644
index 0000000000..387980b893
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmdeferred.c
@@ -0,0 +1,264 @@
+/******************************************************************************
+ *
+ * Module Name: dmdeferred - Disassembly of deferred AML opcodes
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acdispat.h"
+#include "amlcode.h"
+#include "acdisasm.h"
+#include "acparser.h"
+
+#define _COMPONENT ACPI_CA_DISASSEMBLER
+ ACPI_MODULE_NAME ("dmdeferred")
+
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiDmDeferredParse (
+ ACPI_PARSE_OBJECT *Op,
+ UINT8 *Aml,
+ UINT32 AmlLength);
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiDmParseDeferredOps
+ *
+ * PARAMETERS: Root - Root of the parse tree
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse the deferred opcodes (Methods, regions, etc.)
+ *
+ *****************************************************************************/
+
+ACPI_STATUS
+AcpiDmParseDeferredOps (
+ ACPI_PARSE_OBJECT *Root)
+{
+ const ACPI_OPCODE_INFO *OpInfo;
+ ACPI_PARSE_OBJECT *Op = Root;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /* Traverse the entire parse tree */
+
+ while (Op)
+ {
+ OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
+ if (!(OpInfo->Flags & AML_DEFER))
+ {
+ Op = AcpiPsGetDepthNext (Root, Op);
+ continue;
+ }
+
+ /* Now we know we have a deferred opcode */
+
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_METHOD_OP:
+ case AML_BUFFER_OP:
+ case AML_PACKAGE_OP:
+ case AML_VAR_PACKAGE_OP:
+
+ Status = AcpiDmDeferredParse (
+ Op, Op->Named.Data, Op->Named.Length);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ /* We don't need to do anything for these deferred opcodes */
+
+ case AML_REGION_OP:
+ case AML_DATA_REGION_OP:
+ case AML_CREATE_QWORD_FIELD_OP:
+ case AML_CREATE_DWORD_FIELD_OP:
+ case AML_CREATE_WORD_FIELD_OP:
+ case AML_CREATE_BYTE_FIELD_OP:
+ case AML_CREATE_BIT_FIELD_OP:
+ case AML_CREATE_FIELD_OP:
+ case AML_BANK_FIELD_OP:
+
+ break;
+
+ default:
+
+ ACPI_ERROR ((AE_INFO, "Unhandled deferred AML opcode [0x%.4X]",
+ Op->Common.AmlOpcode));
+ break;
+ }
+
+ Op = AcpiPsGetDepthNext (Root, Op);
+ }
+
+ return (AE_OK);
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiDmDeferredParse
+ *
+ * PARAMETERS: Op - Root Op of the deferred opcode
+ * Aml - Pointer to the raw AML
+ * AmlLength - Length of the AML
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse one deferred opcode
+ * (Methods, operation regions, etc.)
+ *
+ *****************************************************************************/
+
+static ACPI_STATUS
+AcpiDmDeferredParse (
+ ACPI_PARSE_OBJECT *Op,
+ UINT8 *Aml,
+ UINT32 AmlLength)
+{
+ ACPI_WALK_STATE *WalkState;
+ ACPI_STATUS Status;
+ ACPI_PARSE_OBJECT *SearchOp;
+ ACPI_PARSE_OBJECT *StartOp;
+ ACPI_PARSE_OBJECT *NewRootOp;
+ ACPI_PARSE_OBJECT *ExtraOp;
+
+
+ ACPI_FUNCTION_TRACE (DmDeferredParse);
+
+
+ if (!Aml || !AmlLength)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Parsing deferred opcode %s [%4.4s]\n",
+ Op->Common.AmlOpName, (char *) &Op->Named.Name));
+
+ /* Need a new walk state to parse the AML */
+
+ WalkState = AcpiDsCreateWalkState (0, Op, NULL, NULL);
+ if (!WalkState)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ Status = AcpiDsInitAmlWalk (WalkState, Op, NULL, Aml,
+ AmlLength, NULL, ACPI_IMODE_LOAD_PASS1);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Parse the AML for this deferred opcode */
+
+ WalkState->ParseFlags &= ~ACPI_PARSE_DELETE_TREE;
+ WalkState->ParseFlags |= ACPI_PARSE_DISASSEMBLE;
+ Status = AcpiPsParseAml (WalkState);
+
+ StartOp = (Op->Common.Value.Arg)->Common.Next;
+ SearchOp = StartOp;
+ while (SearchOp)
+ {
+ SearchOp = AcpiPsGetDepthNext (StartOp, SearchOp);
+ }
+
+ /*
+ * For Buffer and Package opcodes, link the newly parsed subtree
+ * into the main parse tree
+ */
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_BUFFER_OP:
+ case AML_PACKAGE_OP:
+ case AML_VAR_PACKAGE_OP:
+
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_PACKAGE_OP:
+
+ ExtraOp = Op->Common.Value.Arg;
+ NewRootOp = ExtraOp->Common.Next;
+ ACPI_FREE (ExtraOp);
+ break;
+
+ case AML_VAR_PACKAGE_OP:
+ case AML_BUFFER_OP:
+ default:
+
+ NewRootOp = Op->Common.Value.Arg;
+ break;
+ }
+
+ Op->Common.Value.Arg = NewRootOp->Common.Value.Arg;
+
+ /* Must point all parents to the main tree */
+
+ StartOp = Op;
+ SearchOp = StartOp;
+ while (SearchOp)
+ {
+ if (SearchOp->Common.Parent == NewRootOp)
+ {
+ SearchOp->Common.Parent = Op;
+ }
+
+ SearchOp = AcpiPsGetDepthNext (StartOp, SearchOp);
+ }
+
+ ACPI_FREE (NewRootOp);
+ break;
+
+ default:
+
+ break;
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmnames.c b/usr/src/uts/intel/io/acpica/disassembler/dmnames.c
index b00bca8e86..89cfb2e985 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmnames.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmnames.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,17 +41,13 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
-#include "acparser.h"
#include "amlcode.h"
#include "acnamesp.h"
#include "acdisasm.h"
-#ifdef ACPI_DISASSEMBLER
-
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dmnames")
@@ -128,7 +124,7 @@ AcpiDmDumpName (
*
* RETURN: Status
*
- * DESCRIPTION: Diplay the pathname associated with a named object. Two
+ * DESCRIPTION: Diplay the pathname associated with a named object. Two
* versions. One searches the parse tree (for parser-only
* applications suchas AcpiDump), and the other searches the
* ACPI namespace (the parse tree is probably deleted)
@@ -159,15 +155,15 @@ AcpiPsDisplayObjectPathname (
/* Node not defined in this scope, look it up */
Status = AcpiNsLookup (WalkState->ScopeInfo, Op->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
- WalkState, &(Node));
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ WalkState, &(Node));
if (ACPI_FAILURE (Status))
{
/*
- * We can't get the pathname since the object
- * is not in the namespace. This can happen during single
- * stepping where a dynamic named object is *about* to be created.
+ * We can't get the pathname since the object is not in the
+ * namespace. This can happen during single stepping
+ * where a dynamic named object is *about* to be created.
*/
AcpiOsPrintf (" [Path not found]");
goto Exit;
@@ -181,7 +177,7 @@ AcpiPsDisplayObjectPathname (
/* Convert NamedDesc/handle to a full pathname */
Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (Node, &Buffer);
+ Status = AcpiNsHandleToPathname (Node, &Buffer, FALSE);
if (ACPI_FAILURE (Status))
{
AcpiOsPrintf ("****Could not get pathname****)");
@@ -226,7 +222,8 @@ AcpiDmNamestring (
/* Handle all Scope Prefix operators */
- while (AcpiPsIsPrefixChar (ACPI_GET8 (Name)))
+ while (ACPI_IS_ROOT_PREFIX (ACPI_GET8 (Name)) ||
+ ACPI_IS_PARENT_PREFIX (ACPI_GET8 (Name)))
{
/* Append prefix character */
@@ -237,20 +234,24 @@ AcpiDmNamestring (
switch (ACPI_GET8 (Name))
{
case 0:
+
SegCount = 0;
break;
case AML_DUAL_NAME_PREFIX:
+
SegCount = 2;
Name++;
break;
case AML_MULTI_NAME_PREFIX_OP:
+
SegCount = (UINT32) ACPI_GET8 (Name + 1);
Name += 2;
break;
default:
+
SegCount = 1;
break;
}
@@ -268,6 +269,7 @@ AcpiDmNamestring (
AcpiOsPrintf (".");
}
+
Name += ACPI_NAME_SIZE;
}
}
@@ -323,7 +325,7 @@ AcpiDmDisplayPath (
if ((NamePath) &&
(NamePath->Common.Value.String) &&
- (NamePath->Common.Value.String[0] == '\\'))
+ (ACPI_IS_ROOT_PREFIX (NamePath->Common.Value.String[0])))
{
AcpiDmNamestring (NamePath->Common.Value.String);
return;
@@ -331,7 +333,6 @@ AcpiDmDisplayPath (
}
Prev = NULL; /* Start with Root Node */
-
while (Prev != Op)
{
/* Search upwards in the tree to find scope with "prev" as its parent */
@@ -389,6 +390,7 @@ AcpiDmDisplayPath (
DoDot = TRUE;
}
}
+
Prev = Search;
}
}
@@ -411,6 +413,8 @@ AcpiDmValidateName (
char *Name,
ACPI_PARSE_OBJECT *Op)
{
+ ACPI_PARSE_OBJECT *TargetOp;
+
if ((!Name) ||
(!Op->Common.Parent))
@@ -424,9 +428,6 @@ AcpiDmValidateName (
" /**** Name not found or not accessible from this scope ****/ ");
}
- ACPI_PARSE_OBJECT *TargetOp;
-
-
if ((!Name) ||
(!Op->Common.Parent))
{
@@ -437,9 +438,9 @@ AcpiDmValidateName (
if (!TargetOp)
{
/*
- * Didn't find the name in the parse tree. This may be
+ * Didn't find the name in the parse tree. This may be
* a problem, or it may simply be one of the predefined names
- * (such as _OS_). Rather than worry about looking up all
+ * (such as _OS_). Rather than worry about looking up all
* the predefined names, just display the name as given
*/
AcpiOsPrintf (
@@ -447,7 +448,3 @@ AcpiDmValidateName (
}
}
#endif
-
-#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmobject.c b/usr/src/uts/intel/io/acpica/disassembler/dmobject.c
deleted file mode 100644
index 9d5d7dd92c..0000000000
--- a/usr/src/uts/intel/io/acpica/disassembler/dmobject.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: dmobject - ACPI object decode and display
- *
- ******************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2011, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-
-#include "acpi.h"
-#include "accommon.h"
-#include "acnamesp.h"
-#include "acdisasm.h"
-
-
-#ifdef ACPI_DISASSEMBLER
-
-#define _COMPONENT ACPI_CA_DEBUGGER
- ACPI_MODULE_NAME ("dmnames")
-
-/* Local prototypes */
-
-static void
-AcpiDmDecodeNode (
- ACPI_NAMESPACE_NODE *Node);
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDumpMethodInfo
- *
- * PARAMETERS: Status - Method execution status
- * WalkState - Current state of the parse tree walk
- * Op - Executing parse op
- *
- * RETURN: None
- *
- * DESCRIPTION: Called when a method has been aborted because of an error.
- * Dumps the method execution stack, and the method locals/args,
- * and disassembles the AML opcode that failed.
- *
- ******************************************************************************/
-
-void
-AcpiDmDumpMethodInfo (
- ACPI_STATUS Status,
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op)
-{
- ACPI_PARSE_OBJECT *Next;
- ACPI_THREAD_STATE *Thread;
- ACPI_WALK_STATE *NextWalkState;
- ACPI_NAMESPACE_NODE *PreviousMethod = NULL;
-
-
- /* Ignore control codes, they are not errors */
-
- if ((Status & AE_CODE_MASK) == AE_CODE_CONTROL)
- {
- return;
- }
-
- /* We may be executing a deferred opcode */
-
- if (WalkState->DeferredNode)
- {
- AcpiOsPrintf ("Executing subtree for Buffer/Package/Region\n");
- return;
- }
-
- /*
- * If there is no Thread, we are not actually executing a method.
- * This can happen when the iASL compiler calls the interpreter
- * to perform constant folding.
- */
- Thread = WalkState->Thread;
- if (!Thread)
- {
- return;
- }
-
- /* Display exception and method name */
-
- AcpiOsPrintf ("\n**** Exception %s during execution of method ",
- AcpiFormatException (Status));
- AcpiNsPrintNodePathname (WalkState->MethodNode, NULL);
-
- /* Display stack of executing methods */
-
- AcpiOsPrintf ("\n\nMethod Execution Stack:\n");
- NextWalkState = Thread->WalkStateList;
-
- /* Walk list of linked walk states */
-
- while (NextWalkState)
- {
- AcpiOsPrintf (" Method [%4.4s] executing: ",
- AcpiUtGetNodeName (NextWalkState->MethodNode));
-
- /* First method is the currently executing method */
-
- if (NextWalkState == WalkState)
- {
- if (Op)
- {
- /* Display currently executing ASL statement */
-
- Next = Op->Common.Next;
- Op->Common.Next = NULL;
-
- AcpiDmDisassemble (NextWalkState, Op, ACPI_UINT32_MAX);
- Op->Common.Next = Next;
- }
- }
- else
- {
- /*
- * This method has called another method
- * NOTE: the method call parse subtree is already deleted at this
- * point, so we cannot disassemble the method invocation.
- */
- AcpiOsPrintf ("Call to method ");
- AcpiNsPrintNodePathname (PreviousMethod, NULL);
- }
-
- PreviousMethod = NextWalkState->MethodNode;
- NextWalkState = NextWalkState->Next;
- AcpiOsPrintf ("\n");
- }
-
- /* Display the method locals and arguments */
-
- AcpiOsPrintf ("\n");
- AcpiDmDisplayLocals (WalkState);
- AcpiOsPrintf ("\n");
- AcpiDmDisplayArguments (WalkState);
- AcpiOsPrintf ("\n");
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDecodeInternalObject
- *
- * PARAMETERS: ObjDesc - Object to be displayed
- *
- * RETURN: None
- *
- * DESCRIPTION: Short display of an internal object. Numbers/Strings/Buffers.
- *
- ******************************************************************************/
-
-void
-AcpiDmDecodeInternalObject (
- ACPI_OPERAND_OBJECT *ObjDesc)
-{
- UINT32 i;
-
-
- if (!ObjDesc)
- {
- AcpiOsPrintf (" Uninitialized");
- return;
- }
-
- if (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) != ACPI_DESC_TYPE_OPERAND)
- {
- AcpiOsPrintf (" %p [%s]", ObjDesc, AcpiUtGetDescriptorName (ObjDesc));
- return;
- }
-
- AcpiOsPrintf (" %s", AcpiUtGetObjectTypeName (ObjDesc));
-
- switch (ObjDesc->Common.Type)
- {
- case ACPI_TYPE_INTEGER:
-
- AcpiOsPrintf (" %8.8X%8.8X",
- ACPI_FORMAT_UINT64 (ObjDesc->Integer.Value));
- break;
-
-
- case ACPI_TYPE_STRING:
-
- AcpiOsPrintf ("(%u) \"%.24s",
- ObjDesc->String.Length, ObjDesc->String.Pointer);
-
- if (ObjDesc->String.Length > 24)
- {
- AcpiOsPrintf ("...");
- }
- else
- {
- AcpiOsPrintf ("\"");
- }
- break;
-
-
- case ACPI_TYPE_BUFFER:
-
- AcpiOsPrintf ("(%u)", ObjDesc->Buffer.Length);
- for (i = 0; (i < 8) && (i < ObjDesc->Buffer.Length); i++)
- {
- AcpiOsPrintf (" %2.2X", ObjDesc->Buffer.Pointer[i]);
- }
- break;
-
-
- default:
-
- AcpiOsPrintf (" %p", ObjDesc);
- break;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDecodeNode
- *
- * PARAMETERS: Node - Object to be displayed
- *
- * RETURN: None
- *
- * DESCRIPTION: Short display of a namespace node
- *
- ******************************************************************************/
-
-static void
-AcpiDmDecodeNode (
- ACPI_NAMESPACE_NODE *Node)
-{
-
- AcpiOsPrintf ("<Node> Name %4.4s",
- AcpiUtGetNodeName (Node));
-
- if (Node->Flags & ANOBJ_METHOD_ARG)
- {
- AcpiOsPrintf (" [Method Arg]");
- }
- if (Node->Flags & ANOBJ_METHOD_LOCAL)
- {
- AcpiOsPrintf (" [Method Local]");
- }
-
- switch (Node->Type)
- {
- /* These types have no attached object */
-
- case ACPI_TYPE_DEVICE:
- AcpiOsPrintf (" Device");
- break;
-
- case ACPI_TYPE_THERMAL:
- AcpiOsPrintf (" Thermal Zone");
- break;
-
- default:
- AcpiDmDecodeInternalObject (AcpiNsGetAttachedObject (Node));
- break;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDisplayInternalObject
- *
- * PARAMETERS: ObjDesc - Object to be displayed
- * WalkState - Current walk state
- *
- * RETURN: None
- *
- * DESCRIPTION: Short display of an internal object
- *
- ******************************************************************************/
-
-void
-AcpiDmDisplayInternalObject (
- ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState)
-{
- UINT8 Type;
-
-
- AcpiOsPrintf ("%p ", ObjDesc);
-
- if (!ObjDesc)
- {
- AcpiOsPrintf ("<Null Object>\n");
- return;
- }
-
- /* Decode the object type */
-
- switch (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc))
- {
- case ACPI_DESC_TYPE_PARSER:
-
- AcpiOsPrintf ("<Parser> ");
- break;
-
-
- case ACPI_DESC_TYPE_NAMED:
-
- AcpiDmDecodeNode ((ACPI_NAMESPACE_NODE *) ObjDesc);
- break;
-
-
- case ACPI_DESC_TYPE_OPERAND:
-
- Type = ObjDesc->Common.Type;
- if (Type > ACPI_TYPE_LOCAL_MAX)
- {
- AcpiOsPrintf (" Type %X [Invalid Type]", (UINT32) Type);
- return;
- }
-
- /* Decode the ACPI object type */
-
- switch (ObjDesc->Common.Type)
- {
- case ACPI_TYPE_LOCAL_REFERENCE:
-
- AcpiOsPrintf ("[%s] ", AcpiUtGetReferenceName (ObjDesc));
-
- /* Decode the refererence */
-
- switch (ObjDesc->Reference.Class)
- {
- case ACPI_REFCLASS_LOCAL:
-
- AcpiOsPrintf ("%X ", ObjDesc->Reference.Value);
- if (WalkState)
- {
- ObjDesc = WalkState->LocalVariables
- [ObjDesc->Reference.Value].Object;
- AcpiOsPrintf ("%p", ObjDesc);
- AcpiDmDecodeInternalObject (ObjDesc);
- }
- break;
-
-
- case ACPI_REFCLASS_ARG:
-
- AcpiOsPrintf ("%X ", ObjDesc->Reference.Value);
- if (WalkState)
- {
- ObjDesc = WalkState->Arguments
- [ObjDesc->Reference.Value].Object;
- AcpiOsPrintf ("%p", ObjDesc);
- AcpiDmDecodeInternalObject (ObjDesc);
- }
- break;
-
-
- case ACPI_REFCLASS_INDEX:
-
- switch (ObjDesc->Reference.TargetType)
- {
- case ACPI_TYPE_BUFFER_FIELD:
-
- AcpiOsPrintf ("%p", ObjDesc->Reference.Object);
- AcpiDmDecodeInternalObject (ObjDesc->Reference.Object);
- break;
-
- case ACPI_TYPE_PACKAGE:
-
- AcpiOsPrintf ("%p", ObjDesc->Reference.Where);
- if (!ObjDesc->Reference.Where)
- {
- AcpiOsPrintf (" Uninitialized WHERE pointer");
- }
- else
- {
- AcpiDmDecodeInternalObject (
- *(ObjDesc->Reference.Where));
- }
- break;
-
- default:
-
- AcpiOsPrintf ("Unknown index target type");
- break;
- }
- break;
-
-
- case ACPI_REFCLASS_REFOF:
-
- if (!ObjDesc->Reference.Object)
- {
- AcpiOsPrintf ("Uninitialized reference subobject pointer");
- break;
- }
-
- /* Reference can be to a Node or an Operand object */
-
- switch (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc->Reference.Object))
- {
- case ACPI_DESC_TYPE_NAMED:
- AcpiDmDecodeNode (ObjDesc->Reference.Object);
- break;
-
- case ACPI_DESC_TYPE_OPERAND:
- AcpiDmDecodeInternalObject (ObjDesc->Reference.Object);
- break;
-
- default:
- break;
- }
- break;
-
-
- case ACPI_REFCLASS_NAME:
-
- AcpiDmDecodeNode (ObjDesc->Reference.Node);
- break;
-
-
- case ACPI_REFCLASS_DEBUG:
- case ACPI_REFCLASS_TABLE:
-
- AcpiOsPrintf ("\n");
- break;
-
-
- default: /* Unknown reference class */
-
- AcpiOsPrintf ("%2.2X\n", ObjDesc->Reference.Class);
- break;
- }
- break;
-
-
- default:
-
- AcpiOsPrintf ("<Obj> ");
- AcpiDmDecodeInternalObject (ObjDesc);
- break;
- }
- break;
-
-
- default:
-
- AcpiOsPrintf ("<Not a valid ACPI Object Descriptor> [%s]",
- AcpiUtGetDescriptorName (ObjDesc));
- break;
- }
-
- AcpiOsPrintf ("\n");
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDisplayLocals
- *
- * PARAMETERS: WalkState - State for current method
- *
- * RETURN: None
- *
- * DESCRIPTION: Display all locals for the currently running control method
- *
- ******************************************************************************/
-
-void
-AcpiDmDisplayLocals (
- ACPI_WALK_STATE *WalkState)
-{
- UINT32 i;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Node;
-
-
- ObjDesc = WalkState->MethodDesc;
- Node = WalkState->MethodNode;
- if (!Node)
- {
- AcpiOsPrintf (
- "No method node (Executing subtree for buffer or opregion)\n");
- return;
- }
-
- if (Node->Type != ACPI_TYPE_METHOD)
- {
- AcpiOsPrintf ("Executing subtree for Buffer/Package/Region\n");
- return;
- }
-
- AcpiOsPrintf ("Local Variables for method [%4.4s]:\n",
- AcpiUtGetNodeName (Node));
-
- for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++)
- {
- ObjDesc = WalkState->LocalVariables[i].Object;
- AcpiOsPrintf (" Local%X: ", i);
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiDmDisplayArguments
- *
- * PARAMETERS: WalkState - State for current method
- *
- * RETURN: None
- *
- * DESCRIPTION: Display all arguments for the currently running control method
- *
- ******************************************************************************/
-
-void
-AcpiDmDisplayArguments (
- ACPI_WALK_STATE *WalkState)
-{
- UINT32 i;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Node;
-
-
- ObjDesc = WalkState->MethodDesc;
- Node = WalkState->MethodNode;
- if (!Node)
- {
- AcpiOsPrintf (
- "No method node (Executing subtree for buffer or opregion)\n");
- return;
- }
-
- if (Node->Type != ACPI_TYPE_METHOD)
- {
- AcpiOsPrintf ("Executing subtree for Buffer/Package/Region\n");
- return;
- }
-
- AcpiOsPrintf (
- "Arguments for Method [%4.4s]: (%X arguments defined, max concurrency = %X)\n",
- AcpiUtGetNodeName (Node), ObjDesc->Method.ParamCount, ObjDesc->Method.SyncLevel);
-
- for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++)
- {
- ObjDesc = WalkState->Arguments[i].Object;
- AcpiOsPrintf (" Arg%u: ", i);
- AcpiDmDisplayInternalObject (ObjDesc, WalkState);
- }
-}
-
-#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmopcode.c b/usr/src/uts/intel/io/acpica/disassembler/dmopcode.c
index 5da3847714..7ca171cbbb 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmopcode.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmopcode.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,19 +45,395 @@
#include "accommon.h"
#include "acparser.h"
#include "amlcode.h"
-#include "acdisasm.h"
+#include "acinterp.h"
+#include "acnamesp.h"
+#include "acdebug.h"
-#ifdef ACPI_DISASSEMBLER
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dmopcode")
+
/* Local prototypes */
static void
AcpiDmMatchKeyword (
ACPI_PARSE_OBJECT *Op);
+static void
+AcpiDmConvertToElseIf (
+ ACPI_PARSE_OBJECT *Op);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmDisplayTargetPathname
+ *
+ * PARAMETERS: Op - Parse object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: For AML opcodes that have a target operand, display the full
+ * pathname for the target, in a comment field. Handles Return()
+ * statements also.
+ *
+ ******************************************************************************/
+
+void
+AcpiDmDisplayTargetPathname (
+ ACPI_PARSE_OBJECT *Op)
+{
+ ACPI_PARSE_OBJECT *NextOp;
+ ACPI_PARSE_OBJECT *PrevOp = NULL;
+ char *Pathname;
+ const ACPI_OPCODE_INFO *OpInfo;
+
+
+ if (Op->Common.AmlOpcode == AML_RETURN_OP)
+ {
+ PrevOp = Op->Asl.Value.Arg;
+ }
+ else
+ {
+ OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
+ if (!(OpInfo->Flags & AML_HAS_TARGET))
+ {
+ return;
+ }
+
+ /* Target is the last Op in the arg list */
+
+ NextOp = Op->Asl.Value.Arg;
+ while (NextOp)
+ {
+ PrevOp = NextOp;
+ NextOp = PrevOp->Asl.Next;
+ }
+ }
+
+ if (!PrevOp)
+ {
+ return;
+ }
+
+ /* We must have a namepath AML opcode */
+
+ if (PrevOp->Asl.AmlOpcode != AML_INT_NAMEPATH_OP)
+ {
+ return;
+ }
+
+ /* A null string is the "no target specified" case */
+
+ if (!PrevOp->Asl.Value.String)
+ {
+ return;
+ }
+
+ /* No node means "unresolved external reference" */
+
+ if (!PrevOp->Asl.Node)
+ {
+ AcpiOsPrintf (" /* External reference */");
+ return;
+ }
+
+ /* Ignore if path is already from the root */
+
+ if (*PrevOp->Asl.Value.String == '\\')
+ {
+ return;
+ }
+
+ /* Now: we can get the full pathname */
+
+ Pathname = AcpiNsGetExternalPathname (PrevOp->Asl.Node);
+ if (!Pathname)
+ {
+ return;
+ }
+
+ AcpiOsPrintf (" /* %s */", Pathname);
+ ACPI_FREE (Pathname);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmNotifyDescription
+ *
+ * PARAMETERS: Op - Name() parse object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Emit a description comment for the value associated with a
+ * Notify() operator.
+ *
+ ******************************************************************************/
+
+void
+AcpiDmNotifyDescription (
+ ACPI_PARSE_OBJECT *Op)
+{
+ ACPI_PARSE_OBJECT *NextOp;
+ ACPI_NAMESPACE_NODE *Node;
+ UINT8 NotifyValue;
+ UINT8 Type = ACPI_TYPE_ANY;
+
+
+ /* The notify value is the second argument */
+
+ NextOp = Op->Asl.Value.Arg;
+ NextOp = NextOp->Asl.Next;
+
+ switch (NextOp->Common.AmlOpcode)
+ {
+ case AML_ZERO_OP:
+ case AML_ONE_OP:
+
+ NotifyValue = (UINT8) NextOp->Common.AmlOpcode;
+ break;
+
+ case AML_BYTE_OP:
+
+ NotifyValue = (UINT8) NextOp->Asl.Value.Integer;
+ break;
+
+ default:
+ return;
+ }
+
+ /*
+ * Attempt to get the namespace node so we can determine the object type.
+ * Some notify values are dependent on the object type (Device, Thermal,
+ * or Processor).
+ */
+ Node = Op->Asl.Node;
+ if (Node)
+ {
+ Type = Node->Type;
+ }
+
+ AcpiOsPrintf (" // %s", AcpiUtGetNotifyName (NotifyValue, Type));
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmPredefinedDescription
+ *
+ * PARAMETERS: Op - Name() parse object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Emit a description comment for a predefined ACPI name.
+ * Used for iASL compiler only.
+ *
+ ******************************************************************************/
+
+void
+AcpiDmPredefinedDescription (
+ ACPI_PARSE_OBJECT *Op)
+{
+#ifdef ACPI_ASL_COMPILER
+ const AH_PREDEFINED_NAME *Info;
+ char *NameString;
+ int LastCharIsDigit;
+ int LastCharsAreHex;
+
+
+ if (!Op)
+ {
+ return;
+ }
+
+ /* Ensure that the comment field is emitted only once */
+
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_PREDEFINED_CHECKED)
+ {
+ return;
+ }
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_PREDEFINED_CHECKED;
+
+ /* Predefined name must start with an underscore */
+
+ NameString = ACPI_CAST_PTR (char, &Op->Named.Name);
+ if (NameString[0] != '_')
+ {
+ return;
+ }
+
+ /*
+ * Check for the special ACPI names:
+ * _ACd, _ALd, _EJd, _Exx, _Lxx, _Qxx, _Wxx, _T_a
+ * (where d=decimal_digit, x=hex_digit, a=anything)
+ *
+ * Convert these to the generic name for table lookup.
+ * Note: NameString is guaranteed to be upper case here.
+ */
+ LastCharIsDigit =
+ (isdigit ((int) NameString[3])); /* d */
+ LastCharsAreHex =
+ (isxdigit ((int) NameString[2]) && /* xx */
+ isxdigit ((int) NameString[3]));
+
+ switch (NameString[1])
+ {
+ case 'A':
+
+ if ((NameString[2] == 'C') && (LastCharIsDigit))
+ {
+ NameString = "_ACx";
+ }
+ else if ((NameString[2] == 'L') && (LastCharIsDigit))
+ {
+ NameString = "_ALx";
+ }
+ break;
+
+ case 'E':
+
+ if ((NameString[2] == 'J') && (LastCharIsDigit))
+ {
+ NameString = "_EJx";
+ }
+ else if (LastCharsAreHex)
+ {
+ NameString = "_Exx";
+ }
+ break;
+
+ case 'L':
+
+ if (LastCharsAreHex)
+ {
+ NameString = "_Lxx";
+ }
+ break;
+
+ case 'Q':
+
+ if (LastCharsAreHex)
+ {
+ NameString = "_Qxx";
+ }
+ break;
+
+ case 'T':
+
+ if (NameString[2] == '_')
+ {
+ NameString = "_T_x";
+ }
+ break;
+
+ case 'W':
+
+ if (LastCharsAreHex)
+ {
+ NameString = "_Wxx";
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Match the name in the info table */
+
+ Info = AcpiAhMatchPredefinedName (NameString);
+ if (Info)
+ {
+ AcpiOsPrintf (" // %4.4s: %s",
+ NameString, ACPI_CAST_PTR (char, Info->Description));
+ }
+
+#endif
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmFieldPredefinedDescription
+ *
+ * PARAMETERS: Op - Parse object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Emit a description comment for a resource descriptor tag
+ * (which is a predefined ACPI name.) Used for iASL compiler only.
+ *
+ ******************************************************************************/
+
+void
+AcpiDmFieldPredefinedDescription (
+ ACPI_PARSE_OBJECT *Op)
+{
+#ifdef ACPI_ASL_COMPILER
+ ACPI_PARSE_OBJECT *IndexOp;
+ char *Tag;
+ const ACPI_OPCODE_INFO *OpInfo;
+ const AH_PREDEFINED_NAME *Info;
+
+
+ if (!Op)
+ {
+ return;
+ }
+
+ /* Ensure that the comment field is emitted only once */
+
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_PREDEFINED_CHECKED)
+ {
+ return;
+ }
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_PREDEFINED_CHECKED;
+
+ /*
+ * Op must be one of the Create* operators: CreateField, CreateBitField,
+ * CreateByteField, CreateWordField, CreateDwordField, CreateQwordField
+ */
+ OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
+ if (!(OpInfo->Flags & AML_CREATE))
+ {
+ return;
+ }
+
+ /* Second argument is the Index argument */
+
+ IndexOp = Op->Common.Value.Arg;
+ IndexOp = IndexOp->Common.Next;
+
+ /* Index argument must be a namepath */
+
+ if (IndexOp->Common.AmlOpcode != AML_INT_NAMEPATH_OP)
+ {
+ return;
+ }
+
+ /* Major cheat: We previously put the Tag ptr in the Node field */
+
+ Tag = ACPI_CAST_PTR (char, IndexOp->Common.Node);
+ if (!Tag)
+ {
+ return;
+ }
+
+ /* Match the name in the info table */
+
+ Info = AcpiAhMatchPredefinedName (Tag);
+ if (Info)
+ {
+ AcpiOsPrintf (" // %4.4s: %s", Tag,
+ ACPI_CAST_PTR (char, Info->Description));
+ }
+
+#endif
+ return;
+}
+
/*******************************************************************************
*
@@ -195,7 +571,6 @@ AcpiDmRegionFlags (
ACPI_PARSE_OBJECT *Op)
{
-
/* The next Op contains the SpaceId */
Op = AcpiPsGetDepthNext (NULL, Op);
@@ -265,15 +640,14 @@ AcpiDmMatchKeyword (
ACPI_PARSE_OBJECT *Op)
{
-
if (((UINT32) Op->Common.Value.Integer) > ACPI_MAX_MATCH_OPCODE)
{
AcpiOsPrintf ("/* Unknown Match Keyword encoding */");
}
else
{
- AcpiOsPrintf ("%s", ACPI_CAST_PTR (char,
- AcpiGbl_MatchOps[(ACPI_SIZE) Op->Common.Value.Integer]));
+ AcpiOsPrintf ("%s",
+ AcpiGbl_MatchOps[(ACPI_SIZE) Op->Common.Value.Integer]);
}
}
@@ -303,6 +677,8 @@ AcpiDmDisassembleOneOp (
UINT32 Length;
ACPI_PARSE_OBJECT *Child;
ACPI_STATUS Status;
+ UINT8 *Aml;
+ const AH_DEVICE_ID *IdInfo;
if (!Op)
@@ -311,6 +687,11 @@ AcpiDmDisassembleOneOp (
return;
}
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_ELSEIF)
+ {
+ return; /* ElseIf macro was already emitted */
+ }
+
switch (Op->Common.DisasmOpcode)
{
case ACPI_DASM_MATCHOP:
@@ -319,23 +700,28 @@ AcpiDmDisassembleOneOp (
return;
case ACPI_DASM_LNOT_SUFFIX:
- switch (Op->Common.AmlOpcode)
- {
- case AML_LEQUAL_OP:
- AcpiOsPrintf ("LNotEqual");
- break;
- case AML_LGREATER_OP:
- AcpiOsPrintf ("LLessEqual");
- break;
-
- case AML_LLESS_OP:
- AcpiOsPrintf ("LGreaterEqual");
- break;
-
- default:
- break;
+ if (!AcpiGbl_CstyleDisassembly)
+ {
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_LEQUAL_OP:
+ AcpiOsPrintf ("LNotEqual");
+ break;
+
+ case AML_LGREATER_OP:
+ AcpiOsPrintf ("LLessEqual");
+ break;
+
+ case AML_LLESS_OP:
+ AcpiOsPrintf ("LGreaterEqual");
+ break;
+
+ default:
+ break;
+ }
}
+
Op->Common.DisasmOpcode = 0;
Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
return;
@@ -344,7 +730,6 @@ AcpiDmDisassembleOneOp (
break;
}
-
OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
/* The op and arguments */
@@ -372,12 +757,11 @@ AcpiDmDisassembleOneOp (
AcpiOsPrintf ("0x%2.2X", (UINT32) Op->Common.Value.Integer);
break;
-
case AML_WORD_OP:
if (Op->Common.DisasmOpcode == ACPI_DASM_EISAID)
{
- AcpiDmEisaId ((UINT32) Op->Common.Value.Integer);
+ AcpiDmDecompressEisaId ((UINT32) Op->Common.Value.Integer);
}
else
{
@@ -385,12 +769,11 @@ AcpiDmDisassembleOneOp (
}
break;
-
case AML_DWORD_OP:
if (Op->Common.DisasmOpcode == ACPI_DASM_EISAID)
{
- AcpiDmEisaId ((UINT32) Op->Common.Value.Integer);
+ AcpiDmDecompressEisaId ((UINT32) Op->Common.Value.Integer);
}
else
{
@@ -398,24 +781,33 @@ AcpiDmDisassembleOneOp (
}
break;
-
case AML_QWORD_OP:
AcpiOsPrintf ("0x%8.8X%8.8X",
ACPI_FORMAT_UINT64 (Op->Common.Value.Integer));
break;
-
case AML_STRING_OP:
- AcpiUtPrintString (Op->Common.Value.String, ACPI_UINT8_MAX);
- break;
+ AcpiUtPrintString (Op->Common.Value.String, ACPI_UINT16_MAX);
+ /* For _HID/_CID strings, attempt to output a descriptive comment */
- case AML_BUFFER_OP:
+ if (Op->Common.DisasmOpcode == ACPI_DASM_HID_STRING)
+ {
+ /* If we know about the ID, emit the description */
+ IdInfo = AcpiAhMatchHardwareId (Op->Common.Value.String);
+ if (IdInfo)
+ {
+ AcpiOsPrintf (" /* %s */", IdInfo->Description);
+ }
+ }
+ break;
+
+ case AML_BUFFER_OP:
/*
- * Determine the type of buffer. We can have one of the following:
+ * Determine the type of buffer. We can have one of the following:
*
* 1) ResourceTemplate containing Resource Descriptors.
* 2) Unicode String buffer
@@ -426,19 +818,29 @@ AcpiDmDisassembleOneOp (
* types of buffers, we have to closely look at the data in the
* buffer to determine the type.
*/
- Status = AcpiDmIsResourceTemplate (Op);
- if (ACPI_SUCCESS (Status))
+ if (!AcpiGbl_NoResourceDisassembly)
{
- Op->Common.DisasmOpcode = ACPI_DASM_RESOURCE;
- AcpiOsPrintf ("ResourceTemplate");
- break;
+ Status = AcpiDmIsResourceTemplate (WalkState, Op);
+ if (ACPI_SUCCESS (Status))
+ {
+ Op->Common.DisasmOpcode = ACPI_DASM_RESOURCE;
+ AcpiOsPrintf ("ResourceTemplate");
+ break;
+ }
+ else if (Status == AE_AML_NO_RESOURCE_END_TAG)
+ {
+ AcpiOsPrintf (
+ "/**** Is ResourceTemplate, "
+ "but EndTag not at buffer end ****/ ");
+ }
}
- else if (Status == AE_AML_NO_RESOURCE_END_TAG)
+
+ if (AcpiDmIsUuidBuffer (Op))
{
- AcpiOsPrintf ("/**** Is ResourceTemplate, but EndTag not at buffer end ****/ ");
+ Op->Common.DisasmOpcode = ACPI_DASM_UUID;
+ AcpiOsPrintf ("ToUUID (");
}
-
- if (AcpiDmIsUnicodeBuffer (Op))
+ else if (AcpiDmIsUnicodeBuffer (Op))
{
Op->Common.DisasmOpcode = ACPI_DASM_UNICODE;
AcpiOsPrintf ("Unicode (");
@@ -448,33 +850,23 @@ AcpiDmDisassembleOneOp (
Op->Common.DisasmOpcode = ACPI_DASM_STRING;
AcpiOsPrintf ("Buffer");
}
- else
- {
- Op->Common.DisasmOpcode = ACPI_DASM_BUFFER;
- AcpiOsPrintf ("Buffer");
- }
- break;
-
-
- case AML_INT_STATICSTRING_OP:
-
- if (Op->Common.Value.String)
+ else if (AcpiDmIsPldBuffer (Op))
{
- AcpiOsPrintf ("%s", Op->Common.Value.String);
+ Op->Common.DisasmOpcode = ACPI_DASM_PLD_METHOD;
+ AcpiOsPrintf ("ToPLD (");
}
else
{
- AcpiOsPrintf ("\"<NULL STATIC STRING PTR>\"");
+ Op->Common.DisasmOpcode = ACPI_DASM_BUFFER;
+ AcpiOsPrintf ("Buffer");
}
break;
-
case AML_INT_NAMEPATH_OP:
AcpiDmNamestring (Op->Common.Value.Name);
break;
-
case AML_INT_NAMEDFIELD_OP:
Length = AcpiDmDumpName (Op->Named.Name);
@@ -485,7 +877,6 @@ AcpiDmDisassembleOneOp (
Info->BitOffset += (UINT32) Op->Common.Value.Integer;
break;
-
case AML_INT_RESERVEDFIELD_OP:
/* Offset() -- Must account for previous offsets */
@@ -495,7 +886,7 @@ AcpiDmDisassembleOneOp (
if (Info->BitOffset % 8 == 0)
{
- AcpiOsPrintf (" Offset (0x%.2X)", ACPI_DIV_8 (Info->BitOffset));
+ AcpiOsPrintf ("Offset (0x%.2X)", ACPI_DIV_8 (Info->BitOffset));
}
else
{
@@ -505,24 +896,66 @@ AcpiDmDisassembleOneOp (
AcpiDmCommaIfFieldMember (Op);
break;
-
case AML_INT_ACCESSFIELD_OP:
+ case AML_INT_EXTACCESSFIELD_OP:
- AcpiOsPrintf (" AccessAs (%s, ",
- AcpiGbl_AccessTypes [(UINT32) (Op->Common.Value.Integer >> 8) & 0x7]);
+ AcpiOsPrintf ("AccessAs (%s, ",
+ AcpiGbl_AccessTypes [(UINT32) (Op->Common.Value.Integer & 0x7)]);
+
+ AcpiDmDecodeAttribute ((UINT8) (Op->Common.Value.Integer >> 8));
+
+ if (Op->Common.AmlOpcode == AML_INT_EXTACCESSFIELD_OP)
+ {
+ AcpiOsPrintf (" (0x%2.2X)", (unsigned)
+ ((Op->Common.Value.Integer >> 16) & 0xFF));
+ }
- AcpiDmDecodeAttribute ((UINT8) Op->Common.Value.Integer);
AcpiOsPrintf (")");
AcpiDmCommaIfFieldMember (Op);
break;
+ case AML_INT_CONNECTION_OP:
+ /*
+ * Two types of Connection() - one with a buffer object, the
+ * other with a namestring that points to a buffer object.
+ */
+ AcpiOsPrintf ("Connection (");
+ Child = Op->Common.Value.Arg;
+
+ if (Child->Common.AmlOpcode == AML_INT_BYTELIST_OP)
+ {
+ AcpiOsPrintf ("\n");
+
+ Aml = Child->Named.Data;
+ Length = (UINT32) Child->Common.Value.Integer;
+
+ Info->Level += 1;
+ Info->MappingOp = Op;
+ Op->Common.DisasmOpcode = ACPI_DASM_RESOURCE;
+
+ AcpiDmResourceTemplate (Info, Op->Common.Parent, Aml, Length);
+
+ Info->Level -= 1;
+ AcpiDmIndent (Info->Level);
+ }
+ else
+ {
+ AcpiDmNamestring (Child->Common.Value.Name);
+ }
+
+ AcpiOsPrintf (")");
+ AcpiDmCommaIfFieldMember (Op);
+ AcpiOsPrintf ("\n");
+
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; /* for now, ignore in AcpiDmAscendingOp */
+ Child->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ break;
case AML_INT_BYTELIST_OP:
AcpiDmByteList (Info, Op);
break;
-
case AML_INT_METHODCALL_OP:
Op = AcpiPsGetDepthNext (NULL, Op);
@@ -531,6 +964,23 @@ AcpiDmDisassembleOneOp (
AcpiDmNamestring (Op->Common.Value.Name);
break;
+ case AML_ELSE_OP:
+
+ AcpiDmConvertToElseIf (Op);
+ break;
+
+ case AML_EXTERNAL_OP:
+
+ if (AcpiGbl_DmEmitExternalOpcodes)
+ {
+ AcpiOsPrintf ("/* Opcode 0x15 */ ");
+
+ /* Fallthrough */
+ }
+ else
+ {
+ break;
+ }
default:
@@ -546,7 +996,7 @@ AcpiDmDisassembleOneOp (
(WalkState->Results) &&
(WalkState->ResultCount))
{
- AcpiDmDecodeInternalObject (
+ AcpiDbDecodeInternalObject (
WalkState->Results->Results.ObjDesc [
(WalkState->ResultCount - 1) %
ACPI_RESULTS_FRAME_OBJ_NUM]);
@@ -557,4 +1007,118 @@ AcpiDmDisassembleOneOp (
}
}
-#endif /* ACPI_DISASSEMBLER */
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmConvertToElseIf
+ *
+ * PARAMETERS: OriginalElseOp - ELSE Object to be examined
+ *
+ * RETURN: None. Emits either an "Else" or an "ElseIf" ASL operator.
+ *
+ * DESCRIPTION: Detect and convert an If..Else..If sequence to If..ElseIf
+ *
+ * EXAMPLE:
+ *
+ * This If..Else..If nested sequence:
+ *
+ * If (Arg0 == 1)
+ * {
+ * Local0 = 4
+ * }
+ * Else
+ * {
+ * If (Arg0 == 2)
+ * {
+ * Local0 = 5
+ * }
+ * }
+ *
+ * Is converted to this simpler If..ElseIf sequence:
+ *
+ * If (Arg0 == 1)
+ * {
+ * Local0 = 4
+ * }
+ * ElseIf (Arg0 == 2)
+ * {
+ * Local0 = 5
+ * }
+ *
+ * NOTE: There is no actual ElseIf AML opcode. ElseIf is essentially an ASL
+ * macro that emits an Else opcode followed by an If opcode. This function
+ * reverses these AML sequences back to an ElseIf macro where possible. This
+ * can make the disassembled ASL code simpler and more like the original code.
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmConvertToElseIf (
+ ACPI_PARSE_OBJECT *OriginalElseOp)
+{
+ ACPI_PARSE_OBJECT *IfOp;
+ ACPI_PARSE_OBJECT *ElseOp;
+
+
+ /*
+ * To be able to perform the conversion, two conditions must be satisfied:
+ * 1) The first child of the Else must be an If statement.
+ * 2) The If block can only be followed by an Else block and these must
+ * be the only blocks under the original Else.
+ */
+ IfOp = OriginalElseOp->Common.Value.Arg;
+ if (!IfOp ||
+ (IfOp->Common.AmlOpcode != AML_IF_OP) ||
+ (IfOp->Asl.Next && (IfOp->Asl.Next->Common.AmlOpcode != AML_ELSE_OP)))
+ {
+ /* Not an Else..If sequence, cannot convert to ElseIf */
+
+ AcpiOsPrintf ("%s", "Else");
+ return;
+ }
+
+ /* Emit ElseIf, mark the IF as now an ELSEIF */
+
+ AcpiOsPrintf ("%s", "ElseIf");
+ IfOp->Common.DisasmFlags |= ACPI_PARSEOP_ELSEIF;
+
+ /* The IF parent will now be the same as the original ELSE parent */
+
+ IfOp->Common.Parent = OriginalElseOp->Common.Parent;
+
+ /*
+ * Update the NEXT pointers to restructure the parse tree, essentially
+ * promoting an If..Else block up to the same level as the original
+ * Else.
+ *
+ * Check if the IF has a corresponding ELSE peer
+ */
+ ElseOp = IfOp->Common.Next;
+ if (ElseOp &&
+ (ElseOp->Common.AmlOpcode == AML_ELSE_OP))
+ {
+ /* If an ELSE matches the IF, promote it also */
+
+ ElseOp->Common.Parent = OriginalElseOp->Common.Parent;
+ ElseOp->Common.Next = OriginalElseOp->Common.Next;
+ }
+ else
+ {
+ /* Otherwise, set the IF NEXT to the original ELSE NEXT */
+
+ IfOp->Common.Next = OriginalElseOp->Common.Next;
+ }
+
+ /* Detach the child IF block from the original ELSE */
+
+ OriginalElseOp->Common.Value.Arg = NULL;
+
+ /* Ignore the original ELSE from now on */
+
+ OriginalElseOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ OriginalElseOp->Common.DisasmOpcode = ACPI_DASM_LNOT_PREFIX;
+
+ /* Insert IF (now ELSEIF) as next peer of the original ELSE */
+
+ OriginalElseOp->Common.Next = IfOp;
+}
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmresrc.c b/usr/src/uts/intel/io/acpica/disassembler/dmresrc.c
index fe208d5260..a486f1d892 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmresrc.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmresrc.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,13 +41,11 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
#include "acdisasm.h"
-#ifdef ACPI_DISASSEMBLER
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dbresrc")
@@ -55,12 +53,6 @@
/* Dispatch tables for Resource disassembly functions */
-typedef
-void (*ACPI_RESOURCE_HANDLER) (
- AML_RESOURCE *Resource,
- UINT32 Length,
- UINT32 Level);
-
static ACPI_RESOURCE_HANDLER AcpiGbl_DmResourceDispatch [] =
{
/* Small descriptors */
@@ -75,7 +67,7 @@ static ACPI_RESOURCE_HANDLER AcpiGbl_DmResourceDispatch [] =
AcpiDmEndDependentDescriptor, /* 0x07, ACPI_RESOURCE_NAME_END_DEPENDENT */
AcpiDmIoDescriptor, /* 0x08, ACPI_RESOURCE_NAME_IO_PORT */
AcpiDmFixedIoDescriptor, /* 0x09, ACPI_RESOURCE_NAME_FIXED_IO_PORT */
- NULL, /* 0x0A, Reserved */
+ AcpiDmFixedDmaDescriptor, /* 0x0A, ACPI_RESOURCE_NAME_FIXED_DMA */
NULL, /* 0x0B, Reserved */
NULL, /* 0x0C, Reserved */
NULL, /* 0x0D, Reserved */
@@ -95,7 +87,10 @@ static ACPI_RESOURCE_HANDLER AcpiGbl_DmResourceDispatch [] =
AcpiDmWordDescriptor, /* 0x08, ACPI_RESOURCE_NAME_WORD_ADDRESS_SPACE */
AcpiDmInterruptDescriptor, /* 0x09, ACPI_RESOURCE_NAME_EXTENDED_XRUPT */
AcpiDmQwordDescriptor, /* 0x0A, ACPI_RESOURCE_NAME_QWORD_ADDRESS_SPACE */
- AcpiDmExtendedDescriptor /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS_SPACE */
+ AcpiDmExtendedDescriptor, /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS_SPACE */
+ AcpiDmGpioDescriptor, /* 0x0C, ACPI_RESOURCE_NAME_GPIO */
+ NULL, /* 0x0D, Reserved */
+ AcpiDmSerialBusDescriptor /* 0x0E, ACPI_RESOURCE_NAME_SERIAL_BUS */
};
@@ -150,7 +145,7 @@ AcpiDmDescriptorName (
void
AcpiDmDumpInteger8 (
UINT8 Value,
- char *Name)
+ const char *Name)
{
AcpiOsPrintf ("0x%2.2X, // %s\n", Value, Name);
}
@@ -158,7 +153,7 @@ AcpiDmDumpInteger8 (
void
AcpiDmDumpInteger16 (
UINT16 Value,
- char *Name)
+ const char *Name)
{
AcpiOsPrintf ("0x%4.4X, // %s\n", Value, Name);
}
@@ -166,7 +161,7 @@ AcpiDmDumpInteger16 (
void
AcpiDmDumpInteger32 (
UINT32 Value,
- char *Name)
+ const char *Name)
{
AcpiOsPrintf ("0x%8.8X, // %s\n", Value, Name);
}
@@ -174,7 +169,7 @@ AcpiDmDumpInteger32 (
void
AcpiDmDumpInteger64 (
UINT64 Value,
- char *Name)
+ const char *Name)
{
AcpiOsPrintf ("0x%8.8X%8.8X, // %s\n", ACPI_FORMAT_UINT64 (Value), Name);
}
@@ -217,6 +212,7 @@ AcpiDmBitList (
{
AcpiOsPrintf (",");
}
+
Previous = TRUE;
AcpiOsPrintf ("%u", i);
}
@@ -263,6 +259,11 @@ AcpiDmResourceTemplate (
ACPI_NAMESPACE_NODE *Node;
+ if (Op->Asl.AmlOpcode != AML_FIELD_OP)
+ {
+ Info->MappingOp = Op;
+ }
+
Level = Info->Level;
ResourceName = ACPI_DEFAULT_RESNAME;
Node = Op->Common.Node;
@@ -282,10 +283,11 @@ AcpiDmResourceTemplate (
/* Validate the Resource Type and Resource Length */
- Status = AcpiUtValidateResource (Aml, &ResourceIndex);
+ Status = AcpiUtValidateResource (NULL, Aml, &ResourceIndex);
if (ACPI_FAILURE (Status))
{
- AcpiOsPrintf ("/*** Could not validate Resource, type (%X) %s***/\n",
+ AcpiOsPrintf (
+ "/*** Could not validate Resource, type (%X) %s***/\n",
ResourceType, AcpiFormatException (Status));
return;
}
@@ -331,15 +333,17 @@ AcpiDmResourceTemplate (
/* Go ahead and insert EndDependentFn() */
- AcpiDmEndDependentDescriptor (Aml, ResourceLength, Level);
+ AcpiDmEndDependentDescriptor (Info, Aml, ResourceLength, Level);
AcpiDmIndent (Level);
AcpiOsPrintf (
- "/*** Disassembler: inserted missing EndDependentFn () ***/\n");
+ "/*** Disassembler: inserted "
+ "missing EndDependentFn () ***/\n");
}
return;
default:
+
break;
}
@@ -352,7 +356,7 @@ AcpiDmResourceTemplate (
}
AcpiGbl_DmResourceDispatch [ResourceIndex] (
- Aml, ResourceLength, Level);
+ Info, Aml, ResourceLength, Level);
/* Descriptor post-processing */
@@ -369,17 +373,19 @@ AcpiDmResourceTemplate (
*
* FUNCTION: AcpiDmIsResourceTemplate
*
- * PARAMETERS: Op - Buffer Op to be examined
+ * PARAMETERS: WalkState - Current walk info
+ * Op - Buffer Op to be examined
*
* RETURN: Status. AE_OK if valid template
*
* DESCRIPTION: Walk a byte list to determine if it consists of a valid set
- * of resource descriptors. Nothing is output.
+ * of resource descriptors. Nothing is output.
*
******************************************************************************/
ACPI_STATUS
AcpiDmIsResourceTemplate (
+ ACPI_WALK_STATE *WalkState,
ACPI_PARSE_OBJECT *Op)
{
ACPI_STATUS Status;
@@ -399,6 +405,12 @@ AcpiDmIsResourceTemplate (
/* Get the ByteData list and length */
NextOp = Op->Common.Value.Arg;
+ if (!NextOp)
+ {
+ AcpiOsPrintf ("NULL byte list in buffer\n");
+ return (AE_TYPE);
+ }
+
NextOp = NextOp->Common.Next;
if (!NextOp)
{
@@ -410,7 +422,8 @@ AcpiDmIsResourceTemplate (
/* Walk the byte list, abort on any invalid descriptor type or length */
- Status = AcpiUtWalkAmlResources (Aml, Length, NULL, &EndAml);
+ Status = AcpiUtWalkAmlResources (WalkState, Aml, Length,
+ NULL, ACPI_CAST_INDIRECT_PTR (void, &EndAml));
if (ACPI_FAILURE (Status))
{
return (AE_TYPE);
@@ -433,5 +446,3 @@ AcpiDmIsResourceTemplate (
*/
return (AE_OK);
}
-
-#endif
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c b/usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c
index 2284928752..52fd3356b8 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmresrcl.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,21 +41,18 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acdisasm.h"
-#ifdef ACPI_DISASSEMBLER
-
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dbresrcl")
/* Common names for address and memory descriptors */
-static char *AcpiDmAddressNames[] =
+static const char *AcpiDmAddressNames[] =
{
"Granularity",
"Range Minimum",
@@ -64,7 +61,7 @@ static char *AcpiDmAddressNames[] =
"Length"
};
-static char *AcpiDmMemoryNames[] =
+static const char *AcpiDmMemoryNames[] =
{
"Range Minimum",
"Range Maximum",
@@ -153,16 +150,19 @@ AcpiDmMemoryFields (
switch (Type)
{
case 16:
+
AcpiDmDumpInteger16 (ACPI_CAST_PTR (UINT16, Source)[i],
AcpiDmMemoryNames[i]);
break;
case 32:
+
AcpiDmDumpInteger32 (ACPI_CAST_PTR (UINT32, Source)[i],
AcpiDmMemoryNames[i]);
break;
default:
+
return;
}
}
@@ -201,21 +201,25 @@ AcpiDmAddressFields (
switch (Type)
{
case 16:
+
AcpiDmDumpInteger16 (ACPI_CAST_PTR (UINT16, Source)[i],
AcpiDmAddressNames[i]);
break;
case 32:
+
AcpiDmDumpInteger32 (ACPI_CAST_PTR (UINT32, Source)[i],
AcpiDmAddressNames[i]);
break;
case 64:
+
AcpiDmDumpInteger64 (ACPI_CAST_PTR (UINT64, Source)[i],
AcpiDmAddressNames[i]);
break;
default:
+
return;
}
}
@@ -242,22 +246,27 @@ AcpiDmAddressPrefix (
switch (Type)
{
case ACPI_RESOURCE_TYPE_ADDRESS16:
+
AcpiOsPrintf ("Word");
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
+
AcpiOsPrintf ("DWord");
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
+
AcpiOsPrintf ("QWord");
break;
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
+
AcpiOsPrintf ("Extended");
break;
default:
+
return;
}
}
@@ -298,7 +307,8 @@ AcpiDmAddressCommon (
if ((ResourceType > 2) && (ResourceType < 0xC0))
{
- AcpiOsPrintf ("/**** Invalid Resource Type: 0x%X ****/", ResourceType);
+ AcpiOsPrintf (
+ "/**** Invalid Resource Type: 0x%X ****/", ResourceType);
return;
}
@@ -318,7 +328,8 @@ AcpiDmAddressCommon (
/* This is either a Memory, IO, or BusNumber descriptor (0,1,2) */
- AcpiOsPrintf ("%s (", AcpiGbl_WordDecode [ResourceType & 0x3]);
+ AcpiOsPrintf ("%s (",
+ AcpiGbl_WordDecode [ACPI_GET_2BIT_FLAG (ResourceType)]);
/* Decode the general and type-specific flags */
@@ -331,7 +342,8 @@ AcpiDmAddressCommon (
AcpiDmIoFlags (Flags);
if (ResourceType == ACPI_IO_RANGE)
{
- AcpiOsPrintf (" %s,", AcpiGbl_RngDecode [SpecificFlags & 0x3]);
+ AcpiOsPrintf (" %s,",
+ AcpiGbl_RngDecode [ACPI_GET_2BIT_FLAG (SpecificFlags)]);
}
}
}
@@ -383,10 +395,10 @@ AcpiDmSpaceFlags (
{
AcpiOsPrintf ("%s, %s, %s, %s,",
- AcpiGbl_ConsumeDecode [(Flags & 1)],
- AcpiGbl_DecDecode [(Flags & 0x2) >> 1],
- AcpiGbl_MinDecode [(Flags & 0x4) >> 2],
- AcpiGbl_MaxDecode [(Flags & 0x8) >> 3]);
+ AcpiGbl_ConsumeDecode [ACPI_GET_1BIT_FLAG (Flags)],
+ AcpiGbl_DecDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 1)],
+ AcpiGbl_MinDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 2)],
+ AcpiGbl_MaxDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 3)]);
}
@@ -407,10 +419,10 @@ AcpiDmIoFlags (
UINT8 Flags)
{
AcpiOsPrintf ("%s, %s, %s, %s,",
- AcpiGbl_ConsumeDecode [(Flags & 1)],
- AcpiGbl_MinDecode [(Flags & 0x4) >> 2],
- AcpiGbl_MaxDecode [(Flags & 0x8) >> 3],
- AcpiGbl_DecDecode [(Flags & 0x2) >> 1]);
+ AcpiGbl_ConsumeDecode [ACPI_GET_1BIT_FLAG (Flags)],
+ AcpiGbl_MinDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 2)],
+ AcpiGbl_MaxDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 3)],
+ AcpiGbl_DecDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 1)]);
}
@@ -432,14 +444,14 @@ AcpiDmIoFlags2 (
{
AcpiOsPrintf (", %s",
- AcpiGbl_TtpDecode [(SpecificFlags & 0x10) >> 4]);
+ AcpiGbl_TtpDecode [ACPI_EXTRACT_1BIT_FLAG (SpecificFlags, 4)]);
/* TRS is only used if TTP is TypeTranslation */
if (SpecificFlags & 0x10)
{
AcpiOsPrintf (", %s",
- AcpiGbl_TrsDecode [(SpecificFlags & 0x20) >> 5]);
+ AcpiGbl_TrsDecode [ACPI_EXTRACT_1BIT_FLAG (SpecificFlags, 5)]);
}
}
@@ -464,12 +476,12 @@ AcpiDmMemoryFlags (
{
AcpiOsPrintf ("%s, %s, %s, %s, %s, %s,",
- AcpiGbl_ConsumeDecode [(Flags & 1)],
- AcpiGbl_DecDecode [(Flags & 0x2) >> 1],
- AcpiGbl_MinDecode [(Flags & 0x4) >> 2],
- AcpiGbl_MaxDecode [(Flags & 0x8) >> 3],
- AcpiGbl_MemDecode [(SpecificFlags & 0x6) >> 1],
- AcpiGbl_RwDecode [(SpecificFlags & 0x1)]);
+ AcpiGbl_ConsumeDecode [ACPI_GET_1BIT_FLAG (Flags)],
+ AcpiGbl_DecDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 1)],
+ AcpiGbl_MinDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 2)],
+ AcpiGbl_MaxDecode [ACPI_EXTRACT_1BIT_FLAG (Flags, 3)],
+ AcpiGbl_MemDecode [ACPI_EXTRACT_2BIT_FLAG (SpecificFlags, 1)],
+ AcpiGbl_RwDecode [ACPI_GET_1BIT_FLAG (SpecificFlags)]);
}
@@ -491,8 +503,8 @@ AcpiDmMemoryFlags2 (
{
AcpiOsPrintf (", %s, %s",
- AcpiGbl_MtpDecode [(SpecificFlags & 0x18) >> 3],
- AcpiGbl_TtpDecode [(SpecificFlags & 0x20) >> 5]);
+ AcpiGbl_MtpDecode [ACPI_EXTRACT_2BIT_FLAG (SpecificFlags, 3)],
+ AcpiGbl_TtpDecode [ACPI_EXTRACT_1BIT_FLAG (SpecificFlags, 5)]);
}
@@ -553,7 +565,7 @@ AcpiDmResourceSource (
if (TotalLength > (MinimumTotalLength + 1))
{
AcpiOsPrintf (" ");
- AcpiUtPrintString ((char *) &AmlResourceSource[1], ACPI_UINT8_MAX);
+ AcpiUtPrintString ((char *) &AmlResourceSource[1], ACPI_UINT16_MAX);
}
AcpiOsPrintf (", ");
@@ -564,7 +576,8 @@ AcpiDmResourceSource (
*
* FUNCTION: AcpiDmWordDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -576,6 +589,7 @@ AcpiDmResourceSource (
void
AcpiDmWordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -609,7 +623,8 @@ AcpiDmWordDescriptor (
*
* FUNCTION: AcpiDmDwordDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -621,6 +636,7 @@ AcpiDmWordDescriptor (
void
AcpiDmDwordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -654,7 +670,8 @@ AcpiDmDwordDescriptor (
*
* FUNCTION: AcpiDmQwordDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -666,6 +683,7 @@ AcpiDmDwordDescriptor (
void
AcpiDmQwordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -699,7 +717,8 @@ AcpiDmQwordDescriptor (
*
* FUNCTION: AcpiDmExtendedDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -711,6 +730,7 @@ AcpiDmQwordDescriptor (
void
AcpiDmExtendedDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -718,7 +738,8 @@ AcpiDmExtendedDescriptor (
/* Dump resource name and flags */
- AcpiDmAddressCommon (Resource, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64, Level);
+ AcpiDmAddressCommon (
+ Resource, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64, Level);
/* Dump the 5 contiguous QWORD values */
@@ -746,7 +767,8 @@ AcpiDmExtendedDescriptor (
*
* FUNCTION: AcpiDmMemory24Descriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -758,6 +780,7 @@ AcpiDmExtendedDescriptor (
void
AcpiDmMemory24Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -767,7 +790,7 @@ AcpiDmMemory24Descriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("Memory24 (%s,\n",
- AcpiGbl_RwDecode [Resource->Memory24.Flags & 1]);
+ AcpiGbl_RwDecode [ACPI_GET_1BIT_FLAG (Resource->Memory24.Flags)]);
/* Dump the 4 contiguous WORD values */
@@ -785,7 +808,8 @@ AcpiDmMemory24Descriptor (
*
* FUNCTION: AcpiDmMemory32Descriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -797,6 +821,7 @@ AcpiDmMemory24Descriptor (
void
AcpiDmMemory32Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -806,7 +831,7 @@ AcpiDmMemory32Descriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("Memory32 (%s,\n",
- AcpiGbl_RwDecode [Resource->Memory32.Flags & 1]);
+ AcpiGbl_RwDecode [ACPI_GET_1BIT_FLAG (Resource->Memory32.Flags)]);
/* Dump the 4 contiguous DWORD values */
@@ -824,7 +849,8 @@ AcpiDmMemory32Descriptor (
*
* FUNCTION: AcpiDmFixedMemory32Descriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -836,6 +862,7 @@ AcpiDmMemory32Descriptor (
void
AcpiDmFixedMemory32Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -845,13 +872,15 @@ AcpiDmFixedMemory32Descriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("Memory32Fixed (%s,\n",
- AcpiGbl_RwDecode [Resource->FixedMemory32.Flags & 1]);
+ AcpiGbl_RwDecode [ACPI_GET_1BIT_FLAG (Resource->FixedMemory32.Flags)]);
AcpiDmIndent (Level + 1);
- AcpiDmDumpInteger32 (Resource->FixedMemory32.Address, "Address Base");
+ AcpiDmDumpInteger32 (Resource->FixedMemory32.Address,
+ "Address Base");
AcpiDmIndent (Level + 1);
- AcpiDmDumpInteger32 (Resource->FixedMemory32.AddressLength, "Address Length");
+ AcpiDmDumpInteger32 (Resource->FixedMemory32.AddressLength,
+ "Address Length");
/* Insert a descriptor name */
@@ -865,7 +894,8 @@ AcpiDmFixedMemory32Descriptor (
*
* FUNCTION: AcpiDmGenericRegisterDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -877,6 +907,7 @@ AcpiDmFixedMemory32Descriptor (
void
AcpiDmGenericRegisterDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -921,7 +952,8 @@ AcpiDmGenericRegisterDescriptor (
*
* FUNCTION: AcpiDmInterruptDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -933,6 +965,7 @@ AcpiDmGenericRegisterDescriptor (
void
AcpiDmInterruptDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -942,10 +975,10 @@ AcpiDmInterruptDescriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("Interrupt (%s, %s, %s, %s, ",
- AcpiGbl_ConsumeDecode [(Resource->ExtendedIrq.Flags & 1)],
- AcpiGbl_HeDecode [(Resource->ExtendedIrq.Flags >> 1) & 1],
- AcpiGbl_LlDecode [(Resource->ExtendedIrq.Flags >> 2) & 1],
- AcpiGbl_ShrDecode [(Resource->ExtendedIrq.Flags >> 3) & 1]);
+ AcpiGbl_ConsumeDecode [ACPI_GET_1BIT_FLAG (Resource->ExtendedIrq.Flags)],
+ AcpiGbl_HeDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->ExtendedIrq.Flags, 1)],
+ AcpiGbl_LlDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->ExtendedIrq.Flags, 2)],
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->ExtendedIrq.Flags, 3)]);
/*
* The ResourceSource fields are optional and appear after the interrupt
@@ -995,7 +1028,7 @@ AcpiDmInterruptDescriptor (
void
AcpiDmVendorCommon (
- char *Name,
+ const char *Name,
UINT8 *ByteData,
UINT32 Length,
UINT32 Level)
@@ -1027,7 +1060,8 @@ AcpiDmVendorCommon (
*
* FUNCTION: AcpiDmVendorLargeDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -1039,6 +1073,7 @@ AcpiDmVendorCommon (
void
AcpiDmVendorLargeDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -1048,6 +1083,3 @@ AcpiDmVendorLargeDescriptor (
ACPI_ADD_PTR (UINT8, Resource, sizeof (AML_RESOURCE_LARGE_HEADER)),
Length, Level);
}
-
-#endif
-
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmresrcl2.c b/usr/src/uts/intel/io/acpica/disassembler/dmresrcl2.c
new file mode 100644
index 0000000000..2e3273113c
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmresrcl2.c
@@ -0,0 +1,750 @@
+/*******************************************************************************
+ *
+ * Module Name: dmresrcl2.c - "Large" Resource Descriptor disassembly (#2)
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acdisasm.h"
+
+
+#define _COMPONENT ACPI_CA_DEBUGGER
+ ACPI_MODULE_NAME ("dbresrcl2")
+
+/* Local prototypes */
+
+static void
+AcpiDmI2cSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+static void
+AcpiDmSpiSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+static void
+AcpiDmUartSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+static void
+AcpiDmGpioCommon (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Level);
+
+static void
+AcpiDmDumpRawDataBuffer (
+ UINT8 *Buffer,
+ UINT32 Length,
+ UINT32 Level);
+
+
+/* Dispatch table for the serial bus descriptors */
+
+static ACPI_RESOURCE_HANDLER SerialBusResourceDispatch [] =
+{
+ NULL,
+ AcpiDmI2cSerialBusDescriptor,
+ AcpiDmSpiSerialBusDescriptor,
+ AcpiDmUartSerialBusDescriptor
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmDumpRawDataBuffer
+ *
+ * PARAMETERS: Buffer - Pointer to the data bytes
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump a data buffer as a RawDataBuffer() object. Used for
+ * vendor data bytes.
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmDumpRawDataBuffer (
+ UINT8 *Buffer,
+ UINT32 Length,
+ UINT32 Level)
+{
+ UINT32 Index;
+ UINT32 i;
+ UINT32 j;
+
+
+ if (!Length)
+ {
+ return;
+ }
+
+ AcpiOsPrintf ("RawDataBuffer (0x%.2X) // Vendor Data", Length);
+
+ AcpiOsPrintf ("\n");
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("{\n");
+ AcpiDmIndent (Level + 2);
+
+ for (i = 0; i < Length;)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ Index = i + j;
+ if (Index >= Length)
+ {
+ goto Finish;
+ }
+
+ AcpiOsPrintf ("0x%2.2X", Buffer[Index]);
+ if ((Index + 1) >= Length)
+ {
+ goto Finish;
+ }
+
+ AcpiOsPrintf (", ");
+ }
+
+ AcpiOsPrintf ("\n");
+ AcpiDmIndent (Level + 2);
+
+ i += 8;
+ }
+
+Finish:
+ AcpiOsPrintf ("\n");
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("}");
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmGpioCommon
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode common parts of a GPIO Interrupt descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmGpioCommon (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Level)
+{
+ UINT16 *PinList;
+ UINT8 *VendorData;
+ char *DeviceName = NULL;
+ UINT32 PinCount;
+ UINT32 i;
+
+
+ /* ResourceSource, ResourceSourceIndex, ResourceType */
+
+ AcpiDmIndent (Level + 1);
+ if (Resource->Gpio.ResSourceOffset)
+ {
+ DeviceName = ACPI_ADD_PTR (char,
+ Resource, Resource->Gpio.ResSourceOffset),
+ AcpiUtPrintString (DeviceName, ACPI_UINT16_MAX);
+ }
+
+ AcpiOsPrintf (", ");
+ AcpiOsPrintf ("0x%2.2X, ", Resource->Gpio.ResSourceIndex);
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_ConsumeDecode [ACPI_GET_1BIT_FLAG (Resource->Gpio.Flags)]);
+
+ /* Insert a descriptor name */
+
+ AcpiDmDescriptorName ();
+ AcpiOsPrintf (",");
+
+ /* Dump the vendor data */
+
+ if (Resource->Gpio.VendorOffset)
+ {
+ AcpiOsPrintf ("\n");
+ AcpiDmIndent (Level + 1);
+ VendorData = ACPI_ADD_PTR (UINT8, Resource,
+ Resource->Gpio.VendorOffset);
+
+ AcpiDmDumpRawDataBuffer (VendorData,
+ Resource->Gpio.VendorLength, Level);
+ }
+
+ AcpiOsPrintf (")\n");
+
+ /* Dump the interrupt list */
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("{ // Pin list\n");
+
+ PinCount = ((UINT32) (Resource->Gpio.ResSourceOffset -
+ Resource->Gpio.PinTableOffset)) /
+ sizeof (UINT16);
+
+ PinList = (UINT16 *) ACPI_ADD_PTR (char, Resource,
+ Resource->Gpio.PinTableOffset);
+
+ for (i = 0; i < PinCount; i++)
+ {
+ AcpiDmIndent (Level + 2);
+ AcpiOsPrintf ("0x%4.4X%s\n", PinList[i],
+ ((i + 1) < PinCount) ? "," : "");
+ }
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("}\n");
+
+#ifdef ACPI_APPLICATION
+ MpSaveGpioInfo (Info->MappingOp, Resource,
+ PinCount, PinList, DeviceName);
+#endif
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmGpioIntDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a GPIO Interrupt descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmGpioIntDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+
+ /* Dump the GpioInt-specific portion of the descriptor */
+
+ /* EdgeLevel, ActiveLevel, Shared */
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("GpioInt (%s, %s, %s, ",
+ AcpiGbl_HeDecode [ACPI_GET_1BIT_FLAG (Resource->Gpio.IntFlags)],
+ AcpiGbl_LlDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->Gpio.IntFlags, 1)],
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->Gpio.IntFlags, 3)]);
+
+ /* PinConfig, DebounceTimeout */
+
+ if (Resource->Gpio.PinConfig <= 3)
+ {
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_PpcDecode[Resource->Gpio.PinConfig]);
+ }
+ else
+ {
+ AcpiOsPrintf ("0x%2.2X, ", Resource->Gpio.PinConfig);
+ }
+ AcpiOsPrintf ("0x%4.4X,\n", Resource->Gpio.DebounceTimeout);
+
+ /* Dump the GpioInt/GpioIo common portion of the descriptor */
+
+ AcpiDmGpioCommon (Info, Resource, Level);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmGpioIoDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a GPIO I/O descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmGpioIoDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+
+ /* Dump the GpioIo-specific portion of the descriptor */
+
+ /* Shared, PinConfig */
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("GpioIo (%s, ",
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->Gpio.IntFlags, 3)]);
+
+ if (Resource->Gpio.PinConfig <= 3)
+ {
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_PpcDecode[Resource->Gpio.PinConfig]);
+ }
+ else
+ {
+ AcpiOsPrintf ("0x%2.2X, ", Resource->Gpio.PinConfig);
+ }
+
+ /* DebounceTimeout, DriveStrength, IoRestriction */
+
+ AcpiOsPrintf ("0x%4.4X, ", Resource->Gpio.DebounceTimeout);
+ AcpiOsPrintf ("0x%4.4X, ", Resource->Gpio.DriveStrength);
+ AcpiOsPrintf ("%s,\n",
+ AcpiGbl_IorDecode [ACPI_GET_2BIT_FLAG (Resource->Gpio.IntFlags)]);
+
+ /* Dump the GpioInt/GpioIo common portion of the descriptor */
+
+ AcpiDmGpioCommon (Info, Resource, Level);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmGpioDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a GpioInt/GpioIo GPIO Interrupt/IO descriptor
+ *
+ ******************************************************************************/
+
+void
+AcpiDmGpioDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+ UINT8 ConnectionType;
+
+
+ ConnectionType = Resource->Gpio.ConnectionType;
+
+ switch (ConnectionType)
+ {
+ case AML_RESOURCE_GPIO_TYPE_INT:
+
+ AcpiDmGpioIntDescriptor (Info, Resource, Length, Level);
+ break;
+
+ case AML_RESOURCE_GPIO_TYPE_IO:
+
+ AcpiDmGpioIoDescriptor (Info, Resource, Length, Level);
+ break;
+
+ default:
+
+ AcpiOsPrintf ("Unknown GPIO type\n");
+ break;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmDumpSerialBusVendorData
+ *
+ * PARAMETERS: Resource - Pointer to the resource descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump optional serial bus vendor data
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmDumpSerialBusVendorData (
+ AML_RESOURCE *Resource,
+ UINT32 Level)
+{
+ UINT8 *VendorData;
+ UINT32 VendorLength;
+
+
+ /* Get the (optional) vendor data and length */
+
+ switch (Resource->CommonSerialBus.Type)
+ {
+ case AML_RESOURCE_I2C_SERIALBUSTYPE:
+
+ VendorLength = Resource->CommonSerialBus.TypeDataLength -
+ AML_RESOURCE_I2C_MIN_DATA_LEN;
+
+ VendorData = ACPI_ADD_PTR (UINT8, Resource,
+ sizeof (AML_RESOURCE_I2C_SERIALBUS));
+ break;
+
+ case AML_RESOURCE_SPI_SERIALBUSTYPE:
+
+ VendorLength = Resource->CommonSerialBus.TypeDataLength -
+ AML_RESOURCE_SPI_MIN_DATA_LEN;
+
+ VendorData = ACPI_ADD_PTR (UINT8, Resource,
+ sizeof (AML_RESOURCE_SPI_SERIALBUS));
+ break;
+
+ case AML_RESOURCE_UART_SERIALBUSTYPE:
+
+ VendorLength = Resource->CommonSerialBus.TypeDataLength -
+ AML_RESOURCE_UART_MIN_DATA_LEN;
+
+ VendorData = ACPI_ADD_PTR (UINT8, Resource,
+ sizeof (AML_RESOURCE_UART_SERIALBUS));
+ break;
+
+ default:
+
+ return;
+ }
+
+ /* Dump the vendor bytes as a RawDataBuffer object */
+
+ AcpiDmDumpRawDataBuffer (VendorData, VendorLength, Level);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmI2cSerialBusDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a I2C serial bus descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmI2cSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+ UINT32 ResourceSourceOffset;
+ char *DeviceName;
+
+
+ /* SlaveAddress, SlaveMode, ConnectionSpeed, AddressingMode */
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("I2cSerialBusV2 (0x%4.4X, %s, 0x%8.8X,\n",
+ Resource->I2cSerialBus.SlaveAddress,
+ AcpiGbl_SmDecode [ACPI_GET_1BIT_FLAG (Resource->I2cSerialBus.Flags)],
+ Resource->I2cSerialBus.ConnectionSpeed);
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_AmDecode [ACPI_GET_1BIT_FLAG (Resource->I2cSerialBus.TypeSpecificFlags)]);
+
+ /* ResourceSource is a required field */
+
+ ResourceSourceOffset = sizeof (AML_RESOURCE_COMMON_SERIALBUS) +
+ Resource->CommonSerialBus.TypeDataLength;
+
+ DeviceName = ACPI_ADD_PTR (char, Resource, ResourceSourceOffset),
+ AcpiUtPrintString (DeviceName, ACPI_UINT16_MAX);
+
+ /* ResourceSourceIndex, ResourceUsage */
+
+ AcpiOsPrintf (",\n");
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("0x%2.2X, ", Resource->I2cSerialBus.ResSourceIndex);
+
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_ConsumeDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->I2cSerialBus.Flags, 1)]);
+
+ /* Insert a descriptor name */
+
+ AcpiDmDescriptorName ();
+
+ /* Share */
+
+ AcpiOsPrintf (", %s,\n",
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->I2cSerialBus.Flags, 2)]);
+
+ /* Dump the vendor data */
+
+ AcpiDmIndent (Level + 1);
+ AcpiDmDumpSerialBusVendorData (Resource, Level);
+ AcpiOsPrintf (")\n");
+
+#ifdef ACPI_APPLICATION
+ MpSaveSerialInfo (Info->MappingOp, Resource, DeviceName);
+#endif
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmSpiSerialBusDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a SPI serial bus descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmSpiSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+ UINT32 ResourceSourceOffset;
+ char *DeviceName;
+
+
+ /* DeviceSelection, DeviceSelectionPolarity, WireMode, DataBitLength */
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("SpiSerialBusV2 (0x%4.4X, %s, %s, 0x%2.2X,\n",
+ Resource->SpiSerialBus.DeviceSelection,
+ AcpiGbl_DpDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->SpiSerialBus.TypeSpecificFlags, 1)],
+ AcpiGbl_WmDecode [ACPI_GET_1BIT_FLAG (Resource->SpiSerialBus.TypeSpecificFlags)],
+ Resource->SpiSerialBus.DataBitLength);
+
+ /* SlaveMode, ConnectionSpeed, ClockPolarity, ClockPhase */
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("%s, 0x%8.8X, %s,\n",
+ AcpiGbl_SmDecode [ACPI_GET_1BIT_FLAG (Resource->SpiSerialBus.Flags)],
+ Resource->SpiSerialBus.ConnectionSpeed,
+ AcpiGbl_CpoDecode [ACPI_GET_1BIT_FLAG (Resource->SpiSerialBus.ClockPolarity)]);
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_CphDecode [ACPI_GET_1BIT_FLAG (Resource->SpiSerialBus.ClockPhase)]);
+
+ /* ResourceSource is a required field */
+
+ ResourceSourceOffset = sizeof (AML_RESOURCE_COMMON_SERIALBUS) +
+ Resource->CommonSerialBus.TypeDataLength;
+
+ DeviceName = ACPI_ADD_PTR (char, Resource, ResourceSourceOffset),
+ AcpiUtPrintString (DeviceName, ACPI_UINT16_MAX);
+
+ /* ResourceSourceIndex, ResourceUsage */
+
+ AcpiOsPrintf (",\n");
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("0x%2.2X, ", Resource->SpiSerialBus.ResSourceIndex);
+
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_ConsumeDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->SpiSerialBus.Flags, 1)]);
+
+ /* Insert a descriptor name */
+
+ AcpiDmDescriptorName ();
+
+ /* Share */
+
+ AcpiOsPrintf (", %s,\n",
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->SpiSerialBus.Flags, 2)]);
+
+ /* Dump the vendor data */
+
+ AcpiDmIndent (Level + 1);
+ AcpiDmDumpSerialBusVendorData (Resource, Level);
+ AcpiOsPrintf (")\n");
+
+#ifdef ACPI_APPLICATION
+ MpSaveSerialInfo (Info->MappingOp, Resource, DeviceName);
+#endif
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmUartSerialBusDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a UART serial bus descriptor
+ *
+ ******************************************************************************/
+
+static void
+AcpiDmUartSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+ UINT32 ResourceSourceOffset;
+ char *DeviceName;
+
+
+ /* ConnectionSpeed, BitsPerByte, StopBits */
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("UartSerialBusV2 (0x%8.8X, %s, %s,\n",
+ Resource->UartSerialBus.DefaultBaudRate,
+ AcpiGbl_BpbDecode [ACPI_EXTRACT_3BIT_FLAG (Resource->UartSerialBus.TypeSpecificFlags, 4)],
+ AcpiGbl_SbDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->UartSerialBus.TypeSpecificFlags, 2)]);
+
+ /* LinesInUse, IsBigEndian, Parity, FlowControl */
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("0x%2.2X, %s, %s, %s,\n",
+ Resource->UartSerialBus.LinesEnabled,
+ AcpiGbl_EdDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->UartSerialBus.TypeSpecificFlags, 7)],
+ AcpiGbl_PtDecode [ACPI_GET_3BIT_FLAG (Resource->UartSerialBus.Parity)],
+ AcpiGbl_FcDecode [ACPI_GET_2BIT_FLAG (Resource->UartSerialBus.TypeSpecificFlags)]);
+
+ /* ReceiveBufferSize, TransmitBufferSize */
+
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("0x%4.4X, 0x%4.4X, ",
+ Resource->UartSerialBus.RxFifoSize,
+ Resource->UartSerialBus.TxFifoSize);
+
+ /* ResourceSource is a required field */
+
+ ResourceSourceOffset = sizeof (AML_RESOURCE_COMMON_SERIALBUS) +
+ Resource->CommonSerialBus.TypeDataLength;
+
+ DeviceName = ACPI_ADD_PTR (char, Resource, ResourceSourceOffset),
+ AcpiUtPrintString (DeviceName, ACPI_UINT16_MAX);
+
+ /* ResourceSourceIndex, ResourceUsage */
+
+ AcpiOsPrintf (",\n");
+ AcpiDmIndent (Level + 1);
+ AcpiOsPrintf ("0x%2.2X, ", Resource->UartSerialBus.ResSourceIndex);
+
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_ConsumeDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->UartSerialBus.Flags, 1)]);
+
+ /* Insert a descriptor name */
+
+ AcpiDmDescriptorName ();
+
+ /* Share */
+
+ AcpiOsPrintf (", %s,\n",
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->UartSerialBus.Flags, 2)]);
+
+ /* Dump the vendor data */
+
+ AcpiDmIndent (Level + 1);
+ AcpiDmDumpSerialBusVendorData (Resource, Level);
+ AcpiOsPrintf (")\n");
+
+#ifdef ACPI_APPLICATION
+ MpSaveSerialInfo (Info->MappingOp, Resource, DeviceName);
+#endif
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDmSerialBusDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a I2C/SPI/UART serial bus descriptor
+ *
+ ******************************************************************************/
+
+void
+AcpiDmSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+
+ SerialBusResourceDispatch [Resource->CommonSerialBus.Type] (
+ Info, Resource, Length, Level);
+}
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c b/usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c
index 52b0fa3ea3..c601f36492 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmresrcs.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,14 +41,11 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acdisasm.h"
-#ifdef ACPI_DISASSEMBLER
-
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dbresrcs")
@@ -57,7 +54,8 @@
*
* FUNCTION: AcpiDmIrqDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -69,6 +67,7 @@
void
AcpiDmIrqDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -76,16 +75,16 @@ AcpiDmIrqDescriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("%s (",
- AcpiGbl_IrqDecode [Length & 1]);
+ AcpiGbl_IrqDecode [ACPI_GET_1BIT_FLAG (Length)]);
/* Decode flags byte if present */
if (Length & 1)
{
AcpiOsPrintf ("%s, %s, %s, ",
- AcpiGbl_HeDecode [Resource->Irq.Flags & 1],
- AcpiGbl_LlDecode [(Resource->Irq.Flags >> 3) & 1],
- AcpiGbl_ShrDecode [(Resource->Irq.Flags >> 4) & 1]);
+ AcpiGbl_HeDecode [ACPI_GET_1BIT_FLAG (Resource->Irq.Flags)],
+ AcpiGbl_LlDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->Irq.Flags, 3)],
+ AcpiGbl_ShrDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->Irq.Flags, 4)]);
}
/* Insert a descriptor name */
@@ -102,7 +101,8 @@ AcpiDmIrqDescriptor (
*
* FUNCTION: AcpiDmDmaDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -114,6 +114,7 @@ AcpiDmIrqDescriptor (
void
AcpiDmDmaDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -121,9 +122,9 @@ AcpiDmDmaDescriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("DMA (%s, %s, %s, ",
- AcpiGbl_TypDecode [(Resource->Dma.Flags >> 5) & 3],
- AcpiGbl_BmDecode [(Resource->Dma.Flags >> 2) & 1],
- AcpiGbl_SizDecode [(Resource->Dma.Flags >> 0) & 3]);
+ AcpiGbl_TypDecode [ACPI_EXTRACT_2BIT_FLAG (Resource->Dma.Flags, 5)],
+ AcpiGbl_BmDecode [ACPI_EXTRACT_1BIT_FLAG (Resource->Dma.Flags, 2)],
+ AcpiGbl_SizDecode [ACPI_GET_2BIT_FLAG (Resource->Dma.Flags)]);
/* Insert a descriptor name */
@@ -137,9 +138,56 @@ AcpiDmDmaDescriptor (
/*******************************************************************************
*
+ * FUNCTION: AcpiDmFixedDmaDescriptor
+ *
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
+ * Length - Length of the descriptor in bytes
+ * Level - Current source code indentation level
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Decode a FixedDMA descriptor
+ *
+ ******************************************************************************/
+
+void
+AcpiDmFixedDmaDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level)
+{
+
+ AcpiDmIndent (Level);
+ AcpiOsPrintf ("FixedDMA (0x%4.4X, 0x%4.4X, ",
+ Resource->FixedDma.RequestLines,
+ Resource->FixedDma.Channels);
+
+ if (Resource->FixedDma.Width <= 5)
+ {
+ AcpiOsPrintf ("%s, ",
+ AcpiGbl_DtsDecode [Resource->FixedDma.Width]);
+ }
+ else
+ {
+ AcpiOsPrintf ("%X /* INVALID DMA WIDTH */, ",
+ Resource->FixedDma.Width);
+ }
+
+ /* Insert a descriptor name */
+
+ AcpiDmDescriptorName ();
+ AcpiOsPrintf (")\n");
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiDmIoDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -151,6 +199,7 @@ AcpiDmDmaDescriptor (
void
AcpiDmIoDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -158,7 +207,7 @@ AcpiDmIoDescriptor (
AcpiDmIndent (Level);
AcpiOsPrintf ("IO (%s,\n",
- AcpiGbl_IoDecode [(Resource->Io.Flags & 1)]);
+ AcpiGbl_IoDecode [ACPI_GET_1BIT_FLAG (Resource->Io.Flags)]);
AcpiDmIndent (Level + 1);
AcpiDmDumpInteger16 (Resource->Io.Minimum, "Range Minimum");
@@ -184,7 +233,8 @@ AcpiDmIoDescriptor (
*
* FUNCTION: AcpiDmFixedIoDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -196,6 +246,7 @@ AcpiDmIoDescriptor (
void
AcpiDmFixedIoDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -222,7 +273,8 @@ AcpiDmFixedIoDescriptor (
*
* FUNCTION: AcpiDmStartDependentDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -234,6 +286,7 @@ AcpiDmFixedIoDescriptor (
void
AcpiDmStartDependentDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -244,8 +297,8 @@ AcpiDmStartDependentDescriptor (
if (Length & 1)
{
AcpiOsPrintf ("StartDependentFn (0x%2.2X, 0x%2.2X)\n",
- (UINT32) Resource->StartDpf.Flags & 3,
- (UINT32) (Resource->StartDpf.Flags >> 2) & 3);
+ (UINT32) ACPI_GET_2BIT_FLAG (Resource->StartDpf.Flags),
+ (UINT32) ACPI_EXTRACT_2BIT_FLAG (Resource->StartDpf.Flags, 2));
}
else
{
@@ -261,7 +314,8 @@ AcpiDmStartDependentDescriptor (
*
* FUNCTION: AcpiDmEndDependentDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -273,6 +327,7 @@ AcpiDmStartDependentDescriptor (
void
AcpiDmEndDependentDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -289,7 +344,8 @@ AcpiDmEndDependentDescriptor (
*
* FUNCTION: AcpiDmVendorSmallDescriptor
*
- * PARAMETERS: Resource - Pointer to the resource descriptor
+ * PARAMETERS: Info - Extra resource info
+ * Resource - Pointer to the resource descriptor
* Length - Length of the descriptor in bytes
* Level - Current source code indentation level
*
@@ -301,6 +357,7 @@ AcpiDmEndDependentDescriptor (
void
AcpiDmVendorSmallDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level)
@@ -310,6 +367,3 @@ AcpiDmVendorSmallDescriptor (
ACPI_ADD_PTR (UINT8, Resource, sizeof (AML_RESOURCE_SMALL_HEADER)),
Length, Level);
}
-
-#endif
-
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmutils.c b/usr/src/uts/intel/io/acpica/disassembler/dmutils.c
index b3db9312d5..02717bc6ad 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmutils.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmutils.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -51,7 +50,6 @@
#include <acnamesp.h>
#endif
-#ifdef ACPI_DISASSEMBLER
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dmutils")
@@ -135,7 +133,8 @@ const char *AcpiGbl_IrqDecode[] =
*
* RETURN: None
*
- * DESCRIPTION: Decode the AccessAs attribute byte. (Mostly SMBus stuff)
+ * DESCRIPTION: Decode the AccessAs attribute byte. (Mostly SMBus and
+ * GenericSerialBus stuff.)
*
******************************************************************************/
@@ -146,44 +145,61 @@ AcpiDmDecodeAttribute (
switch (Attribute)
{
- case AML_FIELD_ATTRIB_SMB_QUICK:
+ case AML_FIELD_ATTRIB_QUICK:
+
+ AcpiOsPrintf ("AttribQuick");
+ break;
- AcpiOsPrintf ("SMBQuick");
+ case AML_FIELD_ATTRIB_SEND_RCV:
+
+ AcpiOsPrintf ("AttribSendReceive");
break;
- case AML_FIELD_ATTRIB_SMB_SEND_RCV:
+ case AML_FIELD_ATTRIB_BYTE:
- AcpiOsPrintf ("SMBSendReceive");
+ AcpiOsPrintf ("AttribByte");
break;
- case AML_FIELD_ATTRIB_SMB_BYTE:
+ case AML_FIELD_ATTRIB_WORD:
- AcpiOsPrintf ("SMBByte");
+ AcpiOsPrintf ("AttribWord");
break;
- case AML_FIELD_ATTRIB_SMB_WORD:
+ case AML_FIELD_ATTRIB_BLOCK:
- AcpiOsPrintf ("SMBWord");
+ AcpiOsPrintf ("AttribBlock");
break;
- case AML_FIELD_ATTRIB_SMB_WORD_CALL:
+ case AML_FIELD_ATTRIB_MULTIBYTE:
- AcpiOsPrintf ("SMBProcessCall");
+ AcpiOsPrintf ("AttribBytes");
break;
- case AML_FIELD_ATTRIB_SMB_BLOCK:
+ case AML_FIELD_ATTRIB_WORD_CALL:
- AcpiOsPrintf ("SMBBlock");
+ AcpiOsPrintf ("AttribProcessCall");
break;
- case AML_FIELD_ATTRIB_SMB_BLOCK_CALL:
+ case AML_FIELD_ATTRIB_BLOCK_CALL:
- AcpiOsPrintf ("SMBBlockProcessCall");
+ AcpiOsPrintf ("AttribBlockProcessCall");
+ break;
+
+ case AML_FIELD_ATTRIB_RAW_BYTES:
+
+ AcpiOsPrintf ("AttribRawBytes");
+ break;
+
+ case AML_FIELD_ATTRIB_RAW_PROCESS:
+
+ AcpiOsPrintf ("AttribRawProcessBytes");
break;
default:
- AcpiOsPrintf ("0x%.2X", Attribute);
+ /* A ByteConst is allowed by the grammar */
+
+ AcpiOsPrintf ("0x%2.2X", Attribute);
break;
}
}
@@ -211,7 +227,7 @@ AcpiDmIndent (
return;
}
- AcpiOsPrintf ("%*.s", ACPI_MUL_4 (Level), " ");
+ AcpiOsPrintf ("%*.s", (Level * 4), " ");
}
@@ -234,11 +250,18 @@ AcpiDmCommaIfListMember (
if (!Op->Common.Next)
{
- return FALSE;
+ return (FALSE);
}
if (AcpiDmListType (Op->Common.Parent) & BLOCK_COMMA_LIST)
{
+ /* Exit if Target has been marked IGNORE */
+
+ if (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
+ {
+ return (FALSE);
+ }
+
/* Check for a NULL target operand */
if ((Op->Common.Next->Common.AmlOpcode == AML_INT_NAMEPATH_OP) &&
@@ -246,28 +269,34 @@ AcpiDmCommaIfListMember (
{
/*
* To handle the Divide() case where there are two optional
- * targets, look ahead one more op. If null, this null target
- * is the one and only target -- no comma needed. Otherwise,
+ * targets, look ahead one more op. If null, this null target
+ * is the one and only target -- no comma needed. Otherwise,
* we need a comma to prepare for the next target.
*/
if (!Op->Common.Next->Common.Next)
{
- return FALSE;
+ return (FALSE);
}
}
- if ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST) &&
- (!(Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)))
+ if ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) &&
+ (!(Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST)))
{
- return FALSE;
+ return (FALSE);
+ }
+
+ /* Emit comma only if this is not a C-style operator */
+
+ if (!Op->Common.OperatorSymbol)
+ {
+ AcpiOsPrintf (", ");
}
- AcpiOsPrintf (", ");
return (TRUE);
}
- else if ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST) &&
- (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST))
+ else if ((Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST) &&
+ (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST))
{
AcpiOsPrintf (", ");
return (TRUE);
@@ -299,5 +328,3 @@ AcpiDmCommaIfFieldMember (
AcpiOsPrintf (", ");
}
}
-
-#endif
diff --git a/usr/src/uts/intel/io/acpica/disassembler/dmwalk.c b/usr/src/uts/intel/io/acpica/disassembler/dmwalk.c
index 2a4b5e545f..3c953f5465 100644
--- a/usr/src/uts/intel/io/acpica/disassembler/dmwalk.c
+++ b/usr/src/uts/intel/io/acpica/disassembler/dmwalk.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,17 +41,13 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
#include "amlcode.h"
-#include "acdisasm.h"
#include "acdebug.h"
-#ifdef ACPI_DISASSEMBLER
-
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME ("dmwalk")
@@ -98,7 +94,7 @@ AcpiDmBlockType (
*
* RETURN: None
*
- * DESCRIPTION: Disassemble parser object and its children. This is the
+ * DESCRIPTION: Disassemble parser object and its children. This is the
* main entry point of the disassembler.
*
******************************************************************************/
@@ -118,10 +114,11 @@ AcpiDmDisassemble (
return;
}
- Info.Flags = 0;
- Info.Level = 0;
- Info.Count = 0;
+ memset (&Info, 0, sizeof (ACPI_OP_WALK_INFO));
Info.WalkState = WalkState;
+ Info.StartAml = Op->Common.Aml - sizeof (ACPI_TABLE_HEADER);
+ Info.AmlOffset = Op->Common.Aml - Info.StartAml;
+
AcpiDmWalkParseTree (Op, AcpiDmDescendingOp, AcpiDmAscendingOp, &Info);
return;
}
@@ -285,7 +282,9 @@ AcpiDmBlockType (
case AML_BUFFER_OP:
- if (Op->Common.DisasmOpcode == ACPI_DASM_UNICODE)
+ if ((Op->Common.DisasmOpcode == ACPI_DASM_UNICODE) ||
+ (Op->Common.DisasmOpcode == ACPI_DASM_UUID) ||
+ (Op->Common.DisasmOpcode == ACPI_DASM_PLD_METHOD))
{
return (BLOCK_NONE);
}
@@ -301,6 +300,19 @@ AcpiDmBlockType (
return (BLOCK_PAREN);
+ case AML_INT_METHODCALL_OP:
+
+ if (Op->Common.Parent &&
+ ((Op->Common.Parent->Common.AmlOpcode == AML_PACKAGE_OP) ||
+ (Op->Common.Parent->Common.AmlOpcode == AML_VAR_PACKAGE_OP)))
+ {
+ /* This is a reference to a method, not an invocation */
+
+ return (BLOCK_NONE);
+ }
+
+ /*lint -fallthrough */
+
default:
OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
@@ -398,7 +410,43 @@ AcpiDmDescendingOp (
const ACPI_OPCODE_INFO *OpInfo;
UINT32 Name;
ACPI_PARSE_OBJECT *NextOp;
+ ACPI_PARSE_OBJECT *NextOp2;
+ UINT32 AmlOffset;
+
+
+ OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
+
+ /* Listing support to dump the AML code after the ASL statement */
+
+ if (AcpiGbl_DmOpt_Listing)
+ {
+ /* We only care about these classes of objects */
+
+ if ((OpInfo->Class == AML_CLASS_NAMED_OBJECT) ||
+ (OpInfo->Class == AML_CLASS_CONTROL) ||
+ (OpInfo->Class == AML_CLASS_CREATE) ||
+ ((OpInfo->Class == AML_CLASS_EXECUTE) && (!Op->Common.Next)))
+ {
+ if (AcpiGbl_DmOpt_Listing && Info->PreviousAml)
+ {
+ /* Dump the AML byte code for the previous Op */
+
+ if (Op->Common.Aml > Info->PreviousAml)
+ {
+ AcpiOsPrintf ("\n");
+ AcpiUtDumpBuffer (
+ (Info->StartAml + Info->AmlOffset),
+ (Op->Common.Aml - Info->PreviousAml),
+ DB_BYTE_DISPLAY, Info->AmlOffset);
+ AcpiOsPrintf ("\n");
+ }
+
+ Info->AmlOffset = (Op->Common.Aml - Info->StartAml);
+ }
+ Info->PreviousAml = Op->Common.Aml;
+ }
+ }
if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
{
@@ -407,6 +455,38 @@ AcpiDmDescendingOp (
return (AE_CTRL_DEPTH);
}
+ if (Op->Common.AmlOpcode == AML_IF_OP)
+ {
+ NextOp = AcpiPsGetDepthNext (NULL, Op);
+ if (NextOp)
+ {
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
+
+ /* Don't emit the actual embedded externals unless asked */
+
+ if (!AcpiGbl_DmEmitExternalOpcodes)
+ {
+ /*
+ * A Zero predicate indicates the possibility of one or more
+ * External() opcodes within the If() block.
+ */
+ if (NextOp->Common.AmlOpcode == AML_ZERO_OP)
+ {
+ NextOp2 = NextOp->Common.Next;
+
+ if (NextOp2 &&
+ (NextOp2->Common.AmlOpcode == AML_EXTERNAL_OP))
+ {
+ /* Ignore the If 0 block and all children */
+
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ return (AE_CTRL_DEPTH);
+ }
+ }
+ }
+ }
+ }
+
/* Level 0 is at the Definition Block level */
if (Level == 0)
@@ -415,10 +495,15 @@ AcpiDmDescendingOp (
if (Info->WalkState)
{
- VERBOSE_PRINT ((DB_FULL_OP_INFO,
- (Info->WalkState->MethodNode ?
- Info->WalkState->MethodNode->Name.Ascii : " "),
- Op->Common.AmlOffset, (UINT32) Op->Common.AmlOpcode));
+ AmlOffset = (UINT32) ACPI_PTR_DIFF (Op->Common.Aml,
+ Info->WalkState->ParserState.AmlStart);
+ if (AcpiGbl_DmOpt_Verbose)
+ {
+ AcpiOsPrintf (DB_FULL_OP_INFO,
+ (Info->WalkState->MethodNode ?
+ Info->WalkState->MethodNode->Name.Ascii : " "),
+ AmlOffset, (UINT32) Op->Common.AmlOpcode);
+ }
}
if (Op->Common.AmlOpcode == AML_SCOPE_OP)
@@ -434,16 +519,41 @@ AcpiDmDescendingOp (
}
}
else if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
- (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
- (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
+ (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST)) &&
+ (!(Op->Common.DisasmFlags & ACPI_PARSEOP_ELSEIF)) &&
+ (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
{
+ /*
+ * This is a first-level element of a term list,
+ * indent a new line
+ */
+ switch (Op->Common.AmlOpcode)
+ {
+ case AML_NOOP_OP:
/*
- * This is a first-level element of a term list,
- * indent a new line
+ * Optionally just ignore this opcode. Some tables use
+ * NoOp opcodes for "padding" out packages that the BIOS
+ * changes dynamically. This can leave hundreds or
+ * thousands of NoOp opcodes that if disassembled,
+ * cannot be compiled because they are syntactically
+ * incorrect.
*/
+ if (AcpiGbl_IgnoreNoopOperator)
+ {
+ Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
+ return (AE_OK);
+ }
+
+ /* Fallthrough */
+
+ default:
+
AcpiDmIndent (Level);
- Info->LastLevel = Level;
- Info->Count = 0;
+ break;
+ }
+
+ Info->LastLevel = Level;
+ Info->Count = 0;
}
/*
@@ -453,18 +563,26 @@ AcpiDmDescendingOp (
* keep track of the current column.
*/
Info->Count++;
- if (Info->Count /*+Info->LastLevel*/ > 10)
+ if (Info->Count /* +Info->LastLevel */ > 12)
{
Info->Count = 0;
AcpiOsPrintf ("\n");
AcpiDmIndent (Info->LastLevel + 1);
}
+ /* If ASL+ is enabled, check for a C-style operator */
+
+ if (AcpiDmCheckForSymbolicOpcode (Op, Info))
+ {
+ return (AE_OK);
+ }
+
/* Print the opcode name */
AcpiDmDisassembleOneOp (NULL, Info, Op);
- if (Op->Common.DisasmOpcode == ACPI_DASM_LNOT_PREFIX)
+ if ((Op->Common.DisasmOpcode == ACPI_DASM_LNOT_PREFIX) ||
+ (Op->Common.AmlOpcode == AML_INT_CONNECTION_OP))
{
return (AE_OK);
}
@@ -477,8 +595,6 @@ AcpiDmDescendingOp (
/* Start the opcode argument list if necessary */
- OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
-
if ((OpInfo->Flags & AML_HAS_ARGS) ||
(Op->Common.AmlOpcode == AML_EVENT_OP))
{
@@ -518,7 +634,7 @@ AcpiDmDescendingOp (
if (Op->Common.AmlOpcode != AML_INT_NAMEDFIELD_OP)
{
- if (AcpiGbl_DbOpt_verbose)
+ if (AcpiGbl_DmOpt_Verbose)
{
(void) AcpiPsDisplayObjectPathname (NULL, Op);
}
@@ -532,66 +648,63 @@ AcpiDmDescendingOp (
AcpiDmMethodFlags (Op);
AcpiOsPrintf (")");
- break;
+ /* Emit description comment for Method() with a predefined ACPI name */
+
+ AcpiDmPredefinedDescription (Op);
+ break;
case AML_NAME_OP:
/* Check for _HID and related EISAID() */
- AcpiDmIsEisaId (Op);
+ AcpiDmCheckForHardwareId (Op);
AcpiOsPrintf (", ");
break;
-
case AML_REGION_OP:
AcpiDmRegionFlags (Op);
break;
-
case AML_POWER_RES_OP:
/* Mark the next two Ops as part of the parameter list */
AcpiOsPrintf (", ");
NextOp = AcpiPsGetDepthNext (NULL, Op);
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
NextOp = NextOp->Common.Next;
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
return (AE_OK);
-
case AML_PROCESSOR_OP:
/* Mark the next three Ops as part of the parameter list */
AcpiOsPrintf (", ");
NextOp = AcpiPsGetDepthNext (NULL, Op);
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
NextOp = NextOp->Common.Next;
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
NextOp = NextOp->Common.Next;
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
return (AE_OK);
-
case AML_MUTEX_OP:
case AML_DATA_REGION_OP:
AcpiOsPrintf (", ");
return (AE_OK);
-
case AML_EVENT_OP:
case AML_ALIAS_OP:
return (AE_OK);
-
case AML_SCOPE_OP:
case AML_DEVICE_OP:
case AML_THERMAL_ZONE_OP:
@@ -599,10 +712,10 @@ AcpiDmDescendingOp (
AcpiOsPrintf (")");
break;
-
default:
- AcpiOsPrintf ("*** Unhandled named opcode %X\n", Op->Common.AmlOpcode);
+ AcpiOsPrintf ("*** Unhandled named opcode %X\n",
+ Op->Common.AmlOpcode);
break;
}
}
@@ -637,13 +750,14 @@ AcpiDmDescendingOp (
* Bank Value. This is a TermArg in the middle of the parameter
* list, must handle it here.
*
- * Disassemble the TermArg parse tree. ACPI_PARSEOP_PARAMLIST
+ * Disassemble the TermArg parse tree. ACPI_PARSEOP_PARAMETER_LIST
* eliminates newline in the output.
*/
NextOp = NextOp->Common.Next;
- Info->Flags = ACPI_PARSEOP_PARAMLIST;
- AcpiDmWalkParseTree (NextOp, AcpiDmDescendingOp, AcpiDmAscendingOp, Info);
+ Info->Flags = ACPI_PARSEOP_PARAMETER_LIST;
+ AcpiDmWalkParseTree (NextOp, AcpiDmDescendingOp,
+ AcpiDmAscendingOp, Info);
Info->Flags = 0;
Info->Level = Level;
@@ -669,7 +783,6 @@ AcpiDmDescendingOp (
AcpiDmFieldFlags (NextOp);
break;
-
case AML_BUFFER_OP:
/* The next op is the size parameter */
@@ -685,12 +798,18 @@ AcpiDmDescendingOp (
if (Op->Common.DisasmOpcode == ACPI_DASM_RESOURCE)
{
/*
- * We have a resource list. Don't need to output
- * the buffer size Op. Open up a new block
+ * We have a resource list. Don't need to output
+ * the buffer size Op. Open up a new block
*/
NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
NextOp = NextOp->Common.Next;
- AcpiOsPrintf (")\n");
+ AcpiOsPrintf (")");
+
+ /* Emit description comment for Name() with a predefined ACPI name */
+
+ AcpiDmPredefinedDescription (Op->Asl.Parent);
+
+ AcpiOsPrintf ("\n");
AcpiDmIndent (Info->Level);
AcpiOsPrintf ("{\n");
return (AE_OK);
@@ -698,12 +817,11 @@ AcpiDmDescendingOp (
/* Normal Buffer, mark size as in the parameter list */
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
return (AE_OK);
-
- case AML_VAR_PACKAGE_OP:
case AML_IF_OP:
+ case AML_VAR_PACKAGE_OP:
case AML_WHILE_OP:
/* The next op is the size or predicate parameter */
@@ -711,29 +829,26 @@ AcpiDmDescendingOp (
NextOp = AcpiPsGetDepthNext (NULL, Op);
if (NextOp)
{
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
}
return (AE_OK);
-
case AML_PACKAGE_OP:
- /* The next op is the size or predicate parameter */
+ /* The next op is the size parameter */
NextOp = AcpiPsGetDepthNext (NULL, Op);
if (NextOp)
{
- NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
+ NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
}
return (AE_OK);
-
case AML_MATCH_OP:
AcpiDmMatchOp (Op);
break;
-
default:
break;
@@ -760,7 +875,7 @@ AcpiDmDescendingOp (
* RETURN: Status
*
* DESCRIPTION: Second visitation of a parse object, during ascent of parse
- * tree. Close out any parameter lists and complete the opcode.
+ * tree. Close out any parameter lists and complete the opcode.
*
******************************************************************************/
@@ -771,6 +886,7 @@ AcpiDmAscendingOp (
void *Context)
{
ACPI_OP_WALK_INFO *Info = Context;
+ ACPI_PARSE_OBJECT *ParentOp;
if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
@@ -792,23 +908,45 @@ AcpiDmAscendingOp (
{
case BLOCK_PAREN:
- /* Completed an op that has arguments, add closing paren */
+ /* Completed an op that has arguments, add closing paren if needed */
+
+ AcpiDmCloseOperator (Op);
+
+ if (Op->Common.AmlOpcode == AML_NAME_OP)
+ {
+ /* Emit description comment for Name() with a predefined ACPI name */
+
+ AcpiDmPredefinedDescription (Op);
+ }
+ else
+ {
+ /* For Create* operators, attempt to emit resource tag description */
+
+ AcpiDmFieldPredefinedDescription (Op);
+ }
+
+ /* Decode Notify() values */
+
+ if (Op->Common.AmlOpcode == AML_NOTIFY_OP)
+ {
+ AcpiDmNotifyDescription (Op);
+ }
- AcpiOsPrintf (")");
+ AcpiDmDisplayTargetPathname (Op);
/* Could be a nested operator, check if comma required */
if (!AcpiDmCommaIfListMember (Op))
{
if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
- (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
- (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
+ (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST)) &&
+ (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
{
/*
* This is a first-level element of a term list
* start a new line
*/
- if (!(Info->Flags & ACPI_PARSEOP_PARAMLIST))
+ if (!(Info->Flags & ACPI_PARSEOP_PARAMETER_LIST))
{
AcpiOsPrintf ("\n");
}
@@ -816,7 +954,6 @@ AcpiDmAscendingOp (
}
break;
-
case BLOCK_BRACE:
case (BLOCK_BRACE | BLOCK_PAREN):
@@ -856,7 +993,6 @@ AcpiDmAscendingOp (
}
break;
-
case BLOCK_NONE:
default:
@@ -865,8 +1001,8 @@ AcpiDmAscendingOp (
if (!AcpiDmCommaIfListMember (Op))
{
if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
- (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
- (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
+ (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST)) &&
+ (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
{
/*
* This is a first-level element of a term list
@@ -882,7 +1018,7 @@ AcpiDmAscendingOp (
case AML_PACKAGE_OP:
case AML_VAR_PACKAGE_OP:
- if (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST))
+ if (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST))
{
AcpiOsPrintf ("\n");
}
@@ -896,28 +1032,47 @@ AcpiDmAscendingOp (
break;
}
- if (Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)
+ if (Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST)
{
if ((Op->Common.Next) &&
- (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST))
+ (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMETER_LIST))
{
return (AE_OK);
}
/*
+ * The parent Op is guaranteed to be valid because of the flag
+ * ACPI_PARSEOP_PARAMETER_LIST -- which means that this op is part of
+ * a parameter list and thus has a valid parent.
+ */
+ ParentOp = Op->Common.Parent;
+
+ /*
* Just completed a parameter node for something like "Buffer (param)".
* Close the paren and open up the term list block with a brace
*/
if (Op->Common.Next)
{
- AcpiOsPrintf (")\n");
+ AcpiOsPrintf (")");
+
+ /*
+ * Emit a description comment for a Name() operator that is a
+ * predefined ACPI name. Must check the grandparent.
+ */
+ ParentOp = ParentOp->Common.Parent;
+ if (ParentOp &&
+ (ParentOp->Asl.AmlOpcode == AML_NAME_OP))
+ {
+ AcpiDmPredefinedDescription (ParentOp);
+ }
+
+ AcpiOsPrintf ("\n");
AcpiDmIndent (Level - 1);
AcpiOsPrintf ("{\n");
}
else
{
- Op->Common.Parent->Common.DisasmFlags |=
- ACPI_PARSEOP_EMPTY_TERMLIST;
+ ParentOp->Common.DisasmFlags |= ACPI_PARSEOP_EMPTY_TERMLIST;
AcpiOsPrintf (") {");
}
}
@@ -927,8 +1082,19 @@ AcpiDmAscendingOp (
{
Info->Level++;
}
- return (AE_OK);
-}
+ /*
+ * For ASL+, check for and emit a C-style symbol. If valid, the
+ * symbol string has been deferred until after the first operand
+ */
+ if (AcpiGbl_CstyleDisassembly)
+ {
+ if (Op->Asl.OperatorSymbol)
+ {
+ AcpiOsPrintf ("%s", Op->Asl.OperatorSymbol);
+ Op->Asl.OperatorSymbol = NULL;
+ }
+ }
-#endif /* ACPI_DISASSEMBLER */
+ return (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsargs.c b/usr/src/uts/intel/io/acpica/dispatcher/dsargs.c
index 87def86051..5e5e93b4ec 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsargs.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsargs.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSARGS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -96,7 +94,7 @@ AcpiDsExecuteArguments (
/* Allocate a new parser op to be the root of the parsed tree */
- Op = AcpiPsAllocOp (AML_INT_EVAL_SUBTREE_OP);
+ Op = AcpiPsAllocOp (AML_INT_EVAL_SUBTREE_OP, AmlStart);
if (!Op)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -116,7 +114,7 @@ AcpiDsExecuteArguments (
}
Status = AcpiDsInitAmlWalk (WalkState, Op, NULL, AmlStart,
- AmlLength, NULL, ACPI_IMODE_LOAD_PASS1);
+ AmlLength, NULL, ACPI_IMODE_LOAD_PASS1);
if (ACPI_FAILURE (Status))
{
AcpiDsDeleteWalkState (WalkState);
@@ -143,7 +141,7 @@ AcpiDsExecuteArguments (
/* Evaluate the deferred arguments */
- Op = AcpiPsAllocOp (AML_INT_EVAL_SUBTREE_OP);
+ Op = AcpiPsAllocOp (AML_INT_EVAL_SUBTREE_OP, AmlStart);
if (!Op)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -163,7 +161,7 @@ AcpiDsExecuteArguments (
/* Execute the opcode and arguments */
Status = AcpiDsInitAmlWalk (WalkState, Op, NULL, AmlStart,
- AmlLength, NULL, ACPI_IMODE_EXECUTE);
+ AmlLength, NULL, ACPI_IMODE_EXECUTE);
if (ACPI_FAILURE (Status))
{
AcpiDsDeleteWalkState (WalkState);
@@ -216,8 +214,8 @@ AcpiDsGetBufferFieldArguments (
ExtraDesc = AcpiNsGetSecondaryObject (ObjDesc);
Node = ObjDesc->BufferField.Node;
- ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (ACPI_TYPE_BUFFER_FIELD,
- Node, NULL));
+ ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (
+ ACPI_TYPE_BUFFER_FIELD, Node, NULL));
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] BufferField Arg Init\n",
AcpiUtGetNodeName (Node)));
@@ -225,7 +223,7 @@ AcpiDsGetBufferFieldArguments (
/* Execute the AML code for the TermArg arguments */
Status = AcpiDsExecuteArguments (Node, Node->Parent,
- ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
+ ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
return_ACPI_STATUS (Status);
}
@@ -265,8 +263,8 @@ AcpiDsGetBankFieldArguments (
ExtraDesc = AcpiNsGetSecondaryObject (ObjDesc);
Node = ObjDesc->BankField.Node;
- ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (ACPI_TYPE_LOCAL_BANK_FIELD,
- Node, NULL));
+ ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (
+ ACPI_TYPE_LOCAL_BANK_FIELD, Node, NULL));
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] BankField Arg Init\n",
AcpiUtGetNodeName (Node)));
@@ -274,7 +272,7 @@ AcpiDsGetBankFieldArguments (
/* Execute the AML code for the TermArg arguments */
Status = AcpiDsExecuteArguments (Node, Node->Parent,
- ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
+ ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
return_ACPI_STATUS (Status);
}
@@ -314,7 +312,8 @@ AcpiDsGetBufferArguments (
if (!Node)
{
ACPI_ERROR ((AE_INFO,
- "No pointer back to namespace node in buffer object %p", ObjDesc));
+ "No pointer back to namespace node in buffer object %p",
+ ObjDesc));
return_ACPI_STATUS (AE_AML_INTERNAL);
}
@@ -323,7 +322,7 @@ AcpiDsGetBufferArguments (
/* Execute the AML code for the TermArg arguments */
Status = AcpiDsExecuteArguments (Node, Node,
- ObjDesc->Buffer.AmlLength, ObjDesc->Buffer.AmlStart);
+ ObjDesc->Buffer.AmlLength, ObjDesc->Buffer.AmlStart);
return_ACPI_STATUS (Status);
}
@@ -372,7 +371,7 @@ AcpiDsGetPackageArguments (
/* Execute the AML code for the TermArg arguments */
Status = AcpiDsExecuteArguments (Node, Node,
- ObjDesc->Package.AmlLength, ObjDesc->Package.AmlStart);
+ ObjDesc->Package.AmlLength, ObjDesc->Package.AmlStart);
return_ACPI_STATUS (Status);
}
@@ -417,14 +416,23 @@ AcpiDsGetRegionArguments (
Node = ObjDesc->Region.Node;
- ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (ACPI_TYPE_REGION, Node, NULL));
+ ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (
+ ACPI_TYPE_REGION, Node, NULL));
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] OpRegion Arg Init at AML %p\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[%4.4s] OpRegion Arg Init at AML %p\n",
AcpiUtGetNodeName (Node), ExtraDesc->Extra.AmlStart));
/* Execute the argument AML */
- Status = AcpiDsExecuteArguments (Node, Node->Parent,
- ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
+ Status = AcpiDsExecuteArguments (Node, ExtraDesc->Extra.ScopeNode,
+ ExtraDesc->Extra.AmlLength, ExtraDesc->Extra.AmlStart);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Status = AcpiUtAddAddressRange (ObjDesc->Region.SpaceId,
+ ObjDesc->Region.Address, ObjDesc->Region.Length, Node);
return_ACPI_STATUS (Status);
}
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c b/usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c
index 41a44951c2..d60361921b 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dscontrol.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,13 +42,12 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSCONTROL_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acdebug.h"
#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME ("dscontrol")
@@ -86,7 +85,6 @@ AcpiDsExecBeginControlOp (
switch (Op->Common.AmlOpcode)
{
case AML_WHILE_OP:
-
/*
* If this is an additional iteration of a while loop, continue.
* There is no need to allocate a new control state.
@@ -107,7 +105,6 @@ AcpiDsExecBeginControlOp (
/*lint -fallthrough */
case AML_IF_OP:
-
/*
* IF/WHILE: Create a new control state to manage these
* constructs. We need to manage these as a stack, in order
@@ -123,9 +120,12 @@ AcpiDsExecBeginControlOp (
* Save a pointer to the predicate for multiple executions
* of a loop
*/
- ControlState->Control.AmlPredicateStart = WalkState->ParserState.Aml - 1;
- ControlState->Control.PackageEnd = WalkState->ParserState.PkgEnd;
- ControlState->Control.Opcode = Op->Common.AmlOpcode;
+ ControlState->Control.AmlPredicateStart =
+ WalkState->ParserState.Aml - 1;
+ ControlState->Control.PackageEnd =
+ WalkState->ParserState.PkgEnd;
+ ControlState->Control.Opcode =
+ Op->Common.AmlOpcode;
/* Push the control state on this walk's control stack */
@@ -150,6 +150,7 @@ AcpiDsExecBeginControlOp (
break;
default:
+
break;
}
@@ -204,12 +205,10 @@ AcpiDsExecEndControlOp (
AcpiUtDeleteGenericState (ControlState);
break;
-
case AML_ELSE_OP:
break;
-
case AML_WHILE_OP:
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", Op));
@@ -226,7 +225,7 @@ AcpiDsExecEndControlOp (
* loop does not implement a timeout.
*/
ControlState->Control.LoopCount++;
- if (ControlState->Control.LoopCount > ACPI_MAX_LOOP_ITERATIONS)
+ if (ControlState->Control.LoopCount > AcpiGbl_MaxLoopIterations)
{
Status = AE_AML_INFINITE_LOOP;
break;
@@ -237,7 +236,8 @@ AcpiDsExecEndControlOp (
* another time
*/
Status = AE_CTRL_PENDING;
- WalkState->AmlLastWhile = ControlState->Control.AmlPredicateStart;
+ WalkState->AmlLastWhile =
+ ControlState->Control.AmlPredicateStart;
break;
}
@@ -252,7 +252,6 @@ AcpiDsExecEndControlOp (
AcpiUtDeleteGenericState (ControlState);
break;
-
case AML_RETURN_OP:
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
@@ -282,7 +281,8 @@ AcpiDsExecEndControlOp (
* an arg or local), resolve it now because it may
* cease to exist at the end of the method.
*/
- Status = AcpiExResolveToValue (&WalkState->Operands [0], WalkState);
+ Status = AcpiExResolveToValue (
+ &WalkState->Operands [0], WalkState);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -290,7 +290,7 @@ AcpiDsExecEndControlOp (
/*
* Get the return value and save as the last result
- * value. This is the only place where WalkState->ReturnDesc
+ * value. This is the only place where WalkState->ReturnDesc
* is set to anything other than zero!
*/
WalkState->ReturnDesc = WalkState->Operands[0];
@@ -311,11 +311,15 @@ AcpiDsExecEndControlOp (
* Allow references created by the Index operator to return
* unchanged.
*/
- if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) == ACPI_DESC_TYPE_OPERAND) &&
- ((WalkState->Results->Results.ObjDesc [0])->Common.Type == ACPI_TYPE_LOCAL_REFERENCE) &&
- ((WalkState->Results->Results.ObjDesc [0])->Reference.Class != ACPI_REFCLASS_INDEX))
+ if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) ==
+ ACPI_DESC_TYPE_OPERAND) &&
+ ((WalkState->Results->Results.ObjDesc [0])->Common.Type ==
+ ACPI_TYPE_LOCAL_REFERENCE) &&
+ ((WalkState->Results->Results.ObjDesc [0])->Reference.Class !=
+ ACPI_REFCLASS_INDEX))
{
- Status = AcpiExResolveToValue (&WalkState->Results->Results.ObjDesc [0], WalkState);
+ Status = AcpiExResolveToValue (
+ &WalkState->Results->Results.ObjDesc [0], WalkState);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -333,9 +337,9 @@ AcpiDsExecEndControlOp (
AcpiUtRemoveReference (WalkState->Operands [0]);
}
- WalkState->Operands [0] = NULL;
- WalkState->NumOperands = 0;
- WalkState->ReturnDesc = NULL;
+ WalkState->Operands[0] = NULL;
+ WalkState->NumOperands = 0;
+ WalkState->ReturnDesc = NULL;
}
@@ -348,36 +352,27 @@ AcpiDsExecEndControlOp (
Status = AE_CTRL_TERMINATE;
break;
-
case AML_NOOP_OP:
/* Just do nothing! */
- break;
+ break;
case AML_BREAK_POINT_OP:
- /*
- * Set the single-step flag. This will cause the debugger (if present)
- * to break to the console within the AML debugger at the start of the
- * next AML instruction.
- */
- ACPI_DEBUGGER_EXEC (
- AcpiGbl_CmSingleStep = TRUE);
- ACPI_DEBUGGER_EXEC (
- AcpiOsPrintf ("**break** Executed AML BreakPoint opcode\n"));
+#ifdef ACPI_DEBUGGER
+ AcpiDbSignalBreakPoint (WalkState);
/* Call to the OSL in case OS wants a piece of the action */
Status = AcpiOsSignal (ACPI_SIGNAL_BREAKPOINT,
- "Executed AML Breakpoint opcode");
+ "Executed AML Breakpoint opcode");
+#endif
break;
-
case AML_BREAK_OP:
case AML_CONTINUE_OP: /* ACPI 2.0 */
-
/* Pop and delete control states until we find a while */
while (WalkState->ControlState &&
@@ -396,7 +391,8 @@ AcpiDsExecEndControlOp (
/* Was: WalkState->AmlLastWhile = WalkState->ControlState->Control.AmlPredicateStart; */
- WalkState->AmlLastWhile = WalkState->ControlState->Control.PackageEnd;
+ WalkState->AmlLastWhile =
+ WalkState->ControlState->Control.PackageEnd;
/* Return status depending on opcode */
@@ -410,7 +406,6 @@ AcpiDsExecEndControlOp (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown control opcode=0x%X Op=%p",
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsdebug.c b/usr/src/uts/intel/io/acpica/dispatcher/dsdebug.c
new file mode 100644
index 0000000000..adeb448c42
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsdebug.c
@@ -0,0 +1,250 @@
+/******************************************************************************
+ *
+ * Module Name: dsdebug - Parser/Interpreter interface - debugging
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acdispat.h"
+#include "acnamesp.h"
+#include "acdisasm.h"
+#include "acinterp.h"
+
+
+#define _COMPONENT ACPI_DISPATCHER
+ ACPI_MODULE_NAME ("dsdebug")
+
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+/* Local prototypes */
+
+static void
+AcpiDsPrintNodePathname (
+ ACPI_NAMESPACE_NODE *Node,
+ const char *Message);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDsPrintNodePathname
+ *
+ * PARAMETERS: Node - Object
+ * Message - Prefix message
+ *
+ * DESCRIPTION: Print an object's full namespace pathname
+ * Manages allocation/freeing of a pathname buffer
+ *
+ ******************************************************************************/
+
+static void
+AcpiDsPrintNodePathname (
+ ACPI_NAMESPACE_NODE *Node,
+ const char *Message)
+{
+ ACPI_BUFFER Buffer;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (DsPrintNodePathname);
+
+ if (!Node)
+ {
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH, "[NULL NAME]"));
+ return_VOID;
+ }
+
+ /* Convert handle to full pathname and print it (with supplied message) */
+
+ Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
+
+ Status = AcpiNsHandleToPathname (Node, &Buffer, TRUE);
+ if (ACPI_SUCCESS (Status))
+ {
+ if (Message)
+ {
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH, "%s ", Message));
+ }
+
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH, "[%s] (Node %p)",
+ (char *) Buffer.Pointer, Node));
+ ACPI_FREE (Buffer.Pointer);
+ }
+
+ return_VOID;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDsDumpMethodStack
+ *
+ * PARAMETERS: Status - Method execution status
+ * WalkState - Current state of the parse tree walk
+ * Op - Executing parse op
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Called when a method has been aborted because of an error.
+ * Dumps the method execution stack.
+ *
+ ******************************************************************************/
+
+void
+AcpiDsDumpMethodStack (
+ ACPI_STATUS Status,
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT *Op)
+{
+ ACPI_PARSE_OBJECT *Next;
+ ACPI_THREAD_STATE *Thread;
+ ACPI_WALK_STATE *NextWalkState;
+ ACPI_NAMESPACE_NODE *PreviousMethod = NULL;
+ ACPI_OPERAND_OBJECT *MethodDesc;
+
+
+ ACPI_FUNCTION_TRACE (DsDumpMethodStack);
+
+ /* Ignore control codes, they are not errors */
+
+ if ((Status & AE_CODE_MASK) == AE_CODE_CONTROL)
+ {
+ return_VOID;
+ }
+
+ /* We may be executing a deferred opcode */
+
+ if (WalkState->DeferredNode)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "Executing subtree for Buffer/Package/Region\n"));
+ return_VOID;
+ }
+
+ /*
+ * If there is no Thread, we are not actually executing a method.
+ * This can happen when the iASL compiler calls the interpreter
+ * to perform constant folding.
+ */
+ Thread = WalkState->Thread;
+ if (!Thread)
+ {
+ return_VOID;
+ }
+
+ /* Display exception and method name */
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "\n**** Exception %s during execution of method ",
+ AcpiFormatException (Status)));
+
+ AcpiDsPrintNodePathname (WalkState->MethodNode, NULL);
+
+ /* Display stack of executing methods */
+
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH,
+ "\n\nMethod Execution Stack:\n"));
+ NextWalkState = Thread->WalkStateList;
+
+ /* Walk list of linked walk states */
+
+ while (NextWalkState)
+ {
+ MethodDesc = NextWalkState->MethodDesc;
+ if (MethodDesc)
+ {
+ AcpiExStopTraceMethod (
+ (ACPI_NAMESPACE_NODE *) MethodDesc->Method.Node,
+ MethodDesc, WalkState);
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ " Method [%4.4s] executing: ",
+ AcpiUtGetNodeName (NextWalkState->MethodNode)));
+
+ /* First method is the currently executing method */
+
+ if (NextWalkState == WalkState)
+ {
+ if (Op)
+ {
+ /* Display currently executing ASL statement */
+
+ Next = Op->Common.Next;
+ Op->Common.Next = NULL;
+
+#ifdef ACPI_DISASSEMBLER
+ AcpiDmDisassemble (NextWalkState, Op, ACPI_UINT32_MAX);
+#endif
+ Op->Common.Next = Next;
+ }
+ }
+ else
+ {
+ /*
+ * This method has called another method
+ * NOTE: the method call parse subtree is already deleted at
+ * this point, so we cannot disassemble the method invocation.
+ */
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH, "Call to method "));
+ AcpiDsPrintNodePathname (PreviousMethod, NULL);
+ }
+
+ PreviousMethod = NextWalkState->MethodNode;
+ NextWalkState = NextWalkState->Next;
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DISPATCH, "\n"));
+ }
+
+ return_VOID;
+}
+
+#else
+
+void
+AcpiDsDumpMethodStack (
+ ACPI_STATUS Status,
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT *Op)
+{
+ return;
+}
+
+#endif
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsfield.c b/usr/src/uts/intel/io/acpica/dispatcher/dsfield.c
index 886b6c24c5..2516c131dc 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsfield.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsfield.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSFIELD_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -57,6 +55,18 @@
/* Local prototypes */
+#ifdef ACPI_ASL_COMPILER
+#include "acdisasm.h"
+
+static ACPI_STATUS
+AcpiDsCreateExternalRegion (
+ ACPI_STATUS LookupStatus,
+ ACPI_PARSE_OBJECT *Op,
+ char *Path,
+ ACPI_WALK_STATE *WalkState,
+ ACPI_NAMESPACE_NODE **Node);
+#endif
+
static ACPI_STATUS
AcpiDsGetFieldNames (
ACPI_CREATE_FIELD_INFO *Info,
@@ -64,6 +74,70 @@ AcpiDsGetFieldNames (
ACPI_PARSE_OBJECT *Arg);
+#ifdef ACPI_ASL_COMPILER
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDsCreateExternalRegion (iASL Disassembler only)
+ *
+ * PARAMETERS: LookupStatus - Status from NsLookup operation
+ * Op - Op containing the Field definition and args
+ * Path - Pathname of the region
+ * ` WalkState - Current method state
+ * Node - Where the new region node is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add region to the external list if NOT_FOUND. Create a new
+ * region node/object.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiDsCreateExternalRegion (
+ ACPI_STATUS LookupStatus,
+ ACPI_PARSE_OBJECT *Op,
+ char *Path,
+ ACPI_WALK_STATE *WalkState,
+ ACPI_NAMESPACE_NODE **Node)
+{
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *ObjDesc;
+
+
+ if (LookupStatus != AE_NOT_FOUND)
+ {
+ return (LookupStatus);
+ }
+
+ /*
+ * Table disassembly:
+ * OperationRegion not found. Generate an External for it, and
+ * insert the name into the namespace.
+ */
+ AcpiDmAddOpToExternalList (Op, Path, ACPI_TYPE_REGION, 0, 0);
+
+ Status = AcpiNsLookup (WalkState->ScopeInfo, Path, ACPI_TYPE_REGION,
+ ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT, WalkState, Node);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /* Must create and install a region object for the new node */
+
+ ObjDesc = AcpiUtCreateInternalObject (ACPI_TYPE_REGION);
+ if (!ObjDesc)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ ObjDesc->Region.Node = *Node;
+ Status = AcpiNsAttachObject (*Node, ObjDesc, ACPI_TYPE_REGION);
+ return (Status);
+}
+#endif
+
+
/*******************************************************************************
*
* FUNCTION: AcpiDsCreateBufferField
@@ -77,8 +151,8 @@ AcpiDsGetFieldNames (
* CreateBitFieldOp,
* CreateByteFieldOp,
* CreateWordFieldOp,
- * CreateDWordFieldOp,
- * CreateQWordFieldOp,
+ * CreateDwordFieldOp,
+ * CreateQwordFieldOp,
* CreateFieldOp (all of which define a field in a buffer)
*
******************************************************************************/
@@ -137,7 +211,7 @@ AcpiDsCreateBufferField (
/* Creating new namespace node, should not already exist */
Flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
- ACPI_NS_ERROR_IF_FOUND;
+ ACPI_NS_ERROR_IF_FOUND;
/*
* Mark node temporary if we are executing a normal control
@@ -151,9 +225,9 @@ AcpiDsCreateBufferField (
/* Enter the NameString into the namespace */
- Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS1,
- Flags, WalkState, &Node);
+ Status = AcpiNsLookup (WalkState->ScopeInfo,
+ Arg->Common.Value.String, ACPI_TYPE_ANY,
+ ACPI_IMODE_LOAD_PASS1, Flags, WalkState, &Node);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.String, Status);
@@ -194,13 +268,13 @@ AcpiDsCreateBufferField (
}
/*
- * Remember location in AML stream of the field unit opcode and operands --
- * since the buffer and index operands must be evaluated.
+ * Remember location in AML stream of the field unit opcode and operands
+ * -- since the buffer and index operands must be evaluated.
*/
- SecondDesc = ObjDesc->Common.NextObject;
- SecondDesc->Extra.AmlStart = Op->Named.Data;
+ SecondDesc = ObjDesc->Common.NextObject;
+ SecondDesc->Extra.AmlStart = Op->Named.Data;
SecondDesc->Extra.AmlLength = Op->Named.Length;
- ObjDesc->BufferField.Node = Node;
+ ObjDesc->BufferField.Node = Node;
/* Attach constructed field descriptors to parent node */
@@ -230,7 +304,7 @@ Cleanup:
*
* RETURN: Status
*
- * DESCRIPTION: Process all named fields in a field declaration. Names are
+ * DESCRIPTION: Process all named fields in a field declaration. Names are
* entered into the namespace.
*
******************************************************************************/
@@ -243,6 +317,7 @@ AcpiDsGetFieldNames (
{
ACPI_STATUS Status;
UINT64 Position;
+ ACPI_PARSE_OBJECT *Child;
ACPI_FUNCTION_TRACE_PTR (DsGetFieldNames, Info);
@@ -257,17 +332,18 @@ AcpiDsGetFieldNames (
while (Arg)
{
/*
- * Three types of field elements are handled:
- * 1) Offset - specifies a bit offset
- * 2) AccessAs - changes the access mode
- * 3) Name - Enters a new named field into the namespace
+ * Four types of field elements are handled:
+ * 1) Name - Enters a new named field into the namespace
+ * 2) Offset - specifies a bit offset
+ * 3) AccessAs - changes the access mode/attributes
+ * 4) Connection - Associate a resource template with the field
*/
switch (Arg->Common.AmlOpcode)
{
case AML_INT_RESERVEDFIELD_OP:
- Position = (UINT64) Info->FieldBitPosition
- + (UINT64) Arg->Common.Value.Size;
+ Position = (UINT64) Info->FieldBitPosition +
+ (UINT64) Arg->Common.Value.Size;
if (Position > ACPI_UINT32_MAX)
{
@@ -279,33 +355,79 @@ AcpiDsGetFieldNames (
Info->FieldBitPosition = (UINT32) Position;
break;
-
case AML_INT_ACCESSFIELD_OP:
-
+ case AML_INT_EXTACCESSFIELD_OP:
/*
- * Get a new AccessType and AccessAttribute -- to be used for all
- * field units that follow, until field end or another AccessAs
- * keyword.
+ * Get new AccessType, AccessAttribute, and AccessLength fields
+ * -- to be used for all field units that follow, until the
+ * end-of-field or another AccessAs keyword is encountered.
+ * NOTE. These three bytes are encoded in the integer value
+ * of the parseop for convenience.
*
* In FieldFlags, preserve the flag bits other than the
- * ACCESS_TYPE bits
+ * ACCESS_TYPE bits.
*/
+
+ /* AccessType (ByteAcc, WordAcc, etc.) */
+
Info->FieldFlags = (UINT8)
((Info->FieldFlags & ~(AML_FIELD_ACCESS_TYPE_MASK)) |
- ((UINT8) ((UINT32) Arg->Common.Value.Integer >> 8)));
+ ((UINT8) ((UINT32) (Arg->Common.Value.Integer & 0x07))));
+
+ /* AccessAttribute (AttribQuick, AttribByte, etc.) */
+
+ Info->Attribute = (UINT8)
+ ((Arg->Common.Value.Integer >> 8) & 0xFF);
- Info->Attribute = (UINT8) (Arg->Common.Value.Integer);
+ /* AccessLength (for serial/buffer protocols) */
+
+ Info->AccessLength = (UINT8)
+ ((Arg->Common.Value.Integer >> 16) & 0xFF);
break;
+ case AML_INT_CONNECTION_OP:
+ /*
+ * Clear any previous connection. New connection is used for all
+ * fields that follow, similar to AccessAs
+ */
+ Info->ResourceBuffer = NULL;
+ Info->ConnectionNode = NULL;
+ Info->PinNumberIndex = 0;
+
+ /*
+ * A Connection() is either an actual resource descriptor (buffer)
+ * or a named reference to a resource template
+ */
+ Child = Arg->Common.Value.Arg;
+ if (Child->Common.AmlOpcode == AML_INT_BYTELIST_OP)
+ {
+ Info->ResourceBuffer = Child->Named.Data;
+ Info->ResourceLength = (UINT16) Child->Named.Value.Integer;
+ }
+ else
+ {
+ /* Lookup the Connection() namepath, it should already exist */
+
+ Status = AcpiNsLookup (WalkState->ScopeInfo,
+ Child->Common.Value.Name, ACPI_TYPE_ANY,
+ ACPI_IMODE_EXECUTE, ACPI_NS_DONT_OPEN_SCOPE,
+ WalkState, &Info->ConnectionNode);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_ERROR_NAMESPACE (Child->Common.Value.Name, Status);
+ return_ACPI_STATUS (Status);
+ }
+ }
+ break;
case AML_INT_NAMEDFIELD_OP:
/* Lookup the name, it should already exist */
Status = AcpiNsLookup (WalkState->ScopeInfo,
- (char *) &Arg->Named.Name, Info->FieldType,
- ACPI_IMODE_EXECUTE, ACPI_NS_DONT_OPEN_SCOPE,
- WalkState, &Info->FieldNode);
+ (char *) &Arg->Named.Name, Info->FieldType,
+ ACPI_IMODE_EXECUTE, ACPI_NS_DONT_OPEN_SCOPE,
+ WalkState, &Info->FieldNode);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE ((char *) &Arg->Named.Name, Status);
@@ -334,8 +456,8 @@ AcpiDsGetFieldNames (
/* Keep track of bit position for the next field */
- Position = (UINT64) Info->FieldBitPosition
- + (UINT64) Arg->Common.Value.Size;
+ Position = (UINT64) Info->FieldBitPosition +
+ (UINT64) Arg->Common.Value.Size;
if (Position > ACPI_UINT32_MAX)
{
@@ -346,13 +468,14 @@ AcpiDsGetFieldNames (
}
Info->FieldBitPosition += Info->FieldBitLength;
+ Info->PinNumberIndex++; /* Index relative to previous Connection() */
break;
-
default:
ACPI_ERROR ((AE_INFO,
- "Invalid opcode in field list: 0x%X", Arg->Common.AmlOpcode));
+ "Invalid opcode in field list: 0x%X",
+ Arg->Common.AmlOpcode));
return_ACPI_STATUS (AE_AML_BAD_OPCODE);
}
@@ -394,11 +517,16 @@ AcpiDsCreateField (
/* First arg is the name of the parent OpRegion (must already exist) */
Arg = Op->Common.Value.Arg;
+
if (!RegionNode)
{
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.Name,
- ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, WalkState, &RegionNode);
+ ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, WalkState, &RegionNode);
+#ifdef ACPI_ASL_COMPILER
+ Status = AcpiDsCreateExternalRegion (Status, Arg,
+ Arg->Common.Value.Name, WalkState, &RegionNode);
+#endif
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.Name, Status);
@@ -406,6 +534,8 @@ AcpiDsCreateField (
}
}
+ memset (&Info, 0, sizeof (ACPI_CREATE_FIELD_INFO));
+
/* Second arg is the field flags */
Arg = Arg->Common.Next;
@@ -418,7 +548,6 @@ AcpiDsCreateField (
Info.RegionNode = RegionNode;
Status = AcpiDsGetFieldNames (&Info, WalkState, Arg->Common.Next);
-
return_ACPI_STATUS (Status);
}
@@ -474,28 +603,32 @@ AcpiDsInitFieldObjects (
switch (WalkState->Opcode)
{
case AML_FIELD_OP:
+
Arg = AcpiPsGetArg (Op, 2);
Type = ACPI_TYPE_LOCAL_REGION_FIELD;
break;
case AML_BANK_FIELD_OP:
+
Arg = AcpiPsGetArg (Op, 4);
Type = ACPI_TYPE_LOCAL_BANK_FIELD;
break;
case AML_INDEX_FIELD_OP:
+
Arg = AcpiPsGetArg (Op, 3);
Type = ACPI_TYPE_LOCAL_INDEX_FIELD;
break;
default:
+
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
/* Creating new namespace node(s), should not already exist */
Flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
- ACPI_NS_ERROR_IF_FOUND;
+ ACPI_NS_ERROR_IF_FOUND;
/*
* Mark node(s) temporary if we are executing a normal control
@@ -514,14 +647,14 @@ AcpiDsInitFieldObjects (
while (Arg)
{
/*
- * Ignore OFFSET and ACCESSAS terms here; we are only interested in the
- * field names in order to enter them into the namespace.
+ * Ignore OFFSET/ACCESSAS/CONNECTION terms here; we are only interested
+ * in the field names in order to enter them into the namespace.
*/
if (Arg->Common.AmlOpcode == AML_INT_NAMEDFIELD_OP)
{
Status = AcpiNsLookup (WalkState->ScopeInfo,
- (char *) &Arg->Named.Name, Type, ACPI_IMODE_LOAD_PASS1,
- Flags, WalkState, &Node);
+ (char *) &Arg->Named.Name, Type, ACPI_IMODE_LOAD_PASS1,
+ Flags, WalkState, &Node);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE ((char *) &Arg->Named.Name, Status);
@@ -581,8 +714,12 @@ AcpiDsCreateBankField (
if (!RegionNode)
{
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.Name,
- ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, WalkState, &RegionNode);
+ ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, WalkState, &RegionNode);
+#ifdef ACPI_ASL_COMPILER
+ Status = AcpiDsCreateExternalRegion (Status, Arg,
+ Arg->Common.Value.Name, WalkState, &RegionNode);
+#endif
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.Name, Status);
@@ -594,8 +731,8 @@ AcpiDsCreateBankField (
Arg = Arg->Common.Next;
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, WalkState, &Info.RegisterNode);
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, WalkState, &Info.RegisterNode);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.String, Status);
@@ -621,11 +758,12 @@ AcpiDsCreateBankField (
/*
* Use Info.DataRegisterNode to store BankField Op
- * It's safe because DataRegisterNode will never be used when create bank field
- * We store AmlStart and AmlLength in the BankField Op for late evaluation
- * Used in AcpiExPrepFieldValue(Info)
+ * It's safe because DataRegisterNode will never be used when create
+ * bank field \we store AmlStart and AmlLength in the BankField Op for
+ * late evaluation. Used in AcpiExPrepFieldValue(Info)
*
- * TBD: Or, should we add a field in ACPI_CREATE_FIELD_INFO, like "void *ParentOp"?
+ * TBD: Or, should we add a field in ACPI_CREATE_FIELD_INFO, like
+ * "void *ParentOp"?
*/
Info.DataRegisterNode = (ACPI_NAMESPACE_NODE*) Op;
@@ -666,8 +804,8 @@ AcpiDsCreateIndexField (
Arg = Op->Common.Value.Arg;
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, WalkState, &Info.RegisterNode);
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, WalkState, &Info.RegisterNode);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.String, Status);
@@ -678,8 +816,8 @@ AcpiDsCreateIndexField (
Arg = Arg->Common.Next;
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, WalkState, &Info.DataRegisterNode);
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, WalkState, &Info.DataRegisterNode);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR_NAMESPACE (Arg->Common.Value.String, Status);
@@ -697,8 +835,5 @@ AcpiDsCreateIndexField (
Info.RegionNode = RegionNode;
Status = AcpiDsGetFieldNames (&Info, WalkState, Arg->Common.Next);
-
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsinit.c b/usr/src/uts/intel/io/acpica/dispatcher/dsinit.c
index 154e5de34f..74e974da03 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsinit.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsinit.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSINIT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
@@ -52,6 +50,7 @@
#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME ("dsinit")
+
/* Local prototypes */
static ACPI_STATUS
@@ -73,7 +72,7 @@ AcpiDsInitOneObject (
*
* RETURN: Status
*
- * DESCRIPTION: Callback from AcpiWalkNamespace. Invoked for every object
+ * DESCRIPTION: Callback from AcpiWalkNamespace. Invoked for every object
* within the namespace.
*
* Currently, the only objects that require initialization are:
@@ -91,8 +90,8 @@ AcpiDsInitOneObject (
{
ACPI_INIT_WALK_INFO *Info = (ACPI_INIT_WALK_INFO *) Context;
ACPI_NAMESPACE_NODE *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
- ACPI_OBJECT_TYPE Type;
ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *ObjDesc;
ACPI_FUNCTION_ENTRY ();
@@ -111,9 +110,7 @@ AcpiDsInitOneObject (
/* And even then, we are only interested in a few object types */
- Type = AcpiNsGetType (ObjHandle);
-
- switch (Type)
+ switch (AcpiNsGetType (ObjHandle))
{
case ACPI_TYPE_REGION:
@@ -128,20 +125,55 @@ AcpiDsInitOneObject (
Info->OpRegionCount++;
break;
-
case ACPI_TYPE_METHOD:
-
+ /*
+ * Auto-serialization support. We will examine each method that is
+ * NotSerialized to determine if it creates any Named objects. If
+ * it does, it will be marked serialized to prevent problems if
+ * the method is entered by two or more threads and an attempt is
+ * made to create the same named object twice -- which results in
+ * an AE_ALREADY_EXISTS exception and method abort.
+ */
Info->MethodCount++;
- break;
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (!ObjDesc)
+ {
+ break;
+ }
+
+ /* Ignore if already serialized */
+
+ if (ObjDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED)
+ {
+ Info->SerialMethodCount++;
+ break;
+ }
+
+ if (AcpiGbl_AutoSerializeMethods)
+ {
+ /* Parse/scan method and serialize it if necessary */
+ AcpiDsAutoSerializeMethod (Node, ObjDesc);
+ if (ObjDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED)
+ {
+ /* Method was just converted to Serialized */
+
+ Info->SerialMethodCount++;
+ Info->SerializedMethodCount++;
+ break;
+ }
+ }
+
+ Info->NonSerialMethodCount++;
+ break;
case ACPI_TYPE_DEVICE:
Info->DeviceCount++;
break;
-
default:
+
break;
}
@@ -189,11 +221,10 @@ AcpiDsInitializeObjects (
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
"**** Starting initialization of namespace objects ****\n"));
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, "Parsing all Control Methods:"));
/* Set all init info to zero */
- ACPI_MEMSET (&Info, 0, sizeof (ACPI_INIT_WALK_INFO));
+ memset (&Info, 0, sizeof (ACPI_INIT_WALK_INFO));
Info.OwnerId = OwnerId;
Info.TableIndex = TableIndex;
@@ -211,7 +242,7 @@ AcpiDsInitializeObjects (
* the namespace reader lock.
*/
Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, StartNode, ACPI_UINT32_MAX,
- ACPI_NS_WALK_UNLOCK, AcpiDsInitOneObject, NULL, &Info, NULL);
+ ACPI_NS_WALK_UNLOCK, AcpiDsInitOneObject, NULL, &Info, NULL);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status, "During WalkNamespace"));
@@ -224,15 +255,26 @@ AcpiDsInitializeObjects (
return_ACPI_STATUS (Status);
}
+ /* DSDT is always the first AML table */
+
+ if (ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_DSDT))
+ {
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ "\nInitializing Namespace objects:\n"));
+ }
+
+ /* Summary of objects initialized */
+
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
- "\nTable [%4.4s](id %4.4X) - %u Objects with %u Devices %u Methods %u Regions\n",
- Table->Signature, OwnerId, Info.ObjectCount,
- Info.DeviceCount, Info.MethodCount, Info.OpRegionCount));
+ "Table [%4.4s: %-8.8s] (id %.2X) - %4u Objects with %3u Devices, "
+ "%3u Regions, %4u Methods (%u/%u/%u Serial/Non/Cvt)\n",
+ Table->Signature, Table->OemTableId, OwnerId, Info.ObjectCount,
+ Info.DeviceCount,Info.OpRegionCount, Info.MethodCount,
+ Info.SerialMethodCount, Info.NonSerialMethodCount,
+ Info.SerializedMethodCount));
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
- "%u Methods, %u Regions\n", Info.MethodCount, Info.OpRegionCount));
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "%u Methods, %u Regions\n",
+ Info.MethodCount, Info.OpRegionCount));
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c b/usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c
index 1cfa1c6853..334dadcac6 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsmethod.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,14 +41,14 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSMETHOD_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
#include "acinterp.h"
#include "acnamesp.h"
-#include "acdisasm.h"
+#include "acparser.h"
+#include "amlcode.h"
+#include "acdebug.h"
#define _COMPONENT ACPI_DISPATCHER
@@ -57,12 +57,154 @@
/* Local prototypes */
static ACPI_STATUS
+AcpiDsDetectNamedOpcodes (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT **OutOp);
+
+static ACPI_STATUS
AcpiDsCreateMethodMutex (
ACPI_OPERAND_OBJECT *MethodDesc);
/*******************************************************************************
*
+ * FUNCTION: AcpiDsAutoSerializeMethod
+ *
+ * PARAMETERS: Node - Namespace Node of the method
+ * ObjDesc - Method object attached to node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse a control method AML to scan for control methods that
+ * need serialization due to the creation of named objects.
+ *
+ * NOTE: It is a bit of overkill to mark all such methods serialized, since
+ * there is only a problem if the method actually blocks during execution.
+ * A blocking operation is, for example, a Sleep() operation, or any access
+ * to an operation region. However, it is probably not possible to easily
+ * detect whether a method will block or not, so we simply mark all suspicious
+ * methods as serialized.
+ *
+ * NOTE2: This code is essentially a generic routine for parsing a single
+ * control method.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiDsAutoSerializeMethod (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_OPERAND_OBJECT *ObjDesc)
+{
+ ACPI_STATUS Status;
+ ACPI_PARSE_OBJECT *Op = NULL;
+ ACPI_WALK_STATE *WalkState;
+
+
+ ACPI_FUNCTION_TRACE_PTR (DsAutoSerializeMethod, Node);
+
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
+ "Method auto-serialization parse [%4.4s] %p\n",
+ AcpiUtGetNodeName (Node), Node));
+
+ /* Create/Init a root op for the method parse tree */
+
+ Op = AcpiPsAllocOp (AML_METHOD_OP, ObjDesc->Method.AmlStart);
+ if (!Op)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ AcpiPsSetName (Op, Node->Name.Integer);
+ Op->Common.Node = Node;
+
+ /* Create and initialize a new walk state */
+
+ WalkState = AcpiDsCreateWalkState (Node->OwnerId, NULL, NULL, NULL);
+ if (!WalkState)
+ {
+ AcpiPsFreeOp (Op);
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ Status = AcpiDsInitAmlWalk (WalkState, Op, Node,
+ ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, NULL, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiDsDeleteWalkState (WalkState);
+ AcpiPsFreeOp (Op);
+ return_ACPI_STATUS (Status);
+ }
+
+ WalkState->DescendingCallback = AcpiDsDetectNamedOpcodes;
+
+ /* Parse the method, scan for creation of named objects */
+
+ Status = AcpiPsParseAml (WalkState);
+
+ AcpiPsDeleteParseTree (Op);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDsDetectNamedOpcodes
+ *
+ * PARAMETERS: WalkState - Current state of the parse tree walk
+ * OutOp - Unused, required for parser interface
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Descending callback used during the loading of ACPI tables.
+ * Currently used to detect methods that must be marked serialized
+ * in order to avoid problems with the creation of named objects.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiDsDetectNamedOpcodes (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT **OutOp)
+{
+
+ ACPI_FUNCTION_NAME (AcpiDsDetectNamedOpcodes);
+
+
+ /* We are only interested in opcodes that create a new name */
+
+ if (!(WalkState->OpInfo->Flags & (AML_NAMED | AML_CREATE | AML_FIELD)))
+ {
+ return (AE_OK);
+ }
+
+ /*
+ * At this point, we know we have a Named object opcode.
+ * Mark the method as serialized. Later code will create a mutex for
+ * this method to enforce serialization.
+ *
+ * Note, ACPI_METHOD_IGNORE_SYNC_LEVEL flag means that we will ignore the
+ * Sync Level mechanism for this method, even though it is now serialized.
+ * Otherwise, there can be conflicts with existing ASL code that actually
+ * uses sync levels.
+ */
+ WalkState->MethodDesc->Method.SyncLevel = 0;
+ WalkState->MethodDesc->Method.InfoFlags |=
+ (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL);
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Method serialized [%4.4s] %p - [%s] (%4.4X)\n",
+ WalkState->MethodNode->Name.Ascii, WalkState->MethodNode,
+ WalkState->OpInfo->Name, WalkState->Opcode));
+
+ /* Abort the parse, no need to examine this method any further */
+
+ return (AE_CTRL_TERMINATE);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiDsMethodError
*
* PARAMETERS: Status - Execution status
@@ -71,7 +213,7 @@ AcpiDsCreateMethodMutex (
* RETURN: Status
*
* DESCRIPTION: Called on method error. Invoke the global exception handler if
- * present, dump the method data if the disassembler is configured
+ * present, dump the method data if the debugger is configured
*
* Note: Allows the exception handler to change the status code
*
@@ -82,6 +224,9 @@ AcpiDsMethodError (
ACPI_STATUS Status,
ACPI_WALK_STATE *WalkState)
{
+ UINT32 AmlOffset;
+
+
ACPI_FUNCTION_ENTRY ();
@@ -105,23 +250,28 @@ AcpiDsMethodError (
* Handler can map the exception code to anything it wants, including
* AE_OK, in which case the executing method will not be aborted.
*/
+ AmlOffset = (UINT32) ACPI_PTR_DIFF (WalkState->Aml,
+ WalkState->ParserState.AmlStart);
+
Status = AcpiGbl_ExceptionHandler (Status,
- WalkState->MethodNode ?
- WalkState->MethodNode->Name.Integer : 0,
- WalkState->Opcode, WalkState->AmlOffset, NULL);
+ WalkState->MethodNode ?
+ WalkState->MethodNode->Name.Integer : 0,
+ WalkState->Opcode, AmlOffset, NULL);
AcpiExEnterInterpreter ();
}
AcpiDsClearImplicitReturn (WalkState);
-#ifdef ACPI_DISASSEMBLER
if (ACPI_FAILURE (Status))
{
- /* Display method locals/args if disassembler is present */
+ AcpiDsDumpMethodStack (Status, WalkState, WalkState->Op);
- AcpiDmDumpMethodInfo (Status, WalkState, WalkState->Op);
- }
+ /* Display method locals/args if debugger is present */
+
+#ifdef ACPI_DEBUGGER
+ AcpiDbDumpMethodInfo (Status, WalkState);
#endif
+ }
return (Status);
}
@@ -163,6 +313,7 @@ AcpiDsCreateMethodMutex (
Status = AcpiOsCreateMutex (&MutexDesc->Mutex.OsMutex);
if (ACPI_FAILURE (Status))
{
+ AcpiUtDeleteObjectDesc (MutexDesc);
return_ACPI_STATUS (Status);
}
@@ -183,7 +334,7 @@ AcpiDsCreateMethodMutex (
*
* RETURN: Status
*
- * DESCRIPTION: Prepare a method for execution. Parses the method if necessary,
+ * DESCRIPTION: Prepare a method for execution. Parses the method if necessary,
* increments the thread count, and waits at the method semaphore
* for clearance to execute.
*
@@ -206,6 +357,8 @@ AcpiDsBeginMethodExecution (
return_ACPI_STATUS (AE_NULL_ENTRY);
}
+ AcpiExStartTraceMethod (MethodNode, ObjDesc, WalkState);
+
/* Prevent wraparound of thread count */
if (ObjDesc->Method.ThreadCount == ACPI_UINT8_MAX)
@@ -237,15 +390,22 @@ AcpiDsBeginMethodExecution (
/*
* The CurrentSyncLevel (per-thread) must be less than or equal to
* the sync level of the method. This mechanism provides some
- * deadlock prevention
+ * deadlock prevention.
+ *
+ * If the method was auto-serialized, we just ignore the sync level
+ * mechanism, because auto-serialization of methods can interfere
+ * with ASL code that actually uses sync levels.
*
* Top-level method invocation has no walk state at this point
*/
if (WalkState &&
- (WalkState->Thread->CurrentSyncLevel > ObjDesc->Method.Mutex->Mutex.SyncLevel))
+ (!(ObjDesc->Method.InfoFlags & ACPI_METHOD_IGNORE_SYNC_LEVEL)) &&
+ (WalkState->Thread->CurrentSyncLevel >
+ ObjDesc->Method.Mutex->Mutex.SyncLevel))
{
ACPI_ERROR ((AE_INFO,
- "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)",
+ "Cannot acquire Mutex for method [%4.4s]"
+ ", current SyncLevel is too large (%u)",
AcpiUtGetNodeName (MethodNode),
WalkState->Thread->CurrentSyncLevel));
@@ -258,14 +418,15 @@ AcpiDsBeginMethodExecution (
*/
if (!WalkState ||
!ObjDesc->Method.Mutex->Mutex.ThreadId ||
- (WalkState->Thread->ThreadId != ObjDesc->Method.Mutex->Mutex.ThreadId))
+ (WalkState->Thread->ThreadId !=
+ ObjDesc->Method.Mutex->Mutex.ThreadId))
{
/*
* Acquire the method mutex. This releases the interpreter if we
* block (and reacquires it before it returns)
*/
- Status = AcpiExSystemWaitMutex (ObjDesc->Method.Mutex->Mutex.OsMutex,
- ACPI_WAIT_FOREVER);
+ Status = AcpiExSystemWaitMutex (
+ ObjDesc->Method.Mutex->Mutex.OsMutex, ACPI_WAIT_FOREVER);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -278,13 +439,30 @@ AcpiDsBeginMethodExecution (
ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel =
WalkState->Thread->CurrentSyncLevel;
- ObjDesc->Method.Mutex->Mutex.ThreadId = WalkState->Thread->ThreadId;
- WalkState->Thread->CurrentSyncLevel = ObjDesc->Method.SyncLevel;
+ ObjDesc->Method.Mutex->Mutex.ThreadId =
+ WalkState->Thread->ThreadId;
+
+ /*
+ * Update the current SyncLevel only if this is not an auto-
+ * serialized method. In the auto case, we have to ignore
+ * the sync level for the method mutex (created for the
+ * auto-serialization) because we have no idea of what the
+ * sync level should be. Therefore, just ignore it.
+ */
+ if (!(ObjDesc->Method.InfoFlags &
+ ACPI_METHOD_IGNORE_SYNC_LEVEL))
+ {
+ WalkState->Thread->CurrentSyncLevel =
+ ObjDesc->Method.SyncLevel;
+ }
}
else
{
ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel =
ObjDesc->Method.Mutex->Mutex.SyncLevel;
+
+ ObjDesc->Method.Mutex->Mutex.ThreadId =
+ AcpiOsGetThreadId ();
}
}
@@ -357,7 +535,8 @@ AcpiDsCallControlMethod (
ACPI_FUNCTION_TRACE_PTR (DsCallControlMethod, ThisWalkState);
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Calling method %p, currentstate=%p\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "Calling method %p, currentstate=%p\n",
ThisWalkState->PrevOp, ThisWalkState));
/*
@@ -377,8 +556,8 @@ AcpiDsCallControlMethod (
/* Init for new method, possibly wait on method mutex */
- Status = AcpiDsBeginMethodExecution (MethodNode, ObjDesc,
- ThisWalkState);
+ Status = AcpiDsBeginMethodExecution (
+ MethodNode, ObjDesc, ThisWalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -386,8 +565,8 @@ AcpiDsCallControlMethod (
/* Begin method parse/execution. Create a new walk state */
- NextWalkState = AcpiDsCreateWalkState (ObjDesc->Method.OwnerId,
- NULL, ObjDesc, Thread);
+ NextWalkState = AcpiDsCreateWalkState (
+ ObjDesc->Method.OwnerId, NULL, ObjDesc, Thread);
if (!NextWalkState)
{
Status = AE_NO_MEMORY;
@@ -409,14 +588,15 @@ AcpiDsCallControlMethod (
Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
if (!Info)
{
- return_ACPI_STATUS (AE_NO_MEMORY);
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
}
Info->Parameters = &ThisWalkState->Operands[0];
Status = AcpiDsInitAmlWalk (NextWalkState, NULL, MethodNode,
- ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength,
- Info, ACPI_IMODE_EXECUTE);
+ ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength,
+ Info, ACPI_IMODE_EXECUTE);
ACPI_FREE (Info);
if (ACPI_FAILURE (Status))
@@ -461,10 +641,7 @@ Cleanup:
/* On error, we must terminate the method properly */
AcpiDsTerminateControlMethod (ObjDesc, NextWalkState);
- if (NextWalkState)
- {
- AcpiDsDeleteWalkState (NextWalkState);
- }
+ AcpiDsDeleteWalkState (NextWalkState);
return_ACPI_STATUS (Status);
}
@@ -480,7 +657,7 @@ Cleanup:
* RETURN: Status
*
* DESCRIPTION: Restart a method that was preempted by another (nested) method
- * invocation. Handle the return value (if any) from the callee.
+ * invocation. Handle the return value (if any) from the callee.
*
******************************************************************************/
@@ -570,7 +747,7 @@ AcpiDsRestartControlMethod (
*
* RETURN: None
*
- * DESCRIPTION: Terminate a control method. Delete everything that the method
+ * DESCRIPTION: Terminate a control method. Delete everything that the method
* created, delete all locals and arguments, and delete the parse
* tree if requested.
*
@@ -614,7 +791,8 @@ AcpiDsTerminateControlMethod (
WalkState->Thread->CurrentSyncLevel =
MethodDesc->Method.Mutex->Mutex.OriginalSyncLevel;
- AcpiOsReleaseMutex (MethodDesc->Method.Mutex->Mutex.OsMutex);
+ AcpiOsReleaseMutex (
+ MethodDesc->Method.Mutex->Mutex.OsMutex);
MethodDesc->Method.Mutex->Mutex.ThreadId = 0;
}
}
@@ -644,7 +822,8 @@ AcpiDsTerminateControlMethod (
if (MethodDesc->Method.InfoFlags & ACPI_METHOD_MODIFIED_NAMESPACE)
{
AcpiNsDeleteNamespaceByOwner (MethodDesc->Method.OwnerId);
- MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_MODIFIED_NAMESPACE;
+ MethodDesc->Method.InfoFlags &=
+ ~ACPI_METHOD_MODIFIED_NAMESPACE;
}
}
}
@@ -691,8 +870,9 @@ AcpiDsTerminateControlMethod (
{
if (WalkState)
{
- ACPI_INFO ((AE_INFO,
- "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error",
+ ACPI_INFO ((
+ "Marking method %4.4s as Serialized "
+ "because of AE_ALREADY_EXISTS error",
WalkState->MethodNode->Name.Ascii));
}
@@ -707,8 +887,11 @@ AcpiDsTerminateControlMethod (
* marking the method permanently as Serialized when the last
* thread exits here.
*/
- MethodDesc->Method.InfoFlags &= ~ACPI_METHOD_SERIALIZED_PENDING;
- MethodDesc->Method.InfoFlags |= ACPI_METHOD_SERIALIZED;
+ MethodDesc->Method.InfoFlags &=
+ ~ACPI_METHOD_SERIALIZED_PENDING;
+
+ MethodDesc->Method.InfoFlags |=
+ (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL);
MethodDesc->Method.SyncLevel = 0;
}
@@ -720,7 +903,8 @@ AcpiDsTerminateControlMethod (
}
}
+ AcpiExStopTraceMethod ((ACPI_NAMESPACE_NODE *) MethodDesc->Method.Node,
+ MethodDesc, WalkState);
+
return_VOID;
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c b/usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c
index 74ee4bdf31..32f9850a72 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsmthdat.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSMTHDAT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
@@ -86,7 +84,7 @@ AcpiDsMethodDataGetType (
* RETURN: Status
*
* DESCRIPTION: Initialize the data structures that hold the method's arguments
- * and locals. The data struct is an array of namespace nodes for
+ * and locals. The data struct is an array of namespace nodes for
* each - this allows RefOf and DeRefOf to work properly for these
* special data types.
*
@@ -112,7 +110,9 @@ AcpiDsMethodDataInit (
for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++)
{
- ACPI_MOVE_32_TO_32 (&WalkState->Arguments[i].Name, NAMEOF_ARG_NTE);
+ ACPI_MOVE_32_TO_32 (&WalkState->Arguments[i].Name,
+ NAMEOF_ARG_NTE);
+
WalkState->Arguments[i].Name.Integer |= (i << 24);
WalkState->Arguments[i].DescriptorType = ACPI_DESC_TYPE_NAMED;
WalkState->Arguments[i].Type = ACPI_TYPE_ANY;
@@ -123,7 +123,8 @@ AcpiDsMethodDataInit (
for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++)
{
- ACPI_MOVE_32_TO_32 (&WalkState->LocalVariables[i].Name, NAMEOF_LOCAL_NTE);
+ ACPI_MOVE_32_TO_32 (&WalkState->LocalVariables[i].Name,
+ NAMEOF_LOCAL_NTE);
WalkState->LocalVariables[i].Name.Integer |= (i << 24);
WalkState->LocalVariables[i].DescriptorType = ACPI_DESC_TYPE_NAMED;
@@ -143,7 +144,7 @@ AcpiDsMethodDataInit (
*
* RETURN: None
*
- * DESCRIPTION: Delete method locals and arguments. Arguments are only
+ * DESCRIPTION: Delete method locals and arguments. Arguments are only
* deleted if this method was called from another method.
*
******************************************************************************/
@@ -165,7 +166,7 @@ AcpiDsMethodDataDeleteAll (
if (WalkState->LocalVariables[Index].Object)
{
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Deleting Local%u=%p\n",
- Index, WalkState->LocalVariables[Index].Object));
+ Index, WalkState->LocalVariables[Index].Object));
/* Detach object (if present) and remove a reference */
@@ -180,7 +181,7 @@ AcpiDsMethodDataDeleteAll (
if (WalkState->Arguments[Index].Object)
{
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Deleting Arg%u=%p\n",
- Index, WalkState->Arguments[Index].Object));
+ Index, WalkState->Arguments[Index].Object));
/* Detach object (if present) and remove a reference */
@@ -202,7 +203,7 @@ AcpiDsMethodDataDeleteAll (
*
* RETURN: Status
*
- * DESCRIPTION: Initialize arguments for a method. The parameter list is a list
+ * DESCRIPTION: Initialize arguments for a method. The parameter list is a list
* of ACPI operand objects, either null terminated or whose length
* is defined by MaxParamCount.
*
@@ -223,7 +224,8 @@ AcpiDsMethodDataInitArgs (
if (!Params)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "No param list passed to method\n"));
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "No parameter list passed to method\n"));
return_ACPI_STATUS (AE_OK);
}
@@ -238,8 +240,8 @@ AcpiDsMethodDataInitArgs (
* Store the argument in the method/walk descriptor.
* Do not copy the arg in order to implement call by reference
*/
- Status = AcpiDsMethodDataSetValue (ACPI_REFCLASS_ARG, Index,
- Params[Index], WalkState);
+ Status = AcpiDsMethodDataSetValue (
+ ACPI_REFCLASS_ARG, Index, Params[Index], WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -315,6 +317,7 @@ AcpiDsMethodDataGetNode (
break;
default:
+
ACPI_ERROR ((AE_INFO, "Type %u is invalid", Type));
return_ACPI_STATUS (AE_TYPE);
}
@@ -443,7 +446,7 @@ AcpiDsMethodDataGetValue (
* This means that either 1) The expected argument was
* not passed to the method, or 2) A local variable
* was referenced by the method (via the ASL)
- * before it was initialized. Either case is an error.
+ * before it was initialized. Either case is an error.
*/
/* If slack enabled, init the LocalX/ArgX to an Integer of value zero */
@@ -472,7 +475,6 @@ AcpiDsMethodDataGetValue (
return_ACPI_STATUS (AE_AML_UNINITIALIZED_ARG);
case ACPI_REFCLASS_LOCAL:
-
/*
* No error message for this case, will be trapped again later to
* detect and ignore cases of Store(LocalX,LocalX)
@@ -508,7 +510,7 @@ AcpiDsMethodDataGetValue (
*
* RETURN: None
*
- * DESCRIPTION: Delete the entry at Opcode:Index. Inserts
+ * DESCRIPTION: Delete the entry at Opcode:Index. Inserts
* a null into the stack slot after the object is deleted.
*
******************************************************************************/
@@ -573,7 +575,7 @@ AcpiDsMethodDataDeleteValue (
*
* RETURN: Status
*
- * DESCRIPTION: Store a value in an Arg or Local. The ObjDesc is installed
+ * DESCRIPTION: Store a value in an Arg or Local. The ObjDesc is installed
* as the new value for the Arg or Local and the reference count
* for ObjDesc is incremented.
*
@@ -621,7 +623,7 @@ AcpiDsStoreObjectToLocal (
/*
* If the reference count on the object is more than one, we must
- * take a copy of the object before we store. A reference count
+ * take a copy of the object before we store. A reference count
* of exactly 1 means that the object was just created during the
* evaluation of an expression, and we can safely use it since it
* is not used anywhere else.
@@ -629,7 +631,8 @@ AcpiDsStoreObjectToLocal (
NewObjDesc = ObjDesc;
if (ObjDesc->Common.ReferenceCount > 1)
{
- Status = AcpiUtCopyIobjectToIobject (ObjDesc, &NewObjDesc, WalkState);
+ Status = AcpiUtCopyIobjectToIobject (
+ ObjDesc, &NewObjDesc, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -666,13 +669,16 @@ AcpiDsStoreObjectToLocal (
* If we have a valid reference object that came from RefOf(),
* do the indirect store
*/
- if ((ACPI_GET_DESCRIPTOR_TYPE (CurrentObjDesc) == ACPI_DESC_TYPE_OPERAND) &&
- (CurrentObjDesc->Common.Type == ACPI_TYPE_LOCAL_REFERENCE) &&
- (CurrentObjDesc->Reference.Class == ACPI_REFCLASS_REFOF))
+ if ((ACPI_GET_DESCRIPTOR_TYPE (CurrentObjDesc) ==
+ ACPI_DESC_TYPE_OPERAND) &&
+ (CurrentObjDesc->Common.Type ==
+ ACPI_TYPE_LOCAL_REFERENCE) &&
+ (CurrentObjDesc->Reference.Class ==
+ ACPI_REFCLASS_REFOF))
{
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "Arg (%p) is an ObjRef(Node), storing in node %p\n",
- NewObjDesc, CurrentObjDesc));
+ "Arg (%p) is an ObjRef(Node), storing in node %p\n",
+ NewObjDesc, CurrentObjDesc));
/*
* Store this object to the Node (perform the indirect store)
@@ -680,8 +686,8 @@ AcpiDsStoreObjectToLocal (
* specification rules on storing to Locals/Args.
*/
Status = AcpiExStoreObjectToNode (NewObjDesc,
- CurrentObjDesc->Reference.Object, WalkState,
- ACPI_NO_IMPLICIT_CONVERSION);
+ CurrentObjDesc->Reference.Object, WalkState,
+ ACPI_NO_IMPLICIT_CONVERSION);
/* Remove local reference if we copied the object above */
@@ -689,6 +695,7 @@ AcpiDsStoreObjectToLocal (
{
AcpiUtRemoveReference (NewObjDesc);
}
+
return_ACPI_STATUS (Status);
}
}
@@ -768,5 +775,3 @@ AcpiDsMethodDataGetType (
return_VALUE (Object->Type);
}
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsobject.c b/usr/src/uts/intel/io/acpica/dispatcher/dsobject.c
index 69888505c6..3f7b540580 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsobject.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsobject.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSOBJECT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -104,10 +102,10 @@ AcpiDsBuildInternalObject (
if (!Op->Common.Node)
{
Status = AcpiNsLookup (WalkState->ScopeInfo,
- Op->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL,
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &(Op->Common.Node)));
+ Op->Common.Value.String,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL,
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &(Op->Common.Node)));
if (ACPI_FAILURE (Status))
{
/* Check if we are resolving a named reference within a package */
@@ -163,8 +161,8 @@ AcpiDsBuildInternalObject (
ObjDesc = ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, Op->Common.Node);
Status = AcpiExResolveNodeToValue (
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &ObjDesc),
- WalkState);
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &ObjDesc),
+ WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -224,14 +222,14 @@ AcpiDsBuildInternalObject (
/* Create and init a new internal ACPI object */
ObjDesc = AcpiUtCreateInternalObject (
- (AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode))->ObjectType);
+ (AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode))->ObjectType);
if (!ObjDesc)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
- Status = AcpiDsInitObjectFromOp (WalkState, Op, Op->Common.AmlOpcode,
- &ObjDesc);
+ Status = AcpiDsInitObjectFromOp (
+ WalkState, Op, Op->Common.AmlOpcode, &ObjDesc);
if (ACPI_FAILURE (Status))
{
AcpiUtRemoveReference (ObjDesc);
@@ -296,7 +294,7 @@ AcpiDsBuildInternalBufferObj (
/*
* Second arg is the buffer data (optional) ByteList can be either
- * individual bytes or a string initializer. In either case, a
+ * individual bytes or a string initializer. In either case, a
* ByteList appears in the AML.
*/
Arg = Op->Common.Value.Arg; /* skip first arg */
@@ -338,8 +336,8 @@ AcpiDsBuildInternalBufferObj (
}
else
{
- ObjDesc->Buffer.Pointer = ACPI_ALLOCATE_ZEROED (
- ObjDesc->Buffer.Length);
+ ObjDesc->Buffer.Pointer =
+ ACPI_ALLOCATE_ZEROED (ObjDesc->Buffer.Length);
if (!ObjDesc->Buffer.Pointer)
{
AcpiUtDeleteObjectDesc (ObjDesc);
@@ -350,8 +348,8 @@ AcpiDsBuildInternalBufferObj (
if (ByteList)
{
- ACPI_MEMCPY (ObjDesc->Buffer.Pointer, ByteList->Named.Data,
- ByteListLength);
+ memcpy (ObjDesc->Buffer.Pointer, ByteList->Named.Data,
+ ByteListLength);
}
}
@@ -470,8 +468,8 @@ AcpiDsBuildInternalPackageObj (
* invocation, so we special case it here
*/
Arg->Common.AmlOpcode = AML_INT_NAMEPATH_OP;
- Status = AcpiDsBuildInternalObject (WalkState, Arg,
- &ObjDesc->Package.Elements[i]);
+ Status = AcpiDsBuildInternalObject (
+ WalkState, Arg, &ObjDesc->Package.Elements[i]);
}
else
{
@@ -483,8 +481,8 @@ AcpiDsBuildInternalPackageObj (
}
else
{
- Status = AcpiDsBuildInternalObject (WalkState, Arg,
- &ObjDesc->Package.Elements[i]);
+ Status = AcpiDsBuildInternalObject (
+ WalkState, Arg, &ObjDesc->Package.Elements[i]);
}
if (*ObjDescPtr)
@@ -540,8 +538,9 @@ AcpiDsBuildInternalPackageObj (
Arg = Arg->Common.Next;
}
- ACPI_INFO ((AE_INFO,
- "Actual Package length (%u) is larger than NumElements field (%u), truncated\n",
+ ACPI_INFO ((
+ "Actual Package length (%u) is larger than "
+ "NumElements field (%u), truncated",
i, ElementCount));
}
else if (i < ElementCount)
@@ -551,7 +550,8 @@ AcpiDsBuildInternalPackageObj (
* Note: this is not an error, the package is padded out with NULLs.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Package List length (%u) smaller than NumElements count (%u), padded with null elements\n",
+ "Package List length (%u) smaller than NumElements "
+ "count (%u), padded with null elements\n",
i, ElementCount));
}
@@ -590,7 +590,7 @@ AcpiDsCreateNode (
/*
* Because of the execution pass through the non-control-method
- * parts of the table, we can arrive here twice. Only init
+ * parts of the table, we can arrive here twice. Only init
* the named object node the first time through
*/
if (AcpiNsGetAttachedObject (Node))
@@ -607,8 +607,8 @@ AcpiDsCreateNode (
/* Build an internal object for the argument(s) */
- Status = AcpiDsBuildInternalObject (WalkState, Op->Common.Value.Arg,
- &ObjDesc);
+ Status = AcpiDsBuildInternalObject (
+ WalkState, Op->Common.Value.Arg, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -643,7 +643,7 @@ AcpiDsCreateNode (
* RETURN: Status
*
* DESCRIPTION: Initialize a namespace object from a parser Op and its
- * associated arguments. The namespace object is a more compact
+ * associated arguments. The namespace object is a more compact
* representation of the Op and its arguments.
*
******************************************************************************/
@@ -677,29 +677,25 @@ AcpiDsInitObjectFromOp (
switch (ObjDesc->Common.Type)
{
case ACPI_TYPE_BUFFER:
-
/*
* Defer evaluation of Buffer TermArg operand
*/
- ObjDesc->Buffer.Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
- WalkState->Operands[0]);
- ObjDesc->Buffer.AmlStart = Op->Named.Data;
+ ObjDesc->Buffer.Node = ACPI_CAST_PTR (
+ ACPI_NAMESPACE_NODE, WalkState->Operands[0]);
+ ObjDesc->Buffer.AmlStart = Op->Named.Data;
ObjDesc->Buffer.AmlLength = Op->Named.Length;
break;
-
case ACPI_TYPE_PACKAGE:
-
/*
* Defer evaluation of Package TermArg operand
*/
- ObjDesc->Package.Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
- WalkState->Operands[0]);
- ObjDesc->Package.AmlStart = Op->Named.Data;
+ ObjDesc->Package.Node = ACPI_CAST_PTR (
+ ACPI_NAMESPACE_NODE, WalkState->Operands[0]);
+ ObjDesc->Package.AmlStart = Op->Named.Data;
ObjDesc->Package.AmlLength = Op->Named.Length;
break;
-
case ACPI_TYPE_INTEGER:
switch (OpInfo->Type)
@@ -734,7 +730,7 @@ AcpiDsInitObjectFromOp (
/* Truncate value if we are executing from a 32-bit ACPI table */
#ifndef ACPI_NO_METHOD_EXECUTION
- AcpiExTruncateFor32bitTable (ObjDesc);
+ (void) AcpiExTruncateFor32bitTable (ObjDesc);
#endif
break;
@@ -752,17 +748,25 @@ AcpiDsInitObjectFromOp (
}
break;
-
case AML_TYPE_LITERAL:
ObjDesc->Integer.Value = Op->Common.Value.Integer;
+
#ifndef ACPI_NO_METHOD_EXECUTION
- AcpiExTruncateFor32bitTable (ObjDesc);
+ if (AcpiExTruncateFor32bitTable (ObjDesc))
+ {
+ /* Warn if we found a 64-bit constant in a 32-bit table */
+
+ ACPI_WARNING ((AE_INFO,
+ "Truncated 64-bit constant found in 32-bit table: %8.8X%8.8X => %8.8X",
+ ACPI_FORMAT_UINT64 (Op->Common.Value.Integer),
+ (UINT32) ObjDesc->Integer.Value));
+ }
#endif
break;
-
default:
+
ACPI_ERROR ((AE_INFO, "Unknown Integer type 0x%X",
OpInfo->Type));
Status = AE_AML_OPERAND_TYPE;
@@ -770,11 +774,10 @@ AcpiDsInitObjectFromOp (
}
break;
-
case ACPI_TYPE_STRING:
ObjDesc->String.Pointer = Op->Common.Value.String;
- ObjDesc->String.Length = (UINT32) ACPI_STRLEN (Op->Common.Value.String);
+ ObjDesc->String.Length = (UINT32) strlen (Op->Common.Value.String);
/*
* The string is contained in the ACPI table, don't ever try
@@ -783,11 +786,9 @@ AcpiDsInitObjectFromOp (
ObjDesc->Common.Flags |= AOPOBJ_STATIC_POINTER;
break;
-
case ACPI_TYPE_METHOD:
break;
-
case ACPI_TYPE_LOCAL_REFERENCE:
switch (OpInfo->Type)
@@ -801,13 +802,12 @@ AcpiDsInitObjectFromOp (
#ifndef ACPI_NO_METHOD_EXECUTION
Status = AcpiDsMethodDataGetNode (ACPI_REFCLASS_LOCAL,
- ObjDesc->Reference.Value, WalkState,
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE,
- &ObjDesc->Reference.Object));
+ ObjDesc->Reference.Value, WalkState,
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE,
+ &ObjDesc->Reference.Object));
#endif
break;
-
case AML_TYPE_METHOD_ARGUMENT:
/* Arg ID (0-6) is (AML opcode - base AML_ARG_OP) */
@@ -817,9 +817,9 @@ AcpiDsInitObjectFromOp (
#ifndef ACPI_NO_METHOD_EXECUTION
Status = AcpiDsMethodDataGetNode (ACPI_REFCLASS_ARG,
- ObjDesc->Reference.Value, WalkState,
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE,
- &ObjDesc->Reference.Object));
+ ObjDesc->Reference.Value, WalkState,
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE,
+ &ObjDesc->Reference.Object));
#endif
break;
@@ -851,7 +851,6 @@ AcpiDsInitObjectFromOp (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unimplemented data type: 0x%X",
@@ -863,5 +862,3 @@ AcpiDsInitObjectFromOp (
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c b/usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c
index 45fbed1e62..1c6b342570 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsopcode.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: dsopcode - Dispatcher suport for regions and fields
+ * Module Name: dsopcode - Dispatcher support for regions and fields
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSOPCODE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -261,8 +259,8 @@ AcpiDsInitBufferField (
* For FieldFlags, use LOCK_RULE = 0 (NO_LOCK),
* UPDATE_RULE = 0 (UPDATE_PRESERVE)
*/
- Status = AcpiExPrepCommonFieldObject (ObjDesc, FieldFlags, 0,
- BitOffset, BitCount);
+ Status = AcpiExPrepCommonFieldObject (
+ ObjDesc, FieldFlags, 0, BitOffset, BitCount);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -359,8 +357,8 @@ AcpiDsEvalBufferFieldOperands (
/* Resolve the operands */
- Status = AcpiExResolveOperands (Op->Common.AmlOpcode,
- ACPI_WALK_OPERANDS, WalkState);
+ Status = AcpiExResolveOperands (
+ Op->Common.AmlOpcode, ACPI_WALK_OPERANDS, WalkState);
if (ACPI_FAILURE (Status))
{
ACPI_ERROR ((AE_INFO, "(%s) bad operand(s), status 0x%X",
@@ -376,16 +374,16 @@ AcpiDsEvalBufferFieldOperands (
/* NOTE: Slightly different operands for this opcode */
Status = AcpiDsInitBufferField (Op->Common.AmlOpcode, ObjDesc,
- WalkState->Operands[0], WalkState->Operands[1],
- WalkState->Operands[2], WalkState->Operands[3]);
+ WalkState->Operands[0], WalkState->Operands[1],
+ WalkState->Operands[2], WalkState->Operands[3]);
}
else
{
/* All other, CreateXxxField opcodes */
Status = AcpiDsInitBufferField (Op->Common.AmlOpcode, ObjDesc,
- WalkState->Operands[0], WalkState->Operands[1],
- NULL, WalkState->Operands[2]);
+ WalkState->Operands[0], WalkState->Operands[1],
+ NULL, WalkState->Operands[2]);
}
return_ACPI_STATUS (Status);
@@ -445,8 +443,8 @@ AcpiDsEvalRegionOperands (
/* Resolve the length and address operands to numbers */
- Status = AcpiExResolveOperands (Op->Common.AmlOpcode,
- ACPI_WALK_OPERANDS, WalkState);
+ Status = AcpiExResolveOperands (
+ Op->Common.AmlOpcode, ACPI_WALK_OPERANDS, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -474,18 +472,16 @@ AcpiDsEvalRegionOperands (
OperandDesc = WalkState->Operands[WalkState->NumOperands - 2];
ObjDesc->Region.Address = (ACPI_PHYSICAL_ADDRESS)
- OperandDesc->Integer.Value;
+ OperandDesc->Integer.Value;
AcpiUtRemoveReference (OperandDesc);
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
- ObjDesc,
- ACPI_FORMAT_NATIVE_UINT (ObjDesc->Region.Address),
+ ObjDesc, ACPI_FORMAT_UINT64 (ObjDesc->Region.Address),
ObjDesc->Region.Length));
/* Now the address and length are valid for this opregion */
ObjDesc->Region.Flags |= AOPOBJ_DATA_VALID;
-
return_ACPI_STATUS (Status);
}
@@ -515,26 +511,26 @@ AcpiDsEvalTableRegionOperands (
ACPI_OPERAND_OBJECT **Operand;
ACPI_NAMESPACE_NODE *Node;
ACPI_PARSE_OBJECT *NextOp;
- UINT32 TableIndex;
ACPI_TABLE_HEADER *Table;
+ UINT32 TableIndex;
ACPI_FUNCTION_TRACE_PTR (DsEvalTableRegionOperands, Op);
/*
- * This is where we evaluate the SignatureString and OemIDString
- * and OemTableIDString of the DataTableRegion declaration
+ * This is where we evaluate the Signature string, OemId string,
+ * and OemTableId string of the Data Table Region declaration
*/
Node = Op->Common.Node;
- /* NextOp points to SignatureString op */
+ /* NextOp points to Signature string op */
NextOp = Op->Common.Value.Arg;
/*
- * Evaluate/create the SignatureString and OemIDString
- * and OemTableIDString operands
+ * Evaluate/create the Signature string, OemId string,
+ * and OemTableId string operands
*/
Status = AcpiDsCreateOperands (WalkState, NextOp);
if (ACPI_FAILURE (Status))
@@ -542,57 +538,67 @@ AcpiDsEvalTableRegionOperands (
return_ACPI_STATUS (Status);
}
+ Operand = &WalkState->Operands[0];
+
/*
- * Resolve the SignatureString and OemIDString
- * and OemTableIDString operands
+ * Resolve the Signature string, OemId string,
+ * and OemTableId string operands
*/
- Status = AcpiExResolveOperands (Op->Common.AmlOpcode,
- ACPI_WALK_OPERANDS, WalkState);
+ Status = AcpiExResolveOperands (
+ Op->Common.AmlOpcode, ACPI_WALK_OPERANDS, WalkState);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ goto Cleanup;
}
- Operand = &WalkState->Operands[0];
-
/* Find the ACPI table */
- Status = AcpiTbFindTable (Operand[0]->String.Pointer,
- Operand[1]->String.Pointer, Operand[2]->String.Pointer,
- &TableIndex);
+ Status = AcpiTbFindTable (
+ Operand[0]->String.Pointer,
+ Operand[1]->String.Pointer,
+ Operand[2]->String.Pointer, &TableIndex);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ if (Status == AE_NOT_FOUND)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "ACPI Table [%4.4s] OEM:(%s, %s) not found in RSDT/XSDT",
+ Operand[0]->String.Pointer,
+ Operand[1]->String.Pointer,
+ Operand[2]->String.Pointer));
+ }
+ goto Cleanup;
}
- AcpiUtRemoveReference (Operand[0]);
- AcpiUtRemoveReference (Operand[1]);
- AcpiUtRemoveReference (Operand[2]);
-
Status = AcpiGetTableByIndex (TableIndex, &Table);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ goto Cleanup;
}
ObjDesc = AcpiNsGetAttachedObject (Node);
if (!ObjDesc)
{
- return_ACPI_STATUS (AE_NOT_EXIST);
+ Status = AE_NOT_EXIST;
+ goto Cleanup;
}
- ObjDesc->Region.Address = (ACPI_PHYSICAL_ADDRESS) ACPI_TO_INTEGER (Table);
+ ObjDesc->Region.Address = ACPI_PTR_TO_PHYSADDR (Table);
ObjDesc->Region.Length = Table->Length;
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n",
- ObjDesc,
- ACPI_FORMAT_NATIVE_UINT (ObjDesc->Region.Address),
+ ObjDesc, ACPI_FORMAT_UINT64 (ObjDesc->Region.Address),
ObjDesc->Region.Length));
/* Now the address and length are valid for this opregion */
ObjDesc->Region.Flags |= AOPOBJ_DATA_VALID;
+Cleanup:
+ AcpiUtRemoveReference (Operand[0]);
+ AcpiUtRemoveReference (Operand[1]);
+ AcpiUtRemoveReference (Operand[2]);
+
return_ACPI_STATUS (Status);
}
@@ -641,8 +647,8 @@ AcpiDsEvalDataObjectOperands (
}
Status = AcpiExResolveOperands (WalkState->Opcode,
- &(WalkState->Operands [WalkState->NumOperands -1]),
- WalkState);
+ &(WalkState->Operands [WalkState->NumOperands -1]),
+ WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -670,16 +676,19 @@ AcpiDsEvalDataObjectOperands (
{
case AML_BUFFER_OP:
- Status = AcpiDsBuildInternalBufferObj (WalkState, Op, Length, &ObjDesc);
+ Status = AcpiDsBuildInternalBufferObj (
+ WalkState, Op, Length, &ObjDesc);
break;
case AML_PACKAGE_OP:
case AML_VAR_PACKAGE_OP:
- Status = AcpiDsBuildInternalPackageObj (WalkState, Op, Length, &ObjDesc);
+ Status = AcpiDsBuildInternalPackageObj (
+ WalkState, Op, Length, &ObjDesc);
break;
default:
+
return_ACPI_STATUS (AE_AML_BAD_OPCODE);
}
@@ -806,4 +815,3 @@ AcpiDsEvalBankFieldOperands (
AcpiUtRemoveReference (OperandDesc);
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dsutils.c b/usr/src/uts/intel/io/acpica/dispatcher/dsutils.c
index 278009795c..e294c836e0 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dsutils.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dsutils.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSUTILS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -64,7 +62,7 @@
*
* RETURN: None.
*
- * DESCRIPTION: Clear and remove a reference on an implicit return value. Used
+ * DESCRIPTION: Clear and remove a reference on an implicit return value. Used
* to delete "stale" return values (if enabled, the return value
* from every operator is saved at least momentarily, in case the
* parent method exits.)
@@ -117,7 +115,7 @@ AcpiDsClearImplicitReturn (
*
* DESCRIPTION: Implements the optional "implicit return". We save the result
* of every ASL operator and control method invocation in case the
- * parent method exit. Before storing a new return value, we
+ * parent method exit. Before storing a new return value, we
* delete the previous return value.
*
******************************************************************************/
@@ -142,9 +140,9 @@ AcpiDsDoImplicitReturn (
}
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
- "Result %p will be implicitly returned; Prev=%p\n",
- ReturnDesc,
- WalkState->ImplicitReturnObj));
+ "Result %p will be implicitly returned; Prev=%p\n",
+ ReturnDesc,
+ WalkState->ImplicitReturnObj));
/*
* Delete any "stale" implicit return value first. However, in
@@ -220,7 +218,7 @@ AcpiDsIsResultUsed (
*
* If there is no parent, or the parent is a ScopeOp, we are executing
* at the method level. An executing method typically has no parent,
- * since each method is parsed separately. A method invoked externally
+ * since each method is parsed separately. A method invoked externally
* via ExecuteControlMethod has a ScopeOp as the parent.
*/
if ((!Op->Common.Parent) ||
@@ -245,7 +243,7 @@ AcpiDsIsResultUsed (
}
/*
- * Decide what to do with the result based on the parent. If
+ * Decide what to do with the result based on the parent. If
* the parent opcode will not use the result, delete the object.
* Otherwise leave it as is, it will be deleted when it is used
* as an operand later.
@@ -264,12 +262,12 @@ AcpiDsIsResultUsed (
case AML_IF_OP:
case AML_WHILE_OP:
-
/*
* If we are executing the predicate AND this is the predicate op,
* we will use the return value
*/
- if ((WalkState->ControlState->Common.State == ACPI_CONTROL_PREDICATE_EXECUTING) &&
+ if ((WalkState->ControlState->Common.State ==
+ ACPI_CONTROL_PREDICATE_EXECUTING) &&
(WalkState->ControlState->Control.PredicateOp == Op))
{
goto ResultUsed;
@@ -277,7 +275,9 @@ AcpiDsIsResultUsed (
break;
default:
+
/* Ignore other control opcodes */
+
break;
}
@@ -285,16 +285,13 @@ AcpiDsIsResultUsed (
goto ResultNotUsed;
-
case AML_CLASS_CREATE:
-
/*
* These opcodes allow TermArg(s) as operands and therefore
- * the operands can be method calls. The result is used.
+ * the operands can be method calls. The result is used.
*/
goto ResultUsed;
-
case AML_CLASS_NAMED_OBJECT:
if ((Op->Common.Parent->Common.AmlOpcode == AML_REGION_OP) ||
@@ -307,16 +304,14 @@ AcpiDsIsResultUsed (
{
/*
* These opcodes allow TermArg(s) as operands and therefore
- * the operands can be method calls. The result is used.
+ * the operands can be method calls. The result is used.
*/
goto ResultUsed;
}
goto ResultNotUsed;
-
default:
-
/*
* In all other cases. the parent will actually use the return
* object, so keep it.
@@ -354,9 +349,9 @@ ResultNotUsed:
*
* RETURN: Status
*
- * DESCRIPTION: Used after interpretation of an opcode. If there is an internal
+ * DESCRIPTION: Used after interpretation of an opcode. If there is an internal
* result descriptor, check if the parent opcode will actually use
- * this result. If not, delete the result now so that it will
+ * this result. If not, delete the result now so that it will
* not become orphaned.
*
******************************************************************************/
@@ -408,7 +403,7 @@ AcpiDsDeleteResultIfNotUsed (
*
* RETURN: Status
*
- * DESCRIPTION: Resolve all operands to their values. Used to prepare
+ * DESCRIPTION: Resolve all operands to their values. Used to prepare
* arguments to a control method invocation (a call from one
* method to another.)
*
@@ -427,7 +422,7 @@ AcpiDsResolveOperands (
/*
* Attempt to resolve each of the valid operands
- * Method arguments are passed by reference, not by value. This means
+ * Method arguments are passed by reference, not by value. This means
* that the actual objects are passed, not copies of the objects.
*/
for (i = 0; i < WalkState->NumOperands; i++)
@@ -494,7 +489,7 @@ AcpiDsClearOperands (
* RETURN: Status
*
* DESCRIPTION: Translate a parse tree object that is an argument to an AML
- * opcode to the equivalent interpreter object. This may include
+ * opcode to the equivalent interpreter object. This may include
* looking up a name or entering a new name into the internal
* namespace.
*
@@ -529,8 +524,8 @@ AcpiDsCreateOperand (
/* Get the entire name string from the AML stream */
- Status = AcpiExGetNameString (ACPI_TYPE_ANY, Arg->Common.Value.Buffer,
- &NameString, &NameLength);
+ Status = AcpiExGetNameString (ACPI_TYPE_ANY,
+ Arg->Common.Value.Buffer, &NameString, &NameLength);
if (ACPI_FAILURE (Status))
{
@@ -540,20 +535,21 @@ AcpiDsCreateOperand (
/* All prefixes have been handled, and the name is in NameString */
/*
- * Special handling for BufferField declarations. This is a deferred
+ * Special handling for BufferField declarations. This is a deferred
* opcode that unfortunately defines the field name as the last
- * parameter instead of the first. We get here when we are performing
+ * parameter instead of the first. We get here when we are performing
* the deferred execution, so the actual name of the field is already
- * in the namespace. We don't want to attempt to look it up again
+ * in the namespace. We don't want to attempt to look it up again
* because we may be executing in a different scope than where the
* actual opcode exists.
*/
if ((WalkState->DeferredNode) &&
(WalkState->DeferredNode->Type == ACPI_TYPE_BUFFER_FIELD) &&
- (ArgIndex == (UINT32) ((WalkState->Opcode == AML_CREATE_FIELD_OP) ? 3 : 2)))
+ (ArgIndex == (UINT32)
+ ((WalkState->Opcode == AML_CREATE_FIELD_OP) ? 3 : 2)))
{
ObjDesc = ACPI_CAST_PTR (
- ACPI_OPERAND_OBJECT, WalkState->DeferredNode);
+ ACPI_OPERAND_OBJECT, WalkState->DeferredNode);
Status = AE_OK;
}
else /* All other opcodes */
@@ -566,6 +562,7 @@ AcpiDsCreateOperand (
*/
ParentOp = Arg->Common.Parent;
OpInfo = AcpiPsGetOpcodeInfo (ParentOp->Common.AmlOpcode);
+
if ((OpInfo->Flags & AML_NSNODE) &&
(ParentOp->Common.AmlOpcode != AML_INT_METHODCALL_OP) &&
(ParentOp->Common.AmlOpcode != AML_REGION_OP) &&
@@ -583,10 +580,9 @@ AcpiDsCreateOperand (
}
Status = AcpiNsLookup (WalkState->ScopeInfo, NameString,
- ACPI_TYPE_ANY, InterpreterMode,
- ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
- WalkState,
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &ObjDesc));
+ ACPI_TYPE_ANY, InterpreterMode,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, WalkState,
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, &ObjDesc));
/*
* The only case where we pass through (ignore) a NOT_FOUND
* error is for the CondRefOf opcode.
@@ -602,9 +598,20 @@ AcpiDsCreateOperand (
* object to the root
*/
ObjDesc = ACPI_CAST_PTR (
- ACPI_OPERAND_OBJECT, AcpiGbl_RootNode);
+ ACPI_OPERAND_OBJECT, AcpiGbl_RootNode);
Status = AE_OK;
}
+ else if (ParentOp->Common.AmlOpcode == AML_EXTERNAL_OP)
+ {
+ /*
+ * This opcode should never appear here. It is used only
+ * by AML disassemblers and is surrounded by an If(0)
+ * by the ASL compiler.
+ *
+ * Therefore, if we see it here, it is a serious error.
+ */
+ Status = AE_AML_BAD_OPCODE;
+ }
else
{
/*
@@ -639,7 +646,10 @@ AcpiDsCreateOperand (
{
return_ACPI_STATUS (Status);
}
- ACPI_DEBUGGER_EXEC (AcpiDbDisplayArgumentObject (ObjDesc, WalkState));
+
+#ifdef ACPI_DEBUGGER
+ AcpiDbDisplayArgumentObject (ObjDesc, WalkState);
+#endif
}
else
{
@@ -651,8 +661,8 @@ AcpiDsCreateOperand (
/*
* If the name is null, this means that this is an
* optional result parameter that was not specified
- * in the original ASL. Create a Zero Constant for a
- * placeholder. (Store to a constant is a Noop.)
+ * in the original ASL. Create a Zero Constant for a
+ * placeholder. (Store to a constant is a Noop.)
*/
Opcode = AML_ZERO_OP; /* Has no arguments! */
@@ -672,13 +682,16 @@ AcpiDsCreateOperand (
return_ACPI_STATUS (AE_NOT_IMPLEMENTED);
}
- if ((OpInfo->Flags & AML_HAS_RETVAL) || (Arg->Common.Flags & ACPI_PARSEOP_IN_STACK))
+ if ((OpInfo->Flags & AML_HAS_RETVAL) ||
+ (Arg->Common.Flags & ACPI_PARSEOP_IN_STACK))
{
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n"));
- ACPI_DEBUGGER_EXEC (AcpiDbDisplayArgumentObject (
- WalkState->Operands [WalkState->NumOperands - 1], WalkState));
+#ifdef ACPI_DEBUGGER
+ AcpiDbDisplayArgumentObject (
+ WalkState->Operands [WalkState->NumOperands - 1], WalkState);
+#endif
/*
* Use value that was already previously returned
@@ -709,7 +722,7 @@ AcpiDsCreateOperand (
/* Initialize the new object */
Status = AcpiDsInitObjectFromOp (
- WalkState, Arg, Opcode, &ObjDesc);
+ WalkState, Arg, Opcode, &ObjDesc);
if (ACPI_FAILURE (Status))
{
AcpiUtDeleteObjectDesc (ObjDesc);
@@ -725,7 +738,9 @@ AcpiDsCreateOperand (
return_ACPI_STATUS (Status);
}
- ACPI_DEBUGGER_EXEC (AcpiDbDisplayArgumentObject (ObjDesc, WalkState));
+#ifdef ACPI_DEBUGGER
+ AcpiDbDisplayArgumentObject (ObjDesc, WalkState);
+#endif
}
return_ACPI_STATUS (AE_OK);
@@ -783,16 +798,16 @@ AcpiDsCreateOperands (
Index++;
}
- Index--;
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "NumOperands %d, ArgCount %d, Index %d\n",
+ WalkState->NumOperands, ArgCount, Index));
- /* It is the appropriate order to get objects from the Result stack */
+ /* Create the interpreter arguments, in reverse order */
+ Index--;
for (i = 0; i < ArgCount; i++)
{
Arg = Arguments[Index];
-
- /* Force the filling of the operand stack in inverse order */
-
WalkState->OperandIndex = (UINT8) Index;
Status = AcpiDsCreateOperand (WalkState, Arg, Index);
@@ -801,10 +816,10 @@ AcpiDsCreateOperands (
goto Cleanup;
}
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "Created Arg #%u (%p) %u args total\n",
+ Index, Arg, ArgCount));
Index--;
-
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Arg #%u (%p) done, Arg1=%p\n",
- Index, Arg, FirstArg));
}
return_ACPI_STATUS (Status);
@@ -895,7 +910,8 @@ AcpiDsEvaluateNamePath (
AcpiUtRemoveReference (*Operand);
- Status = AcpiUtCopyIobjectToIobject (*Operand, &NewObjDesc, WalkState);
+ Status = AcpiUtCopyIobjectToIobject (
+ *Operand, &NewObjDesc, WalkState);
if (ACPI_FAILURE (Status))
{
goto Exit;
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dswexec.c b/usr/src/uts/intel/io/acpica/dispatcher/dswexec.c
index 7983787958..8408ebf2f6 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dswexec.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dswexec.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSWEXEC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -164,7 +162,7 @@ AcpiDsGetPredicateValue (
/* Truncate the predicate to 32-bits if necessary */
- AcpiExTruncateFor32bitTable (LocalObjDesc);
+ (void) AcpiExTruncateFor32bitTable (LocalObjDesc);
/*
* Save the result of the predicate evaluation on
@@ -191,12 +189,15 @@ AcpiDsGetPredicateValue (
Cleanup:
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Completed a predicate eval=%X Op=%p\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Completed a predicate eval=%X Op=%p\n",
WalkState->ControlState->Common.Value, WalkState->Op));
- /* Break to debugger to display result */
+#ifdef ACPI_DEBUGGER
+ /* Break to debugger to display result */
- ACPI_DEBUGGER_EXEC (AcpiDbDisplayResultObject (LocalObjDesc, WalkState));
+ AcpiDbDisplayResultObject (LocalObjDesc, WalkState);
+#endif
/*
* Delete the predicate result object (we know that
@@ -223,7 +224,7 @@ Cleanup:
* RETURN: Status
*
* DESCRIPTION: Descending callback used during the execution of control
- * methods. This is where most operators and operands are
+ * methods. This is where most operators and operands are
* dispatched to the interpreter.
*
****************************************************************************/
@@ -288,10 +289,12 @@ AcpiDsExecBeginOp (
(WalkState->ControlState->Common.State ==
ACPI_CONTROL_CONDITIONAL_EXECUTING))
{
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Exec predicate Op=%p State=%p\n",
- Op, WalkState));
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Exec predicate Op=%p State=%p\n",
+ Op, WalkState));
- WalkState->ControlState->Common.State = ACPI_CONTROL_PREDICATE_EXECUTING;
+ WalkState->ControlState->Common.State =
+ ACPI_CONTROL_PREDICATE_EXECUTING;
/* Save start of predicate */
@@ -318,14 +321,13 @@ AcpiDsExecBeginOp (
Status = AcpiDsExecBeginControlOp (WalkState, Op);
break;
-
case AML_CLASS_NAMED_OBJECT:
if (WalkState->WalkType & ACPI_WALK_METHOD)
{
/*
* Found a named object declaration during method execution;
- * we must enter this object into the namespace. The created
+ * we must enter this object into the namespace. The created
* object is temporary and will be deleted upon completion of
* the execution of this method.
*
@@ -340,8 +342,8 @@ AcpiDsExecBeginOp (
}
else
{
- Status = AcpiDsScopeStackPush (Op->Named.Node,
- Op->Named.Node->Type, WalkState);
+ Status = AcpiDsScopeStackPush (
+ Op->Named.Node, Op->Named.Node->Type, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -350,14 +352,13 @@ AcpiDsExecBeginOp (
}
break;
-
case AML_CLASS_EXECUTE:
case AML_CLASS_CREATE:
break;
-
default:
+
break;
}
@@ -381,7 +382,7 @@ ErrorExit:
* RETURN: Status
*
* DESCRIPTION: Ascending callback used during the execution of control
- * methods. The only thing we really need to do here is to
+ * methods. The only thing we really need to do here is to
* notice the beginning of IF, ELSE, and WHILE blocks.
*
****************************************************************************/
@@ -401,8 +402,8 @@ AcpiDsExecEndOp (
ACPI_FUNCTION_TRACE_PTR (DsExecEndOp, WalkState);
- Op = WalkState->Op;
- OpType = WalkState->OpInfo->Type;
+ Op = WalkState->Op;
+ OpType = WalkState->OpInfo->Type;
OpClass = WalkState->OpInfo->Class;
if (OpClass == AML_CLASS_UNKNOWN)
@@ -420,10 +421,15 @@ AcpiDsExecEndOp (
WalkState->ReturnDesc = NULL;
WalkState->ResultObj = NULL;
+#ifdef ACPI_DEBUGGER
/* Call debugger for single step support (DEBUG build only) */
- ACPI_DEBUGGER_EXEC (Status = AcpiDbSingleStep (WalkState, Op, OpClass));
- ACPI_DEBUGGER_EXEC (if (ACPI_FAILURE (Status)) {return_ACPI_STATUS (Status);});
+ Status = AcpiDbSingleStep (WalkState, Op, OpClass);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+#endif
/* Decode the Opcode Class */
@@ -441,7 +447,6 @@ AcpiDsExecEndOp (
}
break;
-
case AML_CLASS_EXECUTE: /* Most operators with arguments */
/* Build resolved operand stack */
@@ -461,15 +466,15 @@ AcpiDsExecEndOp (
/* Resolve all operands */
Status = AcpiExResolveOperands (WalkState->Opcode,
- &(WalkState->Operands [WalkState->NumOperands -1]),
- WalkState);
+ &(WalkState->Operands [WalkState->NumOperands -1]),
+ WalkState);
}
if (ACPI_SUCCESS (Status))
{
/*
* Dispatch the request to the appropriate interpreter handler
- * routine. There is one routine per opcode "type" based upon the
+ * routine. There is one routine per opcode "type" based upon the
* number of opcode arguments and return type.
*/
Status = AcpiGbl_OpTypeDispatch[OpType] (WalkState);
@@ -514,7 +519,6 @@ AcpiDsExecEndOp (
}
break;
-
default:
switch (OpType)
@@ -527,9 +531,7 @@ AcpiDsExecEndOp (
break;
-
case AML_TYPE_METHOD_CALL:
-
/*
* If the method is referenced from within a package
* declaration, it is not a invocation of the method, just
@@ -542,12 +544,14 @@ AcpiDsExecEndOp (
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
"Method Reference in a Package, Op=%p\n", Op));
- Op->Common.Node = (ACPI_NAMESPACE_NODE *) Op->Asl.Value.Arg->Asl.Node;
+ Op->Common.Node = (ACPI_NAMESPACE_NODE *)
+ Op->Asl.Value.Arg->Asl.Node;
AcpiUtAddReference (Op->Asl.Value.Arg->Asl.Node->Object);
return_ACPI_STATUS (AE_OK);
}
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Method invocation, Op=%p\n", Op));
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
+ "Method invocation, Op=%p\n", Op));
/*
* (AML_METHODCALL) Op->Asl.Value.Arg->Asl.Node contains
@@ -596,7 +600,6 @@ AcpiDsExecEndOp (
*/
return_ACPI_STATUS (Status);
-
case AML_TYPE_CREATE_FIELD:
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
@@ -620,17 +623,16 @@ AcpiDsExecEndOp (
switch (Op->Common.Parent->Common.AmlOpcode)
{
case AML_NAME_OP:
-
/*
* Put the Node on the object stack (Contains the ACPI Name
* of this object)
*/
- WalkState->Operands[0] = (void *) Op->Common.Parent->Common.Node;
+ WalkState->Operands[0] = (void *)
+ Op->Common.Parent->Common.Node;
WalkState->NumOperands = 1;
Status = AcpiDsCreateNode (WalkState,
- Op->Common.Parent->Common.Node,
- Op->Common.Parent);
+ Op->Common.Parent->Common.Node, Op->Common.Parent);
if (ACPI_FAILURE (Status))
{
break;
@@ -642,7 +644,7 @@ AcpiDsExecEndOp (
case AML_INT_EVAL_SUBTREE_OP:
Status = AcpiDsEvalDataObjectOperands (WalkState, Op,
- AcpiNsGetAttachedObject (Op->Common.Parent->Common.Node));
+ AcpiNsGetAttachedObject (Op->Common.Parent->Common.Node));
break;
default:
@@ -661,7 +663,6 @@ AcpiDsExecEndOp (
}
break;
-
case AML_TYPE_NAMED_FIELD:
case AML_TYPE_NAMED_COMPLEX:
case AML_TYPE_NAMED_SIMPLE:
@@ -708,14 +709,12 @@ AcpiDsExecEndOp (
}
break;
-
case AML_TYPE_UNDEFINED:
ACPI_ERROR ((AE_INFO,
"Undefined opcode type Op=%p", Op));
return_ACPI_STATUS (AE_NOT_IMPLEMENTED);
-
case AML_TYPE_BOGUS:
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
@@ -723,11 +722,11 @@ AcpiDsExecEndOp (
WalkState->Opcode, Op));
break;
-
default:
ACPI_ERROR ((AE_INFO,
- "Unimplemented opcode, class=0x%X type=0x%X Opcode=-0x%X Op=%p",
+ "Unimplemented opcode, class=0x%X "
+ "type=0x%X Opcode=0x%X Op=%p",
OpClass, OpType, Op->Common.AmlOpcode, Op));
Status = AE_NOT_IMPLEMENTED;
@@ -739,7 +738,7 @@ AcpiDsExecEndOp (
* ACPI 2.0 support for 64-bit integers: Truncate numeric
* result value if we are executing from a 32-bit ACPI table
*/
- AcpiExTruncateFor32bitTable (WalkState->ResultObj);
+ (void) AcpiExTruncateFor32bitTable (WalkState->ResultObj);
/*
* Check if we just completed the evaluation of a
@@ -760,10 +759,11 @@ Cleanup:
if (WalkState->ResultObj)
{
+#ifdef ACPI_DEBUGGER
/* Break to debugger to display result */
- ACPI_DEBUGGER_EXEC (AcpiDbDisplayResultObject (WalkState->ResultObj,
- WalkState));
+ AcpiDbDisplayResultObject (WalkState->ResultObj,WalkState);
+#endif
/*
* Delete the result op if and only if:
@@ -793,5 +793,3 @@ Cleanup:
WalkState->NumOperands = 0;
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dswload.c b/usr/src/uts/intel/io/acpica/dispatcher/dswload.c
index d8e77a6538..db55661208 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dswload.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dswload.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSWLOAD_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -80,7 +78,21 @@ AcpiDsInitCallbacks (
switch (PassNumber)
{
+ case 0:
+
+ /* Parse only - caller will setup callbacks */
+
+ WalkState->ParseFlags = ACPI_PARSE_LOAD_PASS1 |
+ ACPI_PARSE_DELETE_TREE |
+ ACPI_PARSE_DISASSEMBLE;
+ WalkState->DescendingCallback = NULL;
+ WalkState->AscendingCallback = NULL;
+ break;
+
case 1:
+
+ /* Load pass 1 */
+
WalkState->ParseFlags = ACPI_PARSE_LOAD_PASS1 |
ACPI_PARSE_DELETE_TREE;
WalkState->DescendingCallback = AcpiDsLoad1BeginOp;
@@ -88,6 +100,9 @@ AcpiDsInitCallbacks (
break;
case 2:
+
+ /* Load pass 2 */
+
WalkState->ParseFlags = ACPI_PARSE_LOAD_PASS1 |
ACPI_PARSE_DELETE_TREE;
WalkState->DescendingCallback = AcpiDsLoad2BeginOp;
@@ -95,6 +110,9 @@ AcpiDsInitCallbacks (
break;
case 3:
+
+ /* Execution pass */
+
#ifndef ACPI_NO_METHOD_EXECUTION
WalkState->ParseFlags |= ACPI_PARSE_EXECUTE |
ACPI_PARSE_DELETE_TREE;
@@ -104,6 +122,7 @@ AcpiDsInitCallbacks (
break;
default:
+
return (AE_BAD_PARAMETER);
}
@@ -169,19 +188,19 @@ AcpiDsLoad1BeginOp (
ObjectType = WalkState->OpInfo->ObjectType;
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
- "State=%p Op=%p [%s]\n", WalkState, Op, AcpiUtGetTypeName (ObjectType)));
+ "State=%p Op=%p [%s]\n", WalkState, Op,
+ AcpiUtGetTypeName (ObjectType)));
switch (WalkState->Opcode)
{
case AML_SCOPE_OP:
-
/*
* The target name of the Scope() operator must exist at this point so
* that we can actually open the scope to enter new names underneath it.
* Allow search-to-root for single namesegs.
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, Path, ObjectType,
- ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, WalkState, &(Node));
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, WalkState, &(Node));
#ifdef ACPI_ASL_COMPILER
if (Status == AE_NOT_FOUND)
{
@@ -190,10 +209,10 @@ AcpiDsLoad1BeginOp (
* Target of Scope() not found. Generate an External for it, and
* insert the name into the namespace.
*/
- AcpiDmAddToExternalList (Op, Path, ACPI_TYPE_DEVICE, 0);
+ AcpiDmAddOpToExternalList (Op, Path, ACPI_TYPE_DEVICE, 0, 0);
Status = AcpiNsLookup (WalkState->ScopeInfo, Path, ObjectType,
- ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT,
- WalkState, &Node);
+ ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT,
+ WalkState, &Node);
}
#endif
if (ACPI_FAILURE (Status))
@@ -221,7 +240,6 @@ AcpiDsLoad1BeginOp (
case ACPI_TYPE_INTEGER:
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
-
/*
* These types we will allow, but we will change the type.
* This enables some existing code of the form:
@@ -241,6 +259,19 @@ AcpiDsLoad1BeginOp (
WalkState->ScopeInfo->Common.Value = ACPI_TYPE_ANY;
break;
+ case ACPI_TYPE_METHOD:
+ /*
+ * Allow scope change to root during execution of module-level
+ * code. Root is typed METHOD during this time.
+ */
+ if ((Node == AcpiGbl_RootNode) &&
+ (WalkState->ParseFlags & ACPI_PARSE_MODULE_LEVEL))
+ {
+ break;
+ }
+
+ /*lint -fallthrough */
+
default:
/* All other types are an error */
@@ -254,7 +285,6 @@ AcpiDsLoad1BeginOp (
}
break;
-
default:
/*
* For all other named opcodes, we will enter the name into
@@ -296,15 +326,24 @@ AcpiDsLoad1BeginOp (
if ((WalkState->Opcode != AML_SCOPE_OP) &&
(!(WalkState->ParseFlags & ACPI_PARSE_DEFERRED_OP)))
{
- Flags |= ACPI_NS_ERROR_IF_FOUND;
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[%s] Cannot already exist\n",
+ if (WalkState->NamespaceOverride)
+ {
+ Flags |= ACPI_NS_OVERRIDE_IF_FOUND;
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[%s] Override allowed\n",
AcpiUtGetTypeName (ObjectType)));
+ }
+ else
+ {
+ Flags |= ACPI_NS_ERROR_IF_FOUND;
+ ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[%s] Cannot already exist\n",
+ AcpiUtGetTypeName (ObjectType)));
+ }
}
else
{
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
"[%s] Both Find or Create allowed\n",
- AcpiUtGetTypeName (ObjectType)));
+ AcpiUtGetTypeName (ObjectType)));
}
/*
@@ -314,7 +353,7 @@ AcpiDsLoad1BeginOp (
* parse tree later.
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, Path, ObjectType,
- ACPI_IMODE_LOAD_PASS1, Flags, WalkState, &Node);
+ ACPI_IMODE_LOAD_PASS1, Flags, WalkState, &Node);
if (ACPI_FAILURE (Status))
{
if (Status == AE_ALREADY_EXISTS)
@@ -334,7 +373,8 @@ AcpiDsLoad1BeginOp (
if (AcpiNsOpensScope (ObjectType))
{
- Status = AcpiDsScopeStackPush (Node, ObjectType, WalkState);
+ Status = AcpiDsScopeStackPush (
+ Node, ObjectType, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -360,7 +400,7 @@ AcpiDsLoad1BeginOp (
{
/* Create a new op */
- Op = AcpiPsAllocOp (WalkState->Opcode);
+ Op = AcpiPsAllocOp (WalkState->Opcode, WalkState->Aml);
if (!Op)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -456,8 +496,9 @@ AcpiDsLoad1EndOp (
if (Op->Common.AmlOpcode == AML_REGION_OP)
{
Status = AcpiExCreateRegion (Op->Named.Data, Op->Named.Length,
- (ACPI_ADR_SPACE_TYPE) ((Op->Common.Value.Arg)->Common.Value.Integer),
- WalkState);
+ (ACPI_ADR_SPACE_TYPE)
+ ((Op->Common.Value.Arg)->Common.Value.Integer),
+ WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -466,7 +507,7 @@ AcpiDsLoad1EndOp (
else if (Op->Common.AmlOpcode == AML_DATA_REGION_OP)
{
Status = AcpiExCreateRegion (Op->Named.Data, Op->Named.Length,
- ACPI_ADR_SPACE_DATA_TABLE, WalkState);
+ ACPI_ADR_SPACE_DATA_TABLE, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -518,11 +559,12 @@ AcpiDsLoad1EndOp (
WalkState->Operands[0] = ACPI_CAST_PTR (void, Op->Named.Node);
WalkState->NumOperands = 1;
- Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
+ Status = AcpiDsCreateOperands (
+ WalkState, Op->Common.Value.Arg);
if (ACPI_SUCCESS (Status))
{
Status = AcpiExCreateMethod (Op->Named.Data,
- Op->Named.Length, WalkState);
+ Op->Named.Length, WalkState);
}
WalkState->Operands[0] = NULL;
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dswload2.c b/usr/src/uts/intel/io/acpica/dispatcher/dswload2.c
index 608d525b36..2c099ef0f0 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dswload2.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dswload2.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSWLOAD2_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -161,8 +159,8 @@ AcpiDsLoad2BeginOp (
* for use later.
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, BufferPtr, ObjectType,
- ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
- WalkState, &(Node));
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ WalkState, &(Node));
break;
case AML_SCOPE_OP:
@@ -187,8 +185,8 @@ AcpiDsLoad2BeginOp (
* for use later.
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, BufferPtr, ObjectType,
- ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
- WalkState, &(Node));
+ ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT,
+ WalkState, &(Node));
if (ACPI_FAILURE (Status))
{
#ifdef ACPI_ASL_COMPILER
@@ -236,13 +234,27 @@ AcpiDsLoad2BeginOp (
*/
ACPI_WARNING ((AE_INFO,
"Type override - [%4.4s] had invalid type (%s) "
- "for Scope operator, changed to type ANY\n",
+ "for Scope operator, changed to type ANY",
AcpiUtGetNodeName (Node), AcpiUtGetTypeName (Node->Type)));
Node->Type = ACPI_TYPE_ANY;
WalkState->ScopeInfo->Common.Value = ACPI_TYPE_ANY;
break;
+ case ACPI_TYPE_METHOD:
+
+ /*
+ * Allow scope change to root during execution of module-level
+ * code. Root is typed METHOD during this time.
+ */
+ if ((Node == AcpiGbl_RootNode) &&
+ (WalkState->ParseFlags & ACPI_PARSE_MODULE_LEVEL))
+ {
+ break;
+ }
+
+ /*lint -fallthrough */
+
default:
/* All other types are an error */
@@ -252,7 +264,7 @@ AcpiDsLoad2BeginOp (
"Scope operator [%4.4s] (Cannot override)",
AcpiUtGetTypeName (Node->Type), AcpiUtGetNodeName (Node)));
- return (AE_AML_OPERAND_TYPE);
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
break;
@@ -311,7 +323,7 @@ AcpiDsLoad2BeginOp (
/* Add new entry or lookup existing entry */
Status = AcpiNsLookup (WalkState->ScopeInfo, BufferPtr, ObjectType,
- ACPI_IMODE_LOAD_PASS2, Flags, WalkState, &Node);
+ ACPI_IMODE_LOAD_PASS2, Flags, WalkState, &Node);
if (ACPI_SUCCESS (Status) && (Flags & ACPI_NS_TEMPORARY))
{
@@ -332,7 +344,7 @@ AcpiDsLoad2BeginOp (
{
/* Create a new op */
- Op = AcpiPsAllocOp (WalkState->Opcode);
+ Op = AcpiPsAllocOp (WalkState->Opcode, WalkState->Aml);
if (!Op)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -389,7 +401,7 @@ AcpiDsLoad2EndOp (
Op = WalkState->Op;
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Opcode [%s] Op %p State %p\n",
- WalkState->OpInfo->Name, Op, WalkState));
+ WalkState->OpInfo->Name, Op, WalkState));
/* Check if opcode had an associated namespace object */
@@ -482,7 +494,6 @@ AcpiDsLoad2EndOp (
Status = AcpiDsCreateBufferField (Op, WalkState);
break;
-
case AML_TYPE_NAMED_FIELD:
/*
* If we are executing a method, initialize the field
@@ -496,8 +507,8 @@ AcpiDsLoad2EndOp (
{
case AML_INDEX_FIELD_OP:
- Status = AcpiDsCreateIndexField (Op, (ACPI_HANDLE) Arg->Common.Node,
- WalkState);
+ Status = AcpiDsCreateIndexField (
+ Op, (ACPI_HANDLE) Arg->Common.Node, WalkState);
break;
case AML_BANK_FIELD_OP:
@@ -511,12 +522,12 @@ AcpiDsLoad2EndOp (
break;
default:
+
/* All NAMED_FIELD opcodes must be handled above */
break;
}
break;
-
case AML_TYPE_NAMED_SIMPLE:
Status = AcpiDsCreateOperands (WalkState, Arg);
@@ -547,13 +558,13 @@ AcpiDsLoad2EndOp (
Status = AcpiExCreateEvent (WalkState);
break;
-
case AML_ALIAS_OP:
Status = AcpiExCreateAlias (WalkState);
break;
default:
+
/* Unknown opcode */
Status = AE_OK;
@@ -582,7 +593,7 @@ AcpiDsLoad2EndOp (
if (Op->Common.AmlOpcode == AML_REGION_OP)
{
RegionSpace = (ACPI_ADR_SPACE_TYPE)
- ((Op->Common.Value.Arg)->Common.Value.Integer);
+ ((Op->Common.Value.Arg)->Common.Value.Integer);
}
else
{
@@ -607,18 +618,18 @@ AcpiDsLoad2EndOp (
* Executing a method: initialize the region and unlock
* the interpreter
*/
- Status = AcpiExCreateRegion (Op->Named.Data, Op->Named.Length,
- RegionSpace, WalkState);
+ Status = AcpiExCreateRegion (Op->Named.Data,
+ Op->Named.Length, RegionSpace, WalkState);
if (ACPI_FAILURE (Status))
{
- return (Status);
+ return_ACPI_STATUS (Status);
}
AcpiExExitInterpreter ();
}
- Status = AcpiEvInitializeRegion (AcpiNsGetAttachedObject (Node),
- FALSE);
+ Status = AcpiEvInitializeRegion (
+ AcpiNsGetAttachedObject (Node), FALSE);
if (WalkState->MethodNode)
{
AcpiExEnterInterpreter ();
@@ -638,13 +649,11 @@ AcpiDsLoad2EndOp (
}
break;
-
case AML_NAME_OP:
Status = AcpiDsCreateNode (WalkState, Node, Op);
break;
-
case AML_METHOD_OP:
/*
* MethodOp PkgLength NameString MethodFlags TermList
@@ -663,12 +672,14 @@ AcpiDsLoad2EndOp (
WalkState->Operands[0] = ACPI_CAST_PTR (void, Op->Named.Node);
WalkState->NumOperands = 1;
- Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
+ Status = AcpiDsCreateOperands (
+ WalkState, Op->Common.Value.Arg);
if (ACPI_SUCCESS (Status))
{
- Status = AcpiExCreateMethod (Op->Named.Data,
- Op->Named.Length, WalkState);
+ Status = AcpiExCreateMethod (
+ Op->Named.Data, Op->Named.Length, WalkState);
}
+
WalkState->Operands[0] = NULL;
WalkState->NumOperands = 0;
@@ -682,18 +693,17 @@ AcpiDsLoad2EndOp (
#endif /* ACPI_NO_METHOD_EXECUTION */
default:
+
/* All NAMED_COMPLEX opcodes must be handled above */
break;
}
break;
-
case AML_CLASS_INTERNAL:
/* case AML_INT_NAMEPATH_OP: */
break;
-
case AML_CLASS_METHOD_CALL:
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
@@ -704,9 +714,9 @@ AcpiDsLoad2EndOp (
* Lookup the method name and save the Node
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, Arg->Common.Value.String,
- ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS2,
- ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
- WalkState, &(NewNode));
+ ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS2,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+ WalkState, &(NewNode));
if (ACPI_SUCCESS (Status))
{
/*
@@ -733,6 +743,7 @@ AcpiDsLoad2EndOp (
default:
+
break;
}
@@ -744,4 +755,3 @@ Cleanup:
WalkState->NumOperands = 0;
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dswscope.c b/usr/src/uts/intel/io/acpica/dispatcher/dswscope.c
index 9842b85b21..a2497cfa33 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dswscope.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dswscope.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __DSWSCOPE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
@@ -84,6 +82,7 @@ AcpiDsScopeStackClear (
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
"Popped object type (%s)\n",
AcpiUtGetTypeName (ScopeInfo->Common.Value)));
+
AcpiUtDeleteGenericState (ScopeInfo);
}
}
@@ -235,5 +234,3 @@ AcpiDsScopeStackPop (
AcpiUtDeleteGenericState (ScopeInfo);
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/dispatcher/dswstate.c b/usr/src/uts/intel/io/acpica/dispatcher/dswstate.c
index f97ccadb60..f56f393aa7 100644
--- a/usr/src/uts/intel/io/acpica/dispatcher/dswstate.c
+++ b/usr/src/uts/intel/io/acpica/dispatcher/dswstate.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __DSWSTATE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -302,8 +299,8 @@ AcpiDsResultStackPop (
if (WalkState->Results == NULL)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Result stack underflow - State=%p\n",
- WalkState));
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Result stack underflow - State=%p\n", WalkState));
return (AE_AML_NO_OPERAND);
}
@@ -385,7 +382,7 @@ AcpiDsObjStackPush (
*
* RETURN: Status
*
- * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT
+ * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT
* deleted by this routine.
*
******************************************************************************/
@@ -549,7 +546,7 @@ AcpiDsPushWalkState (
* RETURN: A WalkState object popped from the thread's stack
*
* DESCRIPTION: Remove and return the walkstate object that is at the head of
- * the walk stack for the given walk list. NULL indicates that
+ * the walk stack for the given walk list. NULL indicates that
* the list is empty.
*
******************************************************************************/
@@ -594,7 +591,7 @@ AcpiDsPopWalkState (
*
* RETURN: Pointer to the new walk state.
*
- * DESCRIPTION: Allocate and initialize a new walk state. The current walk
+ * DESCRIPTION: Allocate and initialize a new walk state. The current walk
* state is set to this new state.
*
******************************************************************************/
@@ -710,7 +707,8 @@ AcpiDsInitAmlWalk (
/* Push start scope on scope stack and make it current */
- Status = AcpiDsScopeStackPush (MethodNode, ACPI_TYPE_METHOD, WalkState);
+ Status = AcpiDsScopeStackPush (
+ MethodNode, ACPI_TYPE_METHOD, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -730,7 +728,7 @@ AcpiDsInitAmlWalk (
/*
* Setup the current scope.
* Find a Named Op that has a namespace node associated with it.
- * search upwards from this Op. Current scope is the first
+ * search upwards from this Op. Current scope is the first
* Op with a namespace node.
*/
ExtraOp = ParserState->StartOp;
@@ -753,7 +751,7 @@ AcpiDsInitAmlWalk (
/* Push start scope on scope stack and make it current */
Status = AcpiDsScopeStackPush (ParserState->StartNode,
- ParserState->StartNode->Type, WalkState);
+ ParserState->StartNode->Type, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -790,14 +788,14 @@ AcpiDsDeleteWalkState (
if (!WalkState)
{
- return;
+ return_VOID;
}
if (WalkState->DescriptorType != ACPI_DESC_TYPE_WALK)
{
ACPI_ERROR ((AE_INFO, "%p is not a valid walk state",
WalkState));
- return;
+ return_VOID;
}
/* There should not be any open scopes */
@@ -842,5 +840,3 @@ AcpiDsDeleteWalkState (
ACPI_FREE (WalkState);
return_VOID;
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/events/evevent.c b/usr/src/uts/intel/io/acpica/events/evevent.c
index 0f5785a634..98b3da506b 100644
--- a/usr/src/uts/intel/io/acpica/events/evevent.c
+++ b/usr/src/uts/intel/io/acpica/events/evevent.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,6 +48,8 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evevent")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+
/* Local prototypes */
static ACPI_STATUS
@@ -81,6 +83,13 @@ AcpiEvInitializeEvents (
ACPI_FUNCTION_TRACE (EvInitializeEvents);
+ /* If Hardware Reduced flag is set, there are no fixed events */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
/*
* Initialize the Fixed and General Purpose Events. This is done prior to
* enabling SCIs to prevent interrupts from occurring before the handlers
@@ -128,6 +137,13 @@ AcpiEvInstallXruptHandlers (
ACPI_FUNCTION_TRACE (EvInstallXruptHandlers);
+ /* If Hardware Reduced flag is set, there is no ACPI h/w */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
/* Install the SCI handler */
Status = AcpiEvInstallSciHandler ();
@@ -187,8 +203,8 @@ AcpiEvFixedEventInitialize (
if (AcpiGbl_FixedEventInfo[i].EnableRegisterId != 0xFF)
{
Status = AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[i].EnableRegisterId,
- ACPI_DISABLE_EVENT);
+ AcpiGbl_FixedEventInfo[i].EnableRegisterId,
+ ACPI_DISABLE_EVENT);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -275,6 +291,8 @@ AcpiEvFixedEventDetect (
*
* DESCRIPTION: Clears the status bit for the requested event, calls the
* handler that previously registered for the event.
+ * NOTE: If there is no handler for the event, the event is
+ * disabled to prevent further interrupts.
*
******************************************************************************/
@@ -289,22 +307,22 @@ AcpiEvFixedEventDispatch (
/* Clear the status bit */
(void) AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[Event].StatusRegisterId,
- ACPI_CLEAR_STATUS);
+ AcpiGbl_FixedEventInfo[Event].StatusRegisterId,
+ ACPI_CLEAR_STATUS);
/*
- * Make sure we've got a handler. If not, report an error. The event is
- * disabled to prevent further interrupts.
+ * Make sure that a handler exists. If not, report an error
+ * and disable the event to prevent further interrupts.
*/
- if (NULL == AcpiGbl_FixedEventHandlers[Event].Handler)
+ if (!AcpiGbl_FixedEventHandlers[Event].Handler)
{
(void) AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
- ACPI_DISABLE_EVENT);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
+ ACPI_DISABLE_EVENT);
ACPI_ERROR ((AE_INFO,
- "No installed handler for fixed event [0x%08X]",
- Event));
+ "No installed handler for fixed event - %s (%u), disabling",
+ AcpiUtGetEventName (Event), Event));
return (ACPI_INTERRUPT_NOT_HANDLED);
}
@@ -312,7 +330,7 @@ AcpiEvFixedEventDispatch (
/* Invoke the Fixed Event handler */
return ((AcpiGbl_FixedEventHandlers[Event].Handler)(
- AcpiGbl_FixedEventHandlers[Event].Context));
+ AcpiGbl_FixedEventHandlers[Event].Context));
}
-
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evglock.c b/usr/src/uts/intel/io/acpica/events/evglock.c
index cab444d06d..caafa977bc 100644
--- a/usr/src/uts/intel/io/acpica/events/evglock.c
+++ b/usr/src/uts/intel/io/acpica/events/evglock.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evglock")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
@@ -79,10 +80,17 @@ AcpiEvInitGlobalLockHandler (
ACPI_FUNCTION_TRACE (EvInitGlobalLockHandler);
+ /* If Hardware Reduced flag is set, there is no global lock */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
/* Attempt installation of the global lock handler */
Status = AcpiInstallFixedEventHandler (ACPI_EVENT_GLOBAL,
- AcpiEvGlobalLockHandler, NULL);
+ AcpiEvGlobalLockHandler, NULL);
/*
* If the global lock does not exist on this platform, the attempt to
@@ -132,10 +140,12 @@ AcpiEvRemoveGlobalLockHandler (
ACPI_FUNCTION_TRACE (EvRemoveGlobalLockHandler);
+
AcpiGbl_GlobalLockPresent = FALSE;
Status = AcpiRemoveFixedEventHandler (ACPI_EVENT_GLOBAL,
- AcpiEvGlobalLockHandler);
+ AcpiEvGlobalLockHandler);
+ AcpiOsDeleteLock (AcpiGbl_GlobalLockPendingLock);
return_ACPI_STATUS (Status);
}
@@ -293,8 +303,8 @@ AcpiEvAcquireGlobalLock (
* Wait for handshake with the global lock interrupt handler.
* This interface releases the interpreter if we must wait.
*/
- Status = AcpiExSystemWaitSemaphore (AcpiGbl_GlobalLockSemaphore,
- ACPI_WAIT_FOREVER);
+ Status = AcpiExSystemWaitSemaphore (
+ AcpiGbl_GlobalLockSemaphore, ACPI_WAIT_FOREVER);
Flags = AcpiOsAcquireLock (AcpiGbl_GlobalLockPendingLock);
@@ -352,7 +362,7 @@ AcpiEvReleaseGlobalLock (
if (Pending)
{
Status = AcpiWriteBitRegister (
- ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT);
+ ACPI_BITREG_GLOBAL_LOCK_RELEASE, ACPI_ENABLE_EVENT);
}
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Released hardware Global Lock\n"));
@@ -365,3 +375,5 @@ AcpiEvReleaseGlobalLock (
AcpiOsReleaseMutex (AcpiGbl_GlobalLockMutex->Mutex.OsMutex);
return_ACPI_STATUS (Status);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evgpe.c b/usr/src/uts/intel/io/acpica/events/evgpe.c
index c4326c721b..d683bfdfaa 100644
--- a/usr/src/uts/intel/io/acpica/events/evgpe.c
+++ b/usr/src/uts/intel/io/acpica/events/evgpe.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+
/* Local prototypes */
static void ACPI_SYSTEM_XFACE
@@ -90,7 +92,7 @@ AcpiEvUpdateGpeEnableMask (
return_ACPI_STATUS (AE_NOT_EXIST);
}
- RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo, GpeRegisterInfo);
+ RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo);
/* Clear the run bit up front */
@@ -103,6 +105,7 @@ AcpiEvUpdateGpeEnableMask (
ACPI_SET_BIT (GpeRegisterInfo->EnableForRun, (UINT8) RegisterBit);
}
+ GpeRegisterInfo->EnableMask = GpeRegisterInfo->EnableForRun;
return_ACPI_STATUS (AE_OK);
}
@@ -129,18 +132,6 @@ AcpiEvEnableGpe (
ACPI_FUNCTION_TRACE (EvEnableGpe);
- /*
- * We will only allow a GPE to be enabled if it has either an associated
- * method (_Lxx/_Exx) or a handler, or is using the implicit notify
- * feature. Otherwise, the GPE will be immediately disabled by
- * AcpiEvGpeDispatch the first time it fires.
- */
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_NONE)
- {
- return_ACPI_STATUS (AE_NO_HANDLER);
- }
-
/* Clear the GPE (of stale events) */
Status = AcpiHwClearGpe (GpeEventInfo);
@@ -336,7 +327,7 @@ AcpiEvGetGpeEventInfo (
for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++)
{
GpeInfo = AcpiEvLowGetGpeInfo (GpeNumber,
- AcpiGbl_GpeFadtBlocks[i]);
+ AcpiGbl_GpeFadtBlocks[i]);
if (GpeInfo)
{
return (GpeInfo);
@@ -381,7 +372,11 @@ AcpiEvGpeDetect (
{
ACPI_STATUS Status;
ACPI_GPE_BLOCK_INFO *GpeBlock;
+ ACPI_NAMESPACE_NODE *GpeDevice;
ACPI_GPE_REGISTER_INFO *GpeRegisterInfo;
+ ACPI_GPE_EVENT_INFO *GpeEventInfo;
+ UINT32 GpeNumber;
+ ACPI_GPE_HANDLER_INFO *GpeHandlerInfo;
UINT32 IntStatus = ACPI_INTERRUPT_NOT_HANDLED;
UINT8 EnabledStatusByte;
UINT32 StatusReg;
@@ -412,6 +407,8 @@ AcpiEvGpeDetect (
GpeBlock = GpeXruptList->GpeBlockListHead;
while (GpeBlock)
{
+ GpeDevice = GpeBlock->Node;
+
/*
* Read all of the 8-bit GPE status and enable registers in this GPE
* block, saving all of them. Find all currently active GP events.
@@ -429,6 +426,13 @@ AcpiEvGpeDetect (
if (!(GpeRegisterInfo->EnableForRun |
GpeRegisterInfo->EnableForWake))
{
+ ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS,
+ "Ignore disabled registers for GPE %02X-%02X: "
+ "RunEnable=%02X, WakeEnable=%02X\n",
+ GpeRegisterInfo->BaseGpeNumber,
+ GpeRegisterInfo->BaseGpeNumber + (ACPI_GPE_REGISTER_WIDTH - 1),
+ GpeRegisterInfo->EnableForRun,
+ GpeRegisterInfo->EnableForWake));
continue;
}
@@ -449,8 +453,13 @@ AcpiEvGpeDetect (
}
ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS,
- "Read GPE Register at GPE%02X: Status=%02X, Enable=%02X\n",
- GpeRegisterInfo->BaseGpeNumber, StatusReg, EnableReg));
+ "Read registers for GPE %02X-%02X: Status=%02X, Enable=%02X, "
+ "RunEnable=%02X, WakeEnable=%02X\n",
+ GpeRegisterInfo->BaseGpeNumber,
+ GpeRegisterInfo->BaseGpeNumber + (ACPI_GPE_REGISTER_WIDTH - 1),
+ StatusReg, EnableReg,
+ GpeRegisterInfo->EnableForRun,
+ GpeRegisterInfo->EnableForWake));
/* Check if there is anything active at all in this register */
@@ -468,16 +477,55 @@ AcpiEvGpeDetect (
{
/* Examine one GPE bit */
+ GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i *
+ ACPI_GPE_REGISTER_WIDTH) + j];
+ GpeNumber = j + GpeRegisterInfo->BaseGpeNumber;
+
if (EnabledStatusByte & (1 << j))
{
- /*
- * Found an active GPE. Dispatch the event to a handler
- * or method.
- */
- IntStatus |= AcpiEvGpeDispatch (GpeBlock->Node,
- &GpeBlock->EventInfo[((ACPI_SIZE) i *
- ACPI_GPE_REGISTER_WIDTH) + j],
- j + GpeRegisterInfo->BaseGpeNumber);
+ /* Invoke global event handler if present */
+
+ AcpiGpeCount++;
+ if (AcpiGbl_GlobalEventHandler)
+ {
+ AcpiGbl_GlobalEventHandler (ACPI_EVENT_TYPE_GPE,
+ GpeDevice, GpeNumber,
+ AcpiGbl_GlobalEventHandlerContext);
+ }
+
+ /* Found an active GPE */
+
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER)
+ {
+ /* Dispatch the event to a raw handler */
+
+ GpeHandlerInfo = GpeEventInfo->Dispatch.Handler;
+
+ /*
+ * There is no protection around the namespace node
+ * and the GPE handler to ensure a safe destruction
+ * because:
+ * 1. The namespace node is expected to always
+ * exist after loading a table.
+ * 2. The GPE handler is expected to be flushed by
+ * AcpiOsWaitEventsComplete() before the
+ * destruction.
+ */
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ IntStatus |= GpeHandlerInfo->Address (
+ GpeDevice, GpeNumber, GpeHandlerInfo->Context);
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
+ }
+ else
+ {
+ /*
+ * Dispatch the event to a standard handler or
+ * method.
+ */
+ IntStatus |= AcpiEvGpeDispatch (GpeDevice,
+ GpeEventInfo, GpeNumber);
+ }
}
}
}
@@ -513,57 +561,19 @@ AcpiEvAsynchExecuteGpeMethod (
void *Context)
{
ACPI_GPE_EVENT_INFO *GpeEventInfo = Context;
- ACPI_STATUS Status;
- ACPI_GPE_EVENT_INFO *LocalGpeEventInfo;
+ ACPI_STATUS Status = AE_OK;
ACPI_EVALUATE_INFO *Info;
+ ACPI_GPE_NOTIFY_INFO *Notify;
ACPI_FUNCTION_TRACE (EvAsynchExecuteGpeMethod);
- /* Allocate a local GPE block */
-
- LocalGpeEventInfo = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_EVENT_INFO));
- if (!LocalGpeEventInfo)
- {
- ACPI_EXCEPTION ((AE_INFO, AE_NO_MEMORY,
- "while handling a GPE"));
- return_VOID;
- }
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
- if (ACPI_FAILURE (Status))
- {
- return_VOID;
- }
-
- /* Must revalidate the GpeNumber/GpeBlock */
-
- if (!AcpiEvValidGpeEvent (GpeEventInfo))
- {
- Status = AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
- return_VOID;
- }
-
- /*
- * Take a snapshot of the GPE info for this level - we copy the info to
- * prevent a race condition with RemoveHandler/RemoveBlock.
- */
- ACPI_MEMCPY (LocalGpeEventInfo, GpeEventInfo,
- sizeof (ACPI_GPE_EVENT_INFO));
-
- Status = AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
- if (ACPI_FAILURE (Status))
- {
- return_VOID;
- }
-
/* Do the correct dispatch - normal method or implicit notify */
- switch (LocalGpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK)
+ switch (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags))
{
case ACPI_GPE_DISPATCH_NOTIFY:
-
/*
* Implicit notify.
* Dispatch a DEVICE_WAKE notify to the appropriate handler.
@@ -571,10 +581,18 @@ AcpiEvAsynchExecuteGpeMethod (
* completes. The notify handlers are NOT invoked synchronously
* from this thread -- because handlers may in turn run other
* control methods.
+ *
+ * June 2012: Expand implicit notify mechanism to support
+ * notifies on multiple device objects.
*/
- Status = AcpiEvQueueNotifyRequest (
- LocalGpeEventInfo->Dispatch.DeviceNode,
- ACPI_NOTIFY_DEVICE_WAKE);
+ Notify = GpeEventInfo->Dispatch.NotifyList;
+ while (ACPI_SUCCESS (Status) && Notify)
+ {
+ Status = AcpiEvQueueNotifyRequest (
+ Notify->DeviceNode, ACPI_NOTIFY_DEVICE_WAKE);
+
+ Notify = Notify->Next;
+ }
break;
case ACPI_GPE_DISPATCH_METHOD:
@@ -592,7 +610,7 @@ AcpiEvAsynchExecuteGpeMethod (
* Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the
* _Lxx/_Exx control method that corresponds to this GPE
*/
- Info->PrefixNode = LocalGpeEventInfo->Dispatch.MethodNode;
+ Info->PrefixNode = GpeEventInfo->Dispatch.MethodNode;
Info->Flags = ACPI_IGNORE_RETURN_VALUE;
Status = AcpiNsEvaluate (Info);
@@ -603,23 +621,26 @@ AcpiEvAsynchExecuteGpeMethod (
{
ACPI_EXCEPTION ((AE_INFO, Status,
"while evaluating GPE method [%4.4s]",
- AcpiUtGetNodeName (LocalGpeEventInfo->Dispatch.MethodNode)));
+ AcpiUtGetNodeName (GpeEventInfo->Dispatch.MethodNode)));
}
-
break;
default:
- return_VOID; /* Should never happen */
+
+ goto ErrorExit; /* Should never happen */
}
/* Defer enabling of GPE until all notify handlers are done */
Status = AcpiOsExecute (OSL_NOTIFY_HANDLER,
- AcpiEvAsynchEnableGpe, LocalGpeEventInfo);
- if (ACPI_FAILURE (Status))
+ AcpiEvAsynchEnableGpe, GpeEventInfo);
+ if (ACPI_SUCCESS (Status))
{
- ACPI_FREE (LocalGpeEventInfo);
+ return_VOID;
}
+
+ErrorExit:
+ AcpiEvAsynchEnableGpe (GpeEventInfo);
return_VOID;
}
@@ -643,11 +664,13 @@ AcpiEvAsynchEnableGpe (
void *Context)
{
ACPI_GPE_EVENT_INFO *GpeEventInfo = Context;
+ ACPI_CPU_FLAGS Flags;
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
(void) AcpiEvFinishGpe (GpeEventInfo);
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
- ACPI_FREE (GpeEventInfo);
return;
}
@@ -688,7 +711,7 @@ AcpiEvFinishGpe (
/*
* Enable this GPE, conditionally. This means that the GPE will
- * only be physically enabled if the EnableForRun bit is set
+ * only be physically enabled if the EnableMask bit is set
* in the EventInfo.
*/
(void) AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_CONDITIONAL_ENABLE);
@@ -726,13 +749,21 @@ AcpiEvGpeDispatch (
ACPI_FUNCTION_TRACE (EvGpeDispatch);
- /* Invoke global event handler if present */
-
- AcpiGpeCount++;
- if (AcpiGbl_GlobalEventHandler)
+ /*
+ * Always disable the GPE so that it does not keep firing before
+ * any asynchronous activity completes (either from the execution
+ * of a GPE method or an asynchronous GPE handler.)
+ *
+ * If there is no handler or method to run, just disable the
+ * GPE and leave it disabled permanently to prevent further such
+ * pointless events from firing.
+ */
+ Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
+ if (ACPI_FAILURE (Status))
{
- AcpiGbl_GlobalEventHandler (ACPI_EVENT_TYPE_GPE, GpeDevice,
- GpeNumber, AcpiGbl_GlobalEventHandlerContext);
+ ACPI_EXCEPTION ((AE_INFO, Status,
+ "Unable to disable GPE %02X", GpeNumber));
+ return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
}
/*
@@ -746,36 +777,21 @@ AcpiEvGpeDispatch (
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to clear GPE%02X", GpeNumber));
+ "Unable to clear GPE %02X", GpeNumber));
+ (void) AcpiHwLowSetGpe (
+ GpeEventInfo, ACPI_GPE_CONDITIONAL_ENABLE);
return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
}
}
/*
- * Always disable the GPE so that it does not keep firing before
- * any asynchronous activity completes (either from the execution
- * of a GPE method or an asynchronous GPE handler.)
- *
- * If there is no handler or method to run, just disable the
- * GPE and leave it disabled permanently to prevent further such
- * pointless events from firing.
- */
- Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to disable GPE%02X", GpeNumber));
- return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
- }
-
- /*
* Dispatch the GPE to either an installed handler or the control
* method associated with this GPE (_Lxx or _Exx). If a handler
* exists, we invoke it and do not attempt to run the method.
* If there is neither a handler nor a method, leave the GPE
* disabled.
*/
- switch (GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK)
+ switch (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags))
{
case ACPI_GPE_DISPATCH_HANDLER:
@@ -795,30 +811,28 @@ AcpiEvGpeDispatch (
case ACPI_GPE_DISPATCH_METHOD:
case ACPI_GPE_DISPATCH_NOTIFY:
-
/*
* Execute the method associated with the GPE
* NOTE: Level-triggered GPEs are cleared after the method completes.
*/
Status = AcpiOsExecute (OSL_GPE_HANDLER,
- AcpiEvAsynchExecuteGpeMethod, GpeEventInfo);
+ AcpiEvAsynchExecuteGpeMethod, GpeEventInfo);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to queue handler for GPE%02X - event disabled",
+ "Unable to queue handler for GPE %02X - event disabled",
GpeNumber));
}
break;
default:
-
/*
* No handler or method to run!
* 03/2010: This case should no longer be possible. We will not allow
* a GPE to be enabled if it has no handler or method.
*/
ACPI_ERROR ((AE_INFO,
- "No handler or method for GPE%02X, disabling event",
+ "No handler or method for GPE %02X, disabling event",
GpeNumber));
break;
}
@@ -826,3 +840,4 @@ AcpiEvGpeDispatch (
return_UINT32 (ACPI_INTERRUPT_HANDLED);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evgpeblk.c b/usr/src/uts/intel/io/acpica/events/evgpeblk.c
index b8b7af254e..2e1d354d15 100644
--- a/usr/src/uts/intel/io/acpica/events/evgpeblk.c
+++ b/usr/src/uts/intel/io/acpica/events/evgpeblk.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evgpeblk")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+
/* Local prototypes */
static ACPI_STATUS
@@ -95,10 +97,9 @@ AcpiEvInstallGpeBlock (
return_ACPI_STATUS (Status);
}
- GpeXruptBlock = AcpiEvGetGpeXruptBlock (InterruptNumber);
- if (!GpeXruptBlock)
+ Status = AcpiEvGetGpeXruptBlock (InterruptNumber, &GpeXruptBlock);
+ if (ACPI_FAILURE (Status))
{
- Status = AE_NO_MEMORY;
goto UnlockAndExit;
}
@@ -126,7 +127,7 @@ AcpiEvInstallGpeBlock (
UnlockAndExit:
- Status = AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
return_ACPI_STATUS (Status);
}
@@ -192,6 +193,7 @@ AcpiEvDeleteGpeBlock (
{
GpeBlock->Next->Previous = GpeBlock->Previous;
}
+
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
}
@@ -240,8 +242,8 @@ AcpiEvCreateGpeInfoBlocks (
/* Allocate the GPE register information block */
GpeRegisterInfo = ACPI_ALLOCATE_ZEROED (
- (ACPI_SIZE) GpeBlock->RegisterCount *
- sizeof (ACPI_GPE_REGISTER_INFO));
+ (ACPI_SIZE) GpeBlock->RegisterCount *
+ sizeof (ACPI_GPE_REGISTER_INFO));
if (!GpeRegisterInfo)
{
ACPI_ERROR ((AE_INFO,
@@ -254,7 +256,7 @@ AcpiEvCreateGpeInfoBlocks (
* per register. Initialization to zeros is sufficient.
*/
GpeEventInfo = ACPI_ALLOCATE_ZEROED ((ACPI_SIZE) GpeBlock->GpeCount *
- sizeof (ACPI_GPE_EVENT_INFO));
+ sizeof (ACPI_GPE_EVENT_INFO));
if (!GpeEventInfo)
{
ACPI_ERROR ((AE_INFO,
@@ -266,7 +268,7 @@ AcpiEvCreateGpeInfoBlocks (
/* Save the new Info arrays in the GPE block */
GpeBlock->RegisterInfo = GpeRegisterInfo;
- GpeBlock->EventInfo = GpeEventInfo;
+ GpeBlock->EventInfo = GpeEventInfo;
/*
* Initialize the GPE Register and Event structures. A goal of these
@@ -275,23 +277,23 @@ AcpiEvCreateGpeInfoBlocks (
* first half, and the enable registers occupy the second half.
*/
ThisRegister = GpeRegisterInfo;
- ThisEvent = GpeEventInfo;
+ ThisEvent = GpeEventInfo;
for (i = 0; i < GpeBlock->RegisterCount; i++)
{
/* Init the RegisterInfo for this GPE register (8 GPEs) */
- ThisRegister->BaseGpeNumber = (UINT8) (GpeBlock->BlockBaseNumber +
- (i * ACPI_GPE_REGISTER_WIDTH));
+ ThisRegister->BaseGpeNumber = (UINT16)
+ (GpeBlock->BlockBaseNumber + (i * ACPI_GPE_REGISTER_WIDTH));
ThisRegister->StatusAddress.Address =
- GpeBlock->BlockAddress.Address + i;
+ GpeBlock->Address + i;
ThisRegister->EnableAddress.Address =
- GpeBlock->BlockAddress.Address + i + GpeBlock->RegisterCount;
+ GpeBlock->Address + i + GpeBlock->RegisterCount;
- ThisRegister->StatusAddress.SpaceId = GpeBlock->BlockAddress.SpaceId;
- ThisRegister->EnableAddress.SpaceId = GpeBlock->BlockAddress.SpaceId;
+ ThisRegister->StatusAddress.SpaceId = GpeBlock->SpaceId;
+ ThisRegister->EnableAddress.SpaceId = GpeBlock->SpaceId;
ThisRegister->StatusAddress.BitWidth = ACPI_GPE_REGISTER_WIDTH;
ThisRegister->EnableAddress.BitWidth = ACPI_GPE_REGISTER_WIDTH;
ThisRegister->StatusAddress.BitOffset = 0;
@@ -364,9 +366,10 @@ ErrorExit:
ACPI_STATUS
AcpiEvCreateGpeBlock (
ACPI_NAMESPACE_NODE *GpeDevice,
- ACPI_GENERIC_ADDRESS *GpeBlockAddress,
+ UINT64 Address,
+ UINT8 SpaceId,
UINT32 RegisterCount,
- UINT8 GpeBlockBaseNumber,
+ UINT16 GpeBlockBaseNumber,
UINT32 InterruptNumber,
ACPI_GPE_BLOCK_INFO **ReturnGpeBlock)
{
@@ -393,15 +396,14 @@ AcpiEvCreateGpeBlock (
/* Initialize the new GPE block */
+ GpeBlock->Address = Address;
+ GpeBlock->SpaceId = SpaceId;
GpeBlock->Node = GpeDevice;
GpeBlock->GpeCount = (UINT16) (RegisterCount * ACPI_GPE_REGISTER_WIDTH);
GpeBlock->Initialized = FALSE;
GpeBlock->RegisterCount = RegisterCount;
GpeBlock->BlockBaseNumber = GpeBlockBaseNumber;
- ACPI_MEMCPY (&GpeBlock->BlockAddress, GpeBlockAddress,
- sizeof (ACPI_GENERIC_ADDRESS));
-
/*
* Create the RegisterInfo and EventInfo sub-structures
* Note: disables and clears all GPEs in the block
@@ -418,6 +420,8 @@ AcpiEvCreateGpeBlock (
Status = AcpiEvInstallGpeBlock (GpeBlock, InterruptNumber);
if (ACPI_FAILURE (Status))
{
+ ACPI_FREE (GpeBlock->RegisterInfo);
+ ACPI_FREE (GpeBlock->EventInfo);
ACPI_FREE (GpeBlock);
return_ACPI_STATUS (Status);
}
@@ -431,8 +435,8 @@ AcpiEvCreateGpeBlock (
WalkInfo.ExecuteByOwnerId = FALSE;
Status = AcpiNsWalkNamespace (ACPI_TYPE_METHOD, GpeDevice,
- ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
- AcpiEvMatchGpeMethod, NULL, &WalkInfo, NULL);
+ ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
+ AcpiEvMatchGpeMethod, NULL, &WalkInfo, NULL);
/* Return the new block */
@@ -441,12 +445,12 @@ AcpiEvCreateGpeBlock (
(*ReturnGpeBlock) = GpeBlock;
}
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
- "GPE %02X to %02X [%4.4s] %u regs on int 0x%X\n",
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ " Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X%s\n",
(UINT32) GpeBlock->BlockBaseNumber,
(UINT32) (GpeBlock->BlockBaseNumber + (GpeBlock->GpeCount - 1)),
- GpeDevice->Name.Ascii, GpeBlock->RegisterCount,
- InterruptNumber));
+ GpeDevice->Name.Ascii, GpeBlock->RegisterCount, InterruptNumber,
+ InterruptNumber == AcpiGbl_FADT.SciInterrupt ? " (SCI)" : ""));
/* Update global count of currently available GPEs */
@@ -515,8 +519,9 @@ AcpiEvInitializeGpeBlock (
* Ignore GPEs that have no corresponding _Lxx/_Exx method
* and GPEs that are used to wake the system
*/
- if (((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_NONE) ||
- ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_HANDLER) ||
+ if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_NONE) ||
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_RAW_HANDLER) ||
(GpeEventInfo->Flags & ACPI_GPE_CAN_WAKE))
{
continue;
@@ -537,11 +542,14 @@ AcpiEvInitializeGpeBlock (
if (GpeEnabledCount)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
- "Enabled %u GPEs in this block\n", GpeEnabledCount));
+ ACPI_INFO ((
+ "Enabled %u GPEs in block %02X to %02X", GpeEnabledCount,
+ (UINT32) GpeBlock->BlockBaseNumber,
+ (UINT32) (GpeBlock->BlockBaseNumber + (GpeBlock->GpeCount - 1))));
}
GpeBlock->Initialized = TRUE;
return_ACPI_STATUS (AE_OK);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evgpeinit.c b/usr/src/uts/intel/io/acpica/events/evgpeinit.c
index f1d38ed853..8e2fd34023 100644
--- a/usr/src/uts/intel/io/acpica/events/evgpeinit.c
+++ b/usr/src/uts/intel/io/acpica/events/evgpeinit.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acevents.h"
@@ -50,6 +49,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evgpeinit")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*
* Note: History of _PRW support in ACPICA
@@ -93,6 +93,9 @@ AcpiEvGpeInitialize (
ACPI_FUNCTION_TRACE (EvGpeInitialize);
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ "Initializing General Purpose Events (GPEs):\n"));
+
Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE (Status))
{
@@ -130,14 +133,15 @@ AcpiEvGpeInitialize (
/* GPE block 0 exists (has both length and address > 0) */
RegisterCount0 = (UINT16) (AcpiGbl_FADT.Gpe0BlockLength / 2);
-
GpeNumberMax = (RegisterCount0 * ACPI_GPE_REGISTER_WIDTH) - 1;
/* Install GPE Block 0 */
Status = AcpiEvCreateGpeBlock (AcpiGbl_FadtGpeDevice,
- &AcpiGbl_FADT.XGpe0Block, RegisterCount0, 0,
- AcpiGbl_FADT.SciInterrupt, &AcpiGbl_GpeFadtBlocks[0]);
+ AcpiGbl_FADT.XGpe0Block.Address,
+ AcpiGbl_FADT.XGpe0Block.SpaceId,
+ RegisterCount0, 0,
+ AcpiGbl_FADT.SciInterrupt, &AcpiGbl_GpeFadtBlocks[0]);
if (ACPI_FAILURE (Status))
{
@@ -174,9 +178,11 @@ AcpiEvGpeInitialize (
/* Install GPE Block 1 */
Status = AcpiEvCreateGpeBlock (AcpiGbl_FadtGpeDevice,
- &AcpiGbl_FADT.XGpe1Block, RegisterCount1,
- AcpiGbl_FADT.Gpe1Base,
- AcpiGbl_FADT.SciInterrupt, &AcpiGbl_GpeFadtBlocks[1]);
+ AcpiGbl_FADT.XGpe1Block.Address,
+ AcpiGbl_FADT.XGpe1Block.SpaceId,
+ RegisterCount1,
+ AcpiGbl_FADT.Gpe1Base,
+ AcpiGbl_FADT.SciInterrupt, &AcpiGbl_GpeFadtBlocks[1]);
if (ACPI_FAILURE (Status))
{
@@ -189,7 +195,7 @@ AcpiEvGpeInitialize (
* space. However, GPE0 always starts at GPE number zero.
*/
GpeNumberMax = AcpiGbl_FADT.Gpe1Base +
- ((RegisterCount1 * ACPI_GPE_REGISTER_WIDTH) - 1);
+ ((RegisterCount1 * ACPI_GPE_REGISTER_WIDTH) - 1);
}
}
@@ -205,16 +211,6 @@ AcpiEvGpeInitialize (
goto Cleanup;
}
- /* Check for Max GPE number out-of-range */
-
- if (GpeNumberMax > ACPI_GPE_MAX)
- {
- ACPI_ERROR ((AE_INFO,
- "Maximum GPE number from FADT is too large: 0x%X",
- GpeNumberMax));
- Status = AE_BAD_VALUE;
- goto Cleanup;
- }
Cleanup:
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
@@ -279,9 +275,9 @@ AcpiEvUpdateGpes (
WalkInfo.GpeDevice = GpeBlock->Node;
Status = AcpiNsWalkNamespace (ACPI_TYPE_METHOD,
- WalkInfo.GpeDevice, ACPI_UINT32_MAX,
- ACPI_NS_WALK_NO_UNLOCK, AcpiEvMatchGpeMethod,
- NULL, &WalkInfo, NULL);
+ WalkInfo.GpeDevice, ACPI_UINT32_MAX,
+ ACPI_NS_WALK_NO_UNLOCK, AcpiEvMatchGpeMethod,
+ NULL, &WalkInfo, NULL);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
@@ -296,7 +292,7 @@ AcpiEvUpdateGpes (
if (WalkInfo.Count)
{
- ACPI_INFO ((AE_INFO, "Enabled %u new GPEs", WalkInfo.Count));
+ ACPI_INFO (("Enabled %u new GPEs", WalkInfo.Count));
}
(void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
@@ -378,14 +374,17 @@ AcpiEvMatchGpeMethod (
switch (Name[1])
{
case 'L':
+
Type = ACPI_GPE_LEVEL_TRIGGERED;
break;
case 'E':
+
Type = ACPI_GPE_EDGE_TRIGGERED;
break;
default:
+
/* Unknown method type, just ignore it */
ACPI_DEBUG_PRINT ((ACPI_DB_LOAD,
@@ -396,7 +395,7 @@ AcpiEvMatchGpeMethod (
/* 4) The last two characters of the name are the hex GPE Number */
- GpeNumber = ACPI_STRTOUL (&Name[2], NULL, 16);
+ GpeNumber = strtoul (&Name[2], NULL, 16);
if (GpeNumber == ACPI_UINT32_MAX)
{
/* Conversion failed; invalid method, just ignore it */
@@ -420,16 +419,18 @@ AcpiEvMatchGpeMethod (
return_ACPI_STATUS (AE_OK);
}
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_HANDLER)
+ if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER))
{
/* If there is already a handler, ignore this GPE method */
return_ACPI_STATUS (AE_OK);
}
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_METHOD)
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_METHOD)
{
/*
* If there is already a method, ignore this method. But check
@@ -444,6 +445,10 @@ AcpiEvMatchGpeMethod (
return_ACPI_STATUS (AE_OK);
}
+ /* Disable the GPE in case it's been enabled already. */
+
+ (void) AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
+
/*
* Add the GPE information from above to the GpeEventInfo block for
* use during dispatch of this GPE.
@@ -457,3 +462,5 @@ AcpiEvMatchGpeMethod (
Name, GpeNumber));
return_ACPI_STATUS (AE_OK);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evgpeutil.c b/usr/src/uts/intel/io/acpica/events/evgpeutil.c
index e706fbb7c7..ff4b1cddc6 100644
--- a/usr/src/uts/intel/io/acpica/events/evgpeutil.c
+++ b/usr/src/uts/intel/io/acpica/events/evgpeutil.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acevents.h"
@@ -50,6 +49,7 @@
ACPI_MODULE_NAME ("evgpeutil")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
* FUNCTION: AcpiEvWalkGpeList
@@ -115,60 +115,6 @@ UnlockAndExit:
/*******************************************************************************
*
- * FUNCTION: AcpiEvValidGpeEvent
- *
- * PARAMETERS: GpeEventInfo - Info for this GPE
- *
- * RETURN: TRUE if the GpeEvent is valid
- *
- * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL.
- * Should be called only when the GPE lists are semaphore locked
- * and not subject to change.
- *
- ******************************************************************************/
-
-BOOLEAN
-AcpiEvValidGpeEvent (
- ACPI_GPE_EVENT_INFO *GpeEventInfo)
-{
- ACPI_GPE_XRUPT_INFO *GpeXruptBlock;
- ACPI_GPE_BLOCK_INFO *GpeBlock;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- /* No need for spin lock since we are not changing any list elements */
-
- /* Walk the GPE interrupt levels */
-
- GpeXruptBlock = AcpiGbl_GpeXruptListHead;
- while (GpeXruptBlock)
- {
- GpeBlock = GpeXruptBlock->GpeBlockListHead;
-
- /* Walk the GPE blocks on this interrupt level */
-
- while (GpeBlock)
- {
- if ((&GpeBlock->EventInfo[0] <= GpeEventInfo) &&
- (&GpeBlock->EventInfo[GpeBlock->GpeCount] > GpeEventInfo))
- {
- return (TRUE);
- }
-
- GpeBlock = GpeBlock->Next;
- }
-
- GpeXruptBlock = GpeXruptBlock->Next;
- }
-
- return (FALSE);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiEvGetGpeDevice
*
* PARAMETERS: GPE_WALK_CALLBACK
@@ -216,9 +162,10 @@ AcpiEvGetGpeDevice (
*
* FUNCTION: AcpiEvGetGpeXruptBlock
*
- * PARAMETERS: InterruptNumber - Interrupt for a GPE block
+ * PARAMETERS: InterruptNumber - Interrupt for a GPE block
+ * GpeXruptBlock - Where the block is returned
*
- * RETURN: A GPE interrupt block
+ * RETURN: Status
*
* DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
* block per unique interrupt level used for GPEs. Should be
@@ -227,9 +174,10 @@ AcpiEvGetGpeDevice (
*
******************************************************************************/
-ACPI_GPE_XRUPT_INFO *
+ACPI_STATUS
AcpiEvGetGpeXruptBlock (
- UINT32 InterruptNumber)
+ UINT32 InterruptNumber,
+ ACPI_GPE_XRUPT_INFO **GpeXruptBlock)
{
ACPI_GPE_XRUPT_INFO *NextGpeXrupt;
ACPI_GPE_XRUPT_INFO *GpeXrupt;
@@ -247,7 +195,8 @@ AcpiEvGetGpeXruptBlock (
{
if (NextGpeXrupt->InterruptNumber == InterruptNumber)
{
- return_PTR (NextGpeXrupt);
+ *GpeXruptBlock = NextGpeXrupt;
+ return_ACPI_STATUS (AE_OK);
}
NextGpeXrupt = NextGpeXrupt->Next;
@@ -258,7 +207,7 @@ AcpiEvGetGpeXruptBlock (
GpeXrupt = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_XRUPT_INFO));
if (!GpeXrupt)
{
- return_PTR (NULL);
+ return_ACPI_STATUS (AE_NO_MEMORY);
}
GpeXrupt->InterruptNumber = InterruptNumber;
@@ -281,6 +230,7 @@ AcpiEvGetGpeXruptBlock (
{
AcpiGbl_GpeXruptListHead = GpeXrupt;
}
+
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
/* Install new interrupt handler if not SCI_INT */
@@ -288,17 +238,18 @@ AcpiEvGetGpeXruptBlock (
if (InterruptNumber != AcpiGbl_FADT.SciInterrupt)
{
Status = AcpiOsInstallInterruptHandler (InterruptNumber,
- AcpiEvGpeXruptHandler, GpeXrupt);
+ AcpiEvGpeXruptHandler, GpeXrupt);
if (ACPI_FAILURE (Status))
{
- ACPI_ERROR ((AE_INFO,
+ ACPI_EXCEPTION ((AE_INFO, Status,
"Could not install GPE interrupt handler at level 0x%X",
InterruptNumber));
- return_PTR (NULL);
+ return_ACPI_STATUS (Status);
}
}
- return_PTR (GpeXrupt);
+ *GpeXruptBlock = GpeXrupt;
+ return_ACPI_STATUS (AE_OK);
}
@@ -337,7 +288,7 @@ AcpiEvDeleteGpeXrupt (
/* Disable this interrupt */
Status = AcpiOsRemoveInterruptHandler (
- GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler);
+ GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -391,6 +342,8 @@ AcpiEvDeleteGpeHandlers (
void *Context)
{
ACPI_GPE_EVENT_INFO *GpeEventInfo;
+ ACPI_GPE_NOTIFY_INFO *Notify;
+ ACPI_GPE_NOTIFY_INFO *Next;
UINT32 i;
UINT32 j;
@@ -409,16 +362,37 @@ AcpiEvDeleteGpeHandlers (
GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i *
ACPI_GPE_REGISTER_WIDTH) + j];
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_HANDLER)
+ if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER))
{
+ /* Delete an installed handler block */
+
ACPI_FREE (GpeEventInfo->Dispatch.Handler);
GpeEventInfo->Dispatch.Handler = NULL;
GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
}
+ else if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)
+ {
+ /* Delete the implicit notification device list */
+
+ Notify = GpeEventInfo->Dispatch.NotifyList;
+ while (Notify)
+ {
+ Next = Notify->Next;
+ ACPI_FREE (Notify);
+ Notify = Next;
+ }
+
+ GpeEventInfo->Dispatch.NotifyList = NULL;
+ GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
+ }
}
}
return_ACPI_STATUS (AE_OK);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evhandler.c b/usr/src/uts/intel/io/acpica/events/evhandler.c
new file mode 100644
index 0000000000..d63cad8d2d
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/events/evhandler.c
@@ -0,0 +1,606 @@
+/******************************************************************************
+ *
+ * Module Name: evhandler - Support for Address Space handlers
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EVENTS
+ ACPI_MODULE_NAME ("evhandler")
+
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiEvInstallHandler (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue);
+
+
+/* These are the address spaces that will get default handlers */
+
+UINT8 AcpiGbl_DefaultAddressSpaces[ACPI_NUM_DEFAULT_SPACES] =
+{
+ ACPI_ADR_SPACE_SYSTEM_MEMORY,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ ACPI_ADR_SPACE_DATA_TABLE
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvInstallRegionHandlers
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Installs the core subsystem default address space handlers.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEvInstallRegionHandlers (
+ void)
+{
+ ACPI_STATUS Status;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_TRACE (EvInstallRegionHandlers);
+
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /*
+ * All address spaces (PCI Config, EC, SMBus) are scope dependent and
+ * registration must occur for a specific device.
+ *
+ * In the case of the system memory and IO address spaces there is
+ * currently no device associated with the address space. For these we
+ * use the root.
+ *
+ * We install the default PCI config space handler at the root so that
+ * this space is immediately available even though the we have not
+ * enumerated all the PCI Root Buses yet. This is to conform to the ACPI
+ * specification which states that the PCI config space must be always
+ * available -- even though we are nowhere near ready to find the PCI root
+ * buses at this point.
+ *
+ * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler
+ * has already been installed (via AcpiInstallAddressSpaceHandler).
+ * Similar for AE_SAME_HANDLER.
+ */
+ for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++)
+ {
+ Status = AcpiEvInstallSpaceHandler (AcpiGbl_RootNode,
+ AcpiGbl_DefaultAddressSpaces[i],
+ ACPI_DEFAULT_HANDLER, NULL, NULL);
+ switch (Status)
+ {
+ case AE_OK:
+ case AE_SAME_HANDLER:
+ case AE_ALREADY_EXISTS:
+
+ /* These exceptions are all OK */
+
+ Status = AE_OK;
+ break;
+
+ default:
+
+ goto UnlockAndExit;
+ }
+ }
+
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvHasDefaultHandler
+ *
+ * PARAMETERS: Node - Namespace node for the device
+ * SpaceId - The address space ID
+ *
+ * RETURN: TRUE if default handler is installed, FALSE otherwise
+ *
+ * DESCRIPTION: Check if the default handler is installed for the requested
+ * space ID.
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiEvHasDefaultHandler (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_ADR_SPACE_TYPE SpaceId)
+{
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *HandlerObj;
+
+
+ /* Must have an existing internal object */
+
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (ObjDesc)
+ {
+ HandlerObj = ObjDesc->CommonNotify.Handler;
+
+ /* Walk the linked list of handlers for this object */
+
+ while (HandlerObj)
+ {
+ if (HandlerObj->AddressSpace.SpaceId == SpaceId)
+ {
+ if (HandlerObj->AddressSpace.HandlerFlags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
+ {
+ return (TRUE);
+ }
+ }
+
+ HandlerObj = HandlerObj->AddressSpace.Next;
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvInstallHandler
+ *
+ * PARAMETERS: WalkNamespace callback
+ *
+ * DESCRIPTION: This routine installs an address handler into objects that are
+ * of type Region or Device.
+ *
+ * If the Object is a Device, and the device has a handler of
+ * the same type then the search is terminated in that branch.
+ *
+ * This is because the existing handler is closer in proximity
+ * to any more regions than the one we are trying to install.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiEvInstallHandler (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue)
+{
+ ACPI_OPERAND_OBJECT *HandlerObj;
+ ACPI_OPERAND_OBJECT *NextHandlerObj;
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_NAMESPACE_NODE *Node;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_NAME (EvInstallHandler);
+
+
+ HandlerObj = (ACPI_OPERAND_OBJECT *) Context;
+
+ /* Parameter validation */
+
+ if (!HandlerObj)
+ {
+ return (AE_OK);
+ }
+
+ /* Convert and validate the device handle */
+
+ Node = AcpiNsValidateHandle (ObjHandle);
+ if (!Node)
+ {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * We only care about regions and objects that are allowed to have
+ * address space handlers
+ */
+ if ((Node->Type != ACPI_TYPE_DEVICE) &&
+ (Node->Type != ACPI_TYPE_REGION) &&
+ (Node != AcpiGbl_RootNode))
+ {
+ return (AE_OK);
+ }
+
+ /* Check for an existing internal object */
+
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (!ObjDesc)
+ {
+ /* No object, just exit */
+
+ return (AE_OK);
+ }
+
+ /* Devices are handled different than regions */
+
+ if (ObjDesc->Common.Type == ACPI_TYPE_DEVICE)
+ {
+ /* Check if this Device already has a handler for this address space */
+
+ NextHandlerObj = AcpiEvFindRegionHandler (
+ HandlerObj->AddressSpace.SpaceId, ObjDesc->CommonNotify.Handler);
+ if (NextHandlerObj)
+ {
+ /* Found a handler, is it for the same address space? */
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
+ "Found handler for region [%s] in device %p(%p) handler %p\n",
+ AcpiUtGetRegionName (HandlerObj->AddressSpace.SpaceId),
+ ObjDesc, NextHandlerObj, HandlerObj));
+
+ /*
+ * Since the object we found it on was a device, then it means
+ * that someone has already installed a handler for the branch
+ * of the namespace from this device on. Just bail out telling
+ * the walk routine to not traverse this branch. This preserves
+ * the scoping rule for handlers.
+ */
+ return (AE_CTRL_DEPTH);
+ }
+
+ /*
+ * As long as the device didn't have a handler for this space we
+ * don't care about it. We just ignore it and proceed.
+ */
+ return (AE_OK);
+ }
+
+ /* Object is a Region */
+
+ if (ObjDesc->Region.SpaceId != HandlerObj->AddressSpace.SpaceId)
+ {
+ /* This region is for a different address space, just ignore it */
+
+ return (AE_OK);
+ }
+
+ /*
+ * Now we have a region and it is for the handler's address space type.
+ *
+ * First disconnect region for any previous handler (if any)
+ */
+ AcpiEvDetachRegion (ObjDesc, FALSE);
+
+ /* Connect the region to the new handler */
+
+ Status = AcpiEvAttachRegion (HandlerObj, ObjDesc, FALSE);
+ return (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvFindRegionHandler
+ *
+ * PARAMETERS: SpaceId - The address space ID
+ * HandlerObj - Head of the handler object list
+ *
+ * RETURN: Matching handler object. NULL if space ID not matched
+ *
+ * DESCRIPTION: Search a handler object list for a match on the address
+ * space ID.
+ *
+ ******************************************************************************/
+
+ACPI_OPERAND_OBJECT *
+AcpiEvFindRegionHandler (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_OPERAND_OBJECT *HandlerObj)
+{
+
+ /* Walk the handler list for this device */
+
+ while (HandlerObj)
+ {
+ /* Same SpaceId indicates a handler is installed */
+
+ if (HandlerObj->AddressSpace.SpaceId == SpaceId)
+ {
+ return (HandlerObj);
+ }
+
+ /* Next handler object */
+
+ HandlerObj = HandlerObj->AddressSpace.Next;
+ }
+
+ return (NULL);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvInstallSpaceHandler
+ *
+ * PARAMETERS: Node - Namespace node for the device
+ * SpaceId - The address space ID
+ * Handler - Address of the handler
+ * Setup - Address of the setup function
+ * Context - Value passed to the handler on each access
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for all OpRegions of a given SpaceId.
+ * Assumes namespace is locked
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEvInstallSpaceHandler (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_ADR_SPACE_HANDLER Handler,
+ ACPI_ADR_SPACE_SETUP Setup,
+ void *Context)
+{
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *HandlerObj;
+ ACPI_STATUS Status = AE_OK;
+ ACPI_OBJECT_TYPE Type;
+ UINT8 Flags = 0;
+
+
+ ACPI_FUNCTION_TRACE (EvInstallSpaceHandler);
+
+
+ /*
+ * This registration is valid for only the types below and the root.
+ * The root node is where the default handlers get installed.
+ */
+ if ((Node->Type != ACPI_TYPE_DEVICE) &&
+ (Node->Type != ACPI_TYPE_PROCESSOR) &&
+ (Node->Type != ACPI_TYPE_THERMAL) &&
+ (Node != AcpiGbl_RootNode))
+ {
+ Status = AE_BAD_PARAMETER;
+ goto UnlockAndExit;
+ }
+
+ if (Handler == ACPI_DEFAULT_HANDLER)
+ {
+ Flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED;
+
+ switch (SpaceId)
+ {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+
+ Handler = AcpiExSystemMemorySpaceHandler;
+ Setup = AcpiEvSystemMemoryRegionSetup;
+ break;
+
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+
+ Handler = AcpiExSystemIoSpaceHandler;
+ Setup = AcpiEvIoSpaceRegionSetup;
+ break;
+
+ case ACPI_ADR_SPACE_PCI_CONFIG:
+
+ Handler = AcpiExPciConfigSpaceHandler;
+ Setup = AcpiEvPciConfigRegionSetup;
+ break;
+
+ case ACPI_ADR_SPACE_CMOS:
+
+ Handler = AcpiExCmosSpaceHandler;
+ Setup = AcpiEvCmosRegionSetup;
+ break;
+
+ case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+
+ Handler = AcpiExPciBarSpaceHandler;
+ Setup = AcpiEvPciBarRegionSetup;
+ break;
+
+ case ACPI_ADR_SPACE_DATA_TABLE:
+
+ Handler = AcpiExDataTableSpaceHandler;
+ Setup = NULL;
+ break;
+
+ default:
+
+ Status = AE_BAD_PARAMETER;
+ goto UnlockAndExit;
+ }
+ }
+
+ /* If the caller hasn't specified a setup routine, use the default */
+
+ if (!Setup)
+ {
+ Setup = AcpiEvDefaultRegionSetup;
+ }
+
+ /* Check for an existing internal object */
+
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (ObjDesc)
+ {
+ /*
+ * The attached device object already exists. Now make sure
+ * the handler is not already installed.
+ */
+ HandlerObj = AcpiEvFindRegionHandler (SpaceId,
+ ObjDesc->CommonNotify.Handler);
+
+ if (HandlerObj)
+ {
+ if (HandlerObj->AddressSpace.Handler == Handler)
+ {
+ /*
+ * It is (relatively) OK to attempt to install the SAME
+ * handler twice. This can easily happen with the
+ * PCI_Config space.
+ */
+ Status = AE_SAME_HANDLER;
+ goto UnlockAndExit;
+ }
+ else
+ {
+ /* A handler is already installed */
+
+ Status = AE_ALREADY_EXISTS;
+ }
+
+ goto UnlockAndExit;
+ }
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
+ "Creating object on Device %p while installing handler\n",
+ Node));
+
+ /* ObjDesc does not exist, create one */
+
+ if (Node->Type == ACPI_TYPE_ANY)
+ {
+ Type = ACPI_TYPE_DEVICE;
+ }
+ else
+ {
+ Type = Node->Type;
+ }
+
+ ObjDesc = AcpiUtCreateInternalObject (Type);
+ if (!ObjDesc)
+ {
+ Status = AE_NO_MEMORY;
+ goto UnlockAndExit;
+ }
+
+ /* Init new descriptor */
+
+ ObjDesc->Common.Type = (UINT8) Type;
+
+ /* Attach the new object to the Node */
+
+ Status = AcpiNsAttachObject (Node, ObjDesc, Type);
+
+ /* Remove local reference to the object */
+
+ AcpiUtRemoveReference (ObjDesc);
+
+ if (ACPI_FAILURE (Status))
+ {
+ goto UnlockAndExit;
+ }
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
+ "Installing address handler for region %s(%X) "
+ "on Device %4.4s %p(%p)\n",
+ AcpiUtGetRegionName (SpaceId), SpaceId,
+ AcpiUtGetNodeName (Node), Node, ObjDesc));
+
+ /*
+ * Install the handler
+ *
+ * At this point there is no existing handler. Just allocate the object
+ * for the handler and link it into the list.
+ */
+ HandlerObj = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_ADDRESS_HANDLER);
+ if (!HandlerObj)
+ {
+ Status = AE_NO_MEMORY;
+ goto UnlockAndExit;
+ }
+
+ /* Init handler obj */
+
+ HandlerObj->AddressSpace.SpaceId = (UINT8) SpaceId;
+ HandlerObj->AddressSpace.HandlerFlags = Flags;
+ HandlerObj->AddressSpace.RegionList = NULL;
+ HandlerObj->AddressSpace.Node = Node;
+ HandlerObj->AddressSpace.Handler = Handler;
+ HandlerObj->AddressSpace.Context = Context;
+ HandlerObj->AddressSpace.Setup = Setup;
+
+ /* Install at head of Device.AddressSpace list */
+
+ HandlerObj->AddressSpace.Next = ObjDesc->CommonNotify.Handler;
+
+ /*
+ * The Device object is the first reference on the HandlerObj.
+ * Each region that uses the handler adds a reference.
+ */
+ ObjDesc->CommonNotify.Handler = HandlerObj;
+
+ /*
+ * Walk the namespace finding all of the regions this handler will
+ * manage.
+ *
+ * Start at the device and search the branch toward the leaf nodes
+ * until either the leaf is encountered or a device is detected that
+ * has an address handler of the same type.
+ *
+ * In either case, back up and search down the remainder of the branch
+ */
+ Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
+ AcpiEvInstallHandler, NULL, HandlerObj, NULL);
+
+UnlockAndExit:
+ return_ACPI_STATUS (Status);
+}
diff --git a/usr/src/uts/intel/io/acpica/events/evmisc.c b/usr/src/uts/intel/io/acpica/events/evmisc.c
index 1667f075db..30fc6f0c28 100644
--- a/usr/src/uts/intel/io/acpica/events/evmisc.c
+++ b/usr/src/uts/intel/io/acpica/events/evmisc.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -75,6 +75,7 @@ BOOLEAN
AcpiEvIsNotifyObject (
ACPI_NAMESPACE_NODE *Node)
{
+
switch (Node->Type)
{
case ACPI_TYPE_DEVICE:
@@ -86,6 +87,7 @@ AcpiEvIsNotifyObject (
return (TRUE);
default:
+
return (FALSE);
}
}
@@ -111,105 +113,82 @@ AcpiEvQueueNotifyRequest (
UINT32 NotifyValue)
{
ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT *HandlerObj = NULL;
- ACPI_GENERIC_STATE *NotifyInfo;
+ ACPI_OPERAND_OBJECT *HandlerListHead = NULL;
+ ACPI_GENERIC_STATE *Info;
+ UINT8 HandlerListId = 0;
ACPI_STATUS Status = AE_OK;
ACPI_FUNCTION_NAME (EvQueueNotifyRequest);
- /*
- * For value 3 (Ejection Request), some device method may need to be run.
- * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need
- * to be run.
- * For value 0x80 (Status Change) on the power button or sleep button,
- * initiate soft-off or sleep operation?
- */
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Dispatching Notify on [%4.4s] Node %p Value 0x%2.2X (%s)\n",
- AcpiUtGetNodeName (Node), Node, NotifyValue,
- AcpiUtGetNotifyName (NotifyValue)));
+ /* Are Notifies allowed on this object? */
- /* Get the notify object attached to the NS Node */
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc)
+ if (!AcpiEvIsNotifyObject (Node))
{
- /* We have the notify object, Get the right handler */
-
- switch (Node->Type)
- {
- /* Notify allowed only on these types */
+ return (AE_TYPE);
+ }
- case ACPI_TYPE_DEVICE:
- case ACPI_TYPE_THERMAL:
- case ACPI_TYPE_PROCESSOR:
+ /* Get the correct notify list type (System or Device) */
- if (NotifyValue <= ACPI_MAX_SYS_NOTIFY)
- {
- HandlerObj = ObjDesc->CommonNotify.SystemNotify;
- }
- else
- {
- HandlerObj = ObjDesc->CommonNotify.DeviceNotify;
- }
- break;
+ if (NotifyValue <= ACPI_MAX_SYS_NOTIFY)
+ {
+ HandlerListId = ACPI_SYSTEM_HANDLER_LIST;
+ }
+ else
+ {
+ HandlerListId = ACPI_DEVICE_HANDLER_LIST;
+ }
- default:
+ /* Get the notify object attached to the namespace Node */
- /* All other types are not supported */
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (ObjDesc)
+ {
+ /* We have an attached object, Get the correct handler list */
- return (AE_TYPE);
- }
+ HandlerListHead = ObjDesc->CommonNotify.NotifyList[HandlerListId];
}
/*
- * If there is any handler to run, schedule the dispatcher.
- * Check for:
- * 1) Global system notify handler
- * 2) Global device notify handler
- * 3) Per-device notify handler
+ * If there is no notify handler (Global or Local)
+ * for this object, just ignore the notify
*/
- if ((AcpiGbl_SystemNotify.Handler &&
- (NotifyValue <= ACPI_MAX_SYS_NOTIFY)) ||
- (AcpiGbl_DeviceNotify.Handler &&
- (NotifyValue > ACPI_MAX_SYS_NOTIFY)) ||
- HandlerObj)
+ if (!AcpiGbl_GlobalNotify[HandlerListId].Handler && !HandlerListHead)
{
- NotifyInfo = AcpiUtCreateGenericState ();
- if (!NotifyInfo)
- {
- return (AE_NO_MEMORY);
- }
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n",
+ AcpiUtGetNodeName (Node), NotifyValue, Node));
- if (!HandlerObj)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Executing system notify handler for Notify (%4.4s, %X) "
- "node %p\n",
- AcpiUtGetNodeName (Node), NotifyValue, Node));
- }
+ return (AE_OK);
+ }
- NotifyInfo->Common.DescriptorType = ACPI_DESC_TYPE_STATE_NOTIFY;
- NotifyInfo->Notify.Node = Node;
- NotifyInfo->Notify.Value = (UINT16) NotifyValue;
- NotifyInfo->Notify.HandlerObj = HandlerObj;
+ /* Setup notify info and schedule the notify dispatcher */
- Status = AcpiOsExecute (
- OSL_NOTIFY_HANDLER, AcpiEvNotifyDispatch, NotifyInfo);
- if (ACPI_FAILURE (Status))
- {
- AcpiUtDeleteGenericState (NotifyInfo);
- }
- }
- else
+ Info = AcpiUtCreateGenericState ();
+ if (!Info)
{
- /* There is no notify handler (per-device or system) for this device */
+ return (AE_NO_MEMORY);
+ }
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "No notify handler for Notify (%4.4s, %X) node %p\n",
- AcpiUtGetNodeName (Node), NotifyValue, Node));
+ Info->Common.DescriptorType = ACPI_DESC_TYPE_STATE_NOTIFY;
+
+ Info->Notify.Node = Node;
+ Info->Notify.Value = (UINT16) NotifyValue;
+ Info->Notify.HandlerListId = HandlerListId;
+ Info->Notify.HandlerListHead = HandlerListHead;
+ Info->Notify.Global = &AcpiGbl_GlobalNotify[HandlerListId];
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n",
+ AcpiUtGetNodeName (Node), AcpiUtGetTypeName (Node->Type),
+ NotifyValue, AcpiUtGetNotifyName (NotifyValue, ACPI_TYPE_ANY), Node));
+
+ Status = AcpiOsExecute (OSL_NOTIFY_HANDLER,
+ AcpiEvNotifyDispatch, Info);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiUtDeleteGenericState (Info);
}
return (Status);
@@ -233,64 +212,41 @@ static void ACPI_SYSTEM_XFACE
AcpiEvNotifyDispatch (
void *Context)
{
- ACPI_GENERIC_STATE *NotifyInfo = (ACPI_GENERIC_STATE *) Context;
- ACPI_NOTIFY_HANDLER GlobalHandler = NULL;
- void *GlobalContext = NULL;
+ ACPI_GENERIC_STATE *Info = (ACPI_GENERIC_STATE *) Context;
ACPI_OPERAND_OBJECT *HandlerObj;
ACPI_FUNCTION_ENTRY ();
- /*
- * We will invoke a global notify handler if installed. This is done
- * _before_ we invoke the per-device handler attached to the device.
- */
- if (NotifyInfo->Notify.Value <= ACPI_MAX_SYS_NOTIFY)
- {
- /* Global system notification handler */
+ /* Invoke a global notify handler if installed */
- if (AcpiGbl_SystemNotify.Handler)
- {
- GlobalHandler = AcpiGbl_SystemNotify.Handler;
- GlobalContext = AcpiGbl_SystemNotify.Context;
- }
- }
- else
+ if (Info->Notify.Global->Handler)
{
- /* Global driver notification handler */
-
- if (AcpiGbl_DeviceNotify.Handler)
- {
- GlobalHandler = AcpiGbl_DeviceNotify.Handler;
- GlobalContext = AcpiGbl_DeviceNotify.Context;
- }
+ Info->Notify.Global->Handler (Info->Notify.Node,
+ Info->Notify.Value,
+ Info->Notify.Global->Context);
}
- /* Invoke the system handler first, if present */
+ /* Now invoke the local notify handler(s) if any are installed */
- if (GlobalHandler)
+ HandlerObj = Info->Notify.HandlerListHead;
+ while (HandlerObj)
{
- GlobalHandler (NotifyInfo->Notify.Node, NotifyInfo->Notify.Value,
- GlobalContext);
- }
-
- /* Now invoke the per-device handler, if present */
-
- HandlerObj = NotifyInfo->Notify.HandlerObj;
- if (HandlerObj)
- {
- HandlerObj->Notify.Handler (NotifyInfo->Notify.Node,
- NotifyInfo->Notify.Value,
+ HandlerObj->Notify.Handler (Info->Notify.Node,
+ Info->Notify.Value,
HandlerObj->Notify.Context);
+
+ HandlerObj = HandlerObj->Notify.Next[Info->Notify.HandlerListId];
}
/* All done with the info object */
- AcpiUtDeleteGenericState (NotifyInfo);
+ AcpiUtDeleteGenericState (Info);
}
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
* FUNCTION: AcpiEvTerminate
@@ -337,21 +293,23 @@ AcpiEvTerminate (
Status = AcpiEvWalkGpeList (AcpiHwDisableGpeBlock, NULL);
- /* Remove SCI handler */
-
- Status = AcpiEvRemoveSciHandler ();
- if (ACPI_FAILURE(Status))
- {
- ACPI_ERROR ((AE_INFO,
- "Could not remove SCI handler"));
- }
-
Status = AcpiEvRemoveGlobalLockHandler ();
if (ACPI_FAILURE(Status))
{
ACPI_ERROR ((AE_INFO,
"Could not remove Global Lock handler"));
}
+
+ AcpiGbl_EventsInitialized = FALSE;
+ }
+
+ /* Remove SCI handlers */
+
+ Status = AcpiEvRemoveAllSciHandlers ();
+ if (ACPI_FAILURE(Status))
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Could not remove SCI handler"));
}
/* Deallocate all handler objects installed within GPE info structs */
@@ -370,3 +328,5 @@ AcpiEvTerminate (
}
return_VOID;
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evregion.c b/usr/src/uts/intel/io/acpica/events/evregion.c
index 193eb07b92..673d44d191 100644
--- a/usr/src/uts/intel/io/acpica/events/evregion.c
+++ b/usr/src/uts/intel/io/acpica/events/evregion.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: evregion - ACPI AddressSpace (OpRegion) handler dispatch
+ * Module Name: evregion - Operation Region support
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EVREGION_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acevents.h"
@@ -54,16 +51,13 @@
ACPI_MODULE_NAME ("evregion")
-/* Local prototypes */
+extern UINT8 AcpiGbl_DefaultAddressSpaces[];
-static BOOLEAN
-AcpiEvHasDefaultHandler (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId);
+/* Local prototypes */
static void
AcpiEvOrphanEcRegMethod (
- void);
+ ACPI_NAMESPACE_NODE *EcDeviceNode);
static ACPI_STATUS
AcpiEvRegRun (
@@ -72,152 +66,6 @@ AcpiEvRegRun (
void *Context,
void **ReturnValue);
-static ACPI_STATUS
-AcpiEvInstallHandler (
- ACPI_HANDLE ObjHandle,
- UINT32 Level,
- void *Context,
- void **ReturnValue);
-
-/* These are the address spaces that will get default handlers */
-
-#define ACPI_NUM_DEFAULT_SPACES 4
-
-static UINT8 AcpiGbl_DefaultAddressSpaces[ACPI_NUM_DEFAULT_SPACES] =
-{
- ACPI_ADR_SPACE_SYSTEM_MEMORY,
- ACPI_ADR_SPACE_SYSTEM_IO,
- ACPI_ADR_SPACE_PCI_CONFIG,
- ACPI_ADR_SPACE_DATA_TABLE
-};
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEvInstallRegionHandlers
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Installs the core subsystem default address space handlers.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEvInstallRegionHandlers (
- void)
-{
- ACPI_STATUS Status;
- UINT32 i;
-
-
- ACPI_FUNCTION_TRACE (EvInstallRegionHandlers);
-
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /*
- * All address spaces (PCI Config, EC, SMBus) are scope dependent and
- * registration must occur for a specific device.
- *
- * In the case of the system memory and IO address spaces there is
- * currently no device associated with the address space. For these we
- * use the root.
- *
- * We install the default PCI config space handler at the root so that
- * this space is immediately available even though the we have not
- * enumerated all the PCI Root Buses yet. This is to conform to the ACPI
- * specification which states that the PCI config space must be always
- * available -- even though we are nowhere near ready to find the PCI root
- * buses at this point.
- *
- * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler
- * has already been installed (via AcpiInstallAddressSpaceHandler).
- * Similar for AE_SAME_HANDLER.
- */
- for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++)
- {
- Status = AcpiEvInstallSpaceHandler (AcpiGbl_RootNode,
- AcpiGbl_DefaultAddressSpaces[i],
- ACPI_DEFAULT_HANDLER, NULL, NULL);
- switch (Status)
- {
- case AE_OK:
- case AE_SAME_HANDLER:
- case AE_ALREADY_EXISTS:
-
- /* These exceptions are all OK */
-
- Status = AE_OK;
- break;
-
- default:
-
- goto UnlockAndExit;
- }
- }
-
-UnlockAndExit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEvHasDefaultHandler
- *
- * PARAMETERS: Node - Namespace node for the device
- * SpaceId - The address space ID
- *
- * RETURN: TRUE if default handler is installed, FALSE otherwise
- *
- * DESCRIPTION: Check if the default handler is installed for the requested
- * space ID.
- *
- ******************************************************************************/
-
-static BOOLEAN
-AcpiEvHasDefaultHandler (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId)
-{
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT *HandlerObj;
-
-
- /* Must have an existing internal object */
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc)
- {
- HandlerObj = ObjDesc->Device.Handler;
-
- /* Walk the linked list of handlers for this object */
-
- while (HandlerObj)
- {
- if (HandlerObj->AddressSpace.SpaceId == SpaceId)
- {
- if (HandlerObj->AddressSpace.HandlerFlags &
- ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
- {
- return (TRUE);
- }
- }
-
- HandlerObj = HandlerObj->AddressSpace.Next;
- }
- }
-
- return (FALSE);
-}
-
/*******************************************************************************
*
@@ -261,13 +109,11 @@ AcpiEvInitializeOpRegions (
if (AcpiEvHasDefaultHandler (AcpiGbl_RootNode,
AcpiGbl_DefaultAddressSpaces[i]))
{
- Status = AcpiEvExecuteRegMethods (AcpiGbl_RootNode,
- AcpiGbl_DefaultAddressSpaces[i]);
+ AcpiEvExecuteRegMethods (AcpiGbl_RootNode,
+ AcpiGbl_DefaultAddressSpaces[i], ACPI_REG_CONNECT);
}
}
- AcpiGbl_RegMethodsExecuted = TRUE;
-
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return_ACPI_STATUS (Status);
}
@@ -275,103 +121,10 @@ AcpiEvInitializeOpRegions (
/*******************************************************************************
*
- * FUNCTION: AcpiEvExecuteRegMethod
- *
- * PARAMETERS: RegionObj - Region object
- * Function - Passed to _REG: On (1) or Off (0)
- *
- * RETURN: Status
- *
- * DESCRIPTION: Execute _REG method for a region
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEvExecuteRegMethod (
- ACPI_OPERAND_OBJECT *RegionObj,
- UINT32 Function)
-{
- ACPI_EVALUATE_INFO *Info;
- ACPI_OPERAND_OBJECT *Args[3];
- ACPI_OPERAND_OBJECT *RegionObj2;
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (EvExecuteRegMethod);
-
-
- RegionObj2 = AcpiNsGetSecondaryObject (RegionObj);
- if (!RegionObj2)
- {
- return_ACPI_STATUS (AE_NOT_EXIST);
- }
-
- if (RegionObj2->Extra.Method_REG == NULL)
- {
- return_ACPI_STATUS (AE_OK);
- }
-
- /* Allocate and initialize the evaluation information block */
-
- Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
- if (!Info)
- {
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
-
- Info->PrefixNode = RegionObj2->Extra.Method_REG;
- Info->Pathname = NULL;
- Info->Parameters = Args;
- Info->Flags = ACPI_IGNORE_RETURN_VALUE;
-
- /*
- * The _REG method has two arguments:
- *
- * Arg0 - Integer:
- * Operation region space ID Same value as RegionObj->Region.SpaceId
- *
- * Arg1 - Integer:
- * connection status 1 for connecting the handler, 0 for disconnecting
- * the handler (Passed as a parameter)
- */
- Args[0] = AcpiUtCreateIntegerObject ((UINT64) RegionObj->Region.SpaceId);
- if (!Args[0])
- {
- Status = AE_NO_MEMORY;
- goto Cleanup1;
- }
-
- Args[1] = AcpiUtCreateIntegerObject ((UINT64) Function);
- if (!Args[1])
- {
- Status = AE_NO_MEMORY;
- goto Cleanup2;
- }
-
- Args[2] = NULL; /* Terminate list */
-
- /* Execute the method, no return value */
-
- ACPI_DEBUG_EXEC (
- AcpiUtDisplayInitPathname (ACPI_TYPE_METHOD, Info->PrefixNode, NULL));
-
- Status = AcpiNsEvaluate (Info);
- AcpiUtRemoveReference (Args[1]);
-
-Cleanup2:
- AcpiUtRemoveReference (Args[0]);
-
-Cleanup1:
- ACPI_FREE (Info);
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiEvAddressSpaceDispatch
*
* PARAMETERS: RegionObj - Internal region object
+ * FieldObj - Corresponding field. Can be NULL.
* Function - Read or Write operation
* RegionOffset - Where in the region to read or write
* BitWidth - Field width in bits (8, 16, 32, or 64)
@@ -383,11 +136,18 @@ Cleanup1:
* DESCRIPTION: Dispatch an address space or operation region access to
* a previously installed handler.
*
+ * NOTE: During early initialization, we always install the default region
+ * handlers for Memory, I/O and PCI_Config. This ensures that these operation
+ * region address spaces are always available as per the ACPI specification.
+ * This is especially needed in order to support the execution of
+ * module-level AML code during loading of the ACPI tables.
+ *
******************************************************************************/
ACPI_STATUS
AcpiEvAddressSpaceDispatch (
ACPI_OPERAND_OBJECT *RegionObj,
+ ACPI_OPERAND_OBJECT *FieldObj,
UINT32 Function,
UINT32 RegionOffset,
UINT32 BitWidth,
@@ -399,6 +159,8 @@ AcpiEvAddressSpaceDispatch (
ACPI_OPERAND_OBJECT *HandlerDesc;
ACPI_OPERAND_OBJECT *RegionObj2;
void *RegionContext = NULL;
+ ACPI_CONNECTION_INFO *Context;
+ ACPI_PHYSICAL_ADDRESS Address;
ACPI_FUNCTION_TRACE (EvAddressSpaceDispatch);
@@ -423,6 +185,8 @@ AcpiEvAddressSpaceDispatch (
return_ACPI_STATUS (AE_NOT_EXIST);
}
+ Context = HandlerDesc->AddressSpace.Context;
+
/*
* It may be the case that the region has never been initialized.
* Some types of regions require special init code
@@ -450,7 +214,7 @@ AcpiEvAddressSpaceDispatch (
AcpiExExitInterpreter ();
Status = RegionSetup (RegionObj, ACPI_REGION_ACTIVATE,
- HandlerDesc->AddressSpace.Context, &RegionContext);
+ Context, &RegionContext);
/* Re-enter the interpreter */
@@ -472,18 +236,12 @@ AcpiEvAddressSpaceDispatch (
{
RegionObj->Region.Flags |= AOPOBJ_SETUP_COMPLETE;
- if (RegionObj2->Extra.RegionContext)
- {
- /* The handler for this region was already installed */
-
- ACPI_FREE (RegionContext);
- }
- else
+ /*
+ * Save the returned context for use in all accesses to
+ * the handler for this particular region
+ */
+ if (!(RegionObj2->Extra.RegionContext))
{
- /*
- * Save the returned context for use in all accesses to
- * this particular region
- */
RegionObj2->Extra.RegionContext = RegionContext;
}
}
@@ -492,15 +250,53 @@ AcpiEvAddressSpaceDispatch (
/* We have everything we need, we can invoke the address space handler */
Handler = HandlerDesc->AddressSpace.Handler;
+ Address = (RegionObj->Region.Address + RegionOffset);
+
+ /*
+ * Special handling for GenericSerialBus and GeneralPurposeIo:
+ * There are three extra parameters that must be passed to the
+ * handler via the context:
+ * 1) Connection buffer, a resource template from Connection() op
+ * 2) Length of the above buffer
+ * 3) Actual access length from the AccessAs() op
+ *
+ * In addition, for GeneralPurposeIo, the Address and BitWidth fields
+ * are defined as follows:
+ * 1) Address is the pin number index of the field (bit offset from
+ * the previous Connection)
+ * 2) BitWidth is the actual bit length of the field (number of pins)
+ */
+ if ((RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS) &&
+ Context &&
+ FieldObj)
+ {
+ /* Get the Connection (ResourceTemplate) buffer */
+
+ Context->Connection = FieldObj->Field.ResourceBuffer;
+ Context->Length = FieldObj->Field.ResourceLength;
+ Context->AccessLength = FieldObj->Field.AccessLength;
+ }
+ if ((RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GPIO) &&
+ Context &&
+ FieldObj)
+ {
+ /* Get the Connection (ResourceTemplate) buffer */
+
+ Context->Connection = FieldObj->Field.ResourceBuffer;
+ Context->Length = FieldObj->Field.ResourceLength;
+ Context->AccessLength = FieldObj->Field.AccessLength;
+ Address = FieldObj->Field.PinNumberIndex;
+ BitWidth = FieldObj->Field.BitLength;
+ }
ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
"Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
&RegionObj->Region.Handler->AddressSpace, Handler,
- ACPI_FORMAT_NATIVE_UINT (RegionObj->Region.Address + RegionOffset),
+ ACPI_FORMAT_UINT64 (Address),
AcpiUtGetRegionName (RegionObj->Region.SpaceId)));
if (!(HandlerDesc->AddressSpace.HandlerFlags &
- ACPI_ADDR_HANDLER_DEFAULT_INSTALLED))
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED))
{
/*
* For handlers other than the default (supplied) handlers, we must
@@ -512,9 +308,8 @@ AcpiEvAddressSpaceDispatch (
/* Call the handler */
- Status = Handler (Function,
- (RegionObj->Region.Address + RegionOffset), BitWidth, Value,
- HandlerDesc->AddressSpace.Context, RegionObj2->Extra.RegionContext);
+ Status = Handler (Function, Address, BitWidth, Value, Context,
+ RegionObj2->Extra.RegionContext);
if (ACPI_FAILURE (Status))
{
@@ -523,13 +318,13 @@ AcpiEvAddressSpaceDispatch (
}
if (!(HandlerDesc->AddressSpace.HandlerFlags &
- ACPI_ADDR_HANDLER_DEFAULT_INSTALLED))
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED))
{
/*
* We just returned from a non-default handler, we must re-enter the
* interpreter
*/
- AcpiExEnterInterpreter ();
+ AcpiExEnterInterpreter ();
}
return_ACPI_STATUS (Status);
@@ -551,12 +346,13 @@ AcpiEvAddressSpaceDispatch (
******************************************************************************/
void
-AcpiEvDetachRegion(
+AcpiEvDetachRegion (
ACPI_OPERAND_OBJECT *RegionObj,
BOOLEAN AcpiNsIsLocked)
{
ACPI_OPERAND_OBJECT *HandlerObj;
ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *StartDesc;
ACPI_OPERAND_OBJECT **LastObjPtr;
ACPI_ADR_SPACE_SETUP RegionSetup;
void **RegionContext;
@@ -587,6 +383,7 @@ AcpiEvDetachRegion(
/* Find this region in the handler's list */
ObjDesc = HandlerObj->AddressSpace.RegionList;
+ StartDesc = ObjDesc;
LastObjPtr = &HandlerObj->AddressSpace.RegionList;
while (ObjDesc)
@@ -641,6 +438,15 @@ AcpiEvDetachRegion(
Status = RegionSetup (RegionObj, ACPI_REGION_DEACTIVATE,
HandlerObj->AddressSpace.Context, RegionContext);
+ /*
+ * RegionContext should have been released by the deactivate
+ * operation. We don't need access to it anymore here.
+ */
+ if (RegionContext)
+ {
+ *RegionContext = NULL;
+ }
+
/* Init routine may fail, Just ignore errors */
if (ACPI_FAILURE (Status))
@@ -672,6 +478,16 @@ AcpiEvDetachRegion(
LastObjPtr = &ObjDesc->Region.Next;
ObjDesc = ObjDesc->Region.Next;
+
+ /* Prevent infinite loop if list is corrupted */
+
+ if (ObjDesc == StartDesc)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Circular handler list in region object %p",
+ RegionObj));
+ return_VOID;
+ }
}
/* If we get here, the region was not in the handler's region list */
@@ -709,6 +525,13 @@ AcpiEvAttachRegion (
ACPI_FUNCTION_TRACE (EvAttachRegion);
+ /* Install the region's handler */
+
+ if (RegionObj->Region.Handler)
+ {
+ return_ACPI_STATUS (AE_ALREADY_EXISTS);
+ }
+
ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
"Adding Region [%4.4s] %p to address handler %p [%s]\n",
AcpiUtGetNodeName (RegionObj->Region.Node),
@@ -719,14 +542,6 @@ AcpiEvAttachRegion (
RegionObj->Region.Next = HandlerObj->AddressSpace.RegionList;
HandlerObj->AddressSpace.RegionList = RegionObj;
-
- /* Install the region's handler */
-
- if (RegionObj->Region.Handler)
- {
- return_ACPI_STATUS (AE_ALREADY_EXISTS);
- }
-
RegionObj->Region.Handler = HandlerObj;
AcpiUtAddReference (HandlerObj);
@@ -736,377 +551,144 @@ AcpiEvAttachRegion (
/*******************************************************************************
*
- * FUNCTION: AcpiEvInstallHandler
- *
- * PARAMETERS: WalkNamespace callback
+ * FUNCTION: AcpiEvExecuteRegMethod
*
- * DESCRIPTION: This routine installs an address handler into objects that are
- * of type Region or Device.
+ * PARAMETERS: RegionObj - Region object
+ * Function - Passed to _REG: On (1) or Off (0)
*
- * If the Object is a Device, and the device has a handler of
- * the same type then the search is terminated in that branch.
+ * RETURN: Status
*
- * This is because the existing handler is closer in proximity
- * to any more regions than the one we are trying to install.
+ * DESCRIPTION: Execute _REG method for a region
*
******************************************************************************/
-static ACPI_STATUS
-AcpiEvInstallHandler (
- ACPI_HANDLE ObjHandle,
- UINT32 Level,
- void *Context,
- void **ReturnValue)
+ACPI_STATUS
+AcpiEvExecuteRegMethod (
+ ACPI_OPERAND_OBJECT *RegionObj,
+ UINT32 Function)
{
- ACPI_OPERAND_OBJECT *HandlerObj;
- ACPI_OPERAND_OBJECT *NextHandlerObj;
- ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_EVALUATE_INFO *Info;
+ ACPI_OPERAND_OBJECT *Args[3];
+ ACPI_OPERAND_OBJECT *RegionObj2;
+ const ACPI_NAME *RegNamePtr = ACPI_CAST_PTR (ACPI_NAME, METHOD_NAME__REG);
+ ACPI_NAMESPACE_NODE *MethodNode;
ACPI_NAMESPACE_NODE *Node;
ACPI_STATUS Status;
- ACPI_FUNCTION_NAME (EvInstallHandler);
-
-
- HandlerObj = (ACPI_OPERAND_OBJECT *) Context;
+ ACPI_FUNCTION_TRACE (EvExecuteRegMethod);
- /* Parameter validation */
- if (!HandlerObj)
+ if (!AcpiGbl_NamespaceInitialized ||
+ RegionObj->Region.Handler == NULL)
{
- return (AE_OK);
+ return_ACPI_STATUS (AE_OK);
}
- /* Convert and validate the device handle */
-
- Node = AcpiNsValidateHandle (ObjHandle);
- if (!Node)
+ RegionObj2 = AcpiNsGetSecondaryObject (RegionObj);
+ if (!RegionObj2)
{
- return (AE_BAD_PARAMETER);
+ return_ACPI_STATUS (AE_NOT_EXIST);
}
/*
- * We only care about regions and objects that are allowed to have
- * address space handlers
+ * Find any "_REG" method associated with this region definition.
+ * The method should always be updated as this function may be
+ * invoked after a namespace change.
*/
- if ((Node->Type != ACPI_TYPE_DEVICE) &&
- (Node->Type != ACPI_TYPE_REGION) &&
- (Node != AcpiGbl_RootNode))
+ Node = RegionObj->Region.Node->Parent;
+ Status = AcpiNsSearchOneScope (
+ *RegNamePtr, Node, ACPI_TYPE_METHOD, &MethodNode);
+ if (ACPI_SUCCESS (Status))
{
- return (AE_OK);
+ /*
+ * The _REG method is optional and there can be only one per
+ * region definition. This will be executed when the handler is
+ * attached or removed.
+ */
+ RegionObj2->Extra.Method_REG = MethodNode;
}
-
- /* Check for an existing internal object */
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (!ObjDesc)
+ if (RegionObj2->Extra.Method_REG == NULL)
{
- /* No object, just exit */
-
- return (AE_OK);
+ return_ACPI_STATUS (AE_OK);
}
- /* Devices are handled different than regions */
+ /* _REG(DISCONNECT) should be paired with _REG(CONNECT) */
- if (ObjDesc->Common.Type == ACPI_TYPE_DEVICE)
+ if ((Function == ACPI_REG_CONNECT &&
+ RegionObj->Common.Flags & AOPOBJ_REG_CONNECTED) ||
+ (Function == ACPI_REG_DISCONNECT &&
+ !(RegionObj->Common.Flags & AOPOBJ_REG_CONNECTED)))
{
- /* Check if this Device already has a handler for this address space */
-
- NextHandlerObj = ObjDesc->Device.Handler;
- while (NextHandlerObj)
- {
- /* Found a handler, is it for the same address space? */
-
- if (NextHandlerObj->AddressSpace.SpaceId ==
- HandlerObj->AddressSpace.SpaceId)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
- "Found handler for region [%s] in device %p(%p) "
- "handler %p\n",
- AcpiUtGetRegionName (HandlerObj->AddressSpace.SpaceId),
- ObjDesc, NextHandlerObj, HandlerObj));
-
- /*
- * Since the object we found it on was a device, then it
- * means that someone has already installed a handler for
- * the branch of the namespace from this device on. Just
- * bail out telling the walk routine to not traverse this
- * branch. This preserves the scoping rule for handlers.
- */
- return (AE_CTRL_DEPTH);
- }
-
- /* Walk the linked list of handlers attached to this device */
-
- NextHandlerObj = NextHandlerObj->AddressSpace.Next;
- }
-
- /*
- * As long as the device didn't have a handler for this space we
- * don't care about it. We just ignore it and proceed.
- */
- return (AE_OK);
+ return_ACPI_STATUS (AE_OK);
}
- /* Object is a Region */
+ /* Allocate and initialize the evaluation information block */
- if (ObjDesc->Region.SpaceId != HandlerObj->AddressSpace.SpaceId)
+ Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
+ if (!Info)
{
- /* This region is for a different address space, just ignore it */
-
- return (AE_OK);
+ return_ACPI_STATUS (AE_NO_MEMORY);
}
- /*
- * Now we have a region and it is for the handler's address space type.
- *
- * First disconnect region for any previous handler (if any)
- */
- AcpiEvDetachRegion (ObjDesc, FALSE);
-
- /* Connect the region to the new handler */
-
- Status = AcpiEvAttachRegion (HandlerObj, ObjDesc, FALSE);
- return (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEvInstallSpaceHandler
- *
- * PARAMETERS: Node - Namespace node for the device
- * SpaceId - The address space ID
- * Handler - Address of the handler
- * Setup - Address of the setup function
- * Context - Value passed to the handler on each access
- *
- * RETURN: Status
- *
- * DESCRIPTION: Install a handler for all OpRegions of a given SpaceId.
- * Assumes namespace is locked
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEvInstallSpaceHandler (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId,
- ACPI_ADR_SPACE_HANDLER Handler,
- ACPI_ADR_SPACE_SETUP Setup,
- void *Context)
-{
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT *HandlerObj;
- ACPI_STATUS Status;
- ACPI_OBJECT_TYPE Type;
- UINT8 Flags = 0;
-
-
- ACPI_FUNCTION_TRACE (EvInstallSpaceHandler);
-
+ Info->PrefixNode = RegionObj2->Extra.Method_REG;
+ Info->RelativePathname = NULL;
+ Info->Parameters = Args;
+ Info->Flags = ACPI_IGNORE_RETURN_VALUE;
/*
- * This registration is valid for only the types below and the root. This
- * is where the default handlers get placed.
+ * The _REG method has two arguments:
+ *
+ * Arg0 - Integer:
+ * Operation region space ID Same value as RegionObj->Region.SpaceId
+ *
+ * Arg1 - Integer:
+ * connection status 1 for connecting the handler, 0 for disconnecting
+ * the handler (Passed as a parameter)
*/
- if ((Node->Type != ACPI_TYPE_DEVICE) &&
- (Node->Type != ACPI_TYPE_PROCESSOR) &&
- (Node->Type != ACPI_TYPE_THERMAL) &&
- (Node != AcpiGbl_RootNode))
- {
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
- }
-
- if (Handler == ACPI_DEFAULT_HANDLER)
+ Args[0] = AcpiUtCreateIntegerObject ((UINT64) RegionObj->Region.SpaceId);
+ if (!Args[0])
{
- Flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED;
-
- switch (SpaceId)
- {
- case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- Handler = AcpiExSystemMemorySpaceHandler;
- Setup = AcpiEvSystemMemoryRegionSetup;
- break;
-
- case ACPI_ADR_SPACE_SYSTEM_IO:
- Handler = AcpiExSystemIoSpaceHandler;
- Setup = AcpiEvIoSpaceRegionSetup;
- break;
-
- case ACPI_ADR_SPACE_PCI_CONFIG:
- Handler = AcpiExPciConfigSpaceHandler;
- Setup = AcpiEvPciConfigRegionSetup;
- break;
-
- case ACPI_ADR_SPACE_CMOS:
- Handler = AcpiExCmosSpaceHandler;
- Setup = AcpiEvCmosRegionSetup;
- break;
-
- case ACPI_ADR_SPACE_PCI_BAR_TARGET:
- Handler = AcpiExPciBarSpaceHandler;
- Setup = AcpiEvPciBarRegionSetup;
- break;
-
- case ACPI_ADR_SPACE_DATA_TABLE:
- Handler = AcpiExDataTableSpaceHandler;
- Setup = NULL;
- break;
-
- default:
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
- }
+ Status = AE_NO_MEMORY;
+ goto Cleanup1;
}
- /* If the caller hasn't specified a setup routine, use the default */
-
- if (!Setup)
+ Args[1] = AcpiUtCreateIntegerObject ((UINT64) Function);
+ if (!Args[1])
{
- Setup = AcpiEvDefaultRegionSetup;
+ Status = AE_NO_MEMORY;
+ goto Cleanup2;
}
- /* Check for an existing internal object */
-
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc)
- {
- /*
- * The attached device object already exists. Make sure the handler
- * is not already installed.
- */
- HandlerObj = ObjDesc->Device.Handler;
-
- /* Walk the handler list for this device */
-
- while (HandlerObj)
- {
- /* Same SpaceId indicates a handler already installed */
+ Args[2] = NULL; /* Terminate list */
- if (HandlerObj->AddressSpace.SpaceId == SpaceId)
- {
- if (HandlerObj->AddressSpace.Handler == Handler)
- {
- /*
- * It is (relatively) OK to attempt to install the SAME
- * handler twice. This can easily happen with the
- * PCI_Config space.
- */
- Status = AE_SAME_HANDLER;
- goto UnlockAndExit;
- }
- else
- {
- /* A handler is already installed */
+ /* Execute the method, no return value */
- Status = AE_ALREADY_EXISTS;
- }
- goto UnlockAndExit;
- }
+ ACPI_DEBUG_EXEC (
+ AcpiUtDisplayInitPathname (ACPI_TYPE_METHOD, Info->PrefixNode, NULL));
- /* Walk the linked list of handlers */
+ Status = AcpiNsEvaluate (Info);
+ AcpiUtRemoveReference (Args[1]);
- HandlerObj = HandlerObj->AddressSpace.Next;
- }
- }
- else
+ if (ACPI_FAILURE (Status))
{
- ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
- "Creating object on Device %p while installing handler\n", Node));
-
- /* ObjDesc does not exist, create one */
-
- if (Node->Type == ACPI_TYPE_ANY)
- {
- Type = ACPI_TYPE_DEVICE;
- }
- else
- {
- Type = Node->Type;
- }
-
- ObjDesc = AcpiUtCreateInternalObject (Type);
- if (!ObjDesc)
- {
- Status = AE_NO_MEMORY;
- goto UnlockAndExit;
- }
-
- /* Init new descriptor */
-
- ObjDesc->Common.Type = (UINT8) Type;
-
- /* Attach the new object to the Node */
-
- Status = AcpiNsAttachObject (Node, ObjDesc, Type);
-
- /* Remove local reference to the object */
-
- AcpiUtRemoveReference (ObjDesc);
-
- if (ACPI_FAILURE (Status))
- {
- goto UnlockAndExit;
- }
+ goto Cleanup2;
}
- ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
- "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n",
- AcpiUtGetRegionName (SpaceId), SpaceId,
- AcpiUtGetNodeName (Node), Node, ObjDesc));
-
- /*
- * Install the handler
- *
- * At this point there is no existing handler. Just allocate the object
- * for the handler and link it into the list.
- */
- HandlerObj = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_ADDRESS_HANDLER);
- if (!HandlerObj)
+ if (Function == ACPI_REG_CONNECT)
{
- Status = AE_NO_MEMORY;
- goto UnlockAndExit;
+ RegionObj->Common.Flags |= AOPOBJ_REG_CONNECTED;
+ }
+ else
+ {
+ RegionObj->Common.Flags &= ~AOPOBJ_REG_CONNECTED;
}
- /* Init handler obj */
-
- HandlerObj->AddressSpace.SpaceId = (UINT8) SpaceId;
- HandlerObj->AddressSpace.HandlerFlags = Flags;
- HandlerObj->AddressSpace.RegionList = NULL;
- HandlerObj->AddressSpace.Node = Node;
- HandlerObj->AddressSpace.Handler = Handler;
- HandlerObj->AddressSpace.Context = Context;
- HandlerObj->AddressSpace.Setup = Setup;
-
- /* Install at head of Device.AddressSpace list */
-
- HandlerObj->AddressSpace.Next = ObjDesc->Device.Handler;
-
- /*
- * The Device object is the first reference on the HandlerObj.
- * Each region that uses the handler adds a reference.
- */
- ObjDesc->Device.Handler = HandlerObj;
-
- /*
- * Walk the namespace finding all of the regions this
- * handler will manage.
- *
- * Start at the device and search the branch toward
- * the leaf nodes until either the leaf is encountered or
- * a device is detected that has an address handler of the
- * same type.
- *
- * In either case, back up and search down the remainder
- * of the branch
- */
- Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node, ACPI_UINT32_MAX,
- ACPI_NS_WALK_UNLOCK, AcpiEvInstallHandler, NULL,
- HandlerObj, NULL);
+Cleanup2:
+ AcpiUtRemoveReference (Args[0]);
-UnlockAndExit:
+Cleanup1:
+ ACPI_FREE (Info);
return_ACPI_STATUS (Status);
}
@@ -1117,24 +699,33 @@ UnlockAndExit:
*
* PARAMETERS: Node - Namespace node for the device
* SpaceId - The address space ID
+ * Function - Passed to _REG: On (1) or Off (0)
*
- * RETURN: Status
+ * RETURN: None
*
* DESCRIPTION: Run all _REG methods for the input Space ID;
* Note: assumes namespace is locked, or system init time.
*
******************************************************************************/
-ACPI_STATUS
+void
AcpiEvExecuteRegMethods (
ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId)
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ UINT32 Function)
{
- ACPI_STATUS Status;
+ ACPI_REG_WALK_INFO Info;
ACPI_FUNCTION_TRACE (EvExecuteRegMethods);
+ Info.SpaceId = SpaceId;
+ Info.Function = Function;
+ Info.RegRunCount = 0;
+
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_NAMES,
+ " Running _REG methods for SpaceId %s\n",
+ AcpiUtGetRegionName (Info.SpaceId)));
/*
* Run all _REG methods for all Operation Regions for this space ID. This
@@ -1142,18 +733,21 @@ AcpiEvExecuteRegMethods (
* regions and _REG methods. (i.e. handlers must be installed for all
* regions of this Space ID before we can run any _REG methods)
*/
- Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node, ACPI_UINT32_MAX,
- ACPI_NS_WALK_UNLOCK, AcpiEvRegRun, NULL,
- &SpaceId, NULL);
+ (void) AcpiNsWalkNamespace (ACPI_TYPE_ANY, Node, ACPI_UINT32_MAX,
+ ACPI_NS_WALK_UNLOCK, AcpiEvRegRun, NULL, &Info, NULL);
/* Special case for EC: handle "orphan" _REG methods with no region */
if (SpaceId == ACPI_ADR_SPACE_EC)
{
- AcpiEvOrphanEcRegMethod ();
+ AcpiEvOrphanEcRegMethod (Node);
}
- return_ACPI_STATUS (Status);
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_NAMES,
+ " Executed %u _REG methods for SpaceId %s\n",
+ Info.RegRunCount, AcpiUtGetRegionName (Info.SpaceId)));
+
+ return_VOID;
}
@@ -1176,11 +770,11 @@ AcpiEvRegRun (
{
ACPI_OPERAND_OBJECT *ObjDesc;
ACPI_NAMESPACE_NODE *Node;
- ACPI_ADR_SPACE_TYPE SpaceId;
ACPI_STATUS Status;
+ ACPI_REG_WALK_INFO *Info;
- SpaceId = *ACPI_CAST_PTR (ACPI_ADR_SPACE_TYPE, Context);
+ Info = ACPI_CAST_PTR (ACPI_REG_WALK_INFO, Context);
/* Convert and validate the device handle */
@@ -1212,14 +806,15 @@ AcpiEvRegRun (
/* Object is a Region */
- if (ObjDesc->Region.SpaceId != SpaceId)
+ if (ObjDesc->Region.SpaceId != Info->SpaceId)
{
/* This region is for a different address space, just ignore it */
return (AE_OK);
}
- Status = AcpiEvExecuteRegMethod (ObjDesc, ACPI_REG_CONNECT);
+ Info->RegRunCount++;
+ Status = AcpiEvExecuteRegMethod (ObjDesc, Info->Function);
return (Status);
}
@@ -1228,7 +823,7 @@ AcpiEvRegRun (
*
* FUNCTION: AcpiEvOrphanEcRegMethod
*
- * PARAMETERS: None
+ * PARAMETERS: EcDeviceNode - Namespace node for an EC device
*
* RETURN: None
*
@@ -1240,41 +835,30 @@ AcpiEvRegRun (
* detected by providing a _REG method object underneath the
* Embedded Controller device."
*
- * To quickly access the EC device, we use the EC_ID that appears
- * within the ECDT. Otherwise, we would need to perform a time-
- * consuming namespace walk, executing _HID methods to find the
- * EC device.
+ * To quickly access the EC device, we use the EcDeviceNode used
+ * during EC handler installation. Otherwise, we would need to
+ * perform a time consuming namespace walk, executing _HID
+ * methods to find the EC device.
+ *
+ * MUTEX: Assumes the namespace is locked
*
******************************************************************************/
static void
AcpiEvOrphanEcRegMethod (
- void)
+ ACPI_NAMESPACE_NODE *EcDeviceNode)
{
- ACPI_TABLE_ECDT *Table;
+ ACPI_HANDLE RegMethod;
+ ACPI_NAMESPACE_NODE *NextNode;
ACPI_STATUS Status;
ACPI_OBJECT_LIST Args;
ACPI_OBJECT Objects[2];
- ACPI_NAMESPACE_NODE *EcDeviceNode;
- ACPI_NAMESPACE_NODE *RegMethod;
- ACPI_NAMESPACE_NODE *NextNode;
ACPI_FUNCTION_TRACE (EvOrphanEcRegMethod);
- /* Get the ECDT (if present in system) */
-
- Status = AcpiGetTable (ACPI_SIG_ECDT, 0,
- ACPI_CAST_INDIRECT_PTR (ACPI_TABLE_HEADER, &Table));
- if (ACPI_FAILURE (Status))
- {
- return_VOID;
- }
-
- /* We need a valid EC_ID string */
-
- if (!(*Table->Id))
+ if (!EcDeviceNode)
{
return_VOID;
}
@@ -1283,23 +867,12 @@ AcpiEvOrphanEcRegMethod (
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
- /* Get a handle to the EC device referenced in the ECDT */
-
- Status = AcpiGetHandle (NULL,
- ACPI_CAST_PTR (char, Table->Id),
- ACPI_CAST_PTR (ACPI_HANDLE, &EcDeviceNode));
- if (ACPI_FAILURE (Status))
- {
- goto Exit;
- }
-
/* Get a handle to a _REG method immediately under the EC device */
- Status = AcpiGetHandle (EcDeviceNode,
- METHOD_NAME__REG, ACPI_CAST_PTR (ACPI_HANDLE, &RegMethod));
+ Status = AcpiGetHandle (EcDeviceNode, METHOD_NAME__REG, &RegMethod);
if (ACPI_FAILURE (Status))
{
- goto Exit;
+ goto Exit; /* There is no _REG method present */
}
/*
@@ -1307,7 +880,7 @@ AcpiEvOrphanEcRegMethod (
* this scope with the Embedded Controller space ID. Otherwise, it
* will already have been executed. Note, this allows for Regions
* with other space IDs to be present; but the code below will then
- * execute the _REG method with the EC space ID argument.
+ * execute the _REG method with the EmbeddedControl SpaceID argument.
*/
NextNode = AcpiNsGetNextNode (EcDeviceNode, NULL);
while (NextNode)
@@ -1316,12 +889,13 @@ AcpiEvOrphanEcRegMethod (
(NextNode->Object) &&
(NextNode->Object->Region.SpaceId == ACPI_ADR_SPACE_EC))
{
- goto Exit; /* Do not execute _REG */
+ goto Exit; /* Do not execute the _REG */
}
+
NextNode = AcpiNsGetNextNode (EcDeviceNode, NextNode);
}
- /* Evaluate the _REG(EC,Connect) method */
+ /* Evaluate the _REG(EmbeddedControl,Connect) method */
Args.Count = 2;
Args.Pointer = Objects;
diff --git a/usr/src/uts/intel/io/acpica/events/evrgnini.c b/usr/src/uts/intel/io/acpica/events/evrgnini.c
index 496e40e1aa..a9e6be0391 100644
--- a/usr/src/uts/intel/io/acpica/events/evrgnini.c
+++ b/usr/src/uts/intel/io/acpica/events/evrgnini.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EVRGNINI_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acevents.h"
@@ -251,9 +248,9 @@ AcpiEvPciConfigRegionSetup (
/* Install a handler for this PCI root bridge */
Status = AcpiInstallAddressSpaceHandler (
- (ACPI_HANDLE) PciRootNode,
- ACPI_ADR_SPACE_PCI_CONFIG,
- ACPI_DEFAULT_HANDLER, NULL, NULL);
+ (ACPI_HANDLE) PciRootNode,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ ACPI_DEFAULT_HANDLER, NULL, NULL);
if (ACPI_FAILURE (Status))
{
if (Status == AE_SAME_HANDLER)
@@ -327,7 +324,7 @@ AcpiEvPciConfigRegionSetup (
* contained in the parent's scope.
*/
Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR,
- PciDeviceNode, &PciValue);
+ PciDeviceNode, &PciValue);
/*
* The default is zero, and since the allocation above zeroed the data,
@@ -342,7 +339,7 @@ AcpiEvPciConfigRegionSetup (
/* The PCI segment number comes from the _SEG method */
Status = AcpiUtEvaluateNumericObject (METHOD_NAME__SEG,
- PciRootNode, &PciValue);
+ PciRootNode, &PciValue);
if (ACPI_SUCCESS (Status))
{
PciId->Segment = ACPI_LOWORD (PciValue);
@@ -351,7 +348,7 @@ AcpiEvPciConfigRegionSetup (
/* The PCI bus number comes from the _BBN method */
Status = AcpiUtEvaluateNumericObject (METHOD_NAME__BBN,
- PciRootNode, &PciValue);
+ PciRootNode, &PciValue);
if (ACPI_SUCCESS (Status))
{
PciId->Bus = ACPI_LOWORD (PciValue);
@@ -389,8 +386,8 @@ AcpiEvIsPciRootBridge (
ACPI_NAMESPACE_NODE *Node)
{
ACPI_STATUS Status;
- ACPI_DEVICE_ID *Hid;
- ACPI_DEVICE_ID_LIST *Cid;
+ ACPI_PNP_DEVICE_ID *Hid;
+ ACPI_PNP_DEVICE_ID_LIST *Cid;
UINT32 i;
BOOLEAN Match;
@@ -570,9 +567,6 @@ AcpiEvInitializeRegion (
ACPI_ADR_SPACE_TYPE SpaceId;
ACPI_NAMESPACE_NODE *Node;
ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *MethodNode;
- ACPI_NAME *RegNamePtr = (ACPI_NAME *) METHOD_NAME__REG;
- ACPI_OPERAND_OBJECT *RegionObj2;
ACPI_FUNCTION_TRACE_U32 (EvInitializeRegion, AcpiNsLocked);
@@ -588,39 +582,14 @@ AcpiEvInitializeRegion (
return_ACPI_STATUS (AE_OK);
}
- RegionObj2 = AcpiNsGetSecondaryObject (RegionObj);
- if (!RegionObj2)
- {
- return_ACPI_STATUS (AE_NOT_EXIST);
- }
+ RegionObj->Common.Flags |= AOPOBJ_OBJECT_INITIALIZED;
Node = RegionObj->Region.Node->Parent;
SpaceId = RegionObj->Region.SpaceId;
- /* Setup defaults */
-
- RegionObj->Region.Handler = NULL;
- RegionObj2->Extra.Method_REG = NULL;
- RegionObj->Common.Flags &= ~(AOPOBJ_SETUP_COMPLETE);
- RegionObj->Common.Flags |= AOPOBJ_OBJECT_INITIALIZED;
-
- /* Find any "_REG" method associated with this region definition */
-
- Status = AcpiNsSearchOneScope (
- *RegNamePtr, Node, ACPI_TYPE_METHOD, &MethodNode);
- if (ACPI_SUCCESS (Status))
- {
- /*
- * The _REG method is optional and there can be only one per region
- * definition. This will be executed when the handler is attached
- * or removed
- */
- RegionObj2->Extra.Method_REG = MethodNode;
- }
-
/*
* The following loop depends upon the root Node having no parent
- * ie: AcpiGbl_RootNode->ParentEntry being set to NULL
+ * ie: AcpiGbl_RootNode->Parent being set to NULL
*/
while (Node)
{
@@ -635,18 +604,10 @@ AcpiEvInitializeRegion (
switch (Node->Type)
{
case ACPI_TYPE_DEVICE:
-
- HandlerObj = ObjDesc->Device.Handler;
- break;
-
case ACPI_TYPE_PROCESSOR:
-
- HandlerObj = ObjDesc->Processor.Handler;
- break;
-
case ACPI_TYPE_THERMAL:
- HandlerObj = ObjDesc->ThermalZone.Handler;
+ HandlerObj = ObjDesc->CommonNotify.Handler;
break;
case ACPI_TYPE_METHOD:
@@ -664,55 +625,49 @@ AcpiEvInitializeRegion (
break;
default:
+
/* Ignore other objects */
+
break;
}
- while (HandlerObj)
+ HandlerObj = AcpiEvFindRegionHandler (SpaceId, HandlerObj);
+ if (HandlerObj)
{
- /* Is this handler of the correct type? */
-
- if (HandlerObj->AddressSpace.SpaceId == SpaceId)
- {
- /* Found correct handler */
+ /* Found correct handler */
- ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
- "Found handler %p for region %p in obj %p\n",
- HandlerObj, RegionObj, ObjDesc));
+ ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
+ "Found handler %p for region %p in obj %p\n",
+ HandlerObj, RegionObj, ObjDesc));
- Status = AcpiEvAttachRegion (HandlerObj, RegionObj,
- AcpiNsLocked);
+ Status = AcpiEvAttachRegion (HandlerObj, RegionObj,
+ AcpiNsLocked);
- /*
- * Tell all users that this region is usable by
- * running the _REG method
- */
- if (AcpiNsLocked)
+ /*
+ * Tell all users that this region is usable by
+ * running the _REG method
+ */
+ if (AcpiNsLocked)
+ {
+ Status = AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
{
- Status = AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
+ return_ACPI_STATUS (Status);
}
+ }
- Status = AcpiEvExecuteRegMethod (RegionObj, ACPI_REG_CONNECT);
+ Status = AcpiEvExecuteRegMethod (RegionObj, ACPI_REG_CONNECT);
- if (AcpiNsLocked)
+ if (AcpiNsLocked)
+ {
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
{
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
+ return_ACPI_STATUS (Status);
}
-
- return_ACPI_STATUS (AE_OK);
}
- /* Try next handler in the list */
-
- HandlerObj = HandlerObj->AddressSpace.Next;
+ return_ACPI_STATUS (AE_OK);
}
}
@@ -729,4 +684,3 @@ AcpiEvInitializeRegion (
return_ACPI_STATUS (AE_NOT_EXIST);
}
-
diff --git a/usr/src/uts/intel/io/acpica/events/evsci.c b/usr/src/uts/intel/io/acpica/events/evsci.c
index 157c184f2b..c35cba702d 100644
--- a/usr/src/uts/intel/io/acpica/events/evsci.c
+++ b/usr/src/uts/intel/io/acpica/events/evsci.c
@@ -6,7 +6,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,6 +50,8 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evsci")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+
/* Local prototypes */
static UINT32 ACPI_SYSTEM_XFACE
@@ -59,6 +61,57 @@ AcpiEvSciXruptHandler (
/*******************************************************************************
*
+ * FUNCTION: AcpiEvSciDispatch
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status code indicates whether interrupt was handled.
+ *
+ * DESCRIPTION: Dispatch the SCI to all host-installed SCI handlers.
+ *
+ ******************************************************************************/
+
+UINT32
+AcpiEvSciDispatch (
+ void)
+{
+ ACPI_SCI_HANDLER_INFO *SciHandler;
+ ACPI_CPU_FLAGS Flags;
+ UINT32 IntStatus = ACPI_INTERRUPT_NOT_HANDLED;
+
+
+ ACPI_FUNCTION_NAME (EvSciDispatch);
+
+
+ /* Are there any host-installed SCI handlers? */
+
+ if (!AcpiGbl_SciHandlerList)
+ {
+ return (IntStatus);
+ }
+
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
+
+ /* Invoke all host-installed SCI handlers */
+
+ SciHandler = AcpiGbl_SciHandlerList;
+ while (SciHandler)
+ {
+ /* Invoke the installed handler (at interrupt level) */
+
+ IntStatus |= SciHandler->Address (
+ SciHandler->Context);
+
+ SciHandler = SciHandler->Next;
+ }
+
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ return (IntStatus);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiEvSciXruptHandler
*
* PARAMETERS: Context - Calling Context
@@ -82,7 +135,7 @@ AcpiEvSciXruptHandler (
/*
- * We are guaranteed by the ACPI CA initialization/shutdown code that
+ * We are guaranteed by the ACPICA initialization/shutdown code that
* if this interrupt handler is installed, ACPI is enabled.
*/
@@ -98,6 +151,10 @@ AcpiEvSciXruptHandler (
*/
InterruptHandled |= AcpiEvGpeDetect (GpeXruptList);
+ /* Invoke all host-installed SCI handlers */
+
+ InterruptHandled |= AcpiEvSciDispatch ();
+
AcpiSciCount++;
return_UINT32 (InterruptHandled);
}
@@ -127,14 +184,13 @@ AcpiEvGpeXruptHandler (
/*
- * We are guaranteed by the ACPI CA initialization/shutdown code that
+ * We are guaranteed by the ACPICA initialization/shutdown code that
* if this interrupt handler is installed, ACPI is enabled.
*/
/* GPEs: Check for and dispatch any GPEs that have occurred */
InterruptHandled |= AcpiEvGpeDetect (GpeXruptList);
-
return_UINT32 (InterruptHandled);
}
@@ -162,22 +218,22 @@ AcpiEvInstallSciHandler (
Status = AcpiOsInstallInterruptHandler ((UINT32) AcpiGbl_FADT.SciInterrupt,
- AcpiEvSciXruptHandler, AcpiGbl_GpeXruptListHead);
+ AcpiEvSciXruptHandler, AcpiGbl_GpeXruptListHead);
return_ACPI_STATUS (Status);
}
/******************************************************************************
*
- * FUNCTION: AcpiEvRemoveSciHandler
+ * FUNCTION: AcpiEvRemoveAllSciHandlers
*
* PARAMETERS: none
*
- * RETURN: E_OK if handler uninstalled OK, E_ERROR if handler was not
+ * RETURN: AE_OK if handler uninstalled, AE_ERROR if handler was not
* installed to begin with
*
* DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be
- * taken.
+ * taken. Remove all host-installed SCI handlers.
*
* Note: It doesn't seem important to disable all events or set the event
* enable registers to their original values. The OS should disable
@@ -187,21 +243,40 @@ AcpiEvInstallSciHandler (
******************************************************************************/
ACPI_STATUS
-AcpiEvRemoveSciHandler (
+AcpiEvRemoveAllSciHandlers (
void)
{
+ ACPI_SCI_HANDLER_INFO *SciHandler;
+ ACPI_CPU_FLAGS Flags;
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (EvRemoveSciHandler);
+ ACPI_FUNCTION_TRACE (EvRemoveAllSciHandlers);
/* Just let the OS remove the handler and disable the level */
Status = AcpiOsRemoveInterruptHandler ((UINT32) AcpiGbl_FADT.SciInterrupt,
- AcpiEvSciXruptHandler);
+ AcpiEvSciXruptHandler);
+
+ if (!AcpiGbl_SciHandlerList)
+ {
+ return (Status);
+ }
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
+
+ /* Free all host-installed SCI handlers */
+
+ while (AcpiGbl_SciHandlerList)
+ {
+ SciHandler = AcpiGbl_SciHandlerList;
+ AcpiGbl_SciHandlerList = SciHandler->Next;
+ ACPI_FREE (SciHandler);
+ }
+
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
return_ACPI_STATUS (Status);
}
-
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evxface.c b/usr/src/uts/intel/io/acpica/events/evxface.c
index 8a903a6b47..edace79649 100644
--- a/usr/src/uts/intel/io/acpica/events/evxface.c
+++ b/usr/src/uts/intel/io/acpica/events/evxface.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EVXFACE_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -53,150 +52,386 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME ("evxface")
+#if (!ACPI_REDUCED_HARDWARE)
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiEvInstallGpeHandler (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber,
+ UINT32 Type,
+ BOOLEAN IsRawHandler,
+ ACPI_GPE_HANDLER Address,
+ void *Context);
+
+#endif
+
/*******************************************************************************
*
- * FUNCTION: AcpiInstallExceptionHandler
+ * FUNCTION: AcpiInstallNotifyHandler
*
- * PARAMETERS: Handler - Pointer to the handler function for the
- * event
+ * PARAMETERS: Device - The device for which notifies will be handled
+ * HandlerType - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: System Handler (00-7F)
+ * ACPI_DEVICE_NOTIFY: Device Handler (80-FF)
+ * ACPI_ALL_NOTIFY: Both System and Device
+ * Handler - Address of the handler
+ * Context - Value passed to the handler on each GPE
*
* RETURN: Status
*
- * DESCRIPTION: Saves the pointer to the handler function
+ * DESCRIPTION: Install a handler for notifications on an ACPI Device,
+ * ThermalZone, or Processor object.
+ *
+ * NOTES: The Root namespace object may have only one handler for each
+ * type of notify (System/Device). Device/Thermal/Processor objects
+ * may have one device notify handler, and multiple system notify
+ * handlers.
*
******************************************************************************/
ACPI_STATUS
-AcpiInstallExceptionHandler (
- ACPI_EXCEPTION_HANDLER Handler)
+AcpiInstallNotifyHandler (
+ ACPI_HANDLE Device,
+ UINT32 HandlerType,
+ ACPI_NOTIFY_HANDLER Handler,
+ void *Context)
{
+ ACPI_NAMESPACE_NODE *Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Device);
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *HandlerObj;
ACPI_STATUS Status;
+ UINT32 i;
- ACPI_FUNCTION_TRACE (AcpiInstallExceptionHandler);
+ ACPI_FUNCTION_TRACE (AcpiInstallNotifyHandler);
- Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
+ /* Parameter validation */
+
+ if ((!Device) || (!Handler) || (!HandlerType) ||
+ (HandlerType > ACPI_MAX_NOTIFY_HANDLER_TYPE))
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
- /* Don't allow two handlers. */
+ /*
+ * Root Object:
+ * Registering a notify handler on the root object indicates that the
+ * caller wishes to receive notifications for all objects. Note that
+ * only one global handler can be registered per notify type.
+ * Ensure that a handler is not already installed.
+ */
+ if (Device == ACPI_ROOT_OBJECT)
+ {
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ if (HandlerType & (i+1))
+ {
+ if (AcpiGbl_GlobalNotify[i].Handler)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto UnlockAndExit;
+ }
+
+ AcpiGbl_GlobalNotify[i].Handler = Handler;
+ AcpiGbl_GlobalNotify[i].Context = Context;
+ }
+ }
- if (AcpiGbl_ExceptionHandler)
+ goto UnlockAndExit; /* Global notify handler installed, all done */
+ }
+
+ /*
+ * All Other Objects:
+ * Caller will only receive notifications specific to the target
+ * object. Note that only certain object types are allowed to
+ * receive notifications.
+ */
+
+ /* Are Notifies allowed on this object? */
+
+ if (!AcpiEvIsNotifyObject (Node))
{
- Status = AE_ALREADY_EXISTS;
- goto Cleanup;
+ Status = AE_TYPE;
+ goto UnlockAndExit;
}
- /* Install the handler */
+ /* Check for an existing internal object, might not exist */
- AcpiGbl_ExceptionHandler = Handler;
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (!ObjDesc)
+ {
+ /* Create a new object */
-Cleanup:
- (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+ ObjDesc = AcpiUtCreateInternalObject (Node->Type);
+ if (!ObjDesc)
+ {
+ Status = AE_NO_MEMORY;
+ goto UnlockAndExit;
+ }
+
+ /* Attach new object to the Node, remove local reference */
+
+ Status = AcpiNsAttachObject (Device, ObjDesc, Node->Type);
+ AcpiUtRemoveReference (ObjDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ goto UnlockAndExit;
+ }
+ }
+
+ /* Ensure that the handler is not already installed in the lists */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ if (HandlerType & (i+1))
+ {
+ HandlerObj = ObjDesc->CommonNotify.NotifyList[i];
+ while (HandlerObj)
+ {
+ if (HandlerObj->Notify.Handler == Handler)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto UnlockAndExit;
+ }
+
+ HandlerObj = HandlerObj->Notify.Next[i];
+ }
+ }
+ }
+
+ /* Create and populate a new notify handler object */
+
+ HandlerObj = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_NOTIFY);
+ if (!HandlerObj)
+ {
+ Status = AE_NO_MEMORY;
+ goto UnlockAndExit;
+ }
+
+ HandlerObj->Notify.Node = Node;
+ HandlerObj->Notify.HandlerType = HandlerType;
+ HandlerObj->Notify.Handler = Handler;
+ HandlerObj->Notify.Context = Context;
+
+ /* Install the handler at the list head(s) */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ if (HandlerType & (i+1))
+ {
+ HandlerObj->Notify.Next[i] =
+ ObjDesc->CommonNotify.NotifyList[i];
+
+ ObjDesc->CommonNotify.NotifyList[i] = HandlerObj;
+ }
+ }
+
+ /* Add an extra reference if handler was installed in both lists */
+
+ if (HandlerType == ACPI_ALL_NOTIFY)
+ {
+ AcpiUtAddReference (HandlerObj);
+ }
+
+
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiInstallExceptionHandler)
+ACPI_EXPORT_SYMBOL (AcpiInstallNotifyHandler)
/*******************************************************************************
*
- * FUNCTION: AcpiInstallGlobalEventHandler
+ * FUNCTION: AcpiRemoveNotifyHandler
*
- * PARAMETERS: Handler - Pointer to the global event handler function
- * Context - Value passed to the handler on each event
+ * PARAMETERS: Device - The device for which the handler is installed
+ * HandlerType - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: System Handler (00-7F)
+ * ACPI_DEVICE_NOTIFY: Device Handler (80-FF)
+ * ACPI_ALL_NOTIFY: Both System and Device
+ * Handler - Address of the handler
*
* RETURN: Status
*
- * DESCRIPTION: Saves the pointer to the handler function. The global handler
- * is invoked upon each incoming GPE and Fixed Event. It is
- * invoked at interrupt level at the time of the event dispatch.
- * Can be used to update event counters, etc.
+ * DESCRIPTION: Remove a handler for notifies on an ACPI device
*
******************************************************************************/
ACPI_STATUS
-AcpiInstallGlobalEventHandler (
- ACPI_GBL_EVENT_HANDLER Handler,
- void *Context)
+AcpiRemoveNotifyHandler (
+ ACPI_HANDLE Device,
+ UINT32 HandlerType,
+ ACPI_NOTIFY_HANDLER Handler)
{
- ACPI_STATUS Status;
+ ACPI_NAMESPACE_NODE *Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Device);
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *HandlerObj;
+ ACPI_OPERAND_OBJECT *PreviousHandlerObj;
+ ACPI_STATUS Status = AE_OK;
+ UINT32 i;
- ACPI_FUNCTION_TRACE (AcpiInstallGlobalEventHandler);
+ ACPI_FUNCTION_TRACE (AcpiRemoveNotifyHandler);
/* Parameter validation */
- if (!Handler)
+ if ((!Device) || (!Handler) || (!HandlerType) ||
+ (HandlerType > ACPI_MAX_NOTIFY_HANDLER_TYPE))
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
- if (ACPI_FAILURE (Status))
+ /* Root Object. Global handlers are removed here */
+
+ if (Device == ACPI_ROOT_OBJECT)
{
- return_ACPI_STATUS (Status);
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ if (HandlerType & (i+1))
+ {
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ if (!AcpiGbl_GlobalNotify[i].Handler ||
+ (AcpiGbl_GlobalNotify[i].Handler != Handler))
+ {
+ Status = AE_NOT_EXIST;
+ goto UnlockAndExit;
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Removing global notify handler\n"));
+
+ AcpiGbl_GlobalNotify[i].Handler = NULL;
+ AcpiGbl_GlobalNotify[i].Context = NULL;
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+ /* Make sure all deferred notify tasks are completed */
+
+ AcpiOsWaitEventsComplete ();
+ }
+ }
+
+ return_ACPI_STATUS (AE_OK);
}
- /* Don't allow two handlers. */
+ /* All other objects: Are Notifies allowed on this object? */
- if (AcpiGbl_GlobalEventHandler)
+ if (!AcpiEvIsNotifyObject (Node))
{
- Status = AE_ALREADY_EXISTS;
- goto Cleanup;
+ return_ACPI_STATUS (AE_TYPE);
}
- AcpiGbl_GlobalEventHandler = Handler;
- AcpiGbl_GlobalEventHandlerContext = Context;
+ /* Must have an existing internal object */
+ ObjDesc = AcpiNsGetAttachedObject (Node);
+ if (!ObjDesc)
+ {
+ return_ACPI_STATUS (AE_NOT_EXIST);
+ }
-Cleanup:
- (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+ /* Internal object exists. Find the handler and remove it */
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ if (HandlerType & (i+1))
+ {
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ HandlerObj = ObjDesc->CommonNotify.NotifyList[i];
+ PreviousHandlerObj = NULL;
+
+ /* Attempt to find the handler in the handler list */
+
+ while (HandlerObj &&
+ (HandlerObj->Notify.Handler != Handler))
+ {
+ PreviousHandlerObj = HandlerObj;
+ HandlerObj = HandlerObj->Notify.Next[i];
+ }
+
+ if (!HandlerObj)
+ {
+ Status = AE_NOT_EXIST;
+ goto UnlockAndExit;
+ }
+
+ /* Remove the handler object from the list */
+
+ if (PreviousHandlerObj) /* Handler is not at the list head */
+ {
+ PreviousHandlerObj->Notify.Next[i] =
+ HandlerObj->Notify.Next[i];
+ }
+ else /* Handler is at the list head */
+ {
+ ObjDesc->CommonNotify.NotifyList[i] =
+ HandlerObj->Notify.Next[i];
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+ /* Make sure all deferred notify tasks are completed */
+
+ AcpiOsWaitEventsComplete ();
+ AcpiUtRemoveReference (HandlerObj);
+ }
+ }
+
+ return_ACPI_STATUS (Status);
+
+
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiInstallGlobalEventHandler)
+ACPI_EXPORT_SYMBOL (AcpiRemoveNotifyHandler)
/*******************************************************************************
*
- * FUNCTION: AcpiInstallFixedEventHandler
+ * FUNCTION: AcpiInstallExceptionHandler
*
- * PARAMETERS: Event - Event type to enable.
- * Handler - Pointer to the handler function for the
+ * PARAMETERS: Handler - Pointer to the handler function for the
* event
- * Context - Value passed to the handler on each GPE
*
* RETURN: Status
*
- * DESCRIPTION: Saves the pointer to the handler function and then enables the
- * event.
+ * DESCRIPTION: Saves the pointer to the handler function
*
******************************************************************************/
ACPI_STATUS
-AcpiInstallFixedEventHandler (
- UINT32 Event,
- ACPI_EVENT_HANDLER Handler,
- void *Context)
+AcpiInstallExceptionHandler (
+ ACPI_EXCEPTION_HANDLER Handler)
{
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiInstallFixedEventHandler);
-
-
- /* Parameter validation */
+ ACPI_FUNCTION_TRACE (AcpiInstallExceptionHandler);
- if (Event > ACPI_EVENT_MAX)
- {
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
if (ACPI_FAILURE (Status))
@@ -206,479 +441,427 @@ AcpiInstallFixedEventHandler (
/* Don't allow two handlers. */
- if (NULL != AcpiGbl_FixedEventHandlers[Event].Handler)
+ if (AcpiGbl_ExceptionHandler)
{
Status = AE_ALREADY_EXISTS;
goto Cleanup;
}
- /* Install the handler before enabling the event */
-
- AcpiGbl_FixedEventHandlers[Event].Handler = Handler;
- AcpiGbl_FixedEventHandlers[Event].Context = Context;
-
- Status = AcpiEnableEvent (Event, 0);
- if (ACPI_FAILURE (Status))
- {
- ACPI_WARNING ((AE_INFO, "Could not enable fixed event 0x%X", Event));
-
- /* Remove the handler */
-
- AcpiGbl_FixedEventHandlers[Event].Handler = NULL;
- AcpiGbl_FixedEventHandlers[Event].Context = NULL;
- }
- else
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Enabled fixed event %X, Handler=%p\n", Event, Handler));
- }
+ /* Install the handler */
+ AcpiGbl_ExceptionHandler = Handler;
Cleanup:
(void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiInstallFixedEventHandler)
+ACPI_EXPORT_SYMBOL (AcpiInstallExceptionHandler)
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
- * FUNCTION: AcpiRemoveFixedEventHandler
+ * FUNCTION: AcpiInstallSciHandler
*
- * PARAMETERS: Event - Event type to disable.
- * Handler - Address of the handler
+ * PARAMETERS: Address - Address of the handler
+ * Context - Value passed to the handler on each SCI
*
* RETURN: Status
*
- * DESCRIPTION: Disables the event and unregisters the event handler.
+ * DESCRIPTION: Install a handler for a System Control Interrupt.
*
******************************************************************************/
ACPI_STATUS
-AcpiRemoveFixedEventHandler (
- UINT32 Event,
- ACPI_EVENT_HANDLER Handler)
+AcpiInstallSciHandler (
+ ACPI_SCI_HANDLER Address,
+ void *Context)
{
- ACPI_STATUS Status = AE_OK;
-
+ ACPI_SCI_HANDLER_INFO *NewSciHandler;
+ ACPI_SCI_HANDLER_INFO *SciHandler;
+ ACPI_CPU_FLAGS Flags;
+ ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiRemoveFixedEventHandler);
+ ACPI_FUNCTION_TRACE (AcpiInstallSciHandler);
- /* Parameter validation */
- if (Event > ACPI_EVENT_MAX)
+ if (!Address)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
+ /* Allocate and init a handler object */
+
+ NewSciHandler = ACPI_ALLOCATE (sizeof (ACPI_SCI_HANDLER_INFO));
+ if (!NewSciHandler)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ NewSciHandler->Address = Address;
+ NewSciHandler->Context = Context;
+
Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ goto Exit;
}
- /* Disable the event before removing the handler */
-
- Status = AcpiDisableEvent (Event, 0);
+ /* Lock list during installation */
- /* Always Remove the handler */
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
+ SciHandler = AcpiGbl_SciHandlerList;
- AcpiGbl_FixedEventHandlers[Event].Handler = NULL;
- AcpiGbl_FixedEventHandlers[Event].Context = NULL;
+ /* Ensure handler does not already exist */
- if (ACPI_FAILURE (Status))
- {
- ACPI_WARNING ((AE_INFO,
- "Could not write to fixed event enable register 0x%X", Event));
- }
- else
+ while (SciHandler)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Disabled fixed event %X\n", Event));
+ if (Address == SciHandler->Address)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto UnlockAndExit;
+ }
+
+ SciHandler = SciHandler->Next;
}
+ /* Install the new handler into the global list (at head) */
+
+ NewSciHandler->Next = AcpiGbl_SciHandlerList;
+ AcpiGbl_SciHandlerList = NewSciHandler;
+
+
+UnlockAndExit:
+
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
(void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+
+Exit:
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_FREE (NewSciHandler);
+ }
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiRemoveFixedEventHandler)
+ACPI_EXPORT_SYMBOL (AcpiInstallSciHandler)
/*******************************************************************************
*
- * FUNCTION: AcpiInstallNotifyHandler
+ * FUNCTION: AcpiRemoveSciHandler
*
- * PARAMETERS: Device - The device for which notifies will be handled
- * HandlerType - The type of handler:
- * ACPI_SYSTEM_NOTIFY: SystemHandler (00-7f)
- * ACPI_DEVICE_NOTIFY: DriverHandler (80-ff)
- * ACPI_ALL_NOTIFY: both system and device
- * Handler - Address of the handler
- * Context - Value passed to the handler on each GPE
+ * PARAMETERS: Address - Address of the handler
*
* RETURN: Status
*
- * DESCRIPTION: Install a handler for notifies on an ACPI device
+ * DESCRIPTION: Remove a handler for a System Control Interrupt.
*
******************************************************************************/
ACPI_STATUS
-AcpiInstallNotifyHandler (
- ACPI_HANDLE Device,
- UINT32 HandlerType,
- ACPI_NOTIFY_HANDLER Handler,
- void *Context)
+AcpiRemoveSciHandler (
+ ACPI_SCI_HANDLER Address)
{
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT *NotifyObj;
- ACPI_NAMESPACE_NODE *Node;
+ ACPI_SCI_HANDLER_INFO *PrevSciHandler;
+ ACPI_SCI_HANDLER_INFO *NextSciHandler;
+ ACPI_CPU_FLAGS Flags;
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiInstallNotifyHandler);
-
+ ACPI_FUNCTION_TRACE (AcpiRemoveSciHandler);
- /* Parameter validation */
- if ((!Device) ||
- (!Handler) ||
- (HandlerType > ACPI_MAX_NOTIFY_HANDLER_TYPE))
+ if (!Address)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
- /* Convert and validate the device handle */
+ /* Remove the SCI handler with lock */
- Node = AcpiNsValidateHandle (Device);
- if (!Node)
- {
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
- }
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
- /*
- * Root Object:
- * Registering a notify handler on the root object indicates that the
- * caller wishes to receive notifications for all objects. Note that
- * only one <external> global handler can be regsitered (per notify type).
- */
- if (Device == ACPI_ROOT_OBJECT)
+ PrevSciHandler = NULL;
+ NextSciHandler = AcpiGbl_SciHandlerList;
+ while (NextSciHandler)
{
- /* Make sure the handler is not already installed */
-
- if (((HandlerType & ACPI_SYSTEM_NOTIFY) &&
- AcpiGbl_SystemNotify.Handler) ||
- ((HandlerType & ACPI_DEVICE_NOTIFY) &&
- AcpiGbl_DeviceNotify.Handler))
+ if (NextSciHandler->Address == Address)
{
- Status = AE_ALREADY_EXISTS;
- goto UnlockAndExit;
- }
+ /* Unlink and free the SCI handler info block */
- if (HandlerType & ACPI_SYSTEM_NOTIFY)
- {
- AcpiGbl_SystemNotify.Node = Node;
- AcpiGbl_SystemNotify.Handler = Handler;
- AcpiGbl_SystemNotify.Context = Context;
- }
+ if (PrevSciHandler)
+ {
+ PrevSciHandler->Next = NextSciHandler->Next;
+ }
+ else
+ {
+ AcpiGbl_SciHandlerList = NextSciHandler->Next;
+ }
- if (HandlerType & ACPI_DEVICE_NOTIFY)
- {
- AcpiGbl_DeviceNotify.Node = Node;
- AcpiGbl_DeviceNotify.Handler = Handler;
- AcpiGbl_DeviceNotify.Context = Context;
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ ACPI_FREE (NextSciHandler);
+ goto UnlockAndExit;
}
- /* Global notify handler installed */
+ PrevSciHandler = NextSciHandler;
+ NextSciHandler = NextSciHandler->Next;
}
- /*
- * All Other Objects:
- * Caller will only receive notifications specific to the target object.
- * Note that only certain object types can receive notifications.
- */
- else
- {
- /* Notifies allowed on this object? */
-
- if (!AcpiEvIsNotifyObject (Node))
- {
- Status = AE_TYPE;
- goto UnlockAndExit;
- }
-
- /* Check for an existing internal object */
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ Status = AE_NOT_EXIST;
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (ObjDesc)
- {
- /* Object exists - make sure there's no handler */
- if (((HandlerType & ACPI_SYSTEM_NOTIFY) &&
- ObjDesc->CommonNotify.SystemNotify) ||
- ((HandlerType & ACPI_DEVICE_NOTIFY) &&
- ObjDesc->CommonNotify.DeviceNotify))
- {
- Status = AE_ALREADY_EXISTS;
- goto UnlockAndExit;
- }
- }
- else
- {
- /* Create a new object */
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+ return_ACPI_STATUS (Status);
+}
- ObjDesc = AcpiUtCreateInternalObject (Node->Type);
- if (!ObjDesc)
- {
- Status = AE_NO_MEMORY;
- goto UnlockAndExit;
- }
+ACPI_EXPORT_SYMBOL (AcpiRemoveSciHandler)
- /* Attach new object to the Node */
- Status = AcpiNsAttachObject (Device, ObjDesc, Node->Type);
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiInstallGlobalEventHandler
+ *
+ * PARAMETERS: Handler - Pointer to the global event handler function
+ * Context - Value passed to the handler on each event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function. The global handler
+ * is invoked upon each incoming GPE and Fixed Event. It is
+ * invoked at interrupt level at the time of the event dispatch.
+ * Can be used to update event counters, etc.
+ *
+ ******************************************************************************/
- /* Remove local reference to the object */
+ACPI_STATUS
+AcpiInstallGlobalEventHandler (
+ ACPI_GBL_EVENT_HANDLER Handler,
+ void *Context)
+{
+ ACPI_STATUS Status;
- AcpiUtRemoveReference (ObjDesc);
- if (ACPI_FAILURE (Status))
- {
- goto UnlockAndExit;
- }
- }
- /* Install the handler */
+ ACPI_FUNCTION_TRACE (AcpiInstallGlobalEventHandler);
- NotifyObj = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_NOTIFY);
- if (!NotifyObj)
- {
- Status = AE_NO_MEMORY;
- goto UnlockAndExit;
- }
- NotifyObj->Notify.Node = Node;
- NotifyObj->Notify.Handler = Handler;
- NotifyObj->Notify.Context = Context;
+ /* Parameter validation */
- if (HandlerType & ACPI_SYSTEM_NOTIFY)
- {
- ObjDesc->CommonNotify.SystemNotify = NotifyObj;
- }
+ if (!Handler)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
- if (HandlerType & ACPI_DEVICE_NOTIFY)
- {
- ObjDesc->CommonNotify.DeviceNotify = NotifyObj;
- }
+ Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
- if (HandlerType == ACPI_ALL_NOTIFY)
- {
- /* Extra ref if installed in both */
+ /* Don't allow two handlers. */
- AcpiUtAddReference (NotifyObj);
- }
+ if (AcpiGbl_GlobalEventHandler)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto Cleanup;
}
+ AcpiGbl_GlobalEventHandler = Handler;
+ AcpiGbl_GlobalEventHandlerContext = Context;
-UnlockAndExit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+Cleanup:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiInstallNotifyHandler)
+ACPI_EXPORT_SYMBOL (AcpiInstallGlobalEventHandler)
/*******************************************************************************
*
- * FUNCTION: AcpiRemoveNotifyHandler
+ * FUNCTION: AcpiInstallFixedEventHandler
*
- * PARAMETERS: Device - The device for which notifies will be handled
- * HandlerType - The type of handler:
- * ACPI_SYSTEM_NOTIFY: SystemHandler (00-7f)
- * ACPI_DEVICE_NOTIFY: DriverHandler (80-ff)
- * ACPI_ALL_NOTIFY: both system and device
- * Handler - Address of the handler
+ * PARAMETERS: Event - Event type to enable.
+ * Handler - Pointer to the handler function for the
+ * event
+ * Context - Value passed to the handler on each GPE
*
* RETURN: Status
*
- * DESCRIPTION: Remove a handler for notifies on an ACPI device
+ * DESCRIPTION: Saves the pointer to the handler function and then enables the
+ * event.
*
******************************************************************************/
ACPI_STATUS
-AcpiRemoveNotifyHandler (
- ACPI_HANDLE Device,
- UINT32 HandlerType,
- ACPI_NOTIFY_HANDLER Handler)
+AcpiInstallFixedEventHandler (
+ UINT32 Event,
+ ACPI_EVENT_HANDLER Handler,
+ void *Context)
{
- ACPI_OPERAND_OBJECT *NotifyObj;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_NAMESPACE_NODE *Node;
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiRemoveNotifyHandler);
+ ACPI_FUNCTION_TRACE (AcpiInstallFixedEventHandler);
/* Parameter validation */
- if ((!Device) ||
- (!Handler) ||
- (HandlerType > ACPI_MAX_NOTIFY_HANDLER_TYPE))
+ if (Event > ACPI_EVENT_MAX)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
- /* Convert and validate the device handle */
+ /* Do not allow multiple handlers */
- Node = AcpiNsValidateHandle (Device);
- if (!Node)
+ if (AcpiGbl_FixedEventHandlers[Event].Handler)
{
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
+ Status = AE_ALREADY_EXISTS;
+ goto Cleanup;
}
- /* Root Object */
+ /* Install the handler before enabling the event */
- if (Device == ACPI_ROOT_OBJECT)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Removing notify handler for namespace root object\n"));
+ AcpiGbl_FixedEventHandlers[Event].Handler = Handler;
+ AcpiGbl_FixedEventHandlers[Event].Context = Context;
- if (((HandlerType & ACPI_SYSTEM_NOTIFY) &&
- !AcpiGbl_SystemNotify.Handler) ||
- ((HandlerType & ACPI_DEVICE_NOTIFY) &&
- !AcpiGbl_DeviceNotify.Handler))
- {
- Status = AE_NOT_EXIST;
- goto UnlockAndExit;
- }
+ Status = AcpiEnableEvent (Event, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_WARNING ((AE_INFO,
+ "Could not enable fixed event - %s (%u)",
+ AcpiUtGetEventName (Event), Event));
- if (HandlerType & ACPI_SYSTEM_NOTIFY)
- {
- AcpiGbl_SystemNotify.Node = NULL;
- AcpiGbl_SystemNotify.Handler = NULL;
- AcpiGbl_SystemNotify.Context = NULL;
- }
+ /* Remove the handler */
- if (HandlerType & ACPI_DEVICE_NOTIFY)
- {
- AcpiGbl_DeviceNotify.Node = NULL;
- AcpiGbl_DeviceNotify.Handler = NULL;
- AcpiGbl_DeviceNotify.Context = NULL;
- }
+ AcpiGbl_FixedEventHandlers[Event].Handler = NULL;
+ AcpiGbl_FixedEventHandlers[Event].Context = NULL;
}
-
- /* All Other Objects */
-
else
{
- /* Notifies allowed on this object? */
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Enabled fixed event %s (%X), Handler=%p\n",
+ AcpiUtGetEventName (Event), Event, Handler));
+ }
- if (!AcpiEvIsNotifyObject (Node))
- {
- Status = AE_TYPE;
- goto UnlockAndExit;
- }
- /* Check for an existing internal object */
+Cleanup:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+ return_ACPI_STATUS (Status);
+}
- ObjDesc = AcpiNsGetAttachedObject (Node);
- if (!ObjDesc)
- {
- Status = AE_NOT_EXIST;
- goto UnlockAndExit;
- }
+ACPI_EXPORT_SYMBOL (AcpiInstallFixedEventHandler)
- /* Object exists - make sure there's an existing handler */
- if (HandlerType & ACPI_SYSTEM_NOTIFY)
- {
- NotifyObj = ObjDesc->CommonNotify.SystemNotify;
- if (!NotifyObj)
- {
- Status = AE_NOT_EXIST;
- goto UnlockAndExit;
- }
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiRemoveFixedEventHandler
+ *
+ * PARAMETERS: Event - Event type to disable.
+ * Handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disables the event and unregisters the event handler.
+ *
+ ******************************************************************************/
- if (NotifyObj->Notify.Handler != Handler)
- {
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
- }
+ACPI_STATUS
+AcpiRemoveFixedEventHandler (
+ UINT32 Event,
+ ACPI_EVENT_HANDLER Handler)
+{
+ ACPI_STATUS Status = AE_OK;
- /* Remove the handler */
- ObjDesc->CommonNotify.SystemNotify = NULL;
- AcpiUtRemoveReference (NotifyObj);
- }
+ ACPI_FUNCTION_TRACE (AcpiRemoveFixedEventHandler);
- if (HandlerType & ACPI_DEVICE_NOTIFY)
- {
- NotifyObj = ObjDesc->CommonNotify.DeviceNotify;
- if (!NotifyObj)
- {
- Status = AE_NOT_EXIST;
- goto UnlockAndExit;
- }
- if (NotifyObj->Notify.Handler != Handler)
- {
- Status = AE_BAD_PARAMETER;
- goto UnlockAndExit;
- }
+ /* Parameter validation */
- /* Remove the handler */
+ if (Event > ACPI_EVENT_MAX)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
- ObjDesc->CommonNotify.DeviceNotify = NULL;
- AcpiUtRemoveReference (NotifyObj);
- }
+ Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
}
+ /* Disable the event before removing the handler */
-UnlockAndExit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ Status = AcpiDisableEvent (Event, 0);
+
+ /* Always Remove the handler */
+
+ AcpiGbl_FixedEventHandlers[Event].Handler = NULL;
+ AcpiGbl_FixedEventHandlers[Event].Context = NULL;
+
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_WARNING ((AE_INFO,
+ "Could not disable fixed event - %s (%u)",
+ AcpiUtGetEventName (Event), Event));
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Disabled fixed event - %s (%X)\n",
+ AcpiUtGetEventName (Event), Event));
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiRemoveNotifyHandler)
+ACPI_EXPORT_SYMBOL (AcpiRemoveFixedEventHandler)
/*******************************************************************************
*
- * FUNCTION: AcpiInstallGpeHandler
+ * FUNCTION: AcpiEvInstallGpeHandler
*
* PARAMETERS: GpeDevice - Namespace node for the GPE (NULL for FADT
* defined GPEs)
* GpeNumber - The GPE number within the GPE block
* Type - Whether this GPE should be treated as an
* edge- or level-triggered interrupt.
+ * IsRawHandler - Whether this GPE should be handled using
+ * the special GPE handler mode.
* Address - Address of the handler
* Context - Value passed to the handler on each GPE
*
* RETURN: Status
*
- * DESCRIPTION: Install a handler for a General Purpose Event.
+ * DESCRIPTION: Internal function to install a handler for a General Purpose
+ * Event.
*
******************************************************************************/
-ACPI_STATUS
-AcpiInstallGpeHandler (
+static ACPI_STATUS
+AcpiEvInstallGpeHandler (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
UINT32 Type,
+ BOOLEAN IsRawHandler,
ACPI_GPE_HANDLER Address,
void *Context)
{
@@ -688,7 +871,7 @@ AcpiInstallGpeHandler (
ACPI_CPU_FLAGS Flags;
- ACPI_FUNCTION_TRACE (AcpiInstallGpeHandler);
+ ACPI_FUNCTION_TRACE (EvInstallGpeHandler);
/* Parameter validation */
@@ -726,8 +909,10 @@ AcpiInstallGpeHandler (
/* Make sure that there isn't a handler there already */
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_HANDLER)
+ if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_HANDLER) ||
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER))
{
Status = AE_ALREADY_EXISTS;
goto FreeAndExit;
@@ -744,8 +929,10 @@ AcpiInstallGpeHandler (
* automatically during initialization, in which case it has to be
* disabled now to avoid spurious execution of the handler.
*/
- if (((Handler->OriginalFlags & ACPI_GPE_DISPATCH_METHOD) ||
- (Handler->OriginalFlags & ACPI_GPE_DISPATCH_NOTIFY)) &&
+ if (((ACPI_GPE_DISPATCH_TYPE (Handler->OriginalFlags) ==
+ ACPI_GPE_DISPATCH_METHOD) ||
+ (ACPI_GPE_DISPATCH_TYPE (Handler->OriginalFlags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)) &&
GpeEventInfo->RuntimeCount)
{
Handler->OriginallyEnabled = TRUE;
@@ -766,7 +953,8 @@ AcpiInstallGpeHandler (
/* Setup up dispatch flags to indicate handler (vs. method/notify) */
GpeEventInfo->Flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
- GpeEventInfo->Flags |= (UINT8) (Type | ACPI_GPE_DISPATCH_HANDLER);
+ GpeEventInfo->Flags |= (UINT8) (Type | (IsRawHandler ?
+ ACPI_GPE_DISPATCH_RAW_HANDLER : ACPI_GPE_DISPATCH_HANDLER));
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
@@ -781,11 +969,91 @@ FreeAndExit:
goto UnlockAndExit;
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiInstallGpeHandler
+ *
+ * PARAMETERS: GpeDevice - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * GpeNumber - The GPE number within the GPE block
+ * Type - Whether this GPE should be treated as an
+ * edge- or level-triggered interrupt.
+ * Address - Address of the handler
+ * Context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for a General Purpose Event.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiInstallGpeHandler (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber,
+ UINT32 Type,
+ ACPI_GPE_HANDLER Address,
+ void *Context)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiInstallGpeHandler);
+
+
+ Status = AcpiEvInstallGpeHandler (GpeDevice, GpeNumber, Type,
+ FALSE, Address, Context);
+
+ return_ACPI_STATUS (Status);
+}
+
ACPI_EXPORT_SYMBOL (AcpiInstallGpeHandler)
/*******************************************************************************
*
+ * FUNCTION: AcpiInstallGpeRawHandler
+ *
+ * PARAMETERS: GpeDevice - Namespace node for the GPE (NULL for FADT
+ * defined GPEs)
+ * GpeNumber - The GPE number within the GPE block
+ * Type - Whether this GPE should be treated as an
+ * edge- or level-triggered interrupt.
+ * Address - Address of the handler
+ * Context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install a handler for a General Purpose Event.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiInstallGpeRawHandler (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber,
+ UINT32 Type,
+ ACPI_GPE_HANDLER Address,
+ void *Context)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiInstallGpeRawHandler);
+
+
+ Status = AcpiEvInstallGpeHandler (GpeDevice, GpeNumber, Type,
+ TRUE, Address, Context);
+
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiInstallGpeRawHandler)
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiRemoveGpeHandler
*
* PARAMETERS: GpeDevice - Namespace node for the GPE (NULL for FADT
@@ -840,8 +1108,10 @@ AcpiRemoveGpeHandler (
/* Make sure that a handler is indeed installed */
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) !=
- ACPI_GPE_DISPATCH_HANDLER)
+ if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) !=
+ ACPI_GPE_DISPATCH_HANDLER) &&
+ (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) !=
+ ACPI_GPE_DISPATCH_RAW_HANDLER))
{
Status = AE_NOT_EXIST;
goto UnlockAndExit;
@@ -858,6 +1128,7 @@ AcpiRemoveGpeHandler (
/* Remove the handler */
Handler = GpeEventInfo->Dispatch.Handler;
+ GpeEventInfo->Dispatch.Handler = NULL;
/* Restore Method node (if any), set dispatch flags */
@@ -871,16 +1142,26 @@ AcpiRemoveGpeHandler (
* enabled, it should be enabled at this point to restore the
* post-initialization configuration.
*/
- if ((Handler->OriginalFlags & ACPI_GPE_DISPATCH_METHOD) &&
+ if (((ACPI_GPE_DISPATCH_TYPE (Handler->OriginalFlags) ==
+ ACPI_GPE_DISPATCH_METHOD) ||
+ (ACPI_GPE_DISPATCH_TYPE (Handler->OriginalFlags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)) &&
Handler->OriginallyEnabled)
{
(void) AcpiEvAddGpeReference (GpeEventInfo);
}
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+
+ /* Make sure all deferred GPE tasks are completed */
+
+ AcpiOsWaitEventsComplete ();
+
/* Now we can free the handler object */
ACPI_FREE (Handler);
-
+ return_ACPI_STATUS (Status);
UnlockAndExit:
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
@@ -929,7 +1210,7 @@ AcpiAcquireGlobalLock (
AcpiExEnterInterpreter ();
Status = AcpiExAcquireMutexObject (Timeout,
- AcpiGbl_GlobalLockMutex, AcpiOsGetThreadId ());
+ AcpiGbl_GlobalLockMutex, AcpiOsGetThreadId ());
if (ACPI_SUCCESS (Status))
{
@@ -975,3 +1256,4 @@ AcpiReleaseGlobalLock (
ACPI_EXPORT_SYMBOL (AcpiReleaseGlobalLock)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evxfevnt.c b/usr/src/uts/intel/io/acpica/events/evxfevnt.c
index a813dd3c96..9991751c67 100644
--- a/usr/src/uts/intel/io/acpica/events/evxfevnt.c
+++ b/usr/src/uts/intel/io/acpica/events/evxfevnt.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EVXFEVNT_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -52,6 +51,7 @@
ACPI_MODULE_NAME ("evxfevnt")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
* FUNCTION: AcpiEnable
@@ -76,16 +76,24 @@ AcpiEnable (
/* ACPI tables must be present */
- if (!AcpiTbTablesLoaded ())
+ if (AcpiGbl_FadtIndex == ACPI_INVALID_TABLE_INDEX)
{
return_ACPI_STATUS (AE_NO_ACPI_TABLES);
}
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
/* Check current mode */
if (AcpiHwGetMode() == ACPI_SYS_MODE_ACPI)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "System is already in ACPI mode\n"));
+ ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
+ "System is already in ACPI mode\n"));
}
else
{
@@ -130,6 +138,13 @@ AcpiDisable (
ACPI_FUNCTION_TRACE (AcpiDisable);
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
if (AcpiHwGetMode() == ACPI_SYS_MODE_LEGACY)
{
ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
@@ -148,7 +163,8 @@ AcpiDisable (
return_ACPI_STATUS (Status);
}
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "ACPI mode disabled\n"));
+ ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
+ "ACPI mode disabled\n"));
}
return_ACPI_STATUS (Status);
@@ -194,8 +210,8 @@ AcpiEnableEvent (
* register bit)
*/
Status = AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
- ACPI_ENABLE_EVENT);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
+ ACPI_ENABLE_EVENT);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -204,7 +220,7 @@ AcpiEnableEvent (
/* Make sure that the hardware responded */
Status = AcpiReadBitRegister (
- AcpiGbl_FixedEventInfo[Event].EnableRegisterId, &Value);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId, &Value);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -260,15 +276,15 @@ AcpiDisableEvent (
* register bit)
*/
Status = AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
- ACPI_DISABLE_EVENT);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId,
+ ACPI_DISABLE_EVENT);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
Status = AcpiReadBitRegister (
- AcpiGbl_FixedEventInfo[Event].EnableRegisterId, &Value);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId, &Value);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -321,8 +337,8 @@ AcpiClearEvent (
* register bit)
*/
Status = AcpiWriteBitRegister (
- AcpiGbl_FixedEventInfo[Event].StatusRegisterId,
- ACPI_CLEAR_STATUS);
+ AcpiGbl_FixedEventInfo[Event].StatusRegisterId,
+ ACPI_CLEAR_STATUS);
return_ACPI_STATUS (Status);
}
@@ -349,7 +365,9 @@ AcpiGetEventStatus (
UINT32 Event,
ACPI_EVENT_STATUS *EventStatus)
{
- ACPI_STATUS Status = AE_OK;
+ ACPI_STATUS Status;
+ ACPI_EVENT_STATUS LocalEventStatus = 0;
+ UINT32 InByte;
ACPI_FUNCTION_TRACE (AcpiGetEventStatus);
@@ -367,14 +385,46 @@ AcpiGetEventStatus (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- /* Get the status of the requested fixed event */
+ /* Fixed event currently can be dispatched? */
+
+ if (AcpiGbl_FixedEventHandlers[Event].Handler)
+ {
+ LocalEventStatus |= ACPI_EVENT_FLAG_HAS_HANDLER;
+ }
+
+ /* Fixed event currently enabled? */
Status = AcpiReadBitRegister (
- AcpiGbl_FixedEventInfo[Event].StatusRegisterId, EventStatus);
+ AcpiGbl_FixedEventInfo[Event].EnableRegisterId, &InByte);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
- return_ACPI_STATUS (Status);
+ if (InByte)
+ {
+ LocalEventStatus |=
+ (ACPI_EVENT_FLAG_ENABLED | ACPI_EVENT_FLAG_ENABLE_SET);
+ }
+
+ /* Fixed event currently active? */
+
+ Status = AcpiReadBitRegister (
+ AcpiGbl_FixedEventInfo[Event].StatusRegisterId, &InByte);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ if (InByte)
+ {
+ LocalEventStatus |= ACPI_EVENT_FLAG_STATUS_SET;
+ }
+
+ (*EventStatus) = LocalEventStatus;
+ return_ACPI_STATUS (AE_OK);
}
ACPI_EXPORT_SYMBOL (AcpiGetEventStatus)
-
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evxfgpe.c b/usr/src/uts/intel/io/acpica/events/evxfgpe.c
index 76019969ec..1d467d9abc 100644
--- a/usr/src/uts/intel/io/acpica/events/evxfgpe.c
+++ b/usr/src/uts/intel/io/acpica/events/evxfgpe.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EVXFGPE_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -53,6 +52,7 @@
ACPI_MODULE_NAME ("evxfgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
* FUNCTION: AcpiUpdateAllGpes
@@ -82,7 +82,7 @@ AcpiUpdateAllGpes (
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiUpdateGpes);
+ ACPI_FUNCTION_TRACE (AcpiUpdateAllGpes);
Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
@@ -139,12 +139,23 @@ AcpiEnableGpe (
Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
- /* Ensure that we have a valid GPE number */
-
+ /*
+ * Ensure that we have a valid GPE number and that there is some way
+ * of handling the GPE (handler or a GPE method). In other words, we
+ * won't allow a valid GPE to be enabled if there is no way to handle it.
+ */
GpeEventInfo = AcpiEvGetGpeEventInfo (GpeDevice, GpeNumber);
if (GpeEventInfo)
{
- Status = AcpiEvAddGpeReference (GpeEventInfo);
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) !=
+ ACPI_GPE_DISPATCH_NONE)
+ {
+ Status = AcpiEvAddGpeReference (GpeEventInfo);
+ }
+ else
+ {
+ Status = AE_NO_HANDLER;
+ }
}
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
@@ -210,12 +221,21 @@ ACPI_EXPORT_SYMBOL (AcpiDisableGpe)
* RETURN: Status
*
* DESCRIPTION: Enable or disable an individual GPE. This function bypasses
- * the reference count mechanism used in the AcpiEnableGpe and
- * AcpiDisableGpe interfaces -- and should be used with care.
- *
- * Note: Typically used to disable a runtime GPE for short period of time,
- * then re-enable it, without disturbing the existing reference counts. This
- * is useful, for example, in the Embedded Controller (EC) driver.
+ * the reference count mechanism used in the AcpiEnableGpe(),
+ * AcpiDisableGpe() interfaces.
+ * This API is typically used by the GPE raw handler mode driver
+ * to switch between the polling mode and the interrupt mode after
+ * the driver has enabled the GPE.
+ * The APIs should be invoked in this order:
+ * AcpiEnableGpe() <- Ensure the reference count > 0
+ * AcpiSetGpe(ACPI_GPE_DISABLE) <- Enter polling mode
+ * AcpiSetGpe(ACPI_GPE_ENABLE) <- Leave polling mode
+ * AcpiDisableGpe() <- Decrease the reference count
+ *
+ * Note: If a GPE is shared by 2 silicon components, then both the drivers
+ * should support GPE polling mode or disabling the GPE for long period
+ * for one driver may break the other. So use it with care since all
+ * firmware _Lxx/_Exx handlers currently rely on the GPE interrupt mode.
*
******************************************************************************/
@@ -249,14 +269,17 @@ AcpiSetGpe (
switch (Action)
{
case ACPI_GPE_ENABLE:
- Status = AcpiEvEnableGpe (GpeEventInfo);
+
+ Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_ENABLE);
break;
case ACPI_GPE_DISABLE:
+
Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
break;
default:
+
Status = AE_BAD_PARAMETER;
break;
}
@@ -271,6 +294,60 @@ ACPI_EXPORT_SYMBOL (AcpiSetGpe)
/*******************************************************************************
*
+ * FUNCTION: AcpiMarkGpeForWake
+ *
+ * PARAMETERS: GpeDevice - Parent GPE Device. NULL for GPE0/GPE1
+ * GpeNumber - GPE level within the GPE block
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Mark a GPE as having the ability to wake the system. Simply
+ * sets the ACPI_GPE_CAN_WAKE flag.
+ *
+ * Some potential callers of AcpiSetupGpeForWake may know in advance that
+ * there won't be any notify handlers installed for device wake notifications
+ * from the given GPE (one example is a button GPE in Linux). For these cases,
+ * AcpiMarkGpeForWake should be used instead of AcpiSetupGpeForWake.
+ * This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to
+ * setup implicit wake notification for it (since there's no handler method).
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiMarkGpeForWake (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber)
+{
+ ACPI_GPE_EVENT_INFO *GpeEventInfo;
+ ACPI_STATUS Status = AE_BAD_PARAMETER;
+ ACPI_CPU_FLAGS Flags;
+
+
+ ACPI_FUNCTION_TRACE (AcpiMarkGpeForWake);
+
+
+ Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
+
+ /* Ensure that we have a valid GPE number */
+
+ GpeEventInfo = AcpiEvGetGpeEventInfo (GpeDevice, GpeNumber);
+ if (GpeEventInfo)
+ {
+ /* Mark the GPE as a possible wake event */
+
+ GpeEventInfo->Flags |= ACPI_GPE_CAN_WAKE;
+ Status = AE_OK;
+ }
+
+ AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiMarkGpeForWake)
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiSetupGpeForWake
*
* PARAMETERS: WakeDevice - Device associated with the GPE (via _PRW)
@@ -294,9 +371,11 @@ AcpiSetupGpeForWake (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber)
{
- ACPI_STATUS Status = AE_BAD_PARAMETER;
+ ACPI_STATUS Status;
ACPI_GPE_EVENT_INFO *GpeEventInfo;
ACPI_NAMESPACE_NODE *DeviceNode;
+ ACPI_GPE_NOTIFY_INFO *Notify;
+ ACPI_GPE_NOTIFY_INFO *NewNotify;
ACPI_CPU_FLAGS Flags;
@@ -332,32 +411,88 @@ AcpiSetupGpeForWake (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
+ /*
+ * Allocate a new notify object up front, in case it is needed.
+ * Memory allocation while holding a spinlock is a big no-no
+ * on some hosts.
+ */
+ NewNotify = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_NOTIFY_INFO));
+ if (!NewNotify)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
/* Ensure that we have a valid GPE number */
GpeEventInfo = AcpiEvGetGpeEventInfo (GpeDevice, GpeNumber);
- if (GpeEventInfo)
+ if (!GpeEventInfo)
+ {
+ Status = AE_BAD_PARAMETER;
+ goto UnlockAndExit;
+ }
+
+ /*
+ * If there is no method or handler for this GPE, then the
+ * WakeDevice will be notified whenever this GPE fires. This is
+ * known as an "implicit notify". Note: The GPE is assumed to be
+ * level-triggered (for windows compatibility).
+ */
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_NONE)
{
/*
- * If there is no method or handler for this GPE, then the
- * WakeDevice will be notified whenever this GPE fires (aka
- * "implicit notify") Note: The GPE is assumed to be
- * level-triggered (for windows compatibility).
+ * This is the first device for implicit notify on this GPE.
+ * Just set the flags here, and enter the NOTIFY block below.
*/
- if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_NONE)
+ GpeEventInfo->Flags =
+ (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
+ }
+
+ /*
+ * If we already have an implicit notify on this GPE, add
+ * this device to the notify list.
+ */
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
+ ACPI_GPE_DISPATCH_NOTIFY)
+ {
+ /* Ensure that the device is not already in the list */
+
+ Notify = GpeEventInfo->Dispatch.NotifyList;
+ while (Notify)
{
- GpeEventInfo->Flags =
- (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
- GpeEventInfo->Dispatch.DeviceNode = DeviceNode;
+ if (Notify->DeviceNode == DeviceNode)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto UnlockAndExit;
+ }
+ Notify = Notify->Next;
}
- GpeEventInfo->Flags |= ACPI_GPE_CAN_WAKE;
- Status = AE_OK;
+ /* Add this device to the notify list for this GPE */
+
+ NewNotify->DeviceNode = DeviceNode;
+ NewNotify->Next = GpeEventInfo->Dispatch.NotifyList;
+ GpeEventInfo->Dispatch.NotifyList = NewNotify;
+ NewNotify = NULL;
}
+ /* Mark the GPE as a possible wake event */
+
+ GpeEventInfo->Flags |= ACPI_GPE_CAN_WAKE;
+ Status = AE_OK;
+
+
+UnlockAndExit:
AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
+
+ /* Delete the notify object if it was not used above */
+
+ if (NewNotify)
+ {
+ ACPI_FREE (NewNotify);
+ }
return_ACPI_STATUS (Status);
}
@@ -421,21 +556,24 @@ AcpiSetGpeWakeMask (
goto UnlockAndExit;
}
- RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo, GpeRegisterInfo);
+ RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo);
/* Perform the action */
switch (Action)
{
case ACPI_GPE_ENABLE:
+
ACPI_SET_BIT (GpeRegisterInfo->EnableForWake, (UINT8) RegisterBit);
break;
case ACPI_GPE_DISABLE:
+
ACPI_CLEAR_BIT (GpeRegisterInfo->EnableForWake, (UINT8) RegisterBit);
break;
default:
+
ACPI_ERROR ((AE_INFO, "%u, Invalid action", Action));
Status = AE_BAD_PARAMETER;
break;
@@ -673,6 +811,44 @@ AcpiEnableAllRuntimeGpes (
ACPI_EXPORT_SYMBOL (AcpiEnableAllRuntimeGpes)
+/******************************************************************************
+ *
+ * FUNCTION: AcpiEnableAllWakeupGpes
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
+ * all GPE blocks.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnableAllWakeupGpes (
+ void)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiEnableAllWakeupGpes);
+
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Status = AcpiHwEnableAllWakeupGpes ();
+ (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
+
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiEnableAllWakeupGpes)
+
+
/*******************************************************************************
*
* FUNCTION: AcpiInstallGpeBlock
@@ -715,7 +891,7 @@ AcpiInstallGpeBlock (
Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE (Status))
{
- return (Status);
+ return_ACPI_STATUS (Status);
}
Node = AcpiNsValidateHandle (GpeDevice);
@@ -725,12 +901,27 @@ AcpiInstallGpeBlock (
goto UnlockAndExit;
}
+ /* Validate the parent device */
+
+ if (Node->Type != ACPI_TYPE_DEVICE)
+ {
+ Status = AE_TYPE;
+ goto UnlockAndExit;
+ }
+
+ if (Node->Object)
+ {
+ Status = AE_ALREADY_EXISTS;
+ goto UnlockAndExit;
+ }
+
/*
* For user-installed GPE Block Devices, the GpeBlockBaseNumber
* is always zero
*/
- Status = AcpiEvCreateGpeBlock (Node, GpeBlockAddress, RegisterCount,
- 0, InterruptNumber, &GpeBlock);
+ Status = AcpiEvCreateGpeBlock (Node, GpeBlockAddress->Address,
+ GpeBlockAddress->SpaceId, RegisterCount,
+ 0, InterruptNumber, &GpeBlock);
if (ACPI_FAILURE (Status))
{
goto UnlockAndExit;
@@ -808,7 +999,7 @@ AcpiRemoveGpeBlock (
Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE (Status))
{
- return (Status);
+ return_ACPI_STATUS (Status);
}
Node = AcpiNsValidateHandle (GpeDevice);
@@ -818,6 +1009,14 @@ AcpiRemoveGpeBlock (
goto UnlockAndExit;
}
+ /* Validate the parent device */
+
+ if (Node->Type != ACPI_TYPE_DEVICE)
+ {
+ Status = AE_TYPE;
+ goto UnlockAndExit;
+ }
+
/* Get the DeviceObject attached to the node */
ObjDesc = AcpiNsGetAttachedObject (Node);
@@ -898,3 +1097,5 @@ AcpiGetGpeDevice (
}
ACPI_EXPORT_SYMBOL (AcpiGetGpeDevice)
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/events/evxfregn.c b/usr/src/uts/intel/io/acpica/events/evxfregn.c
index 4fd637e31f..8537640d6c 100644
--- a/usr/src/uts/intel/io/acpica/events/evxfregn.c
+++ b/usr/src/uts/intel/io/acpica/events/evxfregn.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EVXFREGN_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -114,47 +114,16 @@ AcpiInstallAddressSpaceHandler (
/* Install the handler for all Regions for this Space ID */
- Status = AcpiEvInstallSpaceHandler (Node, SpaceId, Handler, Setup, Context);
+ Status = AcpiEvInstallSpaceHandler (
+ Node, SpaceId, Handler, Setup, Context);
if (ACPI_FAILURE (Status))
{
goto UnlockAndExit;
}
- /*
- * For the default SpaceIDs, (the IDs for which there are default region handlers
- * installed) Only execute the _REG methods if the global initialization _REG
- * methods have already been run (via AcpiInitializeObjects). In other words,
- * we will defer the execution of the _REG methods for these SpaceIDs until
- * execution of AcpiInitializeObjects. This is done because we need the handlers
- * for the default spaces (mem/io/pci/table) to be installed before we can run
- * any control methods (or _REG methods). There is known BIOS code that depends
- * on this.
- *
- * For all other SpaceIDs, we can safely execute the _REG methods immediately.
- * This means that for IDs like EmbeddedController, this function should be called
- * only after AcpiEnableSubsystem has been called.
- */
- switch (SpaceId)
- {
- case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- case ACPI_ADR_SPACE_SYSTEM_IO:
- case ACPI_ADR_SPACE_PCI_CONFIG:
- case ACPI_ADR_SPACE_DATA_TABLE:
-
- if (!AcpiGbl_RegMethodsExecuted)
- {
- /* We will defer execution of the _REG methods for this space */
- goto UnlockAndExit;
- }
- break;
-
- default:
- break;
- }
-
/* Run all _REG methods for this address space */
- Status = AcpiEvExecuteRegMethods (Node, SpaceId);
+ AcpiEvExecuteRegMethods (Node, SpaceId, ACPI_REG_CONNECT);
UnlockAndExit:
@@ -233,8 +202,8 @@ AcpiRemoveAddressSpaceHandler (
/* Find the address handler the user requested */
- HandlerObj = ObjDesc->Device.Handler;
- LastObjPtr = &ObjDesc->Device.Handler;
+ HandlerObj = ObjDesc->CommonNotify.Handler;
+ LastObjPtr = &ObjDesc->CommonNotify.Handler;
while (HandlerObj)
{
/* We have a handler, see if user requested this one */
@@ -310,4 +279,3 @@ UnlockAndExit:
}
ACPI_EXPORT_SYMBOL (AcpiRemoveAddressSpaceHandler)
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exconcat.c b/usr/src/uts/intel/io/acpica/executer/exconcat.c
new file mode 100644
index 0000000000..49b279ad05
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/executer/exconcat.c
@@ -0,0 +1,460 @@
+/******************************************************************************
+ *
+ * Module Name: exconcat - Concatenate-type AML operators
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acinterp.h"
+#include "amlresrc.h"
+
+
+#define _COMPONENT ACPI_EXECUTER
+ ACPI_MODULE_NAME ("exconcat")
+
+/* Local Prototypes */
+
+static ACPI_STATUS
+AcpiExConvertToObjectTypeString (
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_OPERAND_OBJECT **ResultDesc);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExDoConcatenate
+ *
+ * PARAMETERS: Operand0 - First source object
+ * Operand1 - Second source object
+ * ActualReturnDesc - Where to place the return object
+ * WalkState - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Concatenate two objects with the ACPI-defined conversion
+ * rules as necessary.
+ * NOTE:
+ * Per the ACPI spec (up to 6.1), Concatenate only supports Integer,
+ * String, and Buffer objects. However, we support all objects here
+ * as an extension. This improves the usefulness of both Concatenate
+ * and the Printf/Fprintf macros. The extension returns a string
+ * describing the object type for the other objects.
+ * 02/2016.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiExDoConcatenate (
+ ACPI_OPERAND_OBJECT *Operand0,
+ ACPI_OPERAND_OBJECT *Operand1,
+ ACPI_OPERAND_OBJECT **ActualReturnDesc,
+ ACPI_WALK_STATE *WalkState)
+{
+ ACPI_OPERAND_OBJECT *LocalOperand0 = Operand0;
+ ACPI_OPERAND_OBJECT *LocalOperand1 = Operand1;
+ ACPI_OPERAND_OBJECT *TempOperand1 = NULL;
+ ACPI_OPERAND_OBJECT *ReturnDesc;
+ char *Buffer;
+ ACPI_OBJECT_TYPE Operand0Type;
+ ACPI_OBJECT_TYPE Operand1Type;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (ExDoConcatenate);
+
+
+ /* Operand 0 preprocessing */
+
+ switch (Operand0->Common.Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ Operand0Type = Operand0->Common.Type;
+ break;
+
+ default:
+
+ /* For all other types, get the "object type" string */
+
+ Status = AcpiExConvertToObjectTypeString (
+ Operand0, &LocalOperand0);
+ if (ACPI_FAILURE (Status))
+ {
+ goto Cleanup;
+ }
+
+ Operand0Type = ACPI_TYPE_STRING;
+ break;
+ }
+
+ /* Operand 1 preprocessing */
+
+ switch (Operand1->Common.Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ Operand1Type = Operand1->Common.Type;
+ break;
+
+ default:
+
+ /* For all other types, get the "object type" string */
+
+ Status = AcpiExConvertToObjectTypeString (
+ Operand1, &LocalOperand1);
+ if (ACPI_FAILURE (Status))
+ {
+ goto Cleanup;
+ }
+
+ Operand1Type = ACPI_TYPE_STRING;
+ break;
+ }
+
+ /*
+ * Convert the second operand if necessary. The first operand (0)
+ * determines the type of the second operand (1) (See the Data Types
+ * section of the ACPI specification). Both object types are
+ * guaranteed to be either Integer/String/Buffer by the operand
+ * resolution mechanism.
+ */
+ switch (Operand0Type)
+ {
+ case ACPI_TYPE_INTEGER:
+
+ Status = AcpiExConvertToInteger (LocalOperand1, &TempOperand1, 16);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ Status = AcpiExConvertToBuffer (LocalOperand1, &TempOperand1);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ switch (Operand1Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ /* Other types have already been converted to string */
+
+ Status = AcpiExConvertToString (
+ LocalOperand1, &TempOperand1, ACPI_IMPLICIT_CONVERT_HEX);
+ break;
+
+ default:
+
+ Status = AE_OK;
+ break;
+ }
+ break;
+
+ default:
+
+ ACPI_ERROR ((AE_INFO, "Invalid object type: 0x%X",
+ Operand0->Common.Type));
+ Status = AE_AML_INTERNAL;
+ }
+
+ if (ACPI_FAILURE (Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Take care with any newly created operand objects */
+
+ if ((LocalOperand1 != Operand1) &&
+ (LocalOperand1 != TempOperand1))
+ {
+ AcpiUtRemoveReference (LocalOperand1);
+ }
+
+ LocalOperand1 = TempOperand1;
+
+ /*
+ * Both operands are now known to be the same object type
+ * (Both are Integer, String, or Buffer), and we can now perform
+ * the concatenation.
+ *
+ * There are three cases to handle, as per the ACPI spec:
+ *
+ * 1) Two Integers concatenated to produce a new Buffer
+ * 2) Two Strings concatenated to produce a new String
+ * 3) Two Buffers concatenated to produce a new Buffer
+ */
+ switch (Operand0Type)
+ {
+ case ACPI_TYPE_INTEGER:
+
+ /* Result of two Integers is a Buffer */
+ /* Need enough buffer space for two integers */
+
+ ReturnDesc = AcpiUtCreateBufferObject (
+ (ACPI_SIZE) ACPI_MUL_2 (AcpiGbl_IntegerByteWidth));
+ if (!ReturnDesc)
+ {
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ Buffer = (char *) ReturnDesc->Buffer.Pointer;
+
+ /* Copy the first integer, LSB first */
+
+ memcpy (Buffer, &Operand0->Integer.Value,
+ AcpiGbl_IntegerByteWidth);
+
+ /* Copy the second integer (LSB first) after the first */
+
+ memcpy (Buffer + AcpiGbl_IntegerByteWidth,
+ &LocalOperand1->Integer.Value, AcpiGbl_IntegerByteWidth);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ /* Result of two Strings is a String */
+
+ ReturnDesc = AcpiUtCreateStringObject (
+ ((ACPI_SIZE) LocalOperand0->String.Length +
+ LocalOperand1->String.Length));
+ if (!ReturnDesc)
+ {
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ Buffer = ReturnDesc->String.Pointer;
+
+ /* Concatenate the strings */
+
+ strcpy (Buffer, LocalOperand0->String.Pointer);
+ strcat (Buffer, LocalOperand1->String.Pointer);
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Result of two Buffers is a Buffer */
+
+ ReturnDesc = AcpiUtCreateBufferObject (
+ ((ACPI_SIZE) Operand0->Buffer.Length +
+ LocalOperand1->Buffer.Length));
+ if (!ReturnDesc)
+ {
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ Buffer = (char *) ReturnDesc->Buffer.Pointer;
+
+ /* Concatenate the buffers */
+
+ memcpy (Buffer, Operand0->Buffer.Pointer,
+ Operand0->Buffer.Length);
+ memcpy (Buffer + Operand0->Buffer.Length,
+ LocalOperand1->Buffer.Pointer,
+ LocalOperand1->Buffer.Length);
+ break;
+
+ default:
+
+ /* Invalid object type, should not happen here */
+
+ ACPI_ERROR ((AE_INFO, "Invalid object type: 0x%X",
+ Operand0->Common.Type));
+ Status = AE_AML_INTERNAL;
+ goto Cleanup;
+ }
+
+ *ActualReturnDesc = ReturnDesc;
+
+Cleanup:
+ if (LocalOperand0 != Operand0)
+ {
+ AcpiUtRemoveReference (LocalOperand0);
+ }
+
+ if (LocalOperand1 != Operand1)
+ {
+ AcpiUtRemoveReference (LocalOperand1);
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExConvertToObjectTypeString
+ *
+ * PARAMETERS: ObjDesc - Object to be converted
+ * ReturnDesc - Where to place the return object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert an object of arbitrary type to a string object that
+ * contains the namestring for the object. Used for the
+ * concatenate operator.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiExConvertToObjectTypeString (
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_OPERAND_OBJECT **ResultDesc)
+{
+ ACPI_OPERAND_OBJECT *ReturnDesc;
+ const char *TypeString;
+
+
+ TypeString = AcpiUtGetTypeName (ObjDesc->Common.Type);
+
+ ReturnDesc = AcpiUtCreateStringObject (
+ ((ACPI_SIZE) strlen (TypeString) + 9)); /* 9 For "[ Object]" */
+ if (!ReturnDesc)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ strcpy (ReturnDesc->String.Pointer, "[");
+ strcat (ReturnDesc->String.Pointer, TypeString);
+ strcat (ReturnDesc->String.Pointer, " Object]");
+
+ *ResultDesc = ReturnDesc;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExConcatTemplate
+ *
+ * PARAMETERS: Operand0 - First source object
+ * Operand1 - Second source object
+ * ActualReturnDesc - Where to place the return object
+ * WalkState - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Concatenate two resource templates
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiExConcatTemplate (
+ ACPI_OPERAND_OBJECT *Operand0,
+ ACPI_OPERAND_OBJECT *Operand1,
+ ACPI_OPERAND_OBJECT **ActualReturnDesc,
+ ACPI_WALK_STATE *WalkState)
+{
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *ReturnDesc;
+ UINT8 *NewBuf;
+ UINT8 *EndTag;
+ ACPI_SIZE Length0;
+ ACPI_SIZE Length1;
+ ACPI_SIZE NewLength;
+
+
+ ACPI_FUNCTION_TRACE (ExConcatTemplate);
+
+
+ /*
+ * Find the EndTag descriptor in each resource template.
+ * Note1: returned pointers point TO the EndTag, not past it.
+ * Note2: zero-length buffers are allowed; treated like one EndTag
+ */
+
+ /* Get the length of the first resource template */
+
+ Status = AcpiUtGetResourceEndTag (Operand0, &EndTag);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Length0 = ACPI_PTR_DIFF (EndTag, Operand0->Buffer.Pointer);
+
+ /* Get the length of the second resource template */
+
+ Status = AcpiUtGetResourceEndTag (Operand1, &EndTag);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Length1 = ACPI_PTR_DIFF (EndTag, Operand1->Buffer.Pointer);
+
+ /* Combine both lengths, minimum size will be 2 for EndTag */
+
+ NewLength = Length0 + Length1 + sizeof (AML_RESOURCE_END_TAG);
+
+ /* Create a new buffer object for the result (with one EndTag) */
+
+ ReturnDesc = AcpiUtCreateBufferObject (NewLength);
+ if (!ReturnDesc)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the templates to the new buffer, 0 first, then 1 follows. One
+ * EndTag descriptor is copied from Operand1.
+ */
+ NewBuf = ReturnDesc->Buffer.Pointer;
+ memcpy (NewBuf, Operand0->Buffer.Pointer, Length0);
+ memcpy (NewBuf + Length0, Operand1->Buffer.Pointer, Length1);
+
+ /* Insert EndTag and set the checksum to zero, means "ignore checksum" */
+
+ NewBuf[NewLength - 1] = 0;
+ NewBuf[NewLength - 2] = ACPI_RESOURCE_NAME_END_TAG | 1;
+
+ /* Return the completed resource template */
+
+ *ActualReturnDesc = ReturnDesc;
+ return_ACPI_STATUS (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/executer/exconfig.c b/usr/src/uts/intel/io/acpica/executer/exconfig.c
index d7677e99ce..bb0d2bb411 100644
--- a/usr/src/uts/intel/io/acpica/executer/exconfig.c
+++ b/usr/src/uts/intel/io/acpica/executer/exconfig.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXCONFIG_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -50,6 +48,7 @@
#include "actables.h"
#include "acdispat.h"
#include "acevents.h"
+#include "amlcode.h"
#define _COMPONENT ACPI_EXECUTER
@@ -130,7 +129,10 @@ AcpiExAddTable (
/* Execute any module-level code that was found in the table */
AcpiExExitInterpreter ();
- AcpiNsExecModuleCodeList ();
+ if (AcpiGbl_GroupModuleLevelCode)
+ {
+ AcpiNsExecModuleCodeList ();
+ }
AcpiExEnterInterpreter ();
/*
@@ -179,20 +181,12 @@ AcpiExLoadTableOp (
ACPI_FUNCTION_TRACE (ExLoadTableOp);
- /* Validate lengths for the SignatureString, OEMIDString, OEMTableID */
-
- if ((Operand[0]->String.Length > ACPI_NAME_SIZE) ||
- (Operand[1]->String.Length > ACPI_OEM_ID_SIZE) ||
- (Operand[2]->String.Length > ACPI_OEM_TABLE_ID_SIZE))
- {
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
-
/* Find the ACPI table in the RSDT/XSDT */
- Status = AcpiTbFindTable (Operand[0]->String.Pointer,
- Operand[1]->String.Pointer,
- Operand[2]->String.Pointer, &TableIndex);
+ Status = AcpiTbFindTable (
+ Operand[0]->String.Pointer,
+ Operand[1]->String.Pointer,
+ Operand[2]->String.Pointer, &TableIndex);
if (ACPI_FAILURE (Status))
{
if (Status != AE_NOT_FOUND)
@@ -222,11 +216,11 @@ AcpiExLoadTableOp (
if (Operand[3]->String.Length > 0)
{
/*
- * Find the node referenced by the RootPathString. This is the
+ * Find the node referenced by the RootPathString. This is the
* location within the namespace where the table will be loaded.
*/
Status = AcpiNsGetNode (StartNode, Operand[3]->String.Pointer,
- ACPI_NS_SEARCH_PARENT, &ParentNode);
+ ACPI_NS_SEARCH_PARENT, &ParentNode);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -237,8 +231,8 @@ AcpiExLoadTableOp (
if (Operand[4]->String.Length > 0)
{
- if ((Operand[4]->String.Pointer[0] != '\\') &&
- (Operand[4]->String.Pointer[0] != '^'))
+ if ((Operand[4]->String.Pointer[0] != AML_ROOT_PREFIX) &&
+ (Operand[4]->String.Pointer[0] != AML_PARENT_PREFIX))
{
/*
* Path is not absolute, so it will be relative to the node
@@ -250,7 +244,7 @@ AcpiExLoadTableOp (
/* Find the node referenced by the ParameterPathString */
Status = AcpiNsGetNode (StartNode, Operand[4]->String.Pointer,
- ACPI_NS_SEARCH_PARENT, &ParameterNode);
+ ACPI_NS_SEARCH_PARENT, &ParameterNode);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -272,8 +266,7 @@ AcpiExLoadTableOp (
/* Store the parameter data into the optional parameter object */
Status = AcpiExStore (Operand[5],
- ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, ParameterNode),
- WalkState);
+ ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, ParameterNode), WalkState);
if (ACPI_FAILURE (Status))
{
(void) AcpiExUnloadTable (DdbHandle);
@@ -286,7 +279,7 @@ AcpiExLoadTableOp (
Status = AcpiGetTableByIndex (TableIndex, &Table);
if (ACPI_SUCCESS (Status))
{
- ACPI_INFO ((AE_INFO, "Dynamic OEM Table Load:"));
+ ACPI_INFO (("Dynamic OEM Table Load:"));
AcpiTbPrintTableHeader (0, Table);
}
@@ -295,11 +288,11 @@ AcpiExLoadTableOp (
if (AcpiGbl_TableHandler)
{
(void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_LOAD, Table,
- AcpiGbl_TableHandlerContext);
+ AcpiGbl_TableHandlerContext);
}
*ReturnDesc = DdbHandle;
- return_ACPI_STATUS (Status);
+ return_ACPI_STATUS (Status);
}
@@ -334,8 +327,8 @@ AcpiExRegionRead (
for (i = 0; i < Length; i++)
{
- Status = AcpiEvAddressSpaceDispatch (ObjDesc, ACPI_READ,
- RegionOffset, 8, &Value);
+ Status = AcpiEvAddressSpaceDispatch (ObjDesc, NULL, ACPI_READ,
+ RegionOffset, 8, &Value);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -378,8 +371,8 @@ AcpiExLoadOp (
ACPI_WALK_STATE *WalkState)
{
ACPI_OPERAND_OBJECT *DdbHandle;
+ ACPI_TABLE_HEADER *TableHeader;
ACPI_TABLE_HEADER *Table;
- ACPI_TABLE_DESC TableDesc;
UINT32 TableIndex;
ACPI_STATUS Status;
UINT32 Length;
@@ -388,8 +381,6 @@ AcpiExLoadOp (
ACPI_FUNCTION_TRACE (ExLoadOp);
- ACPI_MEMSET (&TableDesc, 0, sizeof (ACPI_TABLE_DESC));
-
/* Source Object can be either an OpRegion or a Buffer/Field */
switch (ObjDesc->Common.Type)
@@ -407,8 +398,8 @@ AcpiExLoadOp (
}
/*
- * If the Region Address and Length have not been previously evaluated,
- * evaluate them now and save the results.
+ * If the Region Address and Length have not been previously
+ * evaluated, evaluate them now and save the results.
*/
if (!(ObjDesc->Common.Flags & AOPOBJ_DATA_VALID))
{
@@ -421,16 +412,16 @@ AcpiExLoadOp (
/* Get the table header first so we can get the table length */
- Table = ACPI_ALLOCATE (sizeof (ACPI_TABLE_HEADER));
- if (!Table)
+ TableHeader = ACPI_ALLOCATE (sizeof (ACPI_TABLE_HEADER));
+ if (!TableHeader)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
Status = AcpiExRegionRead (ObjDesc, sizeof (ACPI_TABLE_HEADER),
- ACPI_CAST_PTR (UINT8, Table));
- Length = Table->Length;
- ACPI_FREE (Table);
+ ACPI_CAST_PTR (UINT8, TableHeader));
+ Length = TableHeader->Length;
+ ACPI_FREE (TableHeader);
if (ACPI_FAILURE (Status))
{
@@ -462,8 +453,8 @@ AcpiExLoadOp (
/* Allocate a buffer for the table */
- TableDesc.Pointer = ACPI_ALLOCATE (Length);
- if (!TableDesc.Pointer)
+ Table = ACPI_ALLOCATE (Length);
+ if (!Table)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
@@ -471,17 +462,14 @@ AcpiExLoadOp (
/* Read the entire table */
Status = AcpiExRegionRead (ObjDesc, Length,
- ACPI_CAST_PTR (UINT8, TableDesc.Pointer));
+ ACPI_CAST_PTR (UINT8, Table));
if (ACPI_FAILURE (Status))
{
- ACPI_FREE (TableDesc.Pointer);
+ ACPI_FREE (Table);
return_ACPI_STATUS (Status);
}
-
- TableDesc.Address = ObjDesc->Region.Address;
break;
-
case ACPI_TYPE_BUFFER: /* Buffer or resolved RegionField */
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
@@ -496,8 +484,9 @@ AcpiExLoadOp (
/* Get the actual table length from the table header */
- Table = ACPI_CAST_PTR (ACPI_TABLE_HEADER, ObjDesc->Buffer.Pointer);
- Length = Table->Length;
+ TableHeader = ACPI_CAST_PTR (
+ ACPI_TABLE_HEADER, ObjDesc->Buffer.Pointer);
+ Length = TableHeader->Length;
/* Table cannot extend beyond the buffer */
@@ -511,46 +500,49 @@ AcpiExLoadOp (
}
/*
- * Copy the table from the buffer because the buffer could be modified
- * or even deleted in the future
+ * Copy the table from the buffer because the buffer could be
+ * modified or even deleted in the future
*/
- TableDesc.Pointer = ACPI_ALLOCATE (Length);
- if (!TableDesc.Pointer)
+ Table = ACPI_ALLOCATE (Length);
+ if (!Table)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
- ACPI_MEMCPY (TableDesc.Pointer, Table, Length);
- TableDesc.Address = ACPI_TO_INTEGER (TableDesc.Pointer);
+ memcpy (Table, TableHeader, Length);
break;
-
default:
+
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
- /* Validate table checksum (will not get validated in TbAddTable) */
+ /* Install the new table into the local data structures */
+
+ ACPI_INFO (("Dynamic OEM Table Load:"));
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- Status = AcpiTbVerifyChecksum (TableDesc.Pointer, Length);
+ Status = AcpiTbInstallStandardTable (ACPI_PTR_TO_PHYSADDR (Table),
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, TRUE, TRUE,
+ &TableIndex);
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
if (ACPI_FAILURE (Status))
{
- ACPI_FREE (TableDesc.Pointer);
+ /* Delete allocated table buffer */
+
+ ACPI_FREE (Table);
return_ACPI_STATUS (Status);
}
- /* Complete the table descriptor */
-
- TableDesc.Length = Length;
- TableDesc.Flags = ACPI_TABLE_ORIGIN_ALLOCATED;
-
- /* Install the new table into the local data structures */
-
- Status = AcpiTbAddTable (&TableDesc, &TableIndex);
+ /*
+ * Note: Now table is "INSTALLED", it must be validated before
+ * loading.
+ */
+ Status = AcpiTbValidateTable (
+ &AcpiGbl_RootTableList.Tables[TableIndex]);
if (ACPI_FAILURE (Status))
{
- /* Delete allocated table buffer */
-
- AcpiTbDeleteTable (&TableDesc);
return_ACPI_STATUS (Status);
}
@@ -582,9 +574,6 @@ AcpiExLoadOp (
return_ACPI_STATUS (Status);
}
- ACPI_INFO ((AE_INFO, "Dynamic OEM Table Load:"));
- AcpiTbPrintTableHeader (0, TableDesc.Pointer);
-
/* Remove the reference by added by AcpiExStore above */
AcpiUtRemoveReference (DdbHandle);
@@ -593,8 +582,8 @@ AcpiExLoadOp (
if (AcpiGbl_TableHandler)
{
- (void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_LOAD, TableDesc.Pointer,
- AcpiGbl_TableHandlerContext);
+ (void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_LOAD, Table,
+ AcpiGbl_TableHandlerContext);
}
return_ACPI_STATUS (Status);
@@ -627,6 +616,14 @@ AcpiExUnloadTable (
/*
+ * Temporarily emit a warning so that the ASL for the machine can be
+ * hopefully obtained. This is to say that the Unload() operator is
+ * extremely rare if not completely unused.
+ */
+ ACPI_WARNING ((AE_INFO,
+ "Received request to unload an ACPI table"));
+
+ /*
* Validate the handle
* Although the handle is partially validated in AcpiExReconfiguration()
* when it calls AcpiExResolveOperands(), the handle is more completely
@@ -641,7 +638,7 @@ AcpiExUnloadTable (
(DdbHandle->Common.Type != ACPI_TYPE_LOCAL_REFERENCE) ||
(!(DdbHandle->Common.Flags & AOPOBJ_DATA_VALID)))
{
- return_ACPI_STATUS (AE_BAD_PARAMETER);
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
/* Get the table index from the DdbHandle */
@@ -663,7 +660,7 @@ AcpiExUnloadTable (
if (ACPI_SUCCESS (Status))
{
(void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_UNLOAD, Table,
- AcpiGbl_TableHandlerContext);
+ AcpiGbl_TableHandlerContext);
}
}
@@ -685,4 +682,3 @@ AcpiExUnloadTable (
DdbHandle->Common.Flags &= ~AOPOBJ_DATA_VALID;
return_ACPI_STATUS (AE_OK);
}
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exconvrt.c b/usr/src/uts/intel/io/acpica/executer/exconvrt.c
index 6894acdec7..ea6f82620d 100644
--- a/usr/src/uts/intel/io/acpica/executer/exconvrt.c
+++ b/usr/src/uts/intel/io/acpica/executer/exconvrt.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EXCONVRT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -114,6 +111,7 @@ AcpiExConvertToInteger (
break;
default:
+
return_ACPI_STATUS (AE_TYPE);
}
@@ -133,21 +131,20 @@ AcpiExConvertToInteger (
switch (ObjDesc->Common.Type)
{
case ACPI_TYPE_STRING:
-
/*
* Convert string to an integer - for most cases, the string must be
* hexadecimal as per the ACPI specification. The only exception (as
* of ACPI 3.0) is that the ToInteger() operator allows both decimal
* and hexadecimal strings (hex prefixed with "0x").
*/
- Status = AcpiUtStrtoul64 ((char *) Pointer, Flags, &Result);
+ Status = AcpiUtStrtoul64 ((char *) Pointer, Flags,
+ AcpiGbl_IntegerByteWidth, &Result);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
break;
-
case ACPI_TYPE_BUFFER:
/* Check for zero-length buffer */
@@ -179,10 +176,10 @@ AcpiExConvertToInteger (
}
break;
-
default:
/* No other types can get here */
+
break;
}
@@ -199,7 +196,7 @@ AcpiExConvertToInteger (
/* Save the Result */
- AcpiExTruncateFor32bitTable (ReturnDesc);
+ (void) AcpiExTruncateFor32bitTable (ReturnDesc);
*ResultDesc = ReturnDesc;
return_ACPI_STATUS (AE_OK);
}
@@ -242,7 +239,6 @@ AcpiExConvertToBuffer (
case ACPI_TYPE_INTEGER:
-
/*
* Create a new Buffer object.
* Need enough space for one integer
@@ -256,14 +252,10 @@ AcpiExConvertToBuffer (
/* Copy the integer to the buffer, LSB first */
NewBuf = ReturnDesc->Buffer.Pointer;
- ACPI_MEMCPY (NewBuf,
- &ObjDesc->Integer.Value,
- AcpiGbl_IntegerByteWidth);
+ memcpy (NewBuf, &ObjDesc->Integer.Value, AcpiGbl_IntegerByteWidth);
break;
-
case ACPI_TYPE_STRING:
-
/*
* Create a new Buffer object
* Size will be the string length
@@ -273,8 +265,8 @@ AcpiExConvertToBuffer (
* ASL/AML code that depends on the null being transferred to the new
* buffer.
*/
- ReturnDesc = AcpiUtCreateBufferObject (
- (ACPI_SIZE) ObjDesc->String.Length + 1);
+ ReturnDesc = AcpiUtCreateBufferObject ((ACPI_SIZE)
+ ObjDesc->String.Length + 1);
if (!ReturnDesc)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -283,12 +275,12 @@ AcpiExConvertToBuffer (
/* Copy the string to the buffer */
NewBuf = ReturnDesc->Buffer.Pointer;
- ACPI_STRNCPY ((char *) NewBuf, (char *) ObjDesc->String.Pointer,
+ strncpy ((char *) NewBuf, (char *) ObjDesc->String.Pointer,
ObjDesc->String.Length);
break;
-
default:
+
return_ACPI_STATUS (AE_TYPE);
}
@@ -344,15 +336,18 @@ AcpiExConvertToAscii (
switch (DataWidth)
{
case 1:
+
DecimalLength = ACPI_MAX8_DECIMAL_DIGITS;
break;
case 4:
+
DecimalLength = ACPI_MAX32_DECIMAL_DIGITS;
break;
case 8:
default:
+
DecimalLength = ACPI_MAX64_DECIMAL_DIGITS;
break;
}
@@ -394,7 +389,8 @@ AcpiExConvertToAscii (
{
/* Get one hex digit, most significant digits first */
- String[k] = (UINT8) AcpiUtHexToAsciiChar (Integer, ACPI_MUL_4 (j));
+ String[k] = (UINT8)
+ AcpiUtHexToAsciiChar (Integer, ACPI_MUL_4 (j));
k++;
}
break;
@@ -461,7 +457,6 @@ AcpiExConvertToString (
*ResultDesc = ObjDesc;
return_ACPI_STATUS (AE_OK);
-
case ACPI_TYPE_INTEGER:
switch (Type)
@@ -496,8 +491,8 @@ AcpiExConvertToString (
/* Convert integer to string */
- StringLength = AcpiExConvertToAscii (ObjDesc->Integer.Value, Base,
- NewBuf, AcpiGbl_IntegerByteWidth);
+ StringLength = AcpiExConvertToAscii (
+ ObjDesc->Integer.Value, Base, NewBuf, AcpiGbl_IntegerByteWidth);
/* Null terminate at the correct place */
@@ -505,7 +500,6 @@ AcpiExConvertToString (
NewBuf [StringLength] = 0;
break;
-
case ACPI_TYPE_BUFFER:
/* Setup string length, base, and separator */
@@ -587,8 +581,7 @@ AcpiExConvertToString (
for (i = 0; i < ObjDesc->Buffer.Length; i++)
{
NewBuf += AcpiExConvertToAscii (
- (UINT64) ObjDesc->Buffer.Pointer[i], Base,
- NewBuf, 1);
+ (UINT64) ObjDesc->Buffer.Pointer[i], Base, NewBuf, 1);
*NewBuf++ = Separator; /* each separated by a comma or space */
}
@@ -604,6 +597,7 @@ AcpiExConvertToString (
break;
default:
+
return_ACPI_STATUS (AE_TYPE);
}
@@ -663,6 +657,7 @@ AcpiExConvertToTargetType (
break;
default:
+
/* No conversion allowed for these types */
if (DestinationType != SourceDesc->Common.Type)
@@ -676,8 +671,8 @@ AcpiExConvertToTargetType (
}
break;
-
case ARGI_TARGETREF:
+ case ARGI_STORE_TARGET:
switch (DestinationType)
{
@@ -689,21 +684,18 @@ AcpiExConvertToTargetType (
* These types require an Integer operand. We can convert
* a Buffer or a String to an Integer if necessary.
*/
- Status = AcpiExConvertToInteger (SourceDesc, ResultDesc,
- 16);
+ Status = AcpiExConvertToInteger (SourceDesc, ResultDesc, 16);
break;
-
case ACPI_TYPE_STRING:
/*
* The operand must be a String. We can convert an
* Integer or Buffer if necessary
*/
Status = AcpiExConvertToString (SourceDesc, ResultDesc,
- ACPI_IMPLICIT_CONVERT_HEX);
+ ACPI_IMPLICIT_CONVERT_HEX);
break;
-
case ACPI_TYPE_BUFFER:
/*
* The operand must be a Buffer. We can convert an
@@ -712,24 +704,24 @@ AcpiExConvertToTargetType (
Status = AcpiExConvertToBuffer (SourceDesc, ResultDesc);
break;
-
default:
- ACPI_ERROR ((AE_INFO, "Bad destination type during conversion: 0x%X",
+
+ ACPI_ERROR ((AE_INFO,
+ "Bad destination type during conversion: 0x%X",
DestinationType));
Status = AE_AML_INTERNAL;
break;
}
break;
-
case ARGI_REFERENCE:
/*
* CreateXxxxField cases - we are storing the field object into the name
*/
break;
-
default:
+
ACPI_ERROR ((AE_INFO,
"Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s",
GET_CURRENT_ARG_TYPE (WalkState->OpInfo->RuntimeArgs),
@@ -750,5 +742,3 @@ AcpiExConvertToTargetType (
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/excreate.c b/usr/src/uts/intel/io/acpica/executer/excreate.c
index fe93e0e940..26dd872a3c 100644
--- a/usr/src/uts/intel/io/acpica/executer/excreate.c
+++ b/usr/src/uts/intel/io/acpica/executer/excreate.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXCREATE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -89,7 +87,7 @@ AcpiExCreateAlias (
{
/*
* Dereference an existing alias so that we don't create a chain
- * of aliases. With this code, we guarantee that an alias is
+ * of aliases. With this code, we guarantee that an alias is
* always exactly one level of indirection away from the
* actual aliased name.
*/
@@ -99,7 +97,7 @@ AcpiExCreateAlias (
/*
* For objects that can never change (i.e., the NS node will
* permanently point to the same object), we can simply attach
- * the object to the new NS node. For other objects (such as
+ * the object to the new NS node. For other objects (such as
* Integers, buffers, etc.), we have to point the Alias node
* to the original Node.
*/
@@ -113,7 +111,6 @@ AcpiExCreateAlias (
case ACPI_TYPE_BUFFER:
case ACPI_TYPE_PACKAGE:
case ACPI_TYPE_BUFFER_FIELD:
-
/*
* These types open a new scope, so we need the NS node in order to access
* any children.
@@ -123,7 +120,6 @@ AcpiExCreateAlias (
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_THERMAL:
case ACPI_TYPE_LOCAL_SCOPE:
-
/*
* The new alias has the type ALIAS and points to the original
* NS node, not the object itself.
@@ -133,7 +129,6 @@ AcpiExCreateAlias (
break;
case ACPI_TYPE_METHOD:
-
/*
* Control method aliases need to be differentiated
*/
@@ -147,12 +142,12 @@ AcpiExCreateAlias (
/*
* The new alias assumes the type of the target, and it points
- * to the same object. The reference count of the object has an
+ * to the same object. The reference count of the object has an
* additional reference to prevent deletion out from under either the
* target node or the alias Node
*/
Status = AcpiNsAttachObject (AliasNode,
- AcpiNsGetAttachedObject (TargetNode), TargetNode->Type);
+ AcpiNsGetAttachedObject (TargetNode), TargetNode->Type);
break;
}
@@ -197,7 +192,7 @@ AcpiExCreateEvent (
* that the event is created in an unsignalled state
*/
Status = AcpiOsCreateSemaphore (ACPI_NO_UNIT_LIMIT, 0,
- &ObjDesc->Event.OsSemaphore);
+ &ObjDesc->Event.OsSemaphore);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -205,8 +200,9 @@ AcpiExCreateEvent (
/* Attach object to the Node */
- Status = AcpiNsAttachObject ((ACPI_NAMESPACE_NODE *) WalkState->Operands[0],
- ObjDesc, ACPI_TYPE_EVENT);
+ Status = AcpiNsAttachObject (
+ (ACPI_NAMESPACE_NODE *) WalkState->Operands[0],
+ ObjDesc, ACPI_TYPE_EVENT);
Cleanup:
/*
@@ -265,7 +261,8 @@ AcpiExCreateMutex (
ObjDesc->Mutex.SyncLevel = (UINT8) WalkState->Operands[1]->Integer.Value;
ObjDesc->Mutex.Node = (ACPI_NAMESPACE_NODE *) WalkState->Operands[0];
- Status = AcpiNsAttachObject (ObjDesc->Mutex.Node, ObjDesc, ACPI_TYPE_MUTEX);
+ Status = AcpiNsAttachObject (
+ ObjDesc->Mutex.Node, ObjDesc, ACPI_TYPE_MUTEX);
Cleanup:
@@ -284,7 +281,7 @@ Cleanup:
*
* PARAMETERS: AmlStart - Pointer to the region declaration AML
* AmlLength - Max length of the declaration AML
- * RegionSpace - SpaceID for the region
+ * SpaceId - Address space ID for the region
* WalkState - Current state
*
* RETURN: Status
@@ -297,7 +294,7 @@ ACPI_STATUS
AcpiExCreateRegion (
UINT8 *AmlStart,
UINT32 AmlLength,
- UINT8 RegionSpace,
+ UINT8 SpaceId,
ACPI_WALK_STATE *WalkState)
{
ACPI_STATUS Status;
@@ -326,16 +323,19 @@ AcpiExCreateRegion (
* Space ID must be one of the predefined IDs, or in the user-defined
* range
*/
- if ((RegionSpace >= ACPI_NUM_PREDEFINED_REGIONS) &&
- (RegionSpace < ACPI_USER_REGION_BEGIN) &&
- (RegionSpace != ACPI_ADR_SPACE_DATA_TABLE))
+ if (!AcpiIsValidSpaceId (SpaceId))
{
- ACPI_ERROR ((AE_INFO, "Invalid AddressSpace type 0x%X", RegionSpace));
- return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
+ /*
+ * Print an error message, but continue. We don't want to abort
+ * a table load for this exception. Instead, if the region is
+ * actually used at runtime, abort the executing method.
+ */
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
}
ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Region Type - %s (0x%X)\n",
- AcpiUtGetRegionName (RegionSpace), RegionSpace));
+ AcpiUtGetRegionName (SpaceId), SpaceId));
/* Create the region descriptor */
@@ -350,16 +350,29 @@ AcpiExCreateRegion (
* Remember location in AML stream of address & length
* operands since they need to be evaluated at run time.
*/
- RegionObj2 = ObjDesc->Common.NextObject;
+ RegionObj2 = AcpiNsGetSecondaryObject (ObjDesc);
RegionObj2->Extra.AmlStart = AmlStart;
RegionObj2->Extra.AmlLength = AmlLength;
+ RegionObj2->Extra.Method_REG = NULL;
+ if (WalkState->ScopeInfo)
+ {
+ RegionObj2->Extra.ScopeNode = WalkState->ScopeInfo->Scope.Node;
+ }
+ else
+ {
+ RegionObj2->Extra.ScopeNode = Node;
+ }
/* Init the region from the operands */
- ObjDesc->Region.SpaceId = RegionSpace;
+ ObjDesc->Region.SpaceId = SpaceId;
ObjDesc->Region.Address = 0;
ObjDesc->Region.Length = 0;
ObjDesc->Region.Node = Node;
+ ObjDesc->Region.Handler = NULL;
+ ObjDesc->Common.Flags &=
+ ~(AOPOBJ_SETUP_COMPLETE | AOPOBJ_REG_CONNECTED |
+ AOPOBJ_OBJECT_INITIALIZED);
/* Install the new region object in the parent Node */
@@ -418,7 +431,7 @@ AcpiExCreateProcessor (
/* Install the processor object in the parent Node */
Status = AcpiNsAttachObject ((ACPI_NAMESPACE_NODE *) Operand[0],
- ObjDesc, ACPI_TYPE_PROCESSOR);
+ ObjDesc, ACPI_TYPE_PROCESSOR);
/* Remove local reference to the object */
@@ -469,7 +482,7 @@ AcpiExCreatePowerResource (
/* Install the power resource object in the parent Node */
Status = AcpiNsAttachObject ((ACPI_NAMESPACE_NODE *) Operand[0],
- ObjDesc, ACPI_TYPE_POWER);
+ ObjDesc, ACPI_TYPE_POWER);
/* Remove local reference to the object */
@@ -521,13 +534,15 @@ AcpiExCreateMethod (
ObjDesc->Method.AmlStart = AmlStart;
ObjDesc->Method.AmlLength = AmlLength;
+ ObjDesc->Method.Node = Operand[0];
/*
* Disassemble the method flags. Split off the ArgCount, Serialized
* flag, and SyncLevel for efficiency.
*/
MethodFlags = (UINT8) Operand[1]->Integer.Value;
- ObjDesc->Method.ParamCount = (UINT8) (MethodFlags & AML_METHOD_ARG_COUNT);
+ ObjDesc->Method.ParamCount = (UINT8)
+ (MethodFlags & AML_METHOD_ARG_COUNT);
/*
* Get the SyncLevel. If method is serialized, a mutex will be
@@ -548,7 +563,7 @@ AcpiExCreateMethod (
/* Attach the new object to the method Node */
Status = AcpiNsAttachObject ((ACPI_NAMESPACE_NODE *) Operand[0],
- ObjDesc, ACPI_TYPE_METHOD);
+ ObjDesc, ACPI_TYPE_METHOD);
/* Remove local reference to the object */
@@ -560,5 +575,3 @@ Exit:
AcpiUtRemoveReference (Operand[1]);
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exdebug.c b/usr/src/uts/intel/io/acpica/executer/exdebug.c
index 62bf810c79..0832337e48 100644
--- a/usr/src/uts/intel/io/acpica/executer/exdebug.c
+++ b/usr/src/uts/intel/io/acpica/executer/exdebug.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXDEBUG_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -82,6 +80,9 @@ AcpiExDoDebugObject (
UINT32 Index)
{
UINT32 i;
+ UINT32 Timer;
+ ACPI_OPERAND_OBJECT *ObjectDesc;
+ UINT32 Value;
ACPI_FUNCTION_TRACE_PTR (ExDoDebugObject, SourceDesc);
@@ -95,20 +96,52 @@ AcpiExDoDebugObject (
return_VOID;
}
+ /* Null string or newline -- don't emit the line header */
+
+ if (SourceDesc &&
+ (ACPI_GET_DESCRIPTOR_TYPE (SourceDesc) == ACPI_DESC_TYPE_OPERAND) &&
+ (SourceDesc->Common.Type == ACPI_TYPE_STRING))
+ {
+ if ((SourceDesc->String.Length == 0) ||
+ ((SourceDesc->String.Length == 1) &&
+ (*SourceDesc->String.Pointer == '\n')))
+ {
+ AcpiOsPrintf ("\n");
+ return_VOID;
+ }
+ }
+
/*
* Print line header as long as we are not in the middle of an
* object display
*/
if (!((Level > 0) && Index == 0))
{
- AcpiOsPrintf ("[ACPI Debug] %*s", Level, " ");
+ if (AcpiGbl_DisplayDebugTimer)
+ {
+ /*
+ * We will emit the current timer value (in microseconds) with each
+ * debug output. Only need the lower 26 bits. This allows for 67
+ * million microseconds or 67 seconds before rollover.
+ *
+ * Convert 100 nanosecond units to microseconds
+ */
+ Timer = ((UINT32) AcpiOsGetTimer () / 10);
+ Timer &= 0x03FFFFFF;
+
+ AcpiOsPrintf ("[ACPI Debug T=0x%8.8X] %*s", Timer, Level, " ");
+ }
+ else
+ {
+ AcpiOsPrintf ("[ACPI Debug] %*s", Level, " ");
+ }
}
/* Display the index for package output only */
if (Index > 0)
{
- AcpiOsPrintf ("(%.2u) ", Index-1);
+ AcpiOsPrintf ("(%.2u) ", Index - 1);
}
if (!SourceDesc)
@@ -119,7 +152,13 @@ AcpiExDoDebugObject (
if (ACPI_GET_DESCRIPTOR_TYPE (SourceDesc) == ACPI_DESC_TYPE_OPERAND)
{
- AcpiOsPrintf ("%s ", AcpiUtGetObjectTypeName (SourceDesc));
+ /* No object type prefix needed for integers and strings */
+
+ if ((SourceDesc->Common.Type != ACPI_TYPE_INTEGER) &&
+ (SourceDesc->Common.Type != ACPI_TYPE_STRING))
+ {
+ AcpiOsPrintf ("%s ", AcpiUtGetObjectTypeName (SourceDesc));
+ }
if (!AcpiUtValidInternalObject (SourceDesc))
{
@@ -129,9 +168,9 @@ AcpiExDoDebugObject (
}
else if (ACPI_GET_DESCRIPTOR_TYPE (SourceDesc) == ACPI_DESC_TYPE_NAMED)
{
- AcpiOsPrintf ("%s: %p\n",
+ AcpiOsPrintf ("%s (Node %p)\n",
AcpiUtGetTypeName (((ACPI_NAMESPACE_NODE *) SourceDesc)->Type),
- SourceDesc);
+ SourceDesc);
return_VOID;
}
else
@@ -162,20 +201,19 @@ AcpiExDoDebugObject (
case ACPI_TYPE_BUFFER:
AcpiOsPrintf ("[0x%.2X]\n", (UINT32) SourceDesc->Buffer.Length);
- AcpiUtDumpBuffer2 (SourceDesc->Buffer.Pointer,
+ AcpiUtDumpBuffer (SourceDesc->Buffer.Pointer,
(SourceDesc->Buffer.Length < 256) ?
- SourceDesc->Buffer.Length : 256, DB_BYTE_DISPLAY);
+ SourceDesc->Buffer.Length : 256, DB_BYTE_DISPLAY, 0);
break;
case ACPI_TYPE_STRING:
- AcpiOsPrintf ("[0x%.2X] \"%s\"\n",
- SourceDesc->String.Length, SourceDesc->String.Pointer);
+ AcpiOsPrintf ("\"%s\"\n", SourceDesc->String.Pointer);
break;
case ACPI_TYPE_PACKAGE:
- AcpiOsPrintf ("[Contains 0x%.2X Elements]\n",
+ AcpiOsPrintf ("(Contains 0x%.2X Elements):\n",
SourceDesc->Package.Count);
/* Output the entire contents of the package */
@@ -183,7 +221,7 @@ AcpiExDoDebugObject (
for (i = 0; i < SourceDesc->Package.Count; i++)
{
AcpiExDoDebugObject (SourceDesc->Package.Elements[i],
- Level+4, i+1);
+ Level + 4, i + 1);
}
break;
@@ -205,9 +243,10 @@ AcpiExDoDebugObject (
/* Case for DdbHandle */
AcpiOsPrintf ("Table Index 0x%X\n", SourceDesc->Reference.Value);
- return;
+ return_VOID;
default:
+
break;
}
@@ -218,7 +257,7 @@ AcpiExDoDebugObject (
if (SourceDesc->Reference.Node)
{
if (ACPI_GET_DESCRIPTOR_TYPE (SourceDesc->Reference.Node) !=
- ACPI_DESC_TYPE_NAMED)
+ ACPI_DESC_TYPE_NAMED)
{
AcpiOsPrintf (" %p - Not a valid namespace node\n",
SourceDesc->Reference.Node);
@@ -241,8 +280,9 @@ AcpiExDoDebugObject (
break;
default:
+
AcpiExDoDebugObject ((SourceDesc->Reference.Node)->Object,
- Level+4, 0);
+ Level + 4, 0);
break;
}
}
@@ -250,23 +290,61 @@ AcpiExDoDebugObject (
else if (SourceDesc->Reference.Object)
{
if (ACPI_GET_DESCRIPTOR_TYPE (SourceDesc->Reference.Object) ==
- ACPI_DESC_TYPE_NAMED)
+ ACPI_DESC_TYPE_NAMED)
{
- AcpiExDoDebugObject (((ACPI_NAMESPACE_NODE *)
- SourceDesc->Reference.Object)->Object,
- Level+4, 0);
+ /* Reference object is a namespace node */
+
+ AcpiExDoDebugObject (ACPI_CAST_PTR (ACPI_OPERAND_OBJECT,
+ SourceDesc->Reference.Object),
+ Level + 4, 0);
}
else
{
- AcpiExDoDebugObject (SourceDesc->Reference.Object,
- Level+4, 0);
+ ObjectDesc = SourceDesc->Reference.Object;
+ Value = SourceDesc->Reference.Value;
+
+ switch (ObjectDesc->Common.Type)
+ {
+ case ACPI_TYPE_BUFFER:
+
+ AcpiOsPrintf ("Buffer[%u] = 0x%2.2X\n",
+ Value, *SourceDesc->Reference.IndexPointer);
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ AcpiOsPrintf ("String[%u] = \"%c\" (0x%2.2X)\n",
+ Value, *SourceDesc->Reference.IndexPointer,
+ *SourceDesc->Reference.IndexPointer);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ AcpiOsPrintf ("Package[%u] = ", Value);
+ if (!(*SourceDesc->Reference.Where))
+ {
+ AcpiOsPrintf ("[Uninitialized Package Element]\n");
+ }
+ else
+ {
+ AcpiExDoDebugObject (*SourceDesc->Reference.Where,
+ Level+4, 0);
+ }
+ break;
+
+ default:
+
+ AcpiOsPrintf ("Unknown Reference object type %X\n",
+ ObjectDesc->Common.Type);
+ break;
+ }
}
}
break;
default:
- AcpiOsPrintf ("%p\n", SourceDesc);
+ AcpiOsPrintf ("(Descriptor %p)\n", SourceDesc);
break;
}
@@ -274,5 +352,3 @@ AcpiExDoDebugObject (
return_VOID;
}
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exdump.c b/usr/src/uts/intel/io/acpica/executer/exdump.c
index 60003e6154..f5d5fd03a5 100644
--- a/usr/src/uts/intel/io/acpica/executer/exdump.c
+++ b/usr/src/uts/intel/io/acpica/executer/exdump.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXDUMP_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -62,13 +60,13 @@
static void
AcpiExOutString (
- char *Title,
- char *Value);
+ const char *Title,
+ const char *Value);
static void
AcpiExOutPointer (
- char *Title,
- void *Value);
+ const char *Title,
+ const void *Value);
static void
AcpiExDumpObject (
@@ -114,13 +112,14 @@ static ACPI_EXDUMP_INFO AcpiExDumpBuffer[5] =
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpBuffer), NULL},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Buffer.Length), "Length"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Buffer.Pointer), "Pointer"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Buffer.Node), "Parent Node"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Buffer.Node), "Parent Node"},
{ACPI_EXD_BUFFER, 0, NULL}
};
-static ACPI_EXDUMP_INFO AcpiExDumpPackage[5] =
+static ACPI_EXDUMP_INFO AcpiExDumpPackage[6] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpPackage), NULL},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Package.Node), "Parent Node"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Package.Flags), "Flags"},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Package.Count), "Elements"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Package.Elements), "Element List"},
@@ -130,9 +129,9 @@ static ACPI_EXDUMP_INFO AcpiExDumpPackage[5] =
static ACPI_EXDUMP_INFO AcpiExDumpDevice[4] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpDevice), NULL},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Device.Handler), "Handler"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Device.SystemNotify), "System Notify"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Device.DeviceNotify), "Device Notify"}
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Device.NotifyList[0]), "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Device.NotifyList[1]), "Device Notify"},
+ {ACPI_EXD_HDLR_LIST,ACPI_EXD_OFFSET (Device.Handler), "Handler"}
};
static ACPI_EXDUMP_INFO AcpiExDumpEvent[2] =
@@ -154,33 +153,36 @@ static ACPI_EXDUMP_INFO AcpiExDumpMethod[9] =
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Method.AmlStart), "Aml Start"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpMutex[5] =
+static ACPI_EXDUMP_INFO AcpiExDumpMutex[6] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpMutex), NULL},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Mutex.SyncLevel), "Sync Level"},
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Mutex.OriginalSyncLevel), "Original Sync Level"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Mutex.OwnerThread), "Owner Thread"},
{ACPI_EXD_UINT16, ACPI_EXD_OFFSET (Mutex.AcquisitionDepth), "Acquire Depth"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Mutex.OsMutex), "OsMutex"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpRegion[7] =
+static ACPI_EXDUMP_INFO AcpiExDumpRegion[8] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpRegion), NULL},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Region.SpaceId), "Space Id"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Region.Flags), "Flags"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Region.Node), "Parent Node"},
{ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET (Region.Address), "Address"},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Region.Length), "Length"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Region.Handler), "Handler"},
+ {ACPI_EXD_HDLR_LIST,ACPI_EXD_OFFSET (Region.Handler), "Handler"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Region.Next), "Next"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpPower[5] =
+static ACPI_EXDUMP_INFO AcpiExDumpPower[6] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpPower), NULL},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (PowerResource.SystemLevel), "System Level"},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (PowerResource.ResourceOrder), "Resource Order"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (PowerResource.SystemNotify), "System Notify"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (PowerResource.DeviceNotify), "Device Notify"}
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (PowerResource.NotifyList[0]), "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (PowerResource.NotifyList[1]), "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (PowerResource.Handler), "Handler"}
};
static ACPI_EXDUMP_INFO AcpiExDumpProcessor[7] =
@@ -189,16 +191,16 @@ static ACPI_EXDUMP_INFO AcpiExDumpProcessor[7] =
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Processor.ProcId), "Processor ID"},
{ACPI_EXD_UINT8 , ACPI_EXD_OFFSET (Processor.Length), "Length"},
{ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET (Processor.Address), "Address"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Processor.SystemNotify), "System Notify"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Processor.DeviceNotify), "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Processor.NotifyList[0]), "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Processor.NotifyList[1]), "Device Notify"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Processor.Handler), "Handler"}
};
static ACPI_EXDUMP_INFO AcpiExDumpThermal[4] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpThermal), NULL},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (ThermalZone.SystemNotify), "System Notify"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (ThermalZone.DeviceNotify), "Device Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (ThermalZone.NotifyList[0]), "System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (ThermalZone.NotifyList[1]), "Device Notify"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (ThermalZone.Handler), "Handler"}
};
@@ -209,11 +211,13 @@ static ACPI_EXDUMP_INFO AcpiExDumpBufferField[3] =
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (BufferField.BufferObj), "Buffer Object"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpRegionField[3] =
+static ACPI_EXDUMP_INFO AcpiExDumpRegionField[5] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpRegionField), NULL},
{ACPI_EXD_FIELD, 0, NULL},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Field.RegionObj), "Region Object"}
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Field.AccessLength), "AccessLength"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Field.RegionObj), "Region Object"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Field.ResourceBuffer), "ResourceBuffer"}
};
static ACPI_EXDUMP_INFO AcpiExDumpBankField[5] =
@@ -234,15 +238,16 @@ static ACPI_EXDUMP_INFO AcpiExDumpIndexField[5] =
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (IndexField.DataObj), "Data Object"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpReference[8] =
+static ACPI_EXDUMP_INFO AcpiExDumpReference[9] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpReference), NULL},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Reference.Class), "Class"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Reference.TargetType), "Target Type"},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Reference.Value), "Value"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Reference.Object), "Object Desc"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Reference.Node), "Node"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Reference.Node), "Node"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Reference.Where), "Where"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Reference.IndexPointer), "Index Pointer"},
{ACPI_EXD_REFERENCE,0, NULL}
};
@@ -250,28 +255,49 @@ static ACPI_EXDUMP_INFO AcpiExDumpAddressHandler[6] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpAddressHandler), NULL},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (AddressSpace.SpaceId), "Space Id"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (AddressSpace.Next), "Next"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (AddressSpace.RegionList), "Region List"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (AddressSpace.Node), "Node"},
+ {ACPI_EXD_HDLR_LIST,ACPI_EXD_OFFSET (AddressSpace.Next), "Next"},
+ {ACPI_EXD_RGN_LIST, ACPI_EXD_OFFSET (AddressSpace.RegionList), "Region List"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (AddressSpace.Node), "Node"},
{ACPI_EXD_POINTER, ACPI_EXD_OFFSET (AddressSpace.Context), "Context"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpNotify[3] =
+static ACPI_EXDUMP_INFO AcpiExDumpNotify[7] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpNotify), NULL},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Node), "Node"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Context), "Context"}
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Notify.Node), "Node"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Notify.HandlerType), "Handler Type"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Handler), "Handler"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Context), "Context"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Next[0]), "Next System Notify"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Notify.Next[1]), "Next Device Notify"}
+};
+
+static ACPI_EXDUMP_INFO AcpiExDumpExtra[6] =
+{
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpExtra), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Extra.Method_REG), "_REG Method"},
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (Extra.ScopeNode), "Scope Node"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Extra.RegionContext), "Region Context"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Extra.AmlStart), "Aml Start"},
+ {ACPI_EXD_UINT32, ACPI_EXD_OFFSET (Extra.AmlLength), "Aml Length"}
};
+static ACPI_EXDUMP_INFO AcpiExDumpData[3] =
+{
+ {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpData), NULL},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Data.Handler), "Handler"},
+ {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (Data.Pointer), "Raw Data"}
+};
/* Miscellaneous tables */
-static ACPI_EXDUMP_INFO AcpiExDumpCommon[4] =
+static ACPI_EXDUMP_INFO AcpiExDumpCommon[5] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpCommon), NULL},
{ACPI_EXD_TYPE , 0, NULL},
{ACPI_EXD_UINT16, ACPI_EXD_OFFSET (Common.ReferenceCount), "Reference Count"},
- {ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Common.Flags), "Flags"}
+ {ACPI_EXD_UINT8, ACPI_EXD_OFFSET (Common.Flags), "Flags"},
+ {ACPI_EXD_LIST, ACPI_EXD_OFFSET (Common.NextObject), "Object List"}
};
static ACPI_EXDUMP_INFO AcpiExDumpFieldCommon[7] =
@@ -282,16 +308,18 @@ static ACPI_EXDUMP_INFO AcpiExDumpFieldCommon[7] =
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (CommonField.BitLength), "Bit Length"},
{ACPI_EXD_UINT8, ACPI_EXD_OFFSET (CommonField.StartFieldBitOffset),"Field Bit Offset"},
{ACPI_EXD_UINT32, ACPI_EXD_OFFSET (CommonField.BaseByteOffset), "Base Byte Offset"},
- {ACPI_EXD_POINTER, ACPI_EXD_OFFSET (CommonField.Node), "Parent Node"}
+ {ACPI_EXD_NODE, ACPI_EXD_OFFSET (CommonField.Node), "Parent Node"}
};
-static ACPI_EXDUMP_INFO AcpiExDumpNode[5] =
+static ACPI_EXDUMP_INFO AcpiExDumpNode[7] =
{
{ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE (AcpiExDumpNode), NULL},
{ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET (Flags), "Flags"},
{ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET (OwnerId), "Owner Id"},
- {ACPI_EXD_POINTER, ACPI_EXD_NSOFFSET (Child), "Child List"},
- {ACPI_EXD_POINTER, ACPI_EXD_NSOFFSET (Peer), "Next Peer"}
+ {ACPI_EXD_LIST, ACPI_EXD_NSOFFSET (Object), "Object List"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET (Parent), "Parent"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET (Child), "Child"},
+ {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET (Peer), "Peer"}
};
@@ -326,7 +354,9 @@ static ACPI_EXDUMP_INFO *AcpiExDumpInfo[] =
AcpiExDumpAddressHandler,
NULL,
NULL,
- NULL
+ NULL,
+ AcpiExDumpExtra,
+ AcpiExDumpData
};
@@ -350,8 +380,12 @@ AcpiExDumpObject (
ACPI_EXDUMP_INFO *Info)
{
UINT8 *Target;
- char *Name;
+ const char *Name;
UINT8 Count;
+ ACPI_OPERAND_OBJECT *Start;
+ ACPI_OPERAND_OBJECT *Data = NULL;
+ ACPI_OPERAND_OBJECT *Next;
+ ACPI_NAMESPACE_NODE *Node;
if (!Info)
@@ -374,11 +408,13 @@ AcpiExDumpObject (
switch (Info->Opcode)
{
case ACPI_EXD_INIT:
+
break;
case ACPI_EXD_TYPE:
- AcpiExOutString ("Type", AcpiUtGetObjectTypeName (ObjDesc));
+ AcpiOsPrintf ("%20s : %2.2X [%s]\n", "Type",
+ ObjDesc->Common.Type, AcpiUtGetObjectTypeName (ObjDesc));
break;
case ACPI_EXD_UINT8:
@@ -416,7 +452,8 @@ AcpiExDumpObject (
case ACPI_EXD_BUFFER:
- ACPI_DUMP_BUFFER (ObjDesc->Buffer.Pointer, ObjDesc->Buffer.Length);
+ ACPI_DUMP_BUFFER (
+ ObjDesc->Buffer.Pointer, ObjDesc->Buffer.Length);
break;
case ACPI_EXD_PACKAGE:
@@ -434,11 +471,129 @@ AcpiExDumpObject (
case ACPI_EXD_REFERENCE:
- AcpiExOutString ("Class Name",
- ACPI_CAST_PTR (char, AcpiUtGetReferenceName (ObjDesc)));
+ AcpiExOutString ("Class Name", AcpiUtGetReferenceName (ObjDesc));
AcpiExDumpReferenceObj (ObjDesc);
break;
+ case ACPI_EXD_LIST:
+
+ Start = *ACPI_CAST_PTR (void *, Target);
+ Next = Start;
+
+ AcpiOsPrintf ("%20s : %p", Name, Next);
+ if (Next)
+ {
+ AcpiOsPrintf ("(%s %2.2X)",
+ AcpiUtGetObjectTypeName (Next), Next->Common.Type);
+
+ while (Next->Common.NextObject)
+ {
+ if ((Next->Common.Type == ACPI_TYPE_LOCAL_DATA) &&
+ !Data)
+ {
+ Data = Next;
+ }
+
+ Next = Next->Common.NextObject;
+ AcpiOsPrintf ("->%p(%s %2.2X)", Next,
+ AcpiUtGetObjectTypeName (Next), Next->Common.Type);
+
+ if ((Next == Start) || (Next == Data))
+ {
+ AcpiOsPrintf (
+ "\n**** Error: Object list appears to be circular linked");
+ break;
+ }
+ }
+ }
+
+ AcpiOsPrintf ("\n");
+ break;
+
+ case ACPI_EXD_HDLR_LIST:
+
+ Start = *ACPI_CAST_PTR (void *, Target);
+ Next = Start;
+
+ AcpiOsPrintf ("%20s : %p", Name, Next);
+ if (Next)
+ {
+ AcpiOsPrintf ("(%s %2.2X)",
+ AcpiUtGetObjectTypeName (Next),
+ Next->AddressSpace.SpaceId);
+
+ while (Next->AddressSpace.Next)
+ {
+ if ((Next->Common.Type == ACPI_TYPE_LOCAL_DATA) &&
+ !Data)
+ {
+ Data = Next;
+ }
+
+ Next = Next->AddressSpace.Next;
+ AcpiOsPrintf ("->%p(%s %2.2X)", Next,
+ AcpiUtGetObjectTypeName (Next),
+ Next->AddressSpace.SpaceId);
+
+ if ((Next == Start) || (Next == Data))
+ {
+ AcpiOsPrintf (
+ "\n**** Error: Handler list appears to be circular linked");
+ break;
+ }
+ }
+ }
+
+ AcpiOsPrintf ("\n");
+ break;
+
+ case ACPI_EXD_RGN_LIST:
+
+ Start = *ACPI_CAST_PTR (void *, Target);
+ Next = Start;
+
+ AcpiOsPrintf ("%20s : %p", Name, Next);
+ if (Next)
+ {
+ AcpiOsPrintf ("(%s %2.2X)",
+ AcpiUtGetObjectTypeName (Next), Next->Common.Type);
+
+ while (Next->Region.Next)
+ {
+ if ((Next->Common.Type == ACPI_TYPE_LOCAL_DATA) &&
+ !Data)
+ {
+ Data = Next;
+ }
+
+ Next = Next->Region.Next;
+ AcpiOsPrintf ("->%p(%s %2.2X)", Next,
+ AcpiUtGetObjectTypeName (Next), Next->Common.Type);
+
+ if ((Next == Start) || (Next == Data))
+ {
+ AcpiOsPrintf (
+ "\n**** Error: Region list appears to be circular linked");
+ break;
+ }
+ }
+ }
+
+ AcpiOsPrintf ("\n");
+ break;
+
+ case ACPI_EXD_NODE:
+
+ Node = *ACPI_CAST_PTR (ACPI_NAMESPACE_NODE *, Target);
+
+ AcpiOsPrintf ("%20s : %p", Name, Node);
+ if (Node)
+ {
+ AcpiOsPrintf (" [%4.4s]", Node->Name.Ascii);
+ }
+ AcpiOsPrintf ("\n");
+ break;
+
default:
AcpiOsPrintf ("**** Invalid table opcode [%X] ****\n",
@@ -477,7 +632,9 @@ AcpiExDumpOperand (
ACPI_FUNCTION_NAME (ExDumpOperand)
- if (!((ACPI_LV_EXEC & AcpiDbgLevel) && (_COMPONENT & AcpiDbgLayer)))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_EXEC, _COMPONENT))
{
return;
}
@@ -524,7 +681,8 @@ AcpiExDumpOperand (
{
case ACPI_TYPE_LOCAL_REFERENCE:
- AcpiOsPrintf ("Reference: [%s] ", AcpiUtGetReferenceName (ObjDesc));
+ AcpiOsPrintf ("Reference: [%s] ",
+ AcpiUtGetReferenceName (ObjDesc));
switch (ObjDesc->Reference.Class)
{
@@ -533,19 +691,16 @@ AcpiExDumpOperand (
AcpiOsPrintf ("\n");
break;
-
case ACPI_REFCLASS_INDEX:
AcpiOsPrintf ("%p\n", ObjDesc->Reference.Object);
break;
-
case ACPI_REFCLASS_TABLE:
AcpiOsPrintf ("Table Index %X\n", ObjDesc->Reference.Value);
break;
-
case ACPI_REFCLASS_REFOF:
AcpiOsPrintf ("%p [%s]\n", ObjDesc->Reference.Object,
@@ -553,20 +708,18 @@ AcpiExDumpOperand (
ObjDesc->Reference.Object)->Common.Type));
break;
-
case ACPI_REFCLASS_NAME:
- AcpiOsPrintf ("- [%4.4s]\n", ObjDesc->Reference.Node->Name.Ascii);
+ AcpiOsPrintf ("- [%4.4s]\n",
+ ObjDesc->Reference.Node->Name.Ascii);
break;
-
case ACPI_REFCLASS_ARG:
case ACPI_REFCLASS_LOCAL:
AcpiOsPrintf ("%X\n", ObjDesc->Reference.Value);
break;
-
default: /* Unknown reference class */
AcpiOsPrintf ("%2.2X\n", ObjDesc->Reference.Class);
@@ -574,7 +727,6 @@ AcpiExDumpOperand (
}
break;
-
case ACPI_TYPE_BUFFER:
AcpiOsPrintf ("Buffer length %.2X @ %p\n",
@@ -590,20 +742,18 @@ AcpiExDumpOperand (
Length = 128;
}
- AcpiOsPrintf ("Buffer Contents: (displaying length 0x%.2X)\n",
- Length);
+ AcpiOsPrintf (
+ "Buffer Contents: (displaying length 0x%.2X)\n", Length);
ACPI_DUMP_BUFFER (ObjDesc->Buffer.Pointer, Length);
}
break;
-
case ACPI_TYPE_INTEGER:
AcpiOsPrintf ("Integer %8.8X%8.8X\n",
ACPI_FORMAT_UINT64 (ObjDesc->Integer.Value));
break;
-
case ACPI_TYPE_PACKAGE:
AcpiOsPrintf ("Package [Len %X] ElementArray %p\n",
@@ -619,12 +769,12 @@ AcpiExDumpOperand (
{
for (Index = 0; Index < ObjDesc->Package.Count; Index++)
{
- AcpiExDumpOperand (ObjDesc->Package.Elements[Index], Depth+1);
+ AcpiExDumpOperand (
+ ObjDesc->Package.Elements[Index], Depth + 1);
}
}
break;
-
case ACPI_TYPE_REGION:
AcpiOsPrintf ("Region %s (%X)",
@@ -642,12 +792,11 @@ AcpiExDumpOperand (
else
{
AcpiOsPrintf (" base %8.8X%8.8X Length %X\n",
- ACPI_FORMAT_NATIVE_UINT (ObjDesc->Region.Address),
+ ACPI_FORMAT_UINT64 (ObjDesc->Region.Address),
ObjDesc->Region.Length);
}
break;
-
case ACPI_TYPE_STRING:
AcpiOsPrintf ("String length %X @ %p ",
@@ -658,13 +807,11 @@ AcpiExDumpOperand (
AcpiOsPrintf ("\n");
break;
-
case ACPI_TYPE_LOCAL_BANK_FIELD:
AcpiOsPrintf ("BankField\n");
break;
-
case ACPI_TYPE_LOCAL_REGION_FIELD:
AcpiOsPrintf ("RegionField: Bits=%X AccWidth=%X Lock=%X Update=%X at "
@@ -676,16 +823,14 @@ AcpiExDumpOperand (
ObjDesc->Field.BaseByteOffset,
ObjDesc->Field.StartFieldBitOffset);
- AcpiExDumpOperand (ObjDesc->Field.RegionObj, Depth+1);
+ AcpiExDumpOperand (ObjDesc->Field.RegionObj, Depth + 1);
break;
-
case ACPI_TYPE_LOCAL_INDEX_FIELD:
AcpiOsPrintf ("IndexField\n");
break;
-
case ACPI_TYPE_BUFFER_FIELD:
AcpiOsPrintf ("BufferField: %X bits at byte %X bit %X of\n",
@@ -698,23 +843,21 @@ AcpiExDumpOperand (
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "*NULL*\n"));
}
else if ((ObjDesc->BufferField.BufferObj)->Common.Type !=
- ACPI_TYPE_BUFFER)
+ ACPI_TYPE_BUFFER)
{
AcpiOsPrintf ("*not a Buffer*\n");
}
else
{
- AcpiExDumpOperand (ObjDesc->BufferField.BufferObj, Depth+1);
+ AcpiExDumpOperand (ObjDesc->BufferField.BufferObj, Depth + 1);
}
break;
-
case ACPI_TYPE_EVENT:
AcpiOsPrintf ("Event\n");
break;
-
case ACPI_TYPE_METHOD:
AcpiOsPrintf ("Method(%X) @ %p:%X\n",
@@ -723,38 +866,33 @@ AcpiExDumpOperand (
ObjDesc->Method.AmlLength);
break;
-
case ACPI_TYPE_MUTEX:
AcpiOsPrintf ("Mutex\n");
break;
-
case ACPI_TYPE_DEVICE:
AcpiOsPrintf ("Device\n");
break;
-
case ACPI_TYPE_POWER:
AcpiOsPrintf ("Power\n");
break;
-
case ACPI_TYPE_PROCESSOR:
AcpiOsPrintf ("Processor\n");
break;
-
case ACPI_TYPE_THERMAL:
AcpiOsPrintf ("Thermal\n");
break;
-
default:
+
/* Unknown Type */
AcpiOsPrintf ("Unknown Type %X\n", ObjDesc->Common.Type);
@@ -822,7 +960,7 @@ AcpiExDumpOperands (
* PARAMETERS: Title - Descriptive text
* Value - Value to be displayed
*
- * DESCRIPTION: Object dump output formatting functions. These functions
+ * DESCRIPTION: Object dump output formatting functions. These functions
* reduce the number of format strings required and keeps them
* all in one place for easy modification.
*
@@ -830,16 +968,16 @@ AcpiExDumpOperands (
static void
AcpiExOutString (
- char *Title,
- char *Value)
+ const char *Title,
+ const char *Value)
{
AcpiOsPrintf ("%20s : %s\n", Title, Value);
}
static void
AcpiExOutPointer (
- char *Title,
- void *Value)
+ const char *Title,
+ const void *Value)
{
AcpiOsPrintf ("%20s : %p\n", Title, Value);
}
@@ -867,16 +1005,17 @@ AcpiExDumpNamespaceNode (
if (!Flags)
{
- if (!((ACPI_LV_OBJECTS & AcpiDbgLevel) && (_COMPONENT & AcpiDbgLayer)))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_OBJECTS, _COMPONENT))
{
return;
}
}
AcpiOsPrintf ("%20s : %4.4s\n", "Name", AcpiUtGetNodeName (Node));
- AcpiExOutString ("Type", AcpiUtGetTypeName (Node->Type));
- AcpiExOutPointer ("Attached Object", AcpiNsGetAttachedObject (Node));
- AcpiExOutPointer ("Parent", Node->Parent);
+ AcpiOsPrintf ("%20s : %2.2X [%s]\n", "Type",
+ Node->Type, AcpiUtGetTypeName (Node->Type));
AcpiExDumpObject (ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, Node),
AcpiExDumpNode);
@@ -907,7 +1046,8 @@ AcpiExDumpReferenceObj (
{
AcpiOsPrintf (" %p ", ObjDesc->Reference.Node);
- Status = AcpiNsHandleToPathname (ObjDesc->Reference.Node, &RetBuf);
+ Status = AcpiNsHandleToPathname (ObjDesc->Reference.Node,
+ &RetBuf, TRUE);
if (ACPI_FAILURE (Status))
{
AcpiOsPrintf (" Could not convert name to pathname\n");
@@ -922,16 +1062,18 @@ AcpiExDumpReferenceObj (
{
if (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) == ACPI_DESC_TYPE_OPERAND)
{
- AcpiOsPrintf (" Target: %p", ObjDesc->Reference.Object);
+ AcpiOsPrintf ("%22s %p", "Target :",
+ ObjDesc->Reference.Object);
if (ObjDesc->Reference.Class == ACPI_REFCLASS_TABLE)
{
- AcpiOsPrintf (" Table Index: %X\n", ObjDesc->Reference.Value);
+ AcpiOsPrintf (" Table Index: %X\n",
+ ObjDesc->Reference.Value);
}
else
{
- AcpiOsPrintf (" Target: %p [%s]\n", ObjDesc->Reference.Object,
+ AcpiOsPrintf (" [%s]\n",
AcpiUtGetTypeName (((ACPI_OPERAND_OBJECT *)
- ObjDesc->Reference.Object)->Common.Type));
+ ObjDesc->Reference.Object)->Common.Type));
}
}
else
@@ -995,24 +1137,20 @@ AcpiExDumpPackageObj (
ACPI_FORMAT_UINT64 (ObjDesc->Integer.Value));
break;
-
case ACPI_TYPE_STRING:
AcpiOsPrintf ("[String] Value: ");
- for (i = 0; i < ObjDesc->String.Length; i++)
- {
- AcpiOsPrintf ("%c", ObjDesc->String.Pointer[i]);
- }
+ AcpiUtPrintString (ObjDesc->String.Pointer, ACPI_UINT8_MAX);
AcpiOsPrintf ("\n");
break;
-
case ACPI_TYPE_BUFFER:
AcpiOsPrintf ("[Buffer] Length %.2X = ", ObjDesc->Buffer.Length);
if (ObjDesc->Buffer.Length)
{
- AcpiUtDumpBuffer (ACPI_CAST_PTR (UINT8, ObjDesc->Buffer.Pointer),
+ AcpiUtDebugDumpBuffer (
+ ACPI_CAST_PTR (UINT8, ObjDesc->Buffer.Pointer),
ObjDesc->Buffer.Length, DB_DWORD_DISPLAY, _COMPONENT);
}
else
@@ -1021,7 +1159,6 @@ AcpiExDumpPackageObj (
}
break;
-
case ACPI_TYPE_PACKAGE:
AcpiOsPrintf ("[Package] Contains %u Elements:\n",
@@ -1029,11 +1166,11 @@ AcpiExDumpPackageObj (
for (i = 0; i < ObjDesc->Package.Count; i++)
{
- AcpiExDumpPackageObj (ObjDesc->Package.Elements[i], Level+1, i);
+ AcpiExDumpPackageObj (
+ ObjDesc->Package.Elements[i], Level + 1, i);
}
break;
-
case ACPI_TYPE_LOCAL_REFERENCE:
AcpiOsPrintf ("[Object Reference] Type [%s] %2.2X",
@@ -1042,7 +1179,6 @@ AcpiExDumpPackageObj (
AcpiExDumpReferenceObj (ObjDesc);
break;
-
default:
AcpiOsPrintf ("[Unknown Type] %X\n", ObjDesc->Common.Type);
@@ -1077,7 +1213,9 @@ AcpiExDumpObjectDescriptor (
if (!Flags)
{
- if (!((ACPI_LV_OBJECTS & AcpiDbgLevel) && (_COMPONENT & AcpiDbgLayer)))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_OBJECTS, _COMPONENT))
{
return_VOID;
}
@@ -1090,24 +1228,30 @@ AcpiExDumpObjectDescriptor (
AcpiOsPrintf ("\nAttached Object (%p):\n",
((ACPI_NAMESPACE_NODE *) ObjDesc)->Object);
- AcpiExDumpObjectDescriptor (
- ((ACPI_NAMESPACE_NODE *) ObjDesc)->Object, Flags);
- return_VOID;
+ ObjDesc = ((ACPI_NAMESPACE_NODE *) ObjDesc)->Object;
+ goto DumpObject;
}
if (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) != ACPI_DESC_TYPE_OPERAND)
{
AcpiOsPrintf (
- "ExDumpObjectDescriptor: %p is not an ACPI operand object: [%s]\n",
+ "%p is not an ACPI operand object: [%s]\n",
ObjDesc, AcpiUtGetDescriptorName (ObjDesc));
return_VOID;
}
- if (ObjDesc->Common.Type > ACPI_TYPE_NS_NODE_MAX)
+ /* Validate the object type */
+
+ if (ObjDesc->Common.Type > ACPI_TYPE_LOCAL_MAX)
{
+ AcpiOsPrintf ("Not a known object type: %2.2X\n",
+ ObjDesc->Common.Type);
return_VOID;
}
+
+DumpObject:
+
/* Common Fields */
AcpiExDumpObject (ObjDesc, AcpiExDumpCommon);
@@ -1115,8 +1259,24 @@ AcpiExDumpObjectDescriptor (
/* Object-specific fields */
AcpiExDumpObject (ObjDesc, AcpiExDumpInfo[ObjDesc->Common.Type]);
+
+ if (ObjDesc->Common.Type == ACPI_TYPE_REGION)
+ {
+ ObjDesc = ObjDesc->Common.NextObject;
+ if (ObjDesc->Common.Type > ACPI_TYPE_LOCAL_MAX)
+ {
+ AcpiOsPrintf (
+ "Secondary object is not a known object type: %2.2X\n",
+ ObjDesc->Common.Type);
+
+ return_VOID;
+ }
+
+ AcpiOsPrintf ("\nExtra attached Object (%p):\n", ObjDesc);
+ AcpiExDumpObject (ObjDesc, AcpiExDumpInfo[ObjDesc->Common.Type]);
+ }
+
return_VOID;
}
#endif
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exfield.c b/usr/src/uts/intel/io/acpica/executer/exfield.c
index 098a18650e..208af63794 100644
--- a/usr/src/uts/intel/io/acpica/executer/exfield.c
+++ b/usr/src/uts/intel/io/acpica/executer/exfield.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,18 +41,84 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EXFIELD_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "amlcode.h"
#define _COMPONENT ACPI_EXECUTER
ACPI_MODULE_NAME ("exfield")
+/* Local prototypes */
+
+static UINT32
+AcpiExGetSerialAccessLength (
+ UINT32 AccessorType,
+ UINT32 AccessLength);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExGetSerialAccessLength
+ *
+ * PARAMETERS: AccessorType - The type of the protocol indicated by region
+ * field access attributes
+ * AccessLength - The access length of the region field
+ *
+ * RETURN: Decoded access length
+ *
+ * DESCRIPTION: This routine returns the length of the GenericSerialBus
+ * protocol bytes
+ *
+ ******************************************************************************/
+
+static UINT32
+AcpiExGetSerialAccessLength (
+ UINT32 AccessorType,
+ UINT32 AccessLength)
+{
+ UINT32 Length;
+
+
+ switch (AccessorType)
+ {
+ case AML_FIELD_ATTRIB_QUICK:
+
+ Length = 0;
+ break;
+
+ case AML_FIELD_ATTRIB_SEND_RCV:
+ case AML_FIELD_ATTRIB_BYTE:
+
+ Length = 1;
+ break;
+
+ case AML_FIELD_ATTRIB_WORD:
+ case AML_FIELD_ATTRIB_WORD_CALL:
+
+ Length = 2;
+ break;
+
+ case AML_FIELD_ATTRIB_MULTIBYTE:
+ case AML_FIELD_ATTRIB_RAW_BYTES:
+ case AML_FIELD_ATTRIB_RAW_PROCESS:
+
+ Length = AccessLength;
+ break;
+
+ case AML_FIELD_ATTRIB_BLOCK:
+ case AML_FIELD_ATTRIB_BLOCK_CALL:
+ default:
+
+ Length = ACPI_GSBUS_BUFFER_SIZE - 2;
+ break;
+ }
+
+ return (Length);
+}
+
/*******************************************************************************
*
@@ -64,7 +130,7 @@
*
* RETURN: Status
*
- * DESCRIPTION: Read from a named field. Returns either an Integer or a
+ * DESCRIPTION: Read from a named field. Returns either an Integer or a
* Buffer, depending on the size of the field.
*
******************************************************************************/
@@ -80,6 +146,7 @@ AcpiExReadDataFromField (
ACPI_SIZE Length;
void *Buffer;
UINT32 Function;
+ UINT16 AccessorType;
ACPI_FUNCTION_TRACE_PTR (ExReadDataFromField, ObjDesc);
@@ -113,19 +180,39 @@ AcpiExReadDataFromField (
}
else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
(ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
+ ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS ||
ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI))
{
/*
- * This is an SMBus or IPMI read. We must create a buffer to hold
- * the data and then directly access the region handler.
+ * This is an SMBus, GSBus or IPMI read. We must create a buffer to
+ * hold the data and then directly access the region handler.
*
- * Note: Smbus protocol value is passed in upper 16-bits of Function
+ * Note: SMBus and GSBus protocol value is passed in upper 16-bits
+ * of Function
*/
- if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS)
+ if (ObjDesc->Field.RegionObj->Region.SpaceId ==
+ ACPI_ADR_SPACE_SMBUS)
{
Length = ACPI_SMBUS_BUFFER_SIZE;
Function = ACPI_READ | (ObjDesc->Field.Attribute << 16);
}
+ else if (ObjDesc->Field.RegionObj->Region.SpaceId ==
+ ACPI_ADR_SPACE_GSBUS)
+ {
+ AccessorType = ObjDesc->Field.Attribute;
+ Length = AcpiExGetSerialAccessLength (
+ AccessorType, ObjDesc->Field.AccessLength);
+
+ /*
+ * Add additional 2 bytes for the GenericSerialBus data buffer:
+ *
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
+ */
+ Length += 2;
+ Function = ACPI_READ | (AccessorType << 16);
+ }
else /* IPMI */
{
Length = ACPI_IPMI_BUFFER_SIZE;
@@ -145,8 +232,8 @@ AcpiExReadDataFromField (
/* Call the region handler for the read */
Status = AcpiExAccessRegion (ObjDesc, 0,
- ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer),
- Function);
+ ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer), Function);
+
AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
goto Exit;
}
@@ -155,13 +242,15 @@ AcpiExReadDataFromField (
* Allocate a buffer for the contents of the field.
*
* If the field is larger than the current integer width, create
- * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
+ * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
* the use of arithmetic operators on the returned value if the
* field size is equal or smaller than an Integer.
*
* Note: Field.length is in bits.
*/
- Length = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (ObjDesc->Field.BitLength);
+ Length = (ACPI_SIZE) ACPI_ROUND_BITS_UP_TO_BYTES (
+ ObjDesc->Field.BitLength);
+
if (Length > AcpiGbl_IntegerByteWidth)
{
/* Field is too large for an Integer, create a Buffer instead */
@@ -187,6 +276,40 @@ AcpiExReadDataFromField (
Buffer = &BufferDesc->Integer.Value;
}
+ if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GPIO))
+ {
+ /*
+ * For GPIO (GeneralPurposeIo), the Address will be the bit offset
+ * from the previous Connection() operator, making it effectively a
+ * pin number index. The BitLength is the length of the field, which
+ * is thus the number of pins.
+ */
+ ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
+ "GPIO FieldRead [FROM]: Pin %u Bits %u\n",
+ ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength));
+
+ /* Lock entire transaction if requested */
+
+ AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
+
+ /* Perform the write */
+
+ Status = AcpiExAccessRegion (
+ ObjDesc, 0, (UINT64 *) Buffer, ACPI_READ);
+
+ AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiUtRemoveReference (BufferDesc);
+ }
+ else
+ {
+ *RetBufferDesc = BufferDesc;
+ }
+ return_ACPI_STATUS (Status);
+ }
+
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n",
ObjDesc, ObjDesc->Common.Type, Buffer, (UINT32) Length));
@@ -245,6 +368,7 @@ AcpiExWriteDataToField (
void *Buffer;
ACPI_OPERAND_OBJECT *BufferDesc;
UINT32 Function;
+ UINT16 AccessorType;
ACPI_FUNCTION_TRACE_PTR (ExWriteDataToField, ObjDesc);
@@ -274,33 +398,55 @@ AcpiExWriteDataToField (
}
else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
(ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
+ ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS ||
ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_IPMI))
{
/*
- * This is an SMBus or IPMI write. We will bypass the entire field
- * mechanism and handoff the buffer directly to the handler. For
- * these address spaces, the buffer is bi-directional; on a write,
- * return data is returned in the same buffer.
+ * This is an SMBus, GSBus or IPMI write. We will bypass the entire
+ * field mechanism and handoff the buffer directly to the handler.
+ * For these address spaces, the buffer is bi-directional; on a
+ * write, return data is returned in the same buffer.
*
* Source must be a buffer of sufficient size:
- * ACPI_SMBUS_BUFFER_SIZE or ACPI_IPMI_BUFFER_SIZE.
+ * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or
+ * ACPI_IPMI_BUFFER_SIZE.
*
- * Note: SMBus protocol type is passed in upper 16-bits of Function
+ * Note: SMBus and GSBus protocol type is passed in upper 16-bits
+ * of Function
*/
if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER)
{
ACPI_ERROR ((AE_INFO,
- "SMBus or IPMI write requires Buffer, found type %s",
+ "SMBus/IPMI/GenericSerialBus write requires "
+ "Buffer, found type %s",
AcpiUtGetObjectTypeName (SourceDesc)));
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
- if (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_SMBUS)
+ if (ObjDesc->Field.RegionObj->Region.SpaceId ==
+ ACPI_ADR_SPACE_SMBUS)
{
Length = ACPI_SMBUS_BUFFER_SIZE;
Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16);
}
+ else if (ObjDesc->Field.RegionObj->Region.SpaceId ==
+ ACPI_ADR_SPACE_GSBUS)
+ {
+ AccessorType = ObjDesc->Field.Attribute;
+ Length = AcpiExGetSerialAccessLength (
+ AccessorType, ObjDesc->Field.AccessLength);
+
+ /*
+ * Add additional 2 bytes for the GenericSerialBus data buffer:
+ *
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
+ */
+ Length += 2;
+ Function = ACPI_WRITE | (AccessorType << 16);
+ }
else /* IPMI */
{
Length = ACPI_IPMI_BUFFER_SIZE;
@@ -310,7 +456,8 @@ AcpiExWriteDataToField (
if (SourceDesc->Buffer.Length < Length)
{
ACPI_ERROR ((AE_INFO,
- "SMBus or IPMI write requires Buffer of length %u, found length %u",
+ "SMBus/IPMI/GenericSerialBus write requires "
+ "Buffer of length %u, found length %u",
Length, SourceDesc->Buffer.Length));
return_ACPI_STATUS (AE_AML_BUFFER_LIMIT);
@@ -325,7 +472,7 @@ AcpiExWriteDataToField (
}
Buffer = BufferDesc->Buffer.Pointer;
- ACPI_MEMCPY (Buffer, SourceDesc->Buffer.Pointer, Length);
+ memcpy (Buffer, SourceDesc->Buffer.Pointer, Length);
/* Lock entire transaction if requested */
@@ -335,34 +482,73 @@ AcpiExWriteDataToField (
* Perform the write (returns status and perhaps data in the
* same buffer)
*/
- Status = AcpiExAccessRegion (ObjDesc, 0,
- (UINT64 *) Buffer, Function);
+ Status = AcpiExAccessRegion (
+ ObjDesc, 0, (UINT64 *) Buffer, Function);
AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
*ResultDesc = BufferDesc;
return_ACPI_STATUS (Status);
}
+ else if ((ObjDesc->Common.Type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+ (ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_GPIO))
+ {
+ /*
+ * For GPIO (GeneralPurposeIo), we will bypass the entire field
+ * mechanism and handoff the bit address and bit width directly to
+ * the handler. The Address will be the bit offset
+ * from the previous Connection() operator, making it effectively a
+ * pin number index. The BitLength is the length of the field, which
+ * is thus the number of pins.
+ */
+ if (SourceDesc->Common.Type != ACPI_TYPE_INTEGER)
+ {
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
+ "GPIO FieldWrite [FROM]: (%s:%X), Val %.8X [TO]: Pin %u Bits %u\n",
+ AcpiUtGetTypeName (SourceDesc->Common.Type),
+ SourceDesc->Common.Type, (UINT32) SourceDesc->Integer.Value,
+ ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength));
+
+ Buffer = &SourceDesc->Integer.Value;
+
+ /* Lock entire transaction if requested */
+
+ AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
+
+ /* Perform the write */
+
+ Status = AcpiExAccessRegion (
+ ObjDesc, 0, (UINT64 *) Buffer, ACPI_WRITE);
+ AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
+ return_ACPI_STATUS (Status);
+ }
/* Get a pointer to the data to be written */
switch (SourceDesc->Common.Type)
{
case ACPI_TYPE_INTEGER:
+
Buffer = &SourceDesc->Integer.Value;
Length = sizeof (SourceDesc->Integer.Value);
break;
case ACPI_TYPE_BUFFER:
+
Buffer = SourceDesc->Buffer.Pointer;
Length = SourceDesc->Buffer.Length;
break;
case ACPI_TYPE_STRING:
+
Buffer = SourceDesc->String.Pointer;
Length = SourceDesc->String.Length;
break;
default:
+
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
@@ -390,5 +576,3 @@ AcpiExWriteDataToField (
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exfldio.c b/usr/src/uts/intel/io/acpica/executer/exfldio.c
index bf663a2f5e..fd46a0c023 100644
--- a/usr/src/uts/intel/io/acpica/executer/exfldio.c
+++ b/usr/src/uts/intel/io/acpica/executer/exfldio.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EXFLDIO_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -86,7 +83,7 @@ AcpiExSetupRegion (
* RETURN: Status
*
* DESCRIPTION: Common processing for AcpiExExtractFromField and
- * AcpiExInsertIntoField. Initialize the Region if necessary and
+ * AcpiExInsertIntoField. Initialize the Region if necessary and
* validate the request.
*
******************************************************************************/
@@ -98,6 +95,7 @@ AcpiExSetupRegion (
{
ACPI_STATUS Status = AE_OK;
ACPI_OPERAND_OBJECT *RgnDesc;
+ UINT8 SpaceId;
ACPI_FUNCTION_TRACE_U32 (ExSetupRegion, FieldDatumByteOffset);
@@ -116,6 +114,17 @@ AcpiExSetupRegion (
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
+ SpaceId = RgnDesc->Region.SpaceId;
+
+ /* Validate the Space ID */
+
+ if (!AcpiIsValidSpaceId (SpaceId))
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unknown Address Space ID: 0x%2.2X", SpaceId));
+ return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
+ }
+
/*
* If the Region Address and Length have not been previously evaluated,
* evaluate them now and save the results.
@@ -130,11 +139,12 @@ AcpiExSetupRegion (
}
/*
- * Exit now for SMBus or IPMI address space, it has a non-linear
+ * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
* address space and the request cannot be directly validated
*/
- if (RgnDesc->Region.SpaceId == ACPI_ADR_SPACE_SMBUS ||
- RgnDesc->Region.SpaceId == ACPI_ADR_SPACE_IPMI)
+ if (SpaceId == ACPI_ADR_SPACE_SMBUS ||
+ SpaceId == ACPI_ADR_SPACE_GSBUS ||
+ SpaceId == ACPI_ADR_SPACE_IPMI)
{
/* SMBus or IPMI has a non-linear address space */
@@ -156,13 +166,13 @@ AcpiExSetupRegion (
#endif
/*
- * Validate the request. The entire request from the byte offset for a
+ * Validate the request. The entire request from the byte offset for a
* length of one field datum (access width) must fit within the region.
* (Region length is specified in bytes)
*/
if (RgnDesc->Region.Length <
- (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
- ObjDesc->CommonField.AccessByteWidth))
+ (ObjDesc->CommonField.BaseByteOffset + FieldDatumByteOffset +
+ ObjDesc->CommonField.AccessByteWidth))
{
if (AcpiGbl_EnableInterpreterSlack)
{
@@ -185,11 +195,12 @@ AcpiExSetupRegion (
{
/*
* This is the case where the AccessType (AccWord, etc.) is wider
- * than the region itself. For example, a region of length one
+ * than the region itself. For example, a region of length one
* byte, and a field with Dword access specified.
*/
ACPI_ERROR ((AE_INFO,
- "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
+ "Field [%4.4s] access width (%u bytes) "
+ "too large for region [%4.4s] (length %u)",
AcpiUtGetNodeName (ObjDesc->CommonField.Node),
ObjDesc->CommonField.AccessByteWidth,
AcpiUtGetNodeName (RgnDesc->Region.Node),
@@ -201,7 +212,8 @@ AcpiExSetupRegion (
* exceeds region length, indicate an error
*/
ACPI_ERROR ((AE_INFO,
- "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
+ "Field [%4.4s] Base+Offset+Width %u+%u+%u "
+ "is beyond end of region [%4.4s] (length %u)",
AcpiUtGetNodeName (ObjDesc->CommonField.Node),
ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset, ObjDesc->CommonField.AccessByteWidth,
@@ -280,18 +292,19 @@ AcpiExAccessRegion (
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD,
- " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
+ " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
AcpiUtGetRegionName (RgnDesc->Region.SpaceId),
RgnDesc->Region.SpaceId,
ObjDesc->CommonField.AccessByteWidth,
ObjDesc->CommonField.BaseByteOffset,
FieldDatumByteOffset,
- ACPI_CAST_PTR (void, (RgnDesc->Region.Address + RegionOffset))));
+ ACPI_FORMAT_UINT64 (RgnDesc->Region.Address + RegionOffset)));
/* Invoke the appropriate AddressSpace/OpRegion handler */
- Status = AcpiEvAddressSpaceDispatch (RgnDesc, Function, RegionOffset,
- ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
+ Status = AcpiEvAddressSpaceDispatch (RgnDesc, ObjDesc,
+ Function, RegionOffset,
+ ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth), Value);
if (ACPI_FAILURE (Status))
{
@@ -326,7 +339,7 @@ AcpiExAccessRegion (
*
* DESCRIPTION: Check if a value is out of range of the field being written.
* Used to check if the values written to Index and Bank registers
- * are out of range. Normally, the value is simply truncated
+ * are out of range. Normally, the value is simply truncated
* to fit the field, but this case is most likely a serious
* coding error in the ASL.
*
@@ -353,6 +366,11 @@ AcpiExRegisterOverflow (
* The Value is larger than the maximum value that can fit into
* the register.
*/
+ ACPI_ERROR ((AE_INFO,
+ "Index value 0x%8.8X%8.8X overflows field width 0x%X",
+ ACPI_FORMAT_UINT64 (Value),
+ ObjDesc->CommonField.BitLength));
+
return (TRUE);
}
@@ -374,7 +392,7 @@ AcpiExRegisterOverflow (
*
* RETURN: Status
*
- * DESCRIPTION: Read or Write a single datum of a field. The FieldType is
+ * DESCRIPTION: Read or Write a single datum of a field. The FieldType is
* demultiplexed here to handle the different types of fields
* (BufferField, RegionField, IndexField, BankField)
*
@@ -441,7 +459,7 @@ AcpiExFieldDatumIo (
* Copy the data from the source buffer.
* Length is the field width in bytes.
*/
- ACPI_MEMCPY (Value,
+ memcpy (Value,
(ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
ObjDesc->BufferField.BaseByteOffset +
FieldDatumByteOffset,
@@ -453,7 +471,7 @@ AcpiExFieldDatumIo (
* Copy the data to the target buffer.
* Length is the field width in bytes.
*/
- ACPI_MEMCPY ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
+ memcpy ((ObjDesc->BufferField.BufferObj)->Buffer.Pointer +
ObjDesc->BufferField.BaseByteOffset +
FieldDatumByteOffset,
Value, ObjDesc->CommonField.AccessByteWidth);
@@ -462,9 +480,7 @@ AcpiExFieldDatumIo (
Status = AE_OK;
break;
-
case ACPI_TYPE_LOCAL_BANK_FIELD:
-
/*
* Ensure that the BankValue is not beyond the capacity of
* the register
@@ -494,20 +510,16 @@ AcpiExFieldDatumIo (
/*lint -fallthrough */
-
case ACPI_TYPE_LOCAL_REGION_FIELD:
/*
* For simple RegionFields, we just directly access the owning
* Operation Region.
*/
- Status = AcpiExAccessRegion (ObjDesc, FieldDatumByteOffset, Value,
- ReadWrite);
+ Status = AcpiExAccessRegion (
+ ObjDesc, FieldDatumByteOffset, Value, ReadWrite);
break;
-
case ACPI_TYPE_LOCAL_INDEX_FIELD:
-
-
/*
* Ensure that the IndexValue is not beyond the capacity of
* the register
@@ -527,8 +539,7 @@ AcpiExFieldDatumIo (
FieldDatumByteOffset));
Status = AcpiExInsertIntoField (ObjDesc->IndexField.IndexObj,
- &FieldDatumByteOffset,
- sizeof (FieldDatumByteOffset));
+ &FieldDatumByteOffset, sizeof (FieldDatumByteOffset));
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -541,8 +552,8 @@ AcpiExFieldDatumIo (
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Read from Data Register\n"));
- Status = AcpiExExtractFromField (ObjDesc->IndexField.DataObj,
- Value, sizeof (UINT64));
+ Status = AcpiExExtractFromField (
+ ObjDesc->IndexField.DataObj, Value, sizeof (UINT64));
}
else
{
@@ -552,12 +563,11 @@ AcpiExFieldDatumIo (
"Write to Data Register: Value %8.8X%8.8X\n",
ACPI_FORMAT_UINT64 (*Value)));
- Status = AcpiExInsertIntoField (ObjDesc->IndexField.DataObj,
- Value, sizeof (UINT64));
+ Status = AcpiExInsertIntoField (
+ ObjDesc->IndexField.DataObj, Value, sizeof (UINT64));
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Wrong object type in field I/O %u",
@@ -636,14 +646,14 @@ AcpiExWriteWithUpdateRule (
* ones) The left shift drops the bits we want to ignore.
*/
if ((~Mask << (ACPI_MUL_8 (sizeof (Mask)) -
- ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
+ ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth))) != 0)
{
/*
* Read the current contents of the byte/word/dword containing
* the field, and merge with the new field value.
*/
- Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
- &CurrentValue, ACPI_READ);
+ Status = AcpiExFieldDatumIo (
+ ObjDesc, FieldDatumByteOffset, &CurrentValue, ACPI_READ);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -671,13 +681,15 @@ AcpiExWriteWithUpdateRule (
ACPI_ERROR ((AE_INFO,
"Unknown UpdateRule value: 0x%X",
- (ObjDesc->CommonField.FieldFlags & AML_FIELD_UPDATE_RULE_MASK)));
+ (ObjDesc->CommonField.FieldFlags &
+ AML_FIELD_UPDATE_RULE_MASK)));
return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
}
}
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
- "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
+ "Mask %8.8X%8.8X, DatumOffset %X, Width %X, "
+ "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
ACPI_FORMAT_UINT64 (Mask),
FieldDatumByteOffset,
ObjDesc->CommonField.AccessByteWidth,
@@ -686,8 +698,8 @@ AcpiExWriteWithUpdateRule (
/* Write the merged value */
- Status = AcpiExFieldDatumIo (ObjDesc, FieldDatumByteOffset,
- &MergedValue, ACPI_WRITE);
+ Status = AcpiExFieldDatumIo (
+ ObjDesc, FieldDatumByteOffset, &MergedValue, ACPI_WRITE);
return_ACPI_STATUS (Status);
}
@@ -740,7 +752,7 @@ AcpiExExtractFromField (
return_ACPI_STATUS (AE_BUFFER_OVERFLOW);
}
- ACPI_MEMSET (Buffer, 0, BufferLength);
+ memset (Buffer, 0, BufferLength);
AccessBitWidth = ACPI_MUL_8 (ObjDesc->CommonField.AccessByteWidth);
/* Handle the simple case here */
@@ -748,7 +760,18 @@ AcpiExExtractFromField (
if ((ObjDesc->CommonField.StartFieldBitOffset == 0) &&
(ObjDesc->CommonField.BitLength == AccessBitWidth))
{
- Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
+ if (BufferLength >= sizeof (UINT64))
+ {
+ Status = AcpiExFieldDatumIo (ObjDesc, 0, Buffer, ACPI_READ);
+ }
+ else
+ {
+ /* Use RawDatum (UINT64) to handle buffers < 64 bits */
+
+ Status = AcpiExFieldDatumIo (ObjDesc, 0, &RawDatum, ACPI_READ);
+ memcpy (Buffer, &RawDatum, BufferLength);
+ }
+
return_ACPI_STATUS (Status);
}
@@ -787,8 +810,8 @@ AcpiExExtractFromField (
/* Get next input datum from the field */
FieldOffset += ObjDesc->CommonField.AccessByteWidth;
- Status = AcpiExFieldDatumIo (ObjDesc, FieldOffset,
- &RawDatum, ACPI_READ);
+ Status = AcpiExFieldDatumIo (
+ ObjDesc, FieldOffset, &RawDatum, ACPI_READ);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -816,7 +839,7 @@ AcpiExExtractFromField (
/* Write merged datum to target buffer */
- ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
+ memcpy (((char *) Buffer) + BufferOffset, &MergedDatum,
ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
BufferLength - BufferOffset));
@@ -834,7 +857,7 @@ AcpiExExtractFromField (
/* Write the last datum to the buffer */
- ACPI_MEMCPY (((char *) Buffer) + BufferOffset, &MergedDatum,
+ memcpy (((char *) Buffer) + BufferOffset, &MergedDatum,
ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
BufferLength - BufferOffset));
@@ -885,10 +908,11 @@ AcpiExInsertIntoField (
NewBuffer = NULL;
RequiredLength = ACPI_ROUND_BITS_UP_TO_BYTES (
- ObjDesc->CommonField.BitLength);
+ ObjDesc->CommonField.BitLength);
+
/*
* We must have a buffer that is at least as long as the field
- * we are writing to. This is because individual fields are
+ * we are writing to. This is because individual fields are
* indivisible and partial writes are not supported -- as per
* the ACPI specification.
*/
@@ -904,10 +928,10 @@ AcpiExInsertIntoField (
/*
* Copy the original data to the new buffer, starting
- * at Byte zero. All unused (upper) bytes of the
+ * at Byte zero. All unused (upper) bytes of the
* buffer will be 0.
*/
- ACPI_MEMCPY ((char *) NewBuffer, (char *) Buffer, BufferLength);
+ memcpy ((char *) NewBuffer, (char *) Buffer, BufferLength);
Buffer = NewBuffer;
BufferLength = RequiredLength;
}
@@ -950,7 +974,7 @@ AcpiExInsertIntoField (
/* Get initial Datum from the input buffer */
- ACPI_MEMCPY (&RawDatum, Buffer,
+ memcpy (&RawDatum, Buffer,
ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
BufferLength - BufferOffset));
@@ -963,8 +987,8 @@ AcpiExInsertIntoField (
/* Write merged datum to the target field */
MergedDatum &= Mask;
- Status = AcpiExWriteWithUpdateRule (ObjDesc, Mask,
- MergedDatum, FieldOffset);
+ Status = AcpiExWriteWithUpdateRule (
+ ObjDesc, Mask, MergedDatum, FieldOffset);
if (ACPI_FAILURE (Status))
{
goto Exit;
@@ -1002,7 +1026,7 @@ AcpiExInsertIntoField (
/* Get the next input datum from the buffer */
BufferOffset += ObjDesc->CommonField.AccessByteWidth;
- ACPI_MEMCPY (&RawDatum, ((char *) Buffer) + BufferOffset,
+ memcpy (&RawDatum, ((char *) Buffer) + BufferOffset,
ACPI_MIN(ObjDesc->CommonField.AccessByteWidth,
BufferLength - BufferOffset));
@@ -1021,8 +1045,8 @@ AcpiExInsertIntoField (
/* Write the last datum to the field */
MergedDatum &= Mask;
- Status = AcpiExWriteWithUpdateRule (ObjDesc,
- Mask, MergedDatum, FieldOffset);
+ Status = AcpiExWriteWithUpdateRule (
+ ObjDesc, Mask, MergedDatum, FieldOffset);
Exit:
/* Free temporary buffer if we used one */
@@ -1033,5 +1057,3 @@ Exit:
}
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exmisc.c b/usr/src/uts/intel/io/acpica/executer/exmisc.c
index c5dd280fe4..a8e5497a9d 100644
--- a/usr/src/uts/intel/io/acpica/executer/exmisc.c
+++ b/usr/src/uts/intel/io/acpica/executer/exmisc.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,13 +41,10 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXMISC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
#include "amlcode.h"
-#include "amlresrc.h"
#define _COMPONENT ACPI_EXECUTER
@@ -110,22 +106,19 @@ AcpiExGetObjectReference (
default:
- ACPI_ERROR ((AE_INFO, "Unknown Reference Class 0x%2.2X",
+ ACPI_ERROR ((AE_INFO, "Invalid Reference Class 0x%2.2X",
ObjDesc->Reference.Class));
- return_ACPI_STATUS (AE_AML_INTERNAL);
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
break;
-
case ACPI_DESC_TYPE_NAMED:
-
/*
* A named reference that has already been resolved to a Node
*/
ReferencedObj = ObjDesc;
break;
-
default:
ACPI_ERROR ((AE_INFO, "Invalid descriptor type 0x%X",
@@ -156,271 +149,6 @@ AcpiExGetObjectReference (
/*******************************************************************************
*
- * FUNCTION: AcpiExConcatTemplate
- *
- * PARAMETERS: Operand0 - First source object
- * Operand1 - Second source object
- * ActualReturnDesc - Where to place the return object
- * WalkState - Current walk state
- *
- * RETURN: Status
- *
- * DESCRIPTION: Concatenate two resource templates
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiExConcatTemplate (
- ACPI_OPERAND_OBJECT *Operand0,
- ACPI_OPERAND_OBJECT *Operand1,
- ACPI_OPERAND_OBJECT **ActualReturnDesc,
- ACPI_WALK_STATE *WalkState)
-{
- ACPI_STATUS Status;
- ACPI_OPERAND_OBJECT *ReturnDesc;
- UINT8 *NewBuf;
- UINT8 *EndTag;
- ACPI_SIZE Length0;
- ACPI_SIZE Length1;
- ACPI_SIZE NewLength;
-
-
- ACPI_FUNCTION_TRACE (ExConcatTemplate);
-
-
- /*
- * Find the EndTag descriptor in each resource template.
- * Note1: returned pointers point TO the EndTag, not past it.
- * Note2: zero-length buffers are allowed; treated like one EndTag
- */
-
- /* Get the length of the first resource template */
-
- Status = AcpiUtGetResourceEndTag (Operand0, &EndTag);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- Length0 = ACPI_PTR_DIFF (EndTag, Operand0->Buffer.Pointer);
-
- /* Get the length of the second resource template */
-
- Status = AcpiUtGetResourceEndTag (Operand1, &EndTag);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- Length1 = ACPI_PTR_DIFF (EndTag, Operand1->Buffer.Pointer);
-
- /* Combine both lengths, minimum size will be 2 for EndTag */
-
- NewLength = Length0 + Length1 + sizeof (AML_RESOURCE_END_TAG);
-
- /* Create a new buffer object for the result (with one EndTag) */
-
- ReturnDesc = AcpiUtCreateBufferObject (NewLength);
- if (!ReturnDesc)
- {
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
-
- /*
- * Copy the templates to the new buffer, 0 first, then 1 follows. One
- * EndTag descriptor is copied from Operand1.
- */
- NewBuf = ReturnDesc->Buffer.Pointer;
- ACPI_MEMCPY (NewBuf, Operand0->Buffer.Pointer, Length0);
- ACPI_MEMCPY (NewBuf + Length0, Operand1->Buffer.Pointer, Length1);
-
- /* Insert EndTag and set the checksum to zero, means "ignore checksum" */
-
- NewBuf[NewLength - 1] = 0;
- NewBuf[NewLength - 2] = ACPI_RESOURCE_NAME_END_TAG | 1;
-
- /* Return the completed resource template */
-
- *ActualReturnDesc = ReturnDesc;
- return_ACPI_STATUS (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiExDoConcatenate
- *
- * PARAMETERS: Operand0 - First source object
- * Operand1 - Second source object
- * ActualReturnDesc - Where to place the return object
- * WalkState - Current walk state
- *
- * RETURN: Status
- *
- * DESCRIPTION: Concatenate two objects OF THE SAME TYPE.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiExDoConcatenate (
- ACPI_OPERAND_OBJECT *Operand0,
- ACPI_OPERAND_OBJECT *Operand1,
- ACPI_OPERAND_OBJECT **ActualReturnDesc,
- ACPI_WALK_STATE *WalkState)
-{
- ACPI_OPERAND_OBJECT *LocalOperand1 = Operand1;
- ACPI_OPERAND_OBJECT *ReturnDesc;
- char *NewBuf;
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (ExDoConcatenate);
-
-
- /*
- * Convert the second operand if necessary. The first operand
- * determines the type of the second operand, (See the Data Types
- * section of the ACPI specification.) Both object types are
- * guaranteed to be either Integer/String/Buffer by the operand
- * resolution mechanism.
- */
- switch (Operand0->Common.Type)
- {
- case ACPI_TYPE_INTEGER:
- Status = AcpiExConvertToInteger (Operand1, &LocalOperand1, 16);
- break;
-
- case ACPI_TYPE_STRING:
- Status = AcpiExConvertToString (Operand1, &LocalOperand1,
- ACPI_IMPLICIT_CONVERT_HEX);
- break;
-
- case ACPI_TYPE_BUFFER:
- Status = AcpiExConvertToBuffer (Operand1, &LocalOperand1);
- break;
-
- default:
- ACPI_ERROR ((AE_INFO, "Invalid object type: 0x%X",
- Operand0->Common.Type));
- Status = AE_AML_INTERNAL;
- }
-
- if (ACPI_FAILURE (Status))
- {
- goto Cleanup;
- }
-
- /*
- * Both operands are now known to be the same object type
- * (Both are Integer, String, or Buffer), and we can now perform the
- * concatenation.
- */
-
- /*
- * There are three cases to handle:
- *
- * 1) Two Integers concatenated to produce a new Buffer
- * 2) Two Strings concatenated to produce a new String
- * 3) Two Buffers concatenated to produce a new Buffer
- */
- switch (Operand0->Common.Type)
- {
- case ACPI_TYPE_INTEGER:
-
- /* Result of two Integers is a Buffer */
- /* Need enough buffer space for two integers */
-
- ReturnDesc = AcpiUtCreateBufferObject ((ACPI_SIZE)
- ACPI_MUL_2 (AcpiGbl_IntegerByteWidth));
- if (!ReturnDesc)
- {
- Status = AE_NO_MEMORY;
- goto Cleanup;
- }
-
- NewBuf = (char *) ReturnDesc->Buffer.Pointer;
-
- /* Copy the first integer, LSB first */
-
- ACPI_MEMCPY (NewBuf, &Operand0->Integer.Value,
- AcpiGbl_IntegerByteWidth);
-
- /* Copy the second integer (LSB first) after the first */
-
- ACPI_MEMCPY (NewBuf + AcpiGbl_IntegerByteWidth,
- &LocalOperand1->Integer.Value,
- AcpiGbl_IntegerByteWidth);
- break;
-
- case ACPI_TYPE_STRING:
-
- /* Result of two Strings is a String */
-
- ReturnDesc = AcpiUtCreateStringObject (
- ((ACPI_SIZE) Operand0->String.Length +
- LocalOperand1->String.Length));
- if (!ReturnDesc)
- {
- Status = AE_NO_MEMORY;
- goto Cleanup;
- }
-
- NewBuf = ReturnDesc->String.Pointer;
-
- /* Concatenate the strings */
-
- ACPI_STRCPY (NewBuf, Operand0->String.Pointer);
- ACPI_STRCPY (NewBuf + Operand0->String.Length,
- LocalOperand1->String.Pointer);
- break;
-
- case ACPI_TYPE_BUFFER:
-
- /* Result of two Buffers is a Buffer */
-
- ReturnDesc = AcpiUtCreateBufferObject (
- ((ACPI_SIZE) Operand0->Buffer.Length +
- LocalOperand1->Buffer.Length));
- if (!ReturnDesc)
- {
- Status = AE_NO_MEMORY;
- goto Cleanup;
- }
-
- NewBuf = (char *) ReturnDesc->Buffer.Pointer;
-
- /* Concatenate the buffers */
-
- ACPI_MEMCPY (NewBuf, Operand0->Buffer.Pointer,
- Operand0->Buffer.Length);
- ACPI_MEMCPY (NewBuf + Operand0->Buffer.Length,
- LocalOperand1->Buffer.Pointer,
- LocalOperand1->Buffer.Length);
- break;
-
- default:
-
- /* Invalid object type, should not happen here */
-
- ACPI_ERROR ((AE_INFO, "Invalid object type: 0x%X",
- Operand0->Common.Type));
- Status =AE_AML_INTERNAL;
- goto Cleanup;
- }
-
- *ActualReturnDesc = ReturnDesc;
-
-Cleanup:
- if (LocalOperand1 != Operand1)
- {
- AcpiUtRemoveReference (LocalOperand1);
- }
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiExDoMathOp
*
* PARAMETERS: Opcode - AML opcode
@@ -451,37 +179,30 @@ AcpiExDoMathOp (
return (Integer0 + Integer1);
-
case AML_BIT_AND_OP: /* And (Integer0, Integer1, Result) */
return (Integer0 & Integer1);
-
case AML_BIT_NAND_OP: /* NAnd (Integer0, Integer1, Result) */
return (~(Integer0 & Integer1));
-
case AML_BIT_OR_OP: /* Or (Integer0, Integer1, Result) */
return (Integer0 | Integer1);
-
case AML_BIT_NOR_OP: /* NOr (Integer0, Integer1, Result) */
return (~(Integer0 | Integer1));
-
case AML_BIT_XOR_OP: /* XOr (Integer0, Integer1, Result) */
return (Integer0 ^ Integer1);
-
case AML_MULTIPLY_OP: /* Multiply (Integer0, Integer1, Result) */
return (Integer0 * Integer1);
-
case AML_SHIFT_LEFT_OP: /* ShiftLeft (Operand, ShiftCount, Result)*/
/*
@@ -494,7 +215,6 @@ AcpiExDoMathOp (
}
return (Integer0 << Integer1);
-
case AML_SHIFT_RIGHT_OP: /* ShiftRight (Operand, ShiftCount, Result) */
/*
@@ -507,7 +227,6 @@ AcpiExDoMathOp (
}
return (Integer0 >> Integer1);
-
case AML_SUBTRACT_OP: /* Subtract (Integer0, Integer1, Result) */
return (Integer0 - Integer1);
@@ -572,6 +291,7 @@ AcpiExDoLogicalNumericOp (
break;
default:
+
Status = AE_AML_INTERNAL;
break;
}
@@ -630,7 +350,7 @@ AcpiExDoLogicalOp (
/*
- * Convert the second operand if necessary. The first operand
+ * Convert the second operand if necessary. The first operand
* determines the type of the second operand, (See the Data Types
* section of the ACPI 3.0+ specification.) Both object types are
* guaranteed to be either Integer/String/Buffer by the operand
@@ -639,19 +359,23 @@ AcpiExDoLogicalOp (
switch (Operand0->Common.Type)
{
case ACPI_TYPE_INTEGER:
+
Status = AcpiExConvertToInteger (Operand1, &LocalOperand1, 16);
break;
case ACPI_TYPE_STRING:
- Status = AcpiExConvertToString (Operand1, &LocalOperand1,
- ACPI_IMPLICIT_CONVERT_HEX);
+
+ Status = AcpiExConvertToString (
+ Operand1, &LocalOperand1, ACPI_IMPLICIT_CONVERT_HEX);
break;
case ACPI_TYPE_BUFFER:
+
Status = AcpiExConvertToBuffer (Operand1, &LocalOperand1);
break;
default:
+
Status = AE_AML_INTERNAL;
break;
}
@@ -700,6 +424,7 @@ AcpiExDoLogicalOp (
break;
default:
+
Status = AE_AML_INTERNAL;
break;
}
@@ -717,9 +442,9 @@ AcpiExDoLogicalOp (
/* Lexicographic compare: compare the data bytes */
- Compare = ACPI_MEMCMP (Operand0->Buffer.Pointer,
- LocalOperand1->Buffer.Pointer,
- (Length0 > Length1) ? Length1 : Length0);
+ Compare = memcmp (Operand0->Buffer.Pointer,
+ LocalOperand1->Buffer.Pointer,
+ (Length0 > Length1) ? Length1 : Length0);
switch (Opcode)
{
@@ -777,6 +502,7 @@ AcpiExDoLogicalOp (
break;
default:
+
Status = AE_AML_INTERNAL;
break;
}
@@ -796,5 +522,3 @@ Cleanup:
*LogicalResult = LocalResult;
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exmutex.c b/usr/src/uts/intel/io/acpica/executer/exmutex.c
index 6a893f0e90..30e10af38c 100644
--- a/usr/src/uts/intel/io/acpica/executer/exmutex.c
+++ b/usr/src/uts/intel/io/acpica/executer/exmutex.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exmutex - ASL Mutex Acquire/Release functions
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXMUTEX_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -212,8 +209,7 @@ AcpiExAcquireMutexObject (
}
else
{
- Status = AcpiExSystemWaitMutex (ObjDesc->Mutex.OsMutex,
- Timeout);
+ Status = AcpiExSystemWaitMutex (ObjDesc->Mutex.OsMutex, Timeout);
}
if (ACPI_FAILURE (Status))
@@ -276,33 +272,48 @@ AcpiExAcquireMutex (
}
/*
- * Current sync level must be less than or equal to the sync level of the
- * mutex. This mechanism provides some deadlock prevention
+ * Current sync level must be less than or equal to the sync level
+ * of the mutex. This mechanism provides some deadlock prevention.
*/
if (WalkState->Thread->CurrentSyncLevel > ObjDesc->Mutex.SyncLevel)
{
ACPI_ERROR ((AE_INFO,
- "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%u)",
+ "Cannot acquire Mutex [%4.4s], "
+ "current SyncLevel is too large (%u)",
AcpiUtGetNodeName (ObjDesc->Mutex.Node),
WalkState->Thread->CurrentSyncLevel));
return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
}
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Acquiring: Mutex SyncLevel %u, Thread SyncLevel %u, "
+ "Depth %u TID %p\n",
+ ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
+ ObjDesc->Mutex.AcquisitionDepth, WalkState->Thread));
+
Status = AcpiExAcquireMutexObject ((UINT16) TimeDesc->Integer.Value,
- ObjDesc, WalkState->Thread->ThreadId);
+ ObjDesc, WalkState->Thread->ThreadId);
+
if (ACPI_SUCCESS (Status) && ObjDesc->Mutex.AcquisitionDepth == 1)
{
/* Save Thread object, original/current sync levels */
ObjDesc->Mutex.OwnerThread = WalkState->Thread;
- ObjDesc->Mutex.OriginalSyncLevel = WalkState->Thread->CurrentSyncLevel;
- WalkState->Thread->CurrentSyncLevel = ObjDesc->Mutex.SyncLevel;
+ ObjDesc->Mutex.OriginalSyncLevel =
+ WalkState->Thread->CurrentSyncLevel;
+ WalkState->Thread->CurrentSyncLevel =
+ ObjDesc->Mutex.SyncLevel;
/* Link the mutex to the current thread for force-unlock at method exit */
AcpiExLinkMutex (ObjDesc, WalkState->Thread);
}
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Acquired: Mutex SyncLevel %u, Thread SyncLevel %u, Depth %u\n",
+ ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
+ ObjDesc->Mutex.AcquisitionDepth));
+
return_ACPI_STATUS (Status);
}
@@ -341,7 +352,7 @@ AcpiExReleaseMutexObject (
if (ObjDesc->Mutex.AcquisitionDepth == 0)
{
- return (AE_NOT_ACQUIRED);
+ return_ACPI_STATUS (AE_NOT_ACQUIRED);
}
/* Match multiple Acquires with multiple Releases */
@@ -398,9 +409,9 @@ AcpiExReleaseMutex (
ACPI_OPERAND_OBJECT *ObjDesc,
ACPI_WALK_STATE *WalkState)
{
- ACPI_STATUS Status = AE_OK;
UINT8 PreviousSyncLevel;
ACPI_THREAD_STATE *OwnerThread;
+ ACPI_STATUS Status = AE_OK;
ACPI_FUNCTION_TRACE (ExReleaseMutex);
@@ -458,7 +469,8 @@ AcpiExReleaseMutex (
if (ObjDesc->Mutex.SyncLevel != OwnerThread->CurrentSyncLevel)
{
ACPI_ERROR ((AE_INFO,
- "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %u current %u",
+ "Cannot release Mutex [%4.4s], SyncLevel mismatch: "
+ "mutex %u current %u",
AcpiUtGetNodeName (ObjDesc->Mutex.Node),
ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel));
return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
@@ -472,6 +484,13 @@ AcpiExReleaseMutex (
PreviousSyncLevel =
OwnerThread->AcquiredMutexList->Mutex.OriginalSyncLevel;
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Releasing: Object SyncLevel %u, Thread SyncLevel %u, "
+ "Prev SyncLevel %u, Depth %u TID %p\n",
+ ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
+ PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth,
+ WalkState->Thread));
+
Status = AcpiExReleaseMutexObject (ObjDesc);
if (ACPI_FAILURE (Status))
{
@@ -485,6 +504,12 @@ AcpiExReleaseMutex (
OwnerThread->CurrentSyncLevel = PreviousSyncLevel;
}
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Released: Object SyncLevel %u, Thread SyncLevel, %u, "
+ "Prev SyncLevel %u, Depth %u\n",
+ ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
+ PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth));
+
return_ACPI_STATUS (Status);
}
@@ -515,7 +540,7 @@ AcpiExReleaseAllMutexes (
ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_FUNCTION_ENTRY ();
+ ACPI_FUNCTION_TRACE (ExReleaseAllMutexes);
/* Traverse the list of owned mutexes, releasing each one */
@@ -523,11 +548,10 @@ AcpiExReleaseAllMutexes (
while (Next)
{
ObjDesc = Next;
- Next = ObjDesc->Mutex.Next;
-
- ObjDesc->Mutex.Prev = NULL;
- ObjDesc->Mutex.Next = NULL;
- ObjDesc->Mutex.AcquisitionDepth = 0;
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Mutex [%4.4s] force-release, SyncLevel %u Depth %u\n",
+ ObjDesc->Mutex.Node->Name.Ascii, ObjDesc->Mutex.SyncLevel,
+ ObjDesc->Mutex.AcquisitionDepth));
/* Release the mutex, special case for Global Lock */
@@ -542,13 +566,20 @@ AcpiExReleaseAllMutexes (
AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex);
}
+ /* Update Thread SyncLevel (Last mutex is the important one) */
+
+ Thread->CurrentSyncLevel = ObjDesc->Mutex.OriginalSyncLevel;
+
/* Mark mutex unowned */
+ Next = ObjDesc->Mutex.Next;
+
+ ObjDesc->Mutex.Prev = NULL;
+ ObjDesc->Mutex.Next = NULL;
+ ObjDesc->Mutex.AcquisitionDepth = 0;
ObjDesc->Mutex.OwnerThread = NULL;
ObjDesc->Mutex.ThreadId = 0;
-
- /* Update Thread SyncLevel (Last mutex is the important one) */
-
- Thread->CurrentSyncLevel = ObjDesc->Mutex.OriginalSyncLevel;
}
+
+ return_VOID;
}
diff --git a/usr/src/uts/intel/io/acpica/executer/exnames.c b/usr/src/uts/intel/io/acpica/executer/exnames.c
index 2188ec0a2f..817a011b2c 100644
--- a/usr/src/uts/intel/io/acpica/executer/exnames.c
+++ b/usr/src/uts/intel/io/acpica/executer/exnames.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exnames - interpreter/scanner name load/execute
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXNAMES_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -73,7 +70,7 @@ AcpiExNameSegment (
* (-1)==root, 0==none
* NumNameSegs - count of 4-character name segments
*
- * RETURN: A pointer to the allocated string segment. This segment must
+ * RETURN: A pointer to the allocated string segment. This segment must
* be deleted by the caller.
*
* DESCRIPTION: Allocate a buffer for a name string. Ensure allocated name
@@ -163,6 +160,7 @@ AcpiExAllocateNameString (
return_PTR (NameString);
}
+
/*******************************************************************************
*
* FUNCTION: AcpiExNameSegment
@@ -192,8 +190,8 @@ AcpiExNameSegment (
/*
- * If first character is a digit, then we know that we aren't looking at a
- * valid name segment
+ * If first character is a digit, then we know that we aren't looking
+ * at a valid name segment
*/
CharBuf[0] = *AmlAddress;
@@ -206,7 +204,7 @@ AcpiExNameSegment (
ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Bytes from stream:\n"));
for (Index = 0;
- (Index < ACPI_NAME_SIZE) && (AcpiUtValidAcpiChar (*AmlAddress, 0));
+ (Index < ACPI_NAME_SIZE) && (AcpiUtValidNameChar (*AmlAddress, 0));
Index++)
{
CharBuf[Index] = *AmlAddress++;
@@ -224,7 +222,7 @@ AcpiExNameSegment (
if (NameString)
{
- ACPI_STRCAT (NameString, CharBuf);
+ strcat (NameString, CharBuf);
ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
"Appended to - %s\n", NameString));
}
@@ -335,7 +333,6 @@ AcpiExGetNameString (
HasPrefix = TRUE;
break;
-
case AML_PARENT_PREFIX:
/* Increment past possibly multiple parent prefixes */
@@ -353,7 +350,6 @@ AcpiExGetNameString (
HasPrefix = TRUE;
break;
-
default:
/* Not a prefix character */
@@ -389,7 +385,6 @@ AcpiExGetNameString (
}
break;
-
case AML_MULTI_NAME_PREFIX_OP:
ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "MultiNamePrefix at %p\n",
@@ -400,7 +395,8 @@ AcpiExGetNameString (
AmlAddress++;
NumSegments = *AmlAddress;
- NameString = AcpiExAllocateNameString (PrefixCount, NumSegments);
+ NameString = AcpiExAllocateNameString (
+ PrefixCount, NumSegments);
if (!NameString)
{
Status = AE_NO_MEMORY;
@@ -421,7 +417,6 @@ AcpiExGetNameString (
break;
-
case 0:
/* NullName valid as of 8-12-98 ASL/AML Grammar Update */
@@ -444,7 +439,6 @@ AcpiExGetNameString (
break;
-
default:
/* Name segment string */
@@ -484,5 +478,3 @@ AcpiExGetNameString (
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exoparg1.c b/usr/src/uts/intel/io/acpica/executer/exoparg1.c
index 6ac685c82b..5a45bead06 100644
--- a/usr/src/uts/intel/io/acpica/executer/exoparg1.c
+++ b/usr/src/uts/intel/io/acpica/executer/exoparg1.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exoparg1 - AML execution - opcodes with 1 argument
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXOPARG1_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -181,37 +178,31 @@ AcpiExOpcode_1A_0T_0R (
Status = AcpiExReleaseMutex (Operand[0], WalkState);
break;
-
case AML_RESET_OP: /* Reset (EventObject) */
Status = AcpiExSystemResetEvent (Operand[0]);
break;
-
case AML_SIGNAL_OP: /* Signal (EventObject) */
Status = AcpiExSystemSignalEvent (Operand[0]);
break;
-
case AML_SLEEP_OP: /* Sleep (MsecTime) */
Status = AcpiExSystemDoSleep (Operand[0]->Integer.Value);
break;
-
case AML_STALL_OP: /* Stall (UsecTime) */
Status = AcpiExSystemDoStall ((UINT32) Operand[0]->Integer.Value);
break;
-
case AML_UNLOAD_OP: /* Unload (Handle) */
Status = AcpiExUnloadTable (Operand[0]);
break;
-
default: /* Unknown opcode */
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
@@ -331,7 +322,6 @@ AcpiExOpcode_1A_1T_1R (
ReturnDesc->Integer.Value = ~Operand[0]->Integer.Value;
break;
-
case AML_FIND_SET_LEFT_BIT_OP: /* FindSetLeftBit (Operand, Result) */
ReturnDesc->Integer.Value = Operand[0]->Integer.Value;
@@ -341,7 +331,7 @@ AcpiExOpcode_1A_1T_1R (
* endian unsigned value, so this boundary condition is valid.
*/
for (Temp32 = 0; ReturnDesc->Integer.Value &&
- Temp32 < ACPI_INTEGER_BIT_SIZE; ++Temp32)
+ Temp32 < ACPI_INTEGER_BIT_SIZE; ++Temp32)
{
ReturnDesc->Integer.Value >>= 1;
}
@@ -349,7 +339,6 @@ AcpiExOpcode_1A_1T_1R (
ReturnDesc->Integer.Value = Temp32;
break;
-
case AML_FIND_SET_RIGHT_BIT_OP: /* FindSetRightBit (Operand, Result) */
ReturnDesc->Integer.Value = Operand[0]->Integer.Value;
@@ -359,7 +348,7 @@ AcpiExOpcode_1A_1T_1R (
* endian unsigned value, so this boundary condition is valid.
*/
for (Temp32 = 0; ReturnDesc->Integer.Value &&
- Temp32 < ACPI_INTEGER_BIT_SIZE; ++Temp32)
+ Temp32 < ACPI_INTEGER_BIT_SIZE; ++Temp32)
{
ReturnDesc->Integer.Value <<= 1;
}
@@ -370,9 +359,7 @@ AcpiExOpcode_1A_1T_1R (
Temp32 == 0 ? 0 : (ACPI_INTEGER_BIT_SIZE + 1) - Temp32;
break;
-
case AML_FROM_BCD_OP: /* FromBcd (BCDValue, Result) */
-
/*
* The 64-bit ACPI integer can hold 16 4-bit BCD characters
* (if table is 32-bit, integer can hold 8 BCD characters)
@@ -417,7 +404,6 @@ AcpiExOpcode_1A_1T_1R (
}
break;
-
case AML_TO_BCD_OP: /* ToBcd (Operand, Result) */
ReturnDesc->Integer.Value = 0;
@@ -449,9 +435,7 @@ AcpiExOpcode_1A_1T_1R (
}
break;
-
case AML_COND_REF_OF_OP: /* CondRefOf (SourceObject, Result) */
-
/*
* This op is a little strange because the internal return value is
* different than the return value stored in the result descriptor
@@ -470,7 +454,7 @@ AcpiExOpcode_1A_1T_1R (
/* Get the object reference, store it, and remove our reference */
Status = AcpiExGetObjectReference (Operand[0],
- &ReturnDesc2, WalkState);
+ &ReturnDesc2, WalkState);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -486,14 +470,14 @@ AcpiExOpcode_1A_1T_1R (
default:
+
/* No other opcodes get here */
+
break;
}
break;
-
case AML_STORE_OP: /* Store (Source, Target) */
-
/*
* A store operand is typically a number, string, buffer or lvalue
* Be careful about deleting the source object,
@@ -520,64 +504,62 @@ AcpiExOpcode_1A_1T_1R (
}
return_ACPI_STATUS (Status);
-
/*
* ACPI 2.0 Opcodes
*/
case AML_COPY_OP: /* Copy (Source, Target) */
- Status = AcpiUtCopyIobjectToIobject (Operand[0], &ReturnDesc,
- WalkState);
+ Status = AcpiUtCopyIobjectToIobject (
+ Operand[0], &ReturnDesc, WalkState);
break;
-
case AML_TO_DECSTRING_OP: /* ToDecimalString (Data, Result) */
- Status = AcpiExConvertToString (Operand[0], &ReturnDesc,
- ACPI_EXPLICIT_CONVERT_DECIMAL);
+ Status = AcpiExConvertToString (
+ Operand[0], &ReturnDesc, ACPI_EXPLICIT_CONVERT_DECIMAL);
if (ReturnDesc == Operand[0])
{
/* No conversion performed, add ref to handle return value */
+
AcpiUtAddReference (ReturnDesc);
}
break;
-
case AML_TO_HEXSTRING_OP: /* ToHexString (Data, Result) */
- Status = AcpiExConvertToString (Operand[0], &ReturnDesc,
- ACPI_EXPLICIT_CONVERT_HEX);
+ Status = AcpiExConvertToString (
+ Operand[0], &ReturnDesc, ACPI_EXPLICIT_CONVERT_HEX);
if (ReturnDesc == Operand[0])
{
/* No conversion performed, add ref to handle return value */
+
AcpiUtAddReference (ReturnDesc);
}
break;
-
case AML_TO_BUFFER_OP: /* ToBuffer (Data, Result) */
Status = AcpiExConvertToBuffer (Operand[0], &ReturnDesc);
if (ReturnDesc == Operand[0])
{
/* No conversion performed, add ref to handle return value */
+
AcpiUtAddReference (ReturnDesc);
}
break;
-
case AML_TO_INTEGER_OP: /* ToInteger (Data, Result) */
- Status = AcpiExConvertToInteger (Operand[0], &ReturnDesc,
- ACPI_ANY_BASE);
+ Status = AcpiExConvertToInteger (
+ Operand[0], &ReturnDesc, ACPI_ANY_BASE);
if (ReturnDesc == Operand[0])
{
/* No conversion performed, add ref to handle return value */
+
AcpiUtAddReference (ReturnDesc);
}
break;
-
case AML_SHIFT_LEFT_BIT_OP: /* ShiftLeftBit (Source, BitNum) */
case AML_SHIFT_RIGHT_BIT_OP: /* ShiftRightBit (Source, BitNum) */
@@ -589,7 +571,6 @@ AcpiExOpcode_1A_1T_1R (
Status = AE_SUPPORT;
goto Cleanup;
-
default: /* Unknown opcode */
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
@@ -668,7 +649,7 @@ AcpiExOpcode_1A_0T_1R (
}
/*
- * Set result to ONES (TRUE) if Value == 0. Note:
+ * Set result to ONES (TRUE) if Value == 0. Note:
* ReturnDesc->Integer.Value is initially == 0 (FALSE) from above.
*/
if (!Operand[0]->Integer.Value)
@@ -677,12 +658,10 @@ AcpiExOpcode_1A_0T_1R (
}
break;
-
case AML_DECREMENT_OP: /* Decrement (Operand) */
case AML_INCREMENT_OP: /* Increment (Operand) */
-
/*
- * Create a new integer. Can't just get the base integer and
+ * Create a new integer. Can't just get the base integer and
* increment it because it may be an Arg or Field.
*/
ReturnDesc = AcpiUtCreateInternalObject (ACPI_TYPE_INTEGER);
@@ -727,11 +706,11 @@ AcpiExOpcode_1A_0T_1R (
*/
if (WalkState->Opcode == AML_INCREMENT_OP)
{
- ReturnDesc->Integer.Value = TempDesc->Integer.Value +1;
+ ReturnDesc->Integer.Value = TempDesc->Integer.Value + 1;
}
else
{
- ReturnDesc->Integer.Value = TempDesc->Integer.Value -1;
+ ReturnDesc->Integer.Value = TempDesc->Integer.Value - 1;
}
/* Finished with this Integer object */
@@ -745,12 +724,10 @@ AcpiExOpcode_1A_0T_1R (
Status = AcpiExStore (ReturnDesc, Operand[0], WalkState);
break;
-
- case AML_TYPE_OP: /* ObjectType (SourceObject) */
-
+ case AML_OBJECT_TYPE_OP: /* ObjectType (SourceObject) */
/*
* Note: The operand is not resolved at this point because we want to
- * get the associated object, not its value. For example, we don't
+ * get the associated object, not its value. For example, we don't
* want to resolve a FieldUnit to its value, we want the actual
* FieldUnit object.
*/
@@ -773,9 +750,7 @@ AcpiExOpcode_1A_0T_1R (
}
break;
-
case AML_SIZE_OF_OP: /* SizeOf (SourceObject) */
-
/*
* Note: The operand is not resolved at this point because we want to
* get the associated object, not its value.
@@ -783,8 +758,8 @@ AcpiExOpcode_1A_0T_1R (
/* Get the base object */
- Status = AcpiExResolveMultiple (WalkState,
- Operand[0], &Type, &TempDesc);
+ Status = AcpiExResolveMultiple (
+ WalkState, Operand[0], &Type, &TempDesc);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -792,7 +767,7 @@ AcpiExOpcode_1A_0T_1R (
/*
* The type of the base object must be integer, buffer, string, or
- * package. All others are not supported.
+ * package. All others are not supported.
*
* NOTE: Integer is not specifically supported by the ACPI spec,
* but is supported implicitly via implicit operand conversion.
@@ -802,10 +777,12 @@ AcpiExOpcode_1A_0T_1R (
switch (Type)
{
case ACPI_TYPE_INTEGER:
+
Value = AcpiGbl_IntegerByteWidth;
break;
case ACPI_TYPE_STRING:
+
Value = TempDesc->String.Length;
break;
@@ -826,9 +803,12 @@ AcpiExOpcode_1A_0T_1R (
break;
default:
+
ACPI_ERROR ((AE_INFO,
- "Operand must be Buffer/Integer/String/Package - found type %s",
+ "Operand must be Buffer/Integer/String/Package"
+ " - found type %s",
AcpiUtGetTypeName (Type)));
+
Status = AE_AML_OPERAND_TYPE;
goto Cleanup;
}
@@ -853,7 +833,8 @@ AcpiExOpcode_1A_0T_1R (
case AML_REF_OF_OP: /* RefOf (SourceObject) */
- Status = AcpiExGetObjectReference (Operand[0], &ReturnDesc, WalkState);
+ Status = AcpiExGetObjectReference (
+ Operand[0], &ReturnDesc, WalkState);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -900,9 +881,9 @@ AcpiExOpcode_1A_0T_1R (
/* Set Operand[0] to the value of the local/arg */
Status = AcpiDsMethodDataGetValue (
- Operand[0]->Reference.Class,
- Operand[0]->Reference.Value,
- WalkState, &TempDesc);
+ Operand[0]->Reference.Class,
+ Operand[0]->Reference.Value,
+ WalkState, &TempDesc);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -933,9 +914,11 @@ AcpiExOpcode_1A_0T_1R (
break;
case ACPI_TYPE_STRING:
+
break;
default:
+
Status = AE_AML_OPERAND_TYPE;
goto Cleanup;
}
@@ -954,19 +937,19 @@ AcpiExOpcode_1A_0T_1R (
* Field, so we need to resolve the node to a value.
*/
Status = AcpiNsGetNode (WalkState->ScopeInfo->Scope.Node,
- Operand[0]->String.Pointer,
- ACPI_NS_SEARCH_PARENT,
- ACPI_CAST_INDIRECT_PTR (
- ACPI_NAMESPACE_NODE, &ReturnDesc));
+ Operand[0]->String.Pointer,
+ ACPI_NS_SEARCH_PARENT,
+ ACPI_CAST_INDIRECT_PTR (
+ ACPI_NAMESPACE_NODE, &ReturnDesc));
if (ACPI_FAILURE (Status))
{
goto Cleanup;
}
Status = AcpiExResolveNodeToValue (
- ACPI_CAST_INDIRECT_PTR (
- ACPI_NAMESPACE_NODE, &ReturnDesc),
- WalkState);
+ ACPI_CAST_INDIRECT_PTR (
+ ACPI_NAMESPACE_NODE, &ReturnDesc),
+ WalkState);
goto Cleanup;
}
}
@@ -982,7 +965,7 @@ AcpiExOpcode_1A_0T_1R (
* dereferenced above.
*/
ReturnDesc = AcpiNsGetAttachedObject (
- (ACPI_NAMESPACE_NODE *) Operand[0]);
+ (ACPI_NAMESPACE_NODE *) Operand[0]);
AcpiUtAddReference (ReturnDesc);
}
else
@@ -994,7 +977,6 @@ AcpiExOpcode_1A_0T_1R (
switch (Operand[0]->Reference.Class)
{
case ACPI_REFCLASS_INDEX:
-
/*
* The target type for the Index operator must be
* either a Buffer or a Package
@@ -1026,50 +1008,83 @@ AcpiExOpcode_1A_0T_1R (
}
break;
-
case ACPI_TYPE_PACKAGE:
-
/*
- * Return the referenced element of the package. We must
+ * Return the referenced element of the package. We must
* add another reference to the referenced object, however.
*/
ReturnDesc = *(Operand[0]->Reference.Where);
- if (ReturnDesc)
+ if (!ReturnDesc)
{
- AcpiUtAddReference (ReturnDesc);
+ /*
+ * Element is NULL, do not allow the dereference.
+ * This provides compatibility with other ACPI
+ * implementations.
+ */
+ return_ACPI_STATUS (AE_AML_UNINITIALIZED_ELEMENT);
}
- break;
+ AcpiUtAddReference (ReturnDesc);
+ break;
default:
ACPI_ERROR ((AE_INFO,
"Unknown Index TargetType 0x%X in reference object %p",
Operand[0]->Reference.TargetType, Operand[0]));
+
Status = AE_AML_OPERAND_TYPE;
goto Cleanup;
}
break;
-
case ACPI_REFCLASS_REFOF:
ReturnDesc = Operand[0]->Reference.Object;
if (ACPI_GET_DESCRIPTOR_TYPE (ReturnDesc) ==
- ACPI_DESC_TYPE_NAMED)
+ ACPI_DESC_TYPE_NAMED)
{
ReturnDesc = AcpiNsGetAttachedObject (
- (ACPI_NAMESPACE_NODE *) ReturnDesc);
- }
+ (ACPI_NAMESPACE_NODE *) ReturnDesc);
+ if (!ReturnDesc)
+ {
+ break;
+ }
- /* Add another reference to the object! */
+ /*
+ * June 2013:
+ * BufferFields/FieldUnits require additional resolution
+ */
+ switch (ReturnDesc->Common.Type)
+ {
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
- AcpiUtAddReference (ReturnDesc);
- break;
+ Status = AcpiExReadDataFromField (
+ WalkState, ReturnDesc, &TempDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ goto Cleanup;
+ }
+
+ ReturnDesc = TempDesc;
+ break;
+ default:
+
+ /* Add another reference to the object */
+
+ AcpiUtAddReference (ReturnDesc);
+ break;
+ }
+ }
+ break;
default:
+
ACPI_ERROR ((AE_INFO,
"Unknown class in reference(%p) - 0x%2.2X",
Operand[0], Operand[0]->Reference.Class));
@@ -1080,11 +1095,11 @@ AcpiExOpcode_1A_0T_1R (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
@@ -1108,4 +1123,3 @@ Cleanup:
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exoparg2.c b/usr/src/uts/intel/io/acpica/executer/exoparg2.c
index d131277aae..7fe91a8181 100644
--- a/usr/src/uts/intel/io/acpica/executer/exoparg2.c
+++ b/usr/src/uts/intel/io/acpica/executer/exoparg2.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EXOPARG2_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -137,14 +134,13 @@ AcpiExOpcode_2A_0T_0R (
/*
* Dispatch the notify to the appropriate handler
* NOTE: the request is queued for execution after this method
- * completes. The notify handlers are NOT invoked synchronously
+ * completes. The notify handlers are NOT invoked synchronously
* from this thread -- because handlers may in turn run other
* control methods.
*/
Status = AcpiEvQueueNotifyRequest (Node, Value);
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
@@ -207,21 +203,22 @@ AcpiExOpcode_2A_2T_1R (
/* Quotient to ReturnDesc1, remainder to ReturnDesc2 */
- Status = AcpiUtDivide (Operand[0]->Integer.Value,
- Operand[1]->Integer.Value,
- &ReturnDesc1->Integer.Value,
- &ReturnDesc2->Integer.Value);
+ Status = AcpiUtDivide (
+ Operand[0]->Integer.Value,
+ Operand[1]->Integer.Value,
+ &ReturnDesc1->Integer.Value,
+ &ReturnDesc2->Integer.Value);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
@@ -286,7 +283,7 @@ AcpiExOpcode_2A_1T_1R (
ACPI_OPERAND_OBJECT *ReturnDesc = NULL;
UINT64 Index;
ACPI_STATUS Status = AE_OK;
- ACPI_SIZE Length;
+ ACPI_SIZE Length = 0;
ACPI_FUNCTION_TRACE_STR (ExOpcode_2A_1T_1R,
@@ -306,9 +303,10 @@ AcpiExOpcode_2A_1T_1R (
goto Cleanup;
}
- ReturnDesc->Integer.Value = AcpiExDoMathOp (WalkState->Opcode,
- Operand[0]->Integer.Value,
- Operand[1]->Integer.Value);
+ ReturnDesc->Integer.Value = AcpiExDoMathOp (
+ WalkState->Opcode,
+ Operand[0]->Integer.Value,
+ Operand[1]->Integer.Value);
goto StoreResultToTarget;
}
@@ -325,22 +323,20 @@ AcpiExOpcode_2A_1T_1R (
/* ReturnDesc will contain the remainder */
- Status = AcpiUtDivide (Operand[0]->Integer.Value,
- Operand[1]->Integer.Value,
- NULL,
- &ReturnDesc->Integer.Value);
+ Status = AcpiUtDivide (
+ Operand[0]->Integer.Value,
+ Operand[1]->Integer.Value,
+ NULL,
+ &ReturnDesc->Integer.Value);
break;
-
case AML_CONCAT_OP: /* Concatenate (Data1, Data2, Result) */
- Status = AcpiExDoConcatenate (Operand[0], Operand[1],
- &ReturnDesc, WalkState);
+ Status = AcpiExDoConcatenate (
+ Operand[0], Operand[1], &ReturnDesc, WalkState);
break;
-
case AML_TO_STRING_OP: /* ToString (Buffer, Length, Result) (ACPI 2.0) */
-
/*
* Input object is guaranteed to be a buffer at this point (it may have
* been converted.) Copy the raw buffer data to a new object of
@@ -356,7 +352,6 @@ AcpiExOpcode_2A_1T_1R (
* NOTE: A length of zero is ok, and will create a zero-length, null
* terminated string.
*/
- Length = 0;
while ((Length < Operand[0]->Buffer.Length) &&
(Length < Operand[1]->Integer.Value) &&
(Operand[0]->Buffer.Pointer[Length]))
@@ -377,20 +372,18 @@ AcpiExOpcode_2A_1T_1R (
* Copy the raw buffer data with no transform.
* (NULL terminated already)
*/
- ACPI_MEMCPY (ReturnDesc->String.Pointer,
+ memcpy (ReturnDesc->String.Pointer,
Operand[0]->Buffer.Pointer, Length);
break;
-
case AML_CONCAT_RES_OP:
/* ConcatenateResTemplate (Buffer, Buffer, Result) (ACPI 2.0) */
- Status = AcpiExConcatTemplate (Operand[0], Operand[1],
- &ReturnDesc, WalkState);
+ Status = AcpiExConcatTemplate (
+ Operand[0], Operand[1], &ReturnDesc, WalkState);
break;
-
case AML_INDEX_OP: /* Index (Source Index Result) */
/* Create the internal return object */
@@ -418,31 +411,39 @@ AcpiExOpcode_2A_1T_1R (
if (Index >= Operand[0]->String.Length)
{
+ Length = Operand[0]->String.Length;
Status = AE_AML_STRING_LIMIT;
}
ReturnDesc->Reference.TargetType = ACPI_TYPE_BUFFER_FIELD;
+ ReturnDesc->Reference.IndexPointer =
+ &(Operand[0]->Buffer.Pointer [Index]);
break;
case ACPI_TYPE_BUFFER:
if (Index >= Operand[0]->Buffer.Length)
{
+ Length = Operand[0]->Buffer.Length;
Status = AE_AML_BUFFER_LIMIT;
}
ReturnDesc->Reference.TargetType = ACPI_TYPE_BUFFER_FIELD;
+ ReturnDesc->Reference.IndexPointer =
+ &(Operand[0]->Buffer.Pointer [Index]);
break;
case ACPI_TYPE_PACKAGE:
if (Index >= Operand[0]->Package.Count)
{
+ Length = Operand[0]->Package.Count;
Status = AE_AML_PACKAGE_LIMIT;
}
ReturnDesc->Reference.TargetType = ACPI_TYPE_PACKAGE;
- ReturnDesc->Reference.Where = &Operand[0]->Package.Elements [Index];
+ ReturnDesc->Reference.Where =
+ &Operand[0]->Package.Elements [Index];
break;
default:
@@ -456,8 +457,8 @@ AcpiExOpcode_2A_1T_1R (
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "Index (0x%8.8X%8.8X) is beyond end of object",
- ACPI_FORMAT_UINT64 (Index)));
+ "Index (0x%X%8.8X) is beyond end of object (length 0x%X)",
+ ACPI_FORMAT_UINT64 (Index), (UINT32) Length));
goto Cleanup;
}
@@ -477,7 +478,6 @@ AcpiExOpcode_2A_1T_1R (
WalkState->ResultObj = ReturnDesc;
goto Cleanup;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
@@ -564,8 +564,8 @@ AcpiExOpcode_2A_0T_1R (
/* LogicalOp (Operand0, Operand1) */
Status = AcpiExDoLogicalNumericOp (WalkState->Opcode,
- Operand[0]->Integer.Value, Operand[1]->Integer.Value,
- &LogicalResult);
+ Operand[0]->Integer.Value, Operand[1]->Integer.Value,
+ &LogicalResult);
goto StoreLogicalResult;
}
else if (WalkState->OpInfo->Flags & AML_LOGICAL)
@@ -573,7 +573,7 @@ AcpiExOpcode_2A_0T_1R (
/* LogicalOp (Operand0, Operand1) */
Status = AcpiExDoLogicalOp (WalkState->Opcode, Operand[0],
- Operand[1], &LogicalResult);
+ Operand[1], &LogicalResult);
goto StoreLogicalResult;
}
@@ -600,11 +600,11 @@ AcpiExOpcode_2A_0T_1R (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
@@ -638,5 +638,3 @@ Cleanup:
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exoparg3.c b/usr/src/uts/intel/io/acpica/executer/exoparg3.c
index 6adb1afac2..de46a97c9c 100644
--- a/usr/src/uts/intel/io/acpica/executer/exoparg3.c
+++ b/usr/src/uts/intel/io/acpica/executer/exoparg3.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exoparg3 - AML execution - opcodes with 3 arguments
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXOPARG3_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -108,7 +105,8 @@ AcpiExOpcode_3A_0T_0R (
case AML_FATAL_OP: /* Fatal (FatalType FatalCode FatalArg) */
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "FatalOp: Type %X Code %X Arg %X <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
+ "FatalOp: Type %X Code %X Arg %X "
+ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
(UINT32) Operand[0]->Integer.Value,
(UINT32) Operand[1]->Integer.Value,
(UINT32) Operand[2]->Integer.Value));
@@ -116,8 +114,8 @@ AcpiExOpcode_3A_0T_0R (
Fatal = ACPI_ALLOCATE (sizeof (ACPI_SIGNAL_FATAL_INFO));
if (Fatal)
{
- Fatal->Type = (UINT32) Operand[0]->Integer.Value;
- Fatal->Code = (UINT32) Operand[1]->Integer.Value;
+ Fatal->Type = (UINT32) Operand[0]->Integer.Value;
+ Fatal->Code = (UINT32) Operand[1]->Integer.Value;
Fatal->Argument = (UINT32) Operand[2]->Integer.Value;
}
@@ -128,13 +126,26 @@ AcpiExOpcode_3A_0T_0R (
/* Might return while OS is shutting down, just continue */
ACPI_FREE (Fatal);
- break;
+ goto Cleanup;
+ case AML_EXTERNAL_OP:
+ /*
+ * If the interpreter sees this opcode, just ignore it. The External
+ * op is intended for use by disassemblers in order to properly
+ * disassemble control method invocations. The opcode or group of
+ * opcodes should be surrounded by an "if (0)" clause to ensure that
+ * AML interpreters never see the opcode. Thus, something is
+ * wrong if an external opcode ever gets here.
+ */
+ ACPI_ERROR ((AE_INFO, "Executed External Op"));
+ Status = AE_OK;
+ goto Cleanup;
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
@@ -177,13 +188,12 @@ AcpiExOpcode_3A_1T_1R (
switch (WalkState->Opcode)
{
case AML_MID_OP: /* Mid (Source[0], Index[1], Length[2], Result[3]) */
-
/*
- * Create the return object. The Source operand is guaranteed to be
+ * Create the return object. The Source operand is guaranteed to be
* either a String or a Buffer, so just use its type.
*/
ReturnDesc = AcpiUtCreateInternalObject (
- (Operand[0])->Common.Type);
+ (Operand[0])->Common.Type);
if (!ReturnDesc)
{
Status = AE_NO_MEMORY;
@@ -208,8 +218,8 @@ AcpiExOpcode_3A_1T_1R (
else if ((Index + Length) > Operand[0]->String.Length)
{
- Length = (ACPI_SIZE) Operand[0]->String.Length -
- (ACPI_SIZE) Index;
+ Length =
+ (ACPI_SIZE) Operand[0]->String.Length - (ACPI_SIZE) Index;
}
/* Strings always have a sub-pointer, not so for buffers */
@@ -255,8 +265,8 @@ AcpiExOpcode_3A_1T_1R (
{
/* We have a buffer, copy the portion requested */
- ACPI_MEMCPY (Buffer, Operand[0]->String.Pointer + Index,
- Length);
+ memcpy (Buffer,
+ Operand[0]->String.Pointer + Index, Length);
}
/* Set the length of the new String/Buffer */
@@ -269,11 +279,11 @@ AcpiExOpcode_3A_1T_1R (
ReturnDesc->Buffer.Flags |= AOPOBJ_DATA_VALID;
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
@@ -291,14 +301,12 @@ Cleanup:
AcpiUtRemoveReference (ReturnDesc);
WalkState->ResultObj = NULL;
}
-
- /* Set the return object and exit */
-
else
{
+ /* Set the return object and exit */
+
WalkState->ResultObj = ReturnDesc;
}
+
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exoparg6.c b/usr/src/uts/intel/io/acpica/executer/exoparg6.c
index fd6e039c01..2cc8f59bb0 100644
--- a/usr/src/uts/intel/io/acpica/executer/exoparg6.c
+++ b/usr/src/uts/intel/io/acpica/executer/exoparg6.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exoparg6 - AML execution - opcodes with 6 arguments
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXOPARG6_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -131,13 +128,12 @@ AcpiExDoMatch (
break;
case MATCH_MEQ:
-
/*
* True if equal: (P[i] == M)
* Change to: (M == P[i])
*/
- Status = AcpiExDoLogicalOp (AML_LEQUAL_OP, MatchObj, PackageObj,
- &LogicalResult);
+ Status = AcpiExDoLogicalOp (
+ AML_LEQUAL_OP, MatchObj, PackageObj, &LogicalResult);
if (ACPI_FAILURE (Status))
{
return (FALSE);
@@ -145,13 +141,12 @@ AcpiExDoMatch (
break;
case MATCH_MLE:
-
/*
* True if less than or equal: (P[i] <= M) (P[i] NotGreater than M)
* Change to: (M >= P[i]) (M NotLess than P[i])
*/
- Status = AcpiExDoLogicalOp (AML_LLESS_OP, MatchObj, PackageObj,
- &LogicalResult);
+ Status = AcpiExDoLogicalOp (
+ AML_LLESS_OP, MatchObj, PackageObj, &LogicalResult);
if (ACPI_FAILURE (Status))
{
return (FALSE);
@@ -160,13 +155,12 @@ AcpiExDoMatch (
break;
case MATCH_MLT:
-
/*
* True if less than: (P[i] < M)
* Change to: (M > P[i])
*/
- Status = AcpiExDoLogicalOp (AML_LGREATER_OP, MatchObj, PackageObj,
- &LogicalResult);
+ Status = AcpiExDoLogicalOp (
+ AML_LGREATER_OP, MatchObj, PackageObj, &LogicalResult);
if (ACPI_FAILURE (Status))
{
return (FALSE);
@@ -174,13 +168,12 @@ AcpiExDoMatch (
break;
case MATCH_MGE:
-
/*
* True if greater than or equal: (P[i] >= M) (P[i] NotLess than M)
* Change to: (M <= P[i]) (M NotGreater than P[i])
*/
- Status = AcpiExDoLogicalOp (AML_LGREATER_OP, MatchObj, PackageObj,
- &LogicalResult);
+ Status = AcpiExDoLogicalOp (
+ AML_LGREATER_OP, MatchObj, PackageObj, &LogicalResult);
if (ACPI_FAILURE (Status))
{
return (FALSE);
@@ -189,13 +182,12 @@ AcpiExDoMatch (
break;
case MATCH_MGT:
-
/*
* True if greater than: (P[i] > M)
* Change to: (M < P[i])
*/
- Status = AcpiExDoLogicalOp (AML_LLESS_OP, MatchObj, PackageObj,
- &LogicalResult);
+ Status = AcpiExDoLogicalOp (
+ AML_LLESS_OP, MatchObj, PackageObj, &LogicalResult);
if (ACPI_FAILURE (Status))
{
return (FALSE);
@@ -209,7 +201,7 @@ AcpiExDoMatch (
return (FALSE);
}
- return LogicalResult;
+ return (LogicalResult);
}
@@ -288,7 +280,7 @@ AcpiExOpcode_6A_0T_1R (
* and the next should be examined.
*
* Upon finding a match, the loop will terminate via "break" at
- * the bottom. If it terminates "normally", MatchValue will be
+ * the bottom. If it terminates "normally", MatchValue will be
* ACPI_UINT64_MAX (Ones) (its initial value) indicating that no
* match was found.
*/
@@ -311,13 +303,13 @@ AcpiExOpcode_6A_0T_1R (
* non-match.
*/
if (!AcpiExDoMatch ((UINT32) Operand[1]->Integer.Value,
- ThisElement, Operand[2]))
+ ThisElement, Operand[2]))
{
continue;
}
if (!AcpiExDoMatch ((UINT32) Operand[3]->Integer.Value,
- ThisElement, Operand[4]))
+ ThisElement, Operand[4]))
{
continue;
}
@@ -329,17 +321,16 @@ AcpiExOpcode_6A_0T_1R (
}
break;
-
case AML_LOAD_TABLE_OP:
Status = AcpiExLoadTableOp (WalkState, &ReturnDesc);
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown AML opcode 0x%X",
WalkState->Opcode));
+
Status = AE_AML_BAD_OPCODE;
goto Cleanup;
}
diff --git a/usr/src/uts/intel/io/acpica/executer/exprep.c b/usr/src/uts/intel/io/acpica/executer/exprep.c
index ee96a6a090..51f2714ab0 100644
--- a/usr/src/uts/intel/io/acpica/executer/exprep.c
+++ b/usr/src/uts/intel/io/acpica/executer/exprep.c
@@ -1,12 +1,11 @@
-
/******************************************************************************
*
- * Module Name: exprep - ACPI AML (p-code) execution - field prep utilities
+ * Module Name: exprep - ACPI AML field prep utilities
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,13 +41,12 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXPREP_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
#include "amlcode.h"
#include "acnamesp.h"
+#include "acdispat.h"
#define _COMPONENT ACPI_EXECUTER
@@ -71,6 +69,7 @@ AcpiExGenerateAccess (
UINT32 FieldBitLength,
UINT32 RegionLength);
+
/*******************************************************************************
*
* FUNCTION: AcpiExGenerateAccess
@@ -86,8 +85,8 @@ AcpiExGenerateAccess (
* AnyAcc keyword.
*
* NOTE: Need to have the RegionLength in order to check for boundary
- * conditions (end-of-region). However, the RegionLength is a deferred
- * operation. Therefore, to complete this implementation, the generation
+ * conditions (end-of-region). However, the RegionLength is a deferred
+ * operation. Therefore, to complete this implementation, the generation
* of this access width must be deferred until the region length has
* been evaluated.
*
@@ -115,10 +114,13 @@ AcpiExGenerateAccess (
/* Round Field start offset and length to "minimal" byte boundaries */
- FieldByteOffset = ACPI_DIV_8 (ACPI_ROUND_DOWN (FieldBitOffset, 8));
- FieldByteEndOffset = ACPI_DIV_8 (ACPI_ROUND_UP (FieldBitLength +
- FieldBitOffset, 8));
- FieldByteLength = FieldByteEndOffset - FieldByteOffset;
+ FieldByteOffset = ACPI_DIV_8 (
+ ACPI_ROUND_DOWN (FieldBitOffset, 8));
+
+ FieldByteEndOffset = ACPI_DIV_8 (
+ ACPI_ROUND_UP (FieldBitLength + FieldBitOffset, 8));
+
+ FieldByteLength = FieldByteEndOffset - FieldByteOffset;
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Bit length %u, Bit offset %u\n",
@@ -143,7 +145,8 @@ AcpiExGenerateAccess (
* are done. (This does not optimize for the perfectly aligned
* case yet).
*/
- if (ACPI_ROUND_UP (FieldByteEndOffset, AccessByteWidth) <= RegionLength)
+ if (ACPI_ROUND_UP (FieldByteEndOffset, AccessByteWidth) <=
+ RegionLength)
{
FieldStartOffset =
ACPI_ROUND_DOWN (FieldByteOffset, AccessByteWidth) /
@@ -167,7 +170,8 @@ AcpiExGenerateAccess (
if (Accesses <= 1)
{
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
- "Entire field can be accessed with one operation of size %u\n",
+ "Entire field can be accessed "
+ "with one operation of size %u\n",
AccessByteWidth));
return_VALUE (AccessByteWidth);
}
@@ -178,14 +182,15 @@ AcpiExGenerateAccess (
*/
if (Accesses < MinimumAccesses)
{
- MinimumAccesses = Accesses;
+ MinimumAccesses = Accesses;
MinimumAccessWidth = AccessByteWidth;
}
}
else
{
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
- "AccessWidth %u end is NOT within region\n", AccessByteWidth));
+ "AccessWidth %u end is NOT within region\n",
+ AccessByteWidth));
if (AccessByteWidth == 1)
{
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
@@ -213,6 +218,7 @@ AcpiExGenerateAccess (
*/
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"Cannot access field in one operation, using width 8\n"));
+
return_VALUE (8);
}
#endif /* ACPI_UNDER_DEVELOPMENT */
@@ -267,31 +273,37 @@ AcpiExDecodeFieldAccess (
case AML_FIELD_ACCESS_BYTE:
case AML_FIELD_ACCESS_BUFFER: /* ACPI 2.0 (SMBus Buffer) */
+
ByteAlignment = 1;
BitLength = 8;
break;
case AML_FIELD_ACCESS_WORD:
+
ByteAlignment = 2;
BitLength = 16;
break;
case AML_FIELD_ACCESS_DWORD:
+
ByteAlignment = 4;
BitLength = 32;
break;
case AML_FIELD_ACCESS_QWORD: /* ACPI 2.0 */
+
ByteAlignment = 8;
BitLength = 64;
break;
default:
+
/* Invalid field access type */
ACPI_ERROR ((AE_INFO,
"Unknown field access type 0x%X",
Access));
+
return_UINT32 (0);
}
@@ -325,7 +337,7 @@ AcpiExDecodeFieldAccess (
* RETURN: Status
*
* DESCRIPTION: Initialize the areas of the field object that are common
- * to the various types of fields. Note: This is very "sensitive"
+ * to the various types of fields. Note: This is very "sensitive"
* code because we are solving the general case for field
* alignment.
*
@@ -357,13 +369,13 @@ AcpiExPrepCommonFieldObject (
ObjDesc->CommonField.BitLength = FieldBitLength;
/*
- * Decode the access type so we can compute offsets. The access type gives
+ * Decode the access type so we can compute offsets. The access type gives
* two pieces of information - the width of each field access and the
* necessary ByteAlignment (address granularity) of the access.
*
* For AnyAcc, the AccessBitWidth is the largest width that is both
* necessary and possible in an attempt to access the whole field in one
- * I/O operation. However, for AnyAcc, the ByteAlignment is always one
+ * I/O operation. However, for AnyAcc, the ByteAlignment is always one
* byte.
*
* For all Buffer Fields, the ByteAlignment is always one byte.
@@ -371,8 +383,8 @@ AcpiExPrepCommonFieldObject (
* For all other access types (Byte, Word, Dword, Qword), the Bitwidth is
* the same (equivalent) as the ByteAlignment.
*/
- AccessBitWidth = AcpiExDecodeFieldAccess (ObjDesc, FieldFlags,
- &ByteAlignment);
+ AccessBitWidth = AcpiExDecodeFieldAccess (
+ ObjDesc, FieldFlags, &ByteAlignment);
if (!AccessBitWidth)
{
return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
@@ -385,7 +397,7 @@ AcpiExPrepCommonFieldObject (
/*
* BaseByteOffset is the address of the start of the field within the
- * region. It is the byte address of the first *datum* (field-width data
+ * region. It is the byte address of the first *datum* (field-width data
* unit) of the field. (i.e., the first datum that contains at least the
* first *bit* of the field.)
*
@@ -417,8 +429,8 @@ AcpiExPrepCommonFieldObject (
*
* RETURN: Status
*
- * DESCRIPTION: Construct an ACPI_OPERAND_OBJECT of type DefField and
- * connect it to the parent Node.
+ * DESCRIPTION: Construct an object of type ACPI_OPERAND_OBJECT with a
+ * subtype of DefField and connect it to the parent Node.
*
******************************************************************************/
@@ -468,8 +480,8 @@ AcpiExPrepFieldValue (
ObjDesc->CommonField.Node = Info->FieldNode;
Status = AcpiExPrepCommonFieldObject (ObjDesc,
- Info->FieldFlags, Info->Attribute,
- Info->FieldBitPosition, Info->FieldBitLength);
+ Info->FieldFlags, Info->Attribute,
+ Info->FieldBitPosition, Info->FieldBitLength);
if (ACPI_FAILURE (Status))
{
AcpiUtDeleteObjectDesc (ObjDesc);
@@ -484,6 +496,36 @@ AcpiExPrepFieldValue (
ObjDesc->Field.RegionObj = AcpiNsGetAttachedObject (Info->RegionNode);
+ /* Fields specific to GenericSerialBus fields */
+
+ ObjDesc->Field.AccessLength = Info->AccessLength;
+
+ if (Info->ConnectionNode)
+ {
+ SecondDesc = Info->ConnectionNode->Object;
+ if (!(SecondDesc->Common.Flags & AOPOBJ_DATA_VALID))
+ {
+ Status = AcpiDsGetBufferArguments (SecondDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiUtDeleteObjectDesc (ObjDesc);
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ ObjDesc->Field.ResourceBuffer =
+ SecondDesc->Buffer.Pointer;
+ ObjDesc->Field.ResourceLength =
+ (UINT16) SecondDesc->Buffer.Length;
+ }
+ else if (Info->ResourceBuffer)
+ {
+ ObjDesc->Field.ResourceBuffer = Info->ResourceBuffer;
+ ObjDesc->Field.ResourceLength = Info->ResourceLength;
+ }
+
+ ObjDesc->Field.PinNumberIndex = Info->PinNumberIndex;
+
/* Allow full data read from EC address space */
if ((ObjDesc->Field.RegionObj->Region.SpaceId == ACPI_ADR_SPACE_EC) &&
@@ -496,7 +538,8 @@ AcpiExPrepFieldValue (
if (AccessByteWidth < 256)
{
- ObjDesc->CommonField.AccessByteWidth = (UINT8) AccessByteWidth;
+ ObjDesc->CommonField.AccessByteWidth =
+ (UINT8) AccessByteWidth;
}
}
@@ -506,11 +549,12 @@ AcpiExPrepFieldValue (
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
"RegionField: BitOff %X, Off %X, Gran %X, Region %p\n",
- ObjDesc->Field.StartFieldBitOffset, ObjDesc->Field.BaseByteOffset,
- ObjDesc->Field.AccessByteWidth, ObjDesc->Field.RegionObj));
+ ObjDesc->Field.StartFieldBitOffset,
+ ObjDesc->Field.BaseByteOffset,
+ ObjDesc->Field.AccessByteWidth,
+ ObjDesc->Field.RegionObj));
break;
-
case ACPI_TYPE_LOCAL_BANK_FIELD:
ObjDesc->BankField.Value = Info->BankValue;
@@ -545,7 +589,6 @@ AcpiExPrepFieldValue (
break;
-
case ACPI_TYPE_LOCAL_INDEX_FIELD:
/* Get the Index and Data registers */
@@ -589,7 +632,8 @@ AcpiExPrepFieldValue (
ObjDesc->IndexField.AccessByteWidth);
ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
- "IndexField: BitOff %X, Off %X, Value %X, Gran %X, Index %p, Data %p\n",
+ "IndexField: BitOff %X, Off %X, Value %X, "
+ "Gran %X, Index %p, Data %p\n",
ObjDesc->IndexField.StartFieldBitOffset,
ObjDesc->IndexField.BaseByteOffset,
ObjDesc->IndexField.Value,
@@ -599,7 +643,9 @@ AcpiExPrepFieldValue (
break;
default:
+
/* No other types should get here */
+
break;
}
@@ -607,10 +653,11 @@ AcpiExPrepFieldValue (
* Store the constructed descriptor (ObjDesc) into the parent Node,
* preserving the current type of that NamedObj.
*/
- Status = AcpiNsAttachObject (Info->FieldNode, ObjDesc,
- AcpiNsGetType (Info->FieldNode));
+ Status = AcpiNsAttachObject (
+ Info->FieldNode, ObjDesc, AcpiNsGetType (Info->FieldNode));
- ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Set NamedObj %p [%4.4s], ObjDesc %p\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD,
+ "Set NamedObj %p [%4.4s], ObjDesc %p\n",
Info->FieldNode, AcpiUtGetNodeName (Info->FieldNode), ObjDesc));
/* Remove local reference to the object */
@@ -618,4 +665,3 @@ AcpiExPrepFieldValue (
AcpiUtRemoveReference (ObjDesc);
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exregion.c b/usr/src/uts/intel/io/acpica/executer/exregion.c
index b3b9aa6e66..58d3a0c24b 100644
--- a/usr/src/uts/intel/io/acpica/executer/exregion.c
+++ b/usr/src/uts/intel/io/acpica/executer/exregion.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exregion - ACPI default OpRegion (address space) handlers
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __EXREGION_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -100,22 +96,27 @@ AcpiExSystemMemorySpaceHandler (
switch (BitWidth)
{
case 8:
+
Length = 1;
break;
case 16:
+
Length = 2;
break;
case 32:
+
Length = 4;
break;
case 64:
+
Length = 8;
break;
default:
+
ACPI_ERROR ((AE_INFO, "Invalid SystemMemory width %u",
BitWidth));
return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
@@ -174,8 +175,8 @@ AcpiExSystemMemorySpaceHandler (
* one page, which is similar to the original code that used a 4k
* maximum window.
*/
- PageBoundaryMapLength =
- ACPI_ROUND_UP (Address, ACPI_DEFAULT_PAGE_SIZE) - Address;
+ PageBoundaryMapLength = (ACPI_SIZE)
+ (ACPI_ROUND_UP (Address, ACPI_DEFAULT_PAGE_SIZE) - Address);
if (PageBoundaryMapLength == 0)
{
PageBoundaryMapLength = ACPI_DEFAULT_PAGE_SIZE;
@@ -188,13 +189,12 @@ AcpiExSystemMemorySpaceHandler (
/* Create a new mapping starting at the address given */
- MemInfo->MappedLogicalAddress = AcpiOsMapMemory (
- (ACPI_PHYSICAL_ADDRESS) Address, MapLength);
+ MemInfo->MappedLogicalAddress = AcpiOsMapMemory (Address, MapLength);
if (!MemInfo->MappedLogicalAddress)
{
ACPI_ERROR ((AE_INFO,
"Could not map memory at 0x%8.8X%8.8X, size %u",
- ACPI_FORMAT_NATIVE_UINT (Address), (UINT32) MapLength));
+ ACPI_FORMAT_UINT64 (Address), (UINT32) MapLength));
MemInfo->MappedLength = 0;
return_ACPI_STATUS (AE_NO_MEMORY);
}
@@ -214,13 +214,13 @@ AcpiExSystemMemorySpaceHandler (
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
"System-Memory (width %u) R/W %u Address=%8.8X%8.8X\n",
- BitWidth, Function, ACPI_FORMAT_NATIVE_UINT (Address)));
+ BitWidth, Function, ACPI_FORMAT_UINT64 (Address)));
/*
* Perform the memory read or write
*
* Note: For machines that do not support non-aligned transfers, the target
- * address was checked for alignment above. We do not attempt to break the
+ * address was checked for alignment above. We do not attempt to break the
* transfer up into smaller (byte-size) chunks because the AML specifically
* asked for a transfer width that the hardware may require.
*/
@@ -232,23 +232,29 @@ AcpiExSystemMemorySpaceHandler (
switch (BitWidth)
{
case 8:
+
*Value = (UINT64) ACPI_GET8 (LogicalAddrPtr);
break;
case 16:
+
*Value = (UINT64) ACPI_GET16 (LogicalAddrPtr);
break;
case 32:
+
*Value = (UINT64) ACPI_GET32 (LogicalAddrPtr);
break;
case 64:
+
*Value = (UINT64) ACPI_GET64 (LogicalAddrPtr);
break;
default:
+
/* BitWidth was already validated */
+
break;
}
break;
@@ -258,28 +264,35 @@ AcpiExSystemMemorySpaceHandler (
switch (BitWidth)
{
case 8:
- ACPI_SET8 (LogicalAddrPtr) = (UINT8) *Value;
+
+ ACPI_SET8 (LogicalAddrPtr, *Value);
break;
case 16:
- ACPI_SET16 (LogicalAddrPtr) = (UINT16) *Value;
+
+ ACPI_SET16 (LogicalAddrPtr, *Value);
break;
case 32:
- ACPI_SET32 ( LogicalAddrPtr) = (UINT32) *Value;
+
+ ACPI_SET32 (LogicalAddrPtr, *Value);
break;
case 64:
- ACPI_SET64 (LogicalAddrPtr) = (UINT64) *Value;
+
+ ACPI_SET64 (LogicalAddrPtr, *Value);
break;
default:
+
/* BitWidth was already validated */
+
break;
}
break;
default:
+
Status = AE_BAD_PARAMETER;
break;
}
@@ -324,7 +337,7 @@ AcpiExSystemIoSpaceHandler (
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
"System-IO (width %u) R/W %u Address=%8.8X%8.8X\n",
- BitWidth, Function, ACPI_FORMAT_NATIVE_UINT (Address)));
+ BitWidth, Function, ACPI_FORMAT_UINT64 (Address)));
/* Decode the function parameter */
@@ -344,6 +357,7 @@ AcpiExSystemIoSpaceHandler (
break;
default:
+
Status = AE_BAD_PARAMETER;
break;
}
@@ -403,7 +417,8 @@ AcpiExPciConfigSpaceHandler (
PciRegister = (UINT16) (UINT32) Address;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Pci-Config %u (%u) Seg(%04x) Bus(%04x) Dev(%04x) Func(%04x) Reg(%04x)\n",
+ "Pci-Config %u (%u) Seg(%04x) Bus(%04x) "
+ "Dev(%04x) Func(%04x) Reg(%04x)\n",
Function, BitWidth, PciId->Segment, PciId->Bus, PciId->Device,
PciId->Function, PciRegister));
@@ -412,14 +427,14 @@ AcpiExPciConfigSpaceHandler (
case ACPI_READ:
*Value = 0;
- Status = AcpiOsReadPciConfiguration (PciId, PciRegister,
- Value, BitWidth);
+ Status = AcpiOsReadPciConfiguration (
+ PciId, PciRegister, Value, BitWidth);
break;
case ACPI_WRITE:
- Status = AcpiOsWritePciConfiguration (PciId, PciRegister,
- *Value, BitWidth);
+ Status = AcpiOsWritePciConfiguration (
+ PciId, PciRegister, *Value, BitWidth);
break;
default:
@@ -544,13 +559,13 @@ AcpiExDataTableSpaceHandler (
{
case ACPI_READ:
- ACPI_MEMCPY (ACPI_CAST_PTR (char, Value), ACPI_PHYSADDR_TO_PTR (Address),
+ memcpy (ACPI_CAST_PTR (char, Value), ACPI_PHYSADDR_TO_PTR (Address),
ACPI_DIV_8 (BitWidth));
break;
case ACPI_WRITE:
- ACPI_MEMCPY (ACPI_PHYSADDR_TO_PTR (Address), ACPI_CAST_PTR (char, Value),
+ memcpy (ACPI_PHYSADDR_TO_PTR (Address), ACPI_CAST_PTR (char, Value),
ACPI_DIV_8 (BitWidth));
break;
@@ -561,5 +576,3 @@ AcpiExDataTableSpaceHandler (
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exresnte.c b/usr/src/uts/intel/io/acpica/executer/exresnte.c
index cf8a55531f..9a85450de9 100644
--- a/usr/src/uts/intel/io/acpica/executer/exresnte.c
+++ b/usr/src/uts/intel/io/acpica/executer/exresnte.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exresnte - AML Interpreter object resolution
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXRESNTE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
@@ -62,8 +59,8 @@
* PARAMETERS: ObjectPtr - Pointer to a location that contains
* a pointer to a NS node, and will receive a
* pointer to the resolved object.
- * WalkState - Current state. Valid only if executing AML
- * code. NULL if simply resolving an object
+ * WalkState - Current state. Valid only if executing AML
+ * code. NULL if simply resolving an object
*
* RETURN: Status
*
@@ -71,7 +68,7 @@
*
* Note: for some of the data types, the pointer attached to the Node
* can be either a pointer to an actual internal object or a pointer into the
- * AML stream itself. These types are currently:
+ * AML stream itself. These types are currently:
*
* ACPI_TYPE_INTEGER
* ACPI_TYPE_STRING
@@ -98,12 +95,12 @@ AcpiExResolveNodeToValue (
/*
- * The stack pointer points to a ACPI_NAMESPACE_NODE (Node). Get the
+ * The stack pointer points to a ACPI_NAMESPACE_NODE (Node). Get the
* object that is attached to the Node.
*/
- Node = *ObjectPtr;
+ Node = *ObjectPtr;
SourceDesc = AcpiNsGetAttachedObject (Node);
- EntryType = AcpiNsGetType ((ACPI_HANDLE) Node);
+ EntryType = AcpiNsGetType ((ACPI_HANDLE) Node);
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Entry=%p SourceDesc=%p [%s]\n",
Node, SourceDesc, AcpiUtGetTypeName (EntryType)));
@@ -113,15 +110,15 @@ AcpiExResolveNodeToValue (
{
/* There is always exactly one level of indirection */
- Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Node->Object);
+ Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Node->Object);
SourceDesc = AcpiNsGetAttachedObject (Node);
- EntryType = AcpiNsGetType ((ACPI_HANDLE) Node);
+ EntryType = AcpiNsGetType ((ACPI_HANDLE) Node);
*ObjectPtr = Node;
}
/*
* Several object types require no further processing:
- * 1) Device/Thermal objects don't have a "real" subobject, return the Node
+ * 1) Device/Thermal objects don't have a "real" subobject, return Node
* 2) Method locals and arguments have a pseudo-Node
* 3) 10/2007: Added method type to assist with Package construction.
*/
@@ -135,9 +132,9 @@ AcpiExResolveNodeToValue (
if (!SourceDesc)
{
- ACPI_ERROR ((AE_INFO, "No object attached to node %p",
- Node));
- return_ACPI_STATUS (AE_AML_NO_OPERAND);
+ ACPI_ERROR ((AE_INFO, "No object attached to node [%4.4s] %p",
+ Node->Name.Ascii, Node));
+ return_ACPI_STATUS (AE_AML_UNINITIALIZED_NODE);
}
/*
@@ -165,7 +162,6 @@ AcpiExResolveNodeToValue (
}
break;
-
case ACPI_TYPE_BUFFER:
if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER)
@@ -185,7 +181,6 @@ AcpiExResolveNodeToValue (
}
break;
-
case ACPI_TYPE_STRING:
if (SourceDesc->Common.Type != ACPI_TYPE_STRING)
@@ -201,7 +196,6 @@ AcpiExResolveNodeToValue (
AcpiUtAddReference (ObjDesc);
break;
-
case ACPI_TYPE_INTEGER:
if (SourceDesc->Common.Type != ACPI_TYPE_INTEGER)
@@ -217,7 +211,6 @@ AcpiExResolveNodeToValue (
AcpiUtAddReference (ObjDesc);
break;
-
case ACPI_TYPE_BUFFER_FIELD:
case ACPI_TYPE_LOCAL_REGION_FIELD:
case ACPI_TYPE_LOCAL_BANK_FIELD:
@@ -253,7 +246,6 @@ AcpiExResolveNodeToValue (
return_ACPI_STATUS (AE_AML_OPERAND_TYPE); /* Cannot be AE_TYPE */
-
case ACPI_TYPE_LOCAL_REFERENCE:
switch (SourceDesc->Reference.Class)
@@ -269,6 +261,7 @@ AcpiExResolveNodeToValue (
break;
default:
+
/* No named references are allowed here */
ACPI_ERROR ((AE_INFO,
@@ -279,7 +272,6 @@ AcpiExResolveNodeToValue (
}
break;
-
default:
/* Default case is for unknown types */
@@ -298,5 +290,3 @@ AcpiExResolveNodeToValue (
*ObjectPtr = (void *) ObjDesc;
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exresolv.c b/usr/src/uts/intel/io/acpica/executer/exresolv.c
index 652d95dd54..4b6202f4fb 100644
--- a/usr/src/uts/intel/io/acpica/executer/exresolv.c
+++ b/usr/src/uts/intel/io/acpica/executer/exresolv.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exresolv - AML Interpreter object resolution
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXRESOLV_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -122,8 +119,8 @@ AcpiExResolveToValue (
if (ACPI_GET_DESCRIPTOR_TYPE (*StackPtr) == ACPI_DESC_TYPE_NAMED)
{
Status = AcpiExResolveNodeToValue (
- ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, StackPtr),
- WalkState);
+ ACPI_CAST_INDIRECT_PTR (ACPI_NAMESPACE_NODE, StackPtr),
+ WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -165,7 +162,7 @@ AcpiExResolveObjectToValue (
StackDesc = *StackPtr;
- /* This is an ACPI_OPERAND_OBJECT */
+ /* This is an object of type ACPI_OPERAND_OBJECT */
switch (StackDesc->Common.Type)
{
@@ -177,13 +174,12 @@ AcpiExResolveObjectToValue (
{
case ACPI_REFCLASS_LOCAL:
case ACPI_REFCLASS_ARG:
-
/*
* Get the local from the method's state info
* Note: this increments the local's object reference count
*/
Status = AcpiDsMethodDataGetValue (RefType,
- StackDesc->Reference.Value, WalkState, &ObjDesc);
+ StackDesc->Reference.Value, WalkState, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -200,7 +196,6 @@ AcpiExResolveObjectToValue (
*StackPtr = ObjDesc;
break;
-
case ACPI_REFCLASS_INDEX:
switch (StackDesc->Reference.TargetType)
@@ -210,7 +205,6 @@ AcpiExResolveObjectToValue (
/* Just return - do not dereference */
break;
-
case ACPI_TYPE_PACKAGE:
/* If method call or CopyObject - do not dereference */
@@ -231,7 +225,6 @@ AcpiExResolveObjectToValue (
* (i.e., dereference the package index)
* Delete the ref object, increment the returned object
*/
- AcpiUtRemoveReference (StackDesc);
AcpiUtAddReference (ObjDesc);
*StackPtr = ObjDesc;
}
@@ -242,13 +235,13 @@ AcpiExResolveObjectToValue (
* the package, can't dereference it
*/
ACPI_ERROR ((AE_INFO,
- "Attempt to dereference an Index to NULL package element Idx=%p",
+ "Attempt to dereference an Index to "
+ "NULL package element Idx=%p",
StackDesc));
Status = AE_AML_UNINITIALIZED_ELEMENT;
}
break;
-
default:
/* Invalid reference object */
@@ -261,7 +254,6 @@ AcpiExResolveObjectToValue (
}
break;
-
case ACPI_REFCLASS_REFOF:
case ACPI_REFCLASS_DEBUG:
case ACPI_REFCLASS_TABLE:
@@ -295,31 +287,30 @@ AcpiExResolveObjectToValue (
default:
ACPI_ERROR ((AE_INFO,
- "Unknown Reference type 0x%X in %p", RefType, StackDesc));
+ "Unknown Reference type 0x%X in %p",
+ RefType, StackDesc));
Status = AE_AML_INTERNAL;
break;
}
break;
-
case ACPI_TYPE_BUFFER:
Status = AcpiDsGetBufferArguments (StackDesc);
break;
-
case ACPI_TYPE_PACKAGE:
Status = AcpiDsGetPackageArguments (StackDesc);
break;
-
case ACPI_TYPE_BUFFER_FIELD:
case ACPI_TYPE_LOCAL_REGION_FIELD:
case ACPI_TYPE_LOCAL_BANK_FIELD:
case ACPI_TYPE_LOCAL_INDEX_FIELD:
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "FieldRead SourceDesc=%p Type=%X\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "FieldRead SourceDesc=%p Type=%X\n",
StackDesc, StackDesc->Common.Type));
Status = AcpiExReadDataFromField (WalkState, StackDesc, &ObjDesc);
@@ -331,6 +322,7 @@ AcpiExResolveObjectToValue (
break;
default:
+
break;
}
@@ -349,7 +341,7 @@ AcpiExResolveObjectToValue (
*
* RETURN: Status
*
- * DESCRIPTION: Return the base object and type. Traverse a reference list if
+ * DESCRIPTION: Return the base object and type. Traverse a reference list if
* necessary to get to the base object.
*
******************************************************************************/
@@ -361,8 +353,8 @@ AcpiExResolveMultiple (
ACPI_OBJECT_TYPE *ReturnType,
ACPI_OPERAND_OBJECT **ReturnDesc)
{
- ACPI_OPERAND_OBJECT *ObjDesc = (void *) Operand;
- ACPI_NAMESPACE_NODE *Node;
+ ACPI_OPERAND_OBJECT *ObjDesc = ACPI_CAST_PTR (void, Operand);
+ ACPI_NAMESPACE_NODE *Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Operand);
ACPI_OBJECT_TYPE Type;
ACPI_STATUS Status;
@@ -375,19 +367,30 @@ AcpiExResolveMultiple (
switch (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc))
{
case ACPI_DESC_TYPE_OPERAND:
+
Type = ObjDesc->Common.Type;
break;
case ACPI_DESC_TYPE_NAMED:
+
Type = ((ACPI_NAMESPACE_NODE *) ObjDesc)->Type;
- ObjDesc = AcpiNsGetAttachedObject ((ACPI_NAMESPACE_NODE *) ObjDesc);
+ ObjDesc = AcpiNsGetAttachedObject (Node);
/* If we had an Alias node, use the attached object for type info */
if (Type == ACPI_TYPE_LOCAL_ALIAS)
{
Type = ((ACPI_NAMESPACE_NODE *) ObjDesc)->Type;
- ObjDesc = AcpiNsGetAttachedObject ((ACPI_NAMESPACE_NODE *) ObjDesc);
+ ObjDesc = AcpiNsGetAttachedObject (
+ (ACPI_NAMESPACE_NODE *) ObjDesc);
+ }
+
+ if (!ObjDesc)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "[%4.4s] Node is unresolved or uninitialized",
+ AcpiUtGetNodeName (Node)));
+ return_ACPI_STATUS (AE_AML_UNINITIALIZED_NODE);
}
break;
@@ -455,7 +458,6 @@ AcpiExResolveMultiple (
}
break;
-
case ACPI_REFCLASS_INDEX:
/* Get the type of this reference (index into another object) */
@@ -483,20 +485,18 @@ AcpiExResolveMultiple (
}
break;
-
case ACPI_REFCLASS_TABLE:
Type = ACPI_TYPE_DDB_HANDLE;
goto Exit;
-
case ACPI_REFCLASS_LOCAL:
case ACPI_REFCLASS_ARG:
if (ReturnDesc)
{
Status = AcpiDsMethodDataGetValue (ObjDesc->Reference.Class,
- ObjDesc->Reference.Value, WalkState, &ObjDesc);
+ ObjDesc->Reference.Value, WalkState, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -506,7 +506,7 @@ AcpiExResolveMultiple (
else
{
Status = AcpiDsMethodDataGetNode (ObjDesc->Reference.Class,
- ObjDesc->Reference.Value, WalkState, &Node);
+ ObjDesc->Reference.Value, WalkState, &Node);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -521,7 +521,6 @@ AcpiExResolveMultiple (
}
break;
-
case ACPI_REFCLASS_DEBUG:
/* The Debug Object is of type "DebugObject" */
@@ -529,11 +528,11 @@ AcpiExResolveMultiple (
Type = ACPI_TYPE_DEBUG_OBJECT;
goto Exit;
-
default:
ACPI_ERROR ((AE_INFO,
- "Unknown Reference Class 0x%2.2X", ObjDesc->Reference.Class));
+ "Unknown Reference Class 0x%2.2X",
+ ObjDesc->Reference.Class));
return_ACPI_STATUS (AE_AML_INTERNAL);
}
}
@@ -565,7 +564,9 @@ Exit:
break;
default:
+
/* No change to Type required */
+
break;
}
@@ -576,5 +577,3 @@ Exit:
}
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exresop.c b/usr/src/uts/intel/io/acpica/executer/exresop.c
index 12c872f99a..32ceb6f021 100644
--- a/usr/src/uts/intel/io/acpica/executer/exresop.c
+++ b/usr/src/uts/intel/io/acpica/executer/exresop.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exresop - AML Interpreter operand/object resolution
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXRESOP_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -98,11 +95,12 @@ AcpiExCheckObjectType (
{
/*
* Allow the AML "Constant" opcodes (Zero, One, etc.) to be reference
- * objects and thus allow them to be targets. (As per the ACPI
+ * objects and thus allow them to be targets. (As per the ACPI
* specification, a store to a constant is a noop.)
*/
if ((ThisType == ACPI_TYPE_INTEGER) &&
- (((ACPI_OPERAND_OBJECT *) Object)->Common.Flags & AOPOBJ_AML_CONSTANT))
+ (((ACPI_OPERAND_OBJECT *) Object)->Common.Flags &
+ AOPOBJ_AML_CONSTANT))
{
return (AE_OK);
}
@@ -219,13 +217,13 @@ AcpiExResolveOperands (
*/
if (ObjectType == ACPI_TYPE_LOCAL_ALIAS)
{
- ObjDesc = AcpiNsGetAttachedObject ((ACPI_NAMESPACE_NODE *) ObjDesc);
+ ObjDesc = AcpiNsGetAttachedObject (
+ (ACPI_NAMESPACE_NODE *) ObjDesc);
*StackPtr = ObjDesc;
ObjectType = ((ACPI_NAMESPACE_NODE *) ObjDesc)->Type;
}
break;
-
case ACPI_DESC_TYPE_OPERAND:
/* ACPI internal object */
@@ -278,7 +276,6 @@ AcpiExResolveOperands (
}
break;
-
default:
/* Invalid descriptor */
@@ -302,7 +299,8 @@ AcpiExResolveOperands (
{
case ARGI_REF_OR_STRING: /* Can be a String or Reference */
- if ((ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) == ACPI_DESC_TYPE_OPERAND) &&
+ if ((ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) ==
+ ACPI_DESC_TYPE_OPERAND) &&
(ObjDesc->Common.Type == ACPI_TYPE_STRING))
{
/*
@@ -325,6 +323,7 @@ AcpiExResolveOperands (
case ARGI_TARGETREF: /* Allows implicit conversion rules before store */
case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */
case ARGI_SIMPLE_TARGET: /* Name, Local, or Arg - no implicit conversion */
+ case ARGI_STORE_TARGET:
/*
* Need an operand of type ACPI_TYPE_LOCAL_REFERENCE
@@ -335,17 +334,15 @@ AcpiExResolveOperands (
goto NextOperand;
}
- Status = AcpiExCheckObjectType (ACPI_TYPE_LOCAL_REFERENCE,
- ObjectType, ObjDesc);
+ Status = AcpiExCheckObjectType (
+ ACPI_TYPE_LOCAL_REFERENCE, ObjectType, ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
goto NextOperand;
-
case ARGI_DATAREFOBJ: /* Store operator only */
-
/*
* We don't want to resolve IndexOp reference objects during
* a store because this would be an implicit DeRefOf operation.
@@ -361,7 +358,9 @@ AcpiExResolveOperands (
break;
default:
+
/* All cases covered above */
+
break;
}
@@ -454,9 +453,7 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_BUFFER:
-
/*
* Need an operand of type ACPI_TYPE_BUFFER,
* But we can implicitly convert from a STRING or INTEGER
@@ -483,16 +480,14 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_STRING:
-
/*
* Need an operand of type ACPI_TYPE_STRING,
* But we can implicitly convert from a BUFFER or INTEGER
* Aka - "Implicit Source Operand Conversion"
*/
- Status = AcpiExConvertToString (ObjDesc, StackPtr,
- ACPI_IMPLICIT_CONVERT_HEX);
+ Status = AcpiExConvertToString (
+ ObjDesc, StackPtr, ACPI_IMPLICIT_CONVERT_HEX);
if (ACPI_FAILURE (Status))
{
if (Status == AE_TYPE)
@@ -513,7 +508,6 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_COMPUTEDATA:
/* Need an operand of type INTEGER, STRING or BUFFER */
@@ -536,7 +530,6 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_BUFFER_OR_STRING:
/* Need an operand of type STRING or BUFFER */
@@ -574,7 +567,6 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_DATAOBJECT:
/*
* ARGI_DATAOBJECT is only used by the SizeOf operator.
@@ -594,6 +586,7 @@ AcpiExResolveOperands (
break;
default:
+
ACPI_ERROR ((AE_INFO,
"Needed [Buffer/String/Package/Reference], found [%s] %p",
AcpiUtGetObjectTypeName (ObjDesc), ObjDesc));
@@ -602,7 +595,6 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_COMPLEXOBJ:
/* Need a buffer or package or (ACPI 2.0) String */
@@ -617,6 +609,7 @@ AcpiExResolveOperands (
break;
default:
+
ACPI_ERROR ((AE_INFO,
"Needed [Buffer/String/Package], found [%s] %p",
AcpiUtGetObjectTypeName (ObjDesc), ObjDesc));
@@ -625,11 +618,12 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_REGION_OR_BUFFER: /* Used by Load() only */
- /* Need an operand of type REGION or a BUFFER (which could be a resolved region field) */
-
+ /*
+ * Need an operand of type REGION or a BUFFER
+ * (which could be a resolved region field)
+ */
switch (ObjDesc->Common.Type)
{
case ACPI_TYPE_BUFFER:
@@ -639,6 +633,7 @@ AcpiExResolveOperands (
break;
default:
+
ACPI_ERROR ((AE_INFO,
"Needed [Region/Buffer], found [%s] %p",
AcpiUtGetObjectTypeName (ObjDesc), ObjDesc));
@@ -647,7 +642,6 @@ AcpiExResolveOperands (
}
goto NextOperand;
-
case ARGI_DATAREFOBJ:
/* Used by the Store() operator only */
@@ -673,9 +667,9 @@ AcpiExResolveOperands (
if (AcpiGbl_EnableInterpreterSlack)
{
/*
- * Enable original behavior of Store(), allowing any and all
- * objects as the source operand. The ACPI spec does not
- * allow this, however.
+ * Enable original behavior of Store(), allowing any
+ * and all objects as the source operand. The ACPI
+ * spec does not allow this, however.
*/
break;
}
@@ -688,14 +682,14 @@ AcpiExResolveOperands (
}
ACPI_ERROR ((AE_INFO,
- "Needed Integer/Buffer/String/Package/Ref/Ddb], found [%s] %p",
+ "Needed Integer/Buffer/String/Package/Ref/Ddb]"
+ ", found [%s] %p",
AcpiUtGetObjectTypeName (ObjDesc), ObjDesc));
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
goto NextOperand;
-
default:
/* Unknown type */
@@ -711,8 +705,8 @@ AcpiExResolveOperands (
* Make sure that the original object was resolved to the
* required object type (Simple cases only).
*/
- Status = AcpiExCheckObjectType (TypeNeeded,
- (*StackPtr)->Common.Type, *StackPtr);
+ Status = AcpiExCheckObjectType (
+ TypeNeeded, (*StackPtr)->Common.Type, *StackPtr);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -734,5 +728,3 @@ NextOperand:
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exstore.c b/usr/src/uts/intel/io/acpica/executer/exstore.c
index 8679e790b8..cb18c036c5 100644
--- a/usr/src/uts/intel/io/acpica/executer/exstore.c
+++ b/usr/src/uts/intel/io/acpica/executer/exstore.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXSTORE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdispat.h"
@@ -62,21 +60,27 @@ AcpiExStoreObjectToIndex (
ACPI_OPERAND_OBJECT *DestDesc,
ACPI_WALK_STATE *WalkState);
+static ACPI_STATUS
+AcpiExStoreDirectToNode (
+ ACPI_OPERAND_OBJECT *SourceDesc,
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_WALK_STATE *WalkState);
+
/*******************************************************************************
*
* FUNCTION: AcpiExStore
*
* PARAMETERS: *SourceDesc - Value to be stored
- * *DestDesc - Where to store it. Must be an NS node
- * or an ACPI_OPERAND_OBJECT of type
+ * *DestDesc - Where to store it. Must be an NS node
+ * or ACPI_OPERAND_OBJECT of type
* Reference;
* WalkState - Current walk state
*
* RETURN: Status
*
* DESCRIPTION: Store the value described by SourceDesc into the location
- * described by DestDesc. Called by various interpreter
+ * described by DestDesc. Called by various interpreter
* functions to store the result of an operation into
* the destination operand -- not just simply the actual "Store"
* ASL operator.
@@ -113,8 +117,8 @@ AcpiExStore (
* Storing an object into a Named node.
*/
Status = AcpiExStoreObjectToNode (SourceDesc,
- (ACPI_NAMESPACE_NODE *) DestDesc, WalkState,
- ACPI_IMPLICIT_CONVERSION);
+ (ACPI_NAMESPACE_NODE *) DestDesc, WalkState,
+ ACPI_IMPLICIT_CONVERSION);
return_ACPI_STATUS (Status);
}
@@ -124,6 +128,7 @@ AcpiExStore (
switch (DestDesc->Common.Type)
{
case ACPI_TYPE_LOCAL_REFERENCE:
+
break;
case ACPI_TYPE_INTEGER:
@@ -142,7 +147,7 @@ AcpiExStore (
/* Destination is not a Reference object */
ACPI_ERROR ((AE_INFO,
- "Target is not a Reference or Constant object - %s [%p]",
+ "Target is not a Reference or Constant object - [%s] %p",
AcpiUtGetObjectTypeName (DestDesc), DestDesc));
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
@@ -163,11 +168,10 @@ AcpiExStore (
/* Storing an object into a Name "container" */
Status = AcpiExStoreObjectToNode (SourceDesc,
- RefDesc->Reference.Object,
- WalkState, ACPI_IMPLICIT_CONVERSION);
+ RefDesc->Reference.Object,
+ WalkState, ACPI_IMPLICIT_CONVERSION);
break;
-
case ACPI_REFCLASS_INDEX:
/* Storing to an Index (pointer into a packager or buffer) */
@@ -175,31 +179,27 @@ AcpiExStore (
Status = AcpiExStoreObjectToIndex (SourceDesc, RefDesc, WalkState);
break;
-
case ACPI_REFCLASS_LOCAL:
case ACPI_REFCLASS_ARG:
/* Store to a method local/arg */
Status = AcpiDsStoreObjectToLocal (RefDesc->Reference.Class,
- RefDesc->Reference.Value, SourceDesc, WalkState);
+ RefDesc->Reference.Value, SourceDesc, WalkState);
break;
-
case ACPI_REFCLASS_DEBUG:
-
/*
* Storing to the Debug object causes the value stored to be
* displayed and otherwise has no effect -- see ACPI Specification
*/
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "**** Write to Debug Object: Object %p %s ****:\n\n",
+ "**** Write to Debug Object: Object %p [%s] ****:\n\n",
SourceDesc, AcpiUtGetObjectTypeName (SourceDesc)));
ACPI_DEBUG_OBJECT (SourceDesc, 0, 0);
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unknown Reference Class 0x%2.2X",
@@ -274,7 +274,8 @@ AcpiExStoreObjectToIndex (
{
/* Normal object, copy it */
- Status = AcpiUtCopyIobjectToIobject (SourceDesc, &NewDesc, WalkState);
+ Status = AcpiUtCopyIobjectToIobject (
+ SourceDesc, &NewDesc, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -308,9 +309,7 @@ AcpiExStoreObjectToIndex (
break;
-
case ACPI_TYPE_BUFFER_FIELD:
-
/*
* Store into a Buffer or String (not actually a real BufferField)
* at a location defined by an Index.
@@ -358,7 +357,7 @@ AcpiExStoreObjectToIndex (
/* All other types are invalid */
ACPI_ERROR ((AE_INFO,
- "Source must be Integer/Buffer/String type, not %s",
+ "Source must be type [Integer/Buffer/String], found [%s]",
AcpiUtGetObjectTypeName (SourceDesc)));
return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
}
@@ -368,11 +367,10 @@ AcpiExStoreObjectToIndex (
ObjDesc->Buffer.Pointer[IndexDesc->Reference.Value] = Value;
break;
-
default:
ACPI_ERROR ((AE_INFO,
- "Target is not a Package or BufferField"));
- Status = AE_AML_OPERAND_TYPE;
+ "Target is not of type [Package/BufferField]"));
+ Status = AE_AML_TARGET_TYPE;
break;
}
@@ -393,16 +391,20 @@ AcpiExStoreObjectToIndex (
*
* DESCRIPTION: Store the object to the named object.
*
- * The Assignment of an object to a named object is handled here
- * The value passed in will replace the current value (if any)
- * with the input value.
+ * The assignment of an object to a named object is handled here.
+ * The value passed in will replace the current value (if any)
+ * with the input value.
*
- * When storing into an object the data is converted to the
- * target object type then stored in the object. This means
- * that the target object type (for an initialized target) will
- * not be changed by a store operation.
+ * When storing into an object the data is converted to the
+ * target object type then stored in the object. This means
+ * that the target object type (for an initialized target) will
+ * not be changed by a store operation. A CopyObject can change
+ * the target type, however.
*
- * Assumes parameters are already validated.
+ * The ImplicitConversion flag is set to NO/FALSE only when
+ * storing to an ArgX -- as per the rules of the ACPI spec.
+ *
+ * Assumes parameters are already validated.
*
******************************************************************************/
@@ -427,9 +429,75 @@ AcpiExStoreObjectToNode (
TargetType = AcpiNsGetType (Node);
TargetDesc = AcpiNsGetAttachedObject (Node);
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Storing %p(%s) into node %p(%s)\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Storing %p [%s] to node %p [%s]\n",
SourceDesc, AcpiUtGetObjectTypeName (SourceDesc),
- Node, AcpiUtGetTypeName (TargetType)));
+ Node, AcpiUtGetTypeName (TargetType)));
+
+ /* Only limited target types possible for everything except CopyObject */
+
+ if (WalkState->Opcode != AML_COPY_OP)
+ {
+ /*
+ * Only CopyObject allows all object types to be overwritten. For
+ * TargetRef(s), there are restrictions on the object types that
+ * are allowed.
+ *
+ * Allowable operations/typing for Store:
+ *
+ * 1) Simple Store
+ * Integer --> Integer (Named/Local/Arg)
+ * String --> String (Named/Local/Arg)
+ * Buffer --> Buffer (Named/Local/Arg)
+ * Package --> Package (Named/Local/Arg)
+ *
+ * 2) Store with implicit conversion
+ * Integer --> String or Buffer (Named)
+ * String --> Integer or Buffer (Named)
+ * Buffer --> Integer or String (Named)
+ */
+ switch (TargetType)
+ {
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * Here, can only store a package to an existing package.
+ * Storing a package to a Local/Arg is OK, and handled
+ * elsewhere.
+ */
+ if (WalkState->Opcode == AML_STORE_OP)
+ {
+ if (SourceDesc->Common.Type != ACPI_TYPE_PACKAGE)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Cannot assign type [%s] to [Package] "
+ "(source must be type Pkg)",
+ AcpiUtGetObjectTypeName (SourceDesc)));
+
+ return_ACPI_STATUS (AE_AML_TARGET_TYPE);
+ }
+ break;
+ }
+
+ /* Fallthrough */
+
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_REGION:
+ case ACPI_TYPE_POWER:
+ case ACPI_TYPE_PROCESSOR:
+ case ACPI_TYPE_THERMAL:
+
+ ACPI_ERROR ((AE_INFO,
+ "Target must be [Buffer/Integer/String/Reference]"
+ ", found [%s] (%4.4s)",
+ AcpiUtGetTypeName (Node->Type), Node->Name.Ascii));
+
+ return_ACPI_STATUS (AE_AML_TARGET_TYPE);
+
+ default:
+ break;
+ }
+ }
/*
* Resolve the source object to an actual value
@@ -441,51 +509,34 @@ AcpiExStoreObjectToNode (
return_ACPI_STATUS (Status);
}
- /* If no implicit conversion, drop into the default case below */
-
- if ((!ImplicitConversion) ||
- ((WalkState->Opcode == AML_COPY_OP) &&
- (TargetType != ACPI_TYPE_LOCAL_REGION_FIELD) &&
- (TargetType != ACPI_TYPE_LOCAL_BANK_FIELD) &&
- (TargetType != ACPI_TYPE_LOCAL_INDEX_FIELD)))
- {
- /*
- * Force execution of default (no implicit conversion). Note:
- * CopyObject does not perform an implicit conversion, as per the ACPI
- * spec -- except in case of region/bank/index fields -- because these
- * objects must retain their original type permanently.
- */
- TargetType = ACPI_TYPE_ANY;
- }
-
/* Do the actual store operation */
switch (TargetType)
{
- case ACPI_TYPE_BUFFER_FIELD:
- case ACPI_TYPE_LOCAL_REGION_FIELD:
- case ACPI_TYPE_LOCAL_BANK_FIELD:
- case ACPI_TYPE_LOCAL_INDEX_FIELD:
-
- /* For fields, copy the source data to the target field. */
-
- Status = AcpiExWriteDataToField (SourceDesc, TargetDesc,
- &WalkState->ResultObj);
- break;
-
-
+ /*
+ * The simple data types all support implicit source operand
+ * conversion before the store.
+ */
case ACPI_TYPE_INTEGER:
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
- /*
- * These target types are all of type Integer/String/Buffer, and
- * therefore support implicit conversion before the store.
- *
- * Copy and/or convert the source object to a new target object
- */
+ if ((WalkState->Opcode == AML_COPY_OP) ||
+ !ImplicitConversion)
+ {
+ /*
+ * However, CopyObject and Stores to ArgX do not perform
+ * an implicit conversion, as per the ACPI specification.
+ * A direct store is performed instead.
+ */
+ Status = AcpiExStoreDirectToNode (SourceDesc, Node, WalkState);
+ break;
+ }
+
+ /* Store with implicit source operand conversion support */
+
Status = AcpiExStoreObjectToObject (SourceDesc, TargetDesc,
- &NewDesc, WalkState);
+ &NewDesc, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -498,30 +549,44 @@ AcpiExStoreObjectToNode (
* the Name's type to that of the value being stored in it.
* SourceDesc reference count is incremented by AttachObject.
*
- * Note: This may change the type of the node if an explicit store
- * has been performed such that the node/object type has been
- * changed.
+ * Note: This may change the type of the node if an explicit
+ * store has been performed such that the node/object type
+ * has been changed.
*/
- Status = AcpiNsAttachObject (Node, NewDesc, NewDesc->Common.Type);
+ Status = AcpiNsAttachObject (
+ Node, NewDesc, NewDesc->Common.Type);
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "Store %s into %s via Convert/Attach\n",
+ "Store type [%s] into [%s] via Convert/Attach\n",
AcpiUtGetObjectTypeName (SourceDesc),
AcpiUtGetObjectTypeName (NewDesc)));
}
break;
+ case ACPI_TYPE_BUFFER_FIELD:
+ case ACPI_TYPE_LOCAL_REGION_FIELD:
+ case ACPI_TYPE_LOCAL_BANK_FIELD:
+ case ACPI_TYPE_LOCAL_INDEX_FIELD:
+ /*
+ * For all fields, always write the source data to the target
+ * field. Any required implicit source operand conversion is
+ * performed in the function below as necessary. Note, field
+ * objects must retain their original type permanently.
+ */
+ Status = AcpiExWriteDataToField (SourceDesc, TargetDesc,
+ &WalkState->ResultObj);
+ break;
default:
-
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "Storing %s (%p) directly into node (%p) with no implicit conversion\n",
- AcpiUtGetObjectTypeName (SourceDesc), SourceDesc, Node));
-
- /* No conversions for all other types. Just attach the source object */
-
- Status = AcpiNsAttachObject (Node, SourceDesc,
- SourceDesc->Common.Type);
+ /*
+ * CopyObject operator: No conversions for all other types.
+ * Instead, directly store a copy of the source object.
+ *
+ * This is the ACPI spec-defined behavior for the CopyObject
+ * operator. (Note, for this default case, all normal
+ * Store/Target operations exited above with an error).
+ */
+ Status = AcpiExStoreDirectToNode (SourceDesc, Node, WalkState);
break;
}
@@ -529,3 +594,51 @@ AcpiExStoreObjectToNode (
}
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExStoreDirectToNode
+ *
+ * PARAMETERS: SourceDesc - Value to be stored
+ * Node - Named object to receive the value
+ * WalkState - Current walk state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: "Store" an object directly to a node. This involves a copy
+ * and an attach.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiExStoreDirectToNode (
+ ACPI_OPERAND_OBJECT *SourceDesc,
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_WALK_STATE *WalkState)
+{
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *NewDesc;
+
+
+ ACPI_FUNCTION_TRACE (ExStoreDirectToNode);
+
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Storing [%s] (%p) directly into node [%s] (%p)"
+ " with no implicit conversion\n",
+ AcpiUtGetObjectTypeName (SourceDesc), SourceDesc,
+ AcpiUtGetTypeName (Node->Type), Node));
+
+ /* Copy the source object to a new object */
+
+ Status = AcpiUtCopyIobjectToIobject (SourceDesc, &NewDesc, WalkState);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Attach the new object to the node */
+
+ Status = AcpiNsAttachObject (Node, NewDesc, NewDesc->Common.Type);
+ AcpiUtRemoveReference (NewDesc);
+ return_ACPI_STATUS (Status);
+}
diff --git a/usr/src/uts/intel/io/acpica/executer/exstoren.c b/usr/src/uts/intel/io/acpica/executer/exstoren.c
index 71d8809a2d..7a69927b12 100644
--- a/usr/src/uts/intel/io/acpica/executer/exstoren.c
+++ b/usr/src/uts/intel/io/acpica/executer/exstoren.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exstoren - AML Interpreter object store support,
@@ -7,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXSTOREN_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -65,7 +62,7 @@
*
* RETURN: Status, resolved object in SourceDescPtr.
*
- * DESCRIPTION: Resolve an object. If the object is a reference, dereference
+ * DESCRIPTION: Resolve an object. If the object is a reference, dereference
* it and return the actual object in the SourceDescPtr.
*
******************************************************************************/
@@ -95,14 +92,12 @@ AcpiExResolveObject (
* These cases all require only Integers or values that
* can be converted to Integers (Strings or Buffers)
*/
-
case ACPI_TYPE_INTEGER:
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
-
/*
* Stores into a Field/Region or into a Integer/Buffer/String
- * are all essentially the same. This case handles the
+ * are all essentially the same. This case handles the
* "interchangeable" types Integer, String, and Buffer.
*/
if (SourceDesc->Common.Type == ACPI_TYPE_LOCAL_REFERENCE)
@@ -129,22 +124,21 @@ AcpiExResolveObject (
(SourceDesc->Common.Type != ACPI_TYPE_BUFFER) &&
(SourceDesc->Common.Type != ACPI_TYPE_STRING) &&
!((SourceDesc->Common.Type == ACPI_TYPE_LOCAL_REFERENCE) &&
- (SourceDesc->Reference.Class== ACPI_REFCLASS_TABLE)))
+ (SourceDesc->Reference.Class== ACPI_REFCLASS_TABLE)))
{
/* Conversion successful but still not a valid type */
ACPI_ERROR ((AE_INFO,
- "Cannot assign type %s to %s (must be type Int/Str/Buf)",
+ "Cannot assign type [%s] to [%s] (must be type Int/Str/Buf)",
AcpiUtGetObjectTypeName (SourceDesc),
AcpiUtGetTypeName (TargetType)));
+
Status = AE_AML_OPERAND_TYPE;
}
break;
-
case ACPI_TYPE_LOCAL_ALIAS:
case ACPI_TYPE_LOCAL_METHOD_ALIAS:
-
/*
* All aliases should have been resolved earlier, during the
* operand resolution phase.
@@ -153,10 +147,8 @@ AcpiExResolveObject (
Status = AE_AML_INTERNAL;
break;
-
case ACPI_TYPE_PACKAGE:
default:
-
/*
* All other types than Alias and the various Fields come here,
* including the untyped case - ACPI_TYPE_ANY.
@@ -179,7 +171,7 @@ AcpiExResolveObject (
*
* RETURN: Status
*
- * DESCRIPTION: "Store" an object to another object. This may include
+ * DESCRIPTION: "Store" an object to another object. This may include
* converting the source type to the target type (implicit
* conversion), and a copy of the value of the source to
* the target.
@@ -190,14 +182,14 @@ AcpiExResolveObject (
* with the input value.
*
* When storing into an object the data is converted to the
- * target object type then stored in the object. This means
+ * target object type then stored in the object. This means
* that the target object type (for an initialized target) will
* not be changed by a store operation.
*
* This module allows destination types of Number, String,
* Buffer, and Package.
*
- * Assumes parameters are already validated. NOTE: SourceDesc
+ * Assumes parameters are already validated. NOTE: SourceDesc
* resolution (from a reference object) must be performed by
* the caller if necessary.
*
@@ -241,7 +233,7 @@ AcpiExStoreObjectToObject (
* converted object.
*/
Status = AcpiExConvertToTargetType (DestDesc->Common.Type,
- SourceDesc, &ActualSrcDesc, WalkState);
+ SourceDesc, &ActualSrcDesc, WalkState);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -270,7 +262,7 @@ AcpiExStoreObjectToObject (
/* Truncate value if we are executing from a 32-bit ACPI table */
- AcpiExTruncateFor32bitTable (DestDesc);
+ (void) AcpiExTruncateFor32bitTable (DestDesc);
break;
case ACPI_TYPE_STRING:
@@ -293,7 +285,7 @@ AcpiExStoreObjectToObject (
/*
* All other types come here.
*/
- ACPI_WARNING ((AE_INFO, "Store into type %s not implemented",
+ ACPI_WARNING ((AE_INFO, "Store into type [%s] not implemented",
AcpiUtGetObjectTypeName (DestDesc)));
Status = AE_NOT_IMPLEMENTED;
@@ -310,5 +302,3 @@ AcpiExStoreObjectToObject (
*NewDesc = DestDesc;
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exstorob.c b/usr/src/uts/intel/io/acpica/executer/exstorob.c
index 4dfcf15fe3..8418fb85a2 100644
--- a/usr/src/uts/intel/io/acpica/executer/exstorob.c
+++ b/usr/src/uts/intel/io/acpica/executer/exstorob.c
@@ -1,12 +1,11 @@
-
/******************************************************************************
*
- * Module Name: exstorob - AML Interpreter object store support, store to object
+ * Module Name: exstorob - AML object store support, store to object
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXSTOROB_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -112,13 +109,13 @@ AcpiExStoreBufferToBuffer (
{
/* Clear existing buffer and copy in the new one */
- ACPI_MEMSET (TargetDesc->Buffer.Pointer, 0, TargetDesc->Buffer.Length);
- ACPI_MEMCPY (TargetDesc->Buffer.Pointer, Buffer, Length);
+ memset (TargetDesc->Buffer.Pointer, 0, TargetDesc->Buffer.Length);
+ memcpy (TargetDesc->Buffer.Pointer, Buffer, Length);
#ifdef ACPI_OBSOLETE_BEHAVIOR
/*
* NOTE: ACPI versions up to 3.0 specified that the buffer must be
- * truncated if the string is smaller than the buffer. However, "other"
+ * truncated if the string is smaller than the buffer. However, "other"
* implementations of ACPI never did this and thus became the defacto
* standard. ACPI 3.0A changes this behavior such that the buffer
* is no longer truncated.
@@ -127,7 +124,7 @@ AcpiExStoreBufferToBuffer (
/*
* OBSOLETE BEHAVIOR:
* If the original source was a string, we must truncate the buffer,
- * according to the ACPI spec. Integer-to-Buffer and Buffer-to-Buffer
+ * according to the ACPI spec. Integer-to-Buffer and Buffer-to-Buffer
* copy must not truncate the original buffer.
*/
if (OriginalSrcType == ACPI_TYPE_STRING)
@@ -142,7 +139,7 @@ AcpiExStoreBufferToBuffer (
{
/* Truncate the source, copy only what will fit */
- ACPI_MEMCPY (TargetDesc->Buffer.Pointer, Buffer,
+ memcpy (TargetDesc->Buffer.Pointer, Buffer,
TargetDesc->Buffer.Length);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
@@ -206,9 +203,9 @@ AcpiExStoreStringToString (
* String will fit in existing non-static buffer.
* Clear old string and copy in the new one
*/
- ACPI_MEMSET (TargetDesc->String.Pointer, 0,
+ memset (TargetDesc->String.Pointer, 0,
(ACPI_SIZE) TargetDesc->String.Length + 1);
- ACPI_MEMCPY (TargetDesc->String.Pointer, Buffer, Length);
+ memcpy (TargetDesc->String.Pointer, Buffer, Length);
}
else
{
@@ -224,15 +221,16 @@ AcpiExStoreStringToString (
ACPI_FREE (TargetDesc->String.Pointer);
}
- TargetDesc->String.Pointer = ACPI_ALLOCATE_ZEROED (
- (ACPI_SIZE) Length + 1);
+ TargetDesc->String.Pointer =
+ ACPI_ALLOCATE_ZEROED ((ACPI_SIZE) Length + 1);
+
if (!TargetDesc->String.Pointer)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
TargetDesc->Common.Flags &= ~AOPOBJ_STATIC_POINTER;
- ACPI_MEMCPY (TargetDesc->String.Pointer, Buffer, Length);
+ memcpy (TargetDesc->String.Pointer, Buffer, Length);
}
/* Set the new target length */
@@ -240,5 +238,3 @@ AcpiExStoreStringToString (
TargetDesc->String.Length = Length;
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/executer/exsystem.c b/usr/src/uts/intel/io/acpica/executer/exsystem.c
index 10d6edc951..a215e8012d 100644
--- a/usr/src/uts/intel/io/acpica/executer/exsystem.c
+++ b/usr/src/uts/intel/io/acpica/executer/exsystem.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exsystem - Interface to OS services
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXSYSTEM_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -62,7 +59,7 @@
* RETURN: Status
*
* DESCRIPTION: Implements a semaphore wait with a check to see if the
- * semaphore is available immediately. If it is not, the
+ * semaphore is available immediately. If it is not, the
* interpreter is released before waiting.
*
******************************************************************************/
@@ -88,8 +85,7 @@ AcpiExSystemWaitSemaphore (
{
/* We must wait, so unlock the interpreter */
- AcpiExRelinquishInterpreter ();
-
+ AcpiExExitInterpreter ();
Status = AcpiOsWaitSemaphore (Semaphore, 1, Timeout);
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
@@ -98,7 +94,7 @@ AcpiExSystemWaitSemaphore (
/* Reacquire the interpreter */
- AcpiExReacquireInterpreter ();
+ AcpiExEnterInterpreter ();
}
return_ACPI_STATUS (Status);
@@ -115,7 +111,7 @@ AcpiExSystemWaitSemaphore (
* RETURN: Status
*
* DESCRIPTION: Implements a mutex wait with a check to see if the
- * mutex is available immediately. If it is not, the
+ * mutex is available immediately. If it is not, the
* interpreter is released before waiting.
*
******************************************************************************/
@@ -141,8 +137,7 @@ AcpiExSystemWaitMutex (
{
/* We must wait, so unlock the interpreter */
- AcpiExRelinquishInterpreter ();
-
+ AcpiExExitInterpreter ();
Status = AcpiOsAcquireMutex (Mutex, Timeout);
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
@@ -151,7 +146,7 @@ AcpiExSystemWaitMutex (
/* Reacquire the interpreter */
- AcpiExReacquireInterpreter ();
+ AcpiExEnterInterpreter ();
}
return_ACPI_STATUS (Status);
@@ -170,7 +165,7 @@ AcpiExSystemWaitMutex (
* DESCRIPTION: Suspend running thread for specified amount of time.
* Note: ACPI specification requires that Stall() does not
* relinquish the processor, and delays longer than 100 usec
- * should use Sleep() instead. We allow stalls up to 255 usec
+ * should use Sleep() instead. We allow stalls up to 255 usec
* for compatibility with other interpreters and existing BIOSs.
*
******************************************************************************/
@@ -193,8 +188,8 @@ AcpiExSystemDoStall (
* (ACPI specifies 100 usec as max, but this gives some slack in
* order to support existing BIOSs)
*/
- ACPI_ERROR ((AE_INFO, "Time parameter is too large (%u)",
- HowLong));
+ ACPI_ERROR ((AE_INFO,
+ "Time parameter is too large (%u)", HowLong));
Status = AE_AML_OPERAND_VALUE;
}
else
@@ -228,7 +223,7 @@ AcpiExSystemDoSleep (
/* Since this thread will sleep, we must release the interpreter */
- AcpiExRelinquishInterpreter ();
+ AcpiExExitInterpreter ();
/*
* For compatibility with other ACPI implementations and to prevent
@@ -243,7 +238,7 @@ AcpiExSystemDoSleep (
/* And now we must get the interpreter again */
- AcpiExReacquireInterpreter ();
+ AcpiExEnterInterpreter ();
return (AE_OK);
}
@@ -290,7 +285,7 @@ AcpiExSystemSignalEvent (
* RETURN: Status
*
* DESCRIPTION: Provides an access point to perform synchronization operations
- * within the AML. This operation is a request to wait for an
+ * within the AML. This operation is a request to wait for an
* event.
*
******************************************************************************/
@@ -309,7 +304,7 @@ AcpiExSystemWaitEvent (
if (ObjDesc)
{
Status = AcpiExSystemWaitSemaphore (ObjDesc->Event.OsSemaphore,
- (UINT16) TimeDesc->Integer.Value);
+ (UINT16) TimeDesc->Integer.Value);
}
return_ACPI_STATUS (Status);
@@ -352,4 +347,3 @@ AcpiExSystemResetEvent (
return (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/executer/extrace.c b/usr/src/uts/intel/io/acpica/executer/extrace.c
new file mode 100644
index 0000000000..6f8a707ae4
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/executer/extrace.c
@@ -0,0 +1,427 @@
+/******************************************************************************
+ *
+ * Module Name: extrace - Support for interpreter execution tracing
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+
+#define _COMPONENT ACPI_EXECUTER
+ ACPI_MODULE_NAME ("extrace")
+
+
+static ACPI_OPERAND_OBJECT *AcpiGbl_TraceMethodObject = NULL;
+
+/* Local prototypes */
+
+#ifdef ACPI_DEBUG_OUTPUT
+static const char *
+AcpiExGetTraceEventName (
+ ACPI_TRACE_EVENT_TYPE Type);
+#endif
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExInterpreterTraceEnabled
+ *
+ * PARAMETERS: Name - Whether method name should be matched,
+ * this should be checked before starting
+ * the tracer
+ *
+ * RETURN: TRUE if interpreter trace is enabled.
+ *
+ * DESCRIPTION: Check whether interpreter trace is enabled
+ *
+ ******************************************************************************/
+
+static BOOLEAN
+AcpiExInterpreterTraceEnabled (
+ char *Name)
+{
+
+ /* Check if tracing is enabled */
+
+ if (!(AcpiGbl_TraceFlags & ACPI_TRACE_ENABLED))
+ {
+ return (FALSE);
+ }
+
+ /*
+ * Check if tracing is filtered:
+ *
+ * 1. If the tracer is started, AcpiGbl_TraceMethodObject should have
+ * been filled by the trace starter
+ * 2. If the tracer is not started, AcpiGbl_TraceMethodName should be
+ * matched if it is specified
+ * 3. If the tracer is oneshot style, AcpiGbl_TraceMethodName should
+ * not be cleared by the trace stopper during the first match
+ */
+ if (AcpiGbl_TraceMethodObject)
+ {
+ return (TRUE);
+ }
+
+ if (Name &&
+ (AcpiGbl_TraceMethodName &&
+ strcmp (AcpiGbl_TraceMethodName, Name)))
+ {
+ return (FALSE);
+ }
+
+ if ((AcpiGbl_TraceFlags & ACPI_TRACE_ONESHOT) &&
+ !AcpiGbl_TraceMethodName)
+ {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExGetTraceEventName
+ *
+ * PARAMETERS: Type - Trace event type
+ *
+ * RETURN: Trace event name.
+ *
+ * DESCRIPTION: Used to obtain the full trace event name.
+ *
+ ******************************************************************************/
+
+#ifdef ACPI_DEBUG_OUTPUT
+
+static const char *
+AcpiExGetTraceEventName (
+ ACPI_TRACE_EVENT_TYPE Type)
+{
+
+ switch (Type)
+ {
+ case ACPI_TRACE_AML_METHOD:
+
+ return "Method";
+
+ case ACPI_TRACE_AML_OPCODE:
+
+ return "Opcode";
+
+ case ACPI_TRACE_AML_REGION:
+
+ return "Region";
+
+ default:
+
+ return "";
+ }
+}
+
+#endif
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExTracePoint
+ *
+ * PARAMETERS: Type - Trace event type
+ * Begin - TRUE if before execution
+ * Aml - Executed AML address
+ * Pathname - Object path
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Internal interpreter execution trace.
+ *
+ ******************************************************************************/
+
+void
+AcpiExTracePoint (
+ ACPI_TRACE_EVENT_TYPE Type,
+ BOOLEAN Begin,
+ UINT8 *Aml,
+ char *Pathname)
+{
+
+ ACPI_FUNCTION_NAME (ExTracePoint);
+
+
+ if (Pathname)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p:%s] execution.\n",
+ AcpiExGetTraceEventName (Type), Begin ? "Begin" : "End",
+ Aml, Pathname));
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p] execution.\n",
+ AcpiExGetTraceEventName (Type), Begin ? "Begin" : "End",
+ Aml));
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExStartTraceMethod
+ *
+ * PARAMETERS: MethodNode - Node of the method
+ * ObjDesc - The method object
+ * WalkState - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start control method execution trace
+ *
+ ******************************************************************************/
+
+void
+AcpiExStartTraceMethod (
+ ACPI_NAMESPACE_NODE *MethodNode,
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_WALK_STATE *WalkState)
+{
+ ACPI_STATUS Status;
+ char *Pathname = NULL;
+ BOOLEAN Enabled = FALSE;
+
+
+ ACPI_FUNCTION_NAME (ExStartTraceMethod);
+
+
+ if (MethodNode)
+ {
+ Pathname = AcpiNsGetNormalizedPathname (MethodNode, TRUE);
+ }
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ goto Exit;
+ }
+
+ Enabled = AcpiExInterpreterTraceEnabled (Pathname);
+ if (Enabled && !AcpiGbl_TraceMethodObject)
+ {
+ AcpiGbl_TraceMethodObject = ObjDesc;
+ AcpiGbl_OriginalDbgLevel = AcpiDbgLevel;
+ AcpiGbl_OriginalDbgLayer = AcpiDbgLayer;
+ AcpiDbgLevel = ACPI_TRACE_LEVEL_ALL;
+ AcpiDbgLayer = ACPI_TRACE_LAYER_ALL;
+
+ if (AcpiGbl_TraceDbgLevel)
+ {
+ AcpiDbgLevel = AcpiGbl_TraceDbgLevel;
+ }
+
+ if (AcpiGbl_TraceDbgLayer)
+ {
+ AcpiDbgLayer = AcpiGbl_TraceDbgLayer;
+ }
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+Exit:
+ if (Enabled)
+ {
+ ACPI_TRACE_POINT (ACPI_TRACE_AML_METHOD, TRUE,
+ ObjDesc ? ObjDesc->Method.AmlStart : NULL, Pathname);
+ }
+
+ if (Pathname)
+ {
+ ACPI_FREE (Pathname);
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExStopTraceMethod
+ *
+ * PARAMETERS: MethodNode - Node of the method
+ * ObjDesc - The method object
+ * WalkState - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop control method execution trace
+ *
+ ******************************************************************************/
+
+void
+AcpiExStopTraceMethod (
+ ACPI_NAMESPACE_NODE *MethodNode,
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_WALK_STATE *WalkState)
+{
+ ACPI_STATUS Status;
+ char *Pathname = NULL;
+ BOOLEAN Enabled;
+
+
+ ACPI_FUNCTION_NAME (ExStopTraceMethod);
+
+
+ if (MethodNode)
+ {
+ Pathname = AcpiNsGetNormalizedPathname (MethodNode, TRUE);
+ }
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ goto ExitPath;
+ }
+
+ Enabled = AcpiExInterpreterTraceEnabled (NULL);
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+ if (Enabled)
+ {
+ ACPI_TRACE_POINT (ACPI_TRACE_AML_METHOD, FALSE,
+ ObjDesc ? ObjDesc->Method.AmlStart : NULL, Pathname);
+ }
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ goto ExitPath;
+ }
+
+ /* Check whether the tracer should be stopped */
+
+ if (AcpiGbl_TraceMethodObject == ObjDesc)
+ {
+ /* Disable further tracing if type is one-shot */
+
+ if (AcpiGbl_TraceFlags & ACPI_TRACE_ONESHOT)
+ {
+ AcpiGbl_TraceMethodName = NULL;
+ }
+
+ AcpiDbgLevel = AcpiGbl_OriginalDbgLevel;
+ AcpiDbgLayer = AcpiGbl_OriginalDbgLayer;
+ AcpiGbl_TraceMethodObject = NULL;
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
+ExitPath:
+ if (Pathname)
+ {
+ ACPI_FREE (Pathname);
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExStartTraceOpcode
+ *
+ * PARAMETERS: Op - The parser opcode object
+ * WalkState - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+AcpiExStartTraceOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_WALK_STATE *WalkState)
+{
+
+ ACPI_FUNCTION_NAME (ExStartTraceOpcode);
+
+
+ if (AcpiExInterpreterTraceEnabled (NULL) &&
+ (AcpiGbl_TraceFlags & ACPI_TRACE_OPCODE))
+ {
+ ACPI_TRACE_POINT (ACPI_TRACE_AML_OPCODE, TRUE,
+ Op->Common.Aml, Op->Common.AmlOpName);
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExStopTraceOpcode
+ *
+ * PARAMETERS: Op - The parser opcode object
+ * WalkState - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+AcpiExStopTraceOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_WALK_STATE *WalkState)
+{
+
+ ACPI_FUNCTION_NAME (ExStopTraceOpcode);
+
+
+ if (AcpiExInterpreterTraceEnabled (NULL) &&
+ (AcpiGbl_TraceFlags & ACPI_TRACE_OPCODE))
+ {
+ ACPI_TRACE_POINT (ACPI_TRACE_AML_OPCODE, FALSE,
+ Op->Common.Aml, Op->Common.AmlOpName);
+ }
+}
diff --git a/usr/src/uts/intel/io/acpica/executer/exutils.c b/usr/src/uts/intel/io/acpica/executer/exutils.c
index 4daa44f527..4843a20891 100644
--- a/usr/src/uts/intel/io/acpica/executer/exutils.c
+++ b/usr/src/uts/intel/io/acpica/executer/exutils.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: exutils - interpreter/scanner utilities
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,17 +41,15 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __EXUTILS_C__
-
/*
* DEFINE_AML_GLOBALS is tested in amlcode.h
* to determine whether certain global names should be "defined" or only
- * "declared" in the current compilation. This enhances maintainability
+ * "declared" in the current compilation. This enhances maintainability
* by enabling a single header file to embody all knowledge of the names
* in question.
*
* Exactly one module of any executable should #define DEFINE_GLOBALS
- * before #including the header files which use this convention. The
+ * before #including the header files which use this convention. The
* names in question will be defined and initialized in that module,
* and declared as extern in all other modules which #include those
* header files.
@@ -113,42 +110,6 @@ AcpiExEnterInterpreter (
/*******************************************************************************
*
- * FUNCTION: AcpiExReacquireInterpreter
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Reacquire the interpreter execution region from within the
- * interpreter code. Failure to enter the interpreter region is a
- * fatal system error. Used in conjuction with
- * RelinquishInterpreter
- *
- ******************************************************************************/
-
-void
-AcpiExReacquireInterpreter (
- void)
-{
- ACPI_FUNCTION_TRACE (ExReacquireInterpreter);
-
-
- /*
- * If the global serialized flag is set, do not release the interpreter,
- * since it was not actually released by AcpiExRelinquishInterpreter.
- * This forces the interpreter to be single threaded.
- */
- if (!AcpiGbl_AllMethodsSerialized)
- {
- AcpiExEnterInterpreter ();
- }
-
- return_VOID;
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiExExitInterpreter
*
* PARAMETERS: None
@@ -157,7 +118,16 @@ AcpiExReacquireInterpreter (
*
* DESCRIPTION: Exit the interpreter execution region. This is the top level
* routine used to exit the interpreter when all processing has
- * been completed.
+ * been completed, or when the method blocks.
+ *
+ * Cases where the interpreter is unlocked internally:
+ * 1) Method will be blocked on a Sleep() AML opcode
+ * 2) Method will be blocked on an Acquire() AML opcode
+ * 3) Method will be blocked on a Wait() AML opcode
+ * 4) Method will be blocked to acquire the global lock
+ * 5) Method will be blocked waiting to execute a serialized control
+ * method that is currently executing
+ * 6) About to invoke a user-installed opregion handler
*
******************************************************************************/
@@ -183,61 +153,18 @@ AcpiExExitInterpreter (
/*******************************************************************************
*
- * FUNCTION: AcpiExRelinquishInterpreter
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Exit the interpreter execution region, from within the
- * interpreter - before attempting an operation that will possibly
- * block the running thread.
- *
- * Cases where the interpreter is unlocked internally
- * 1) Method to be blocked on a Sleep() AML opcode
- * 2) Method to be blocked on an Acquire() AML opcode
- * 3) Method to be blocked on a Wait() AML opcode
- * 4) Method to be blocked to acquire the global lock
- * 5) Method to be blocked waiting to execute a serialized control method
- * that is currently executing
- * 6) About to invoke a user-installed opregion handler
- *
- ******************************************************************************/
-
-void
-AcpiExRelinquishInterpreter (
- void)
-{
- ACPI_FUNCTION_TRACE (ExRelinquishInterpreter);
-
-
- /*
- * If the global serialized flag is set, do not release the interpreter.
- * This forces the interpreter to be single threaded.
- */
- if (!AcpiGbl_AllMethodsSerialized)
- {
- AcpiExExitInterpreter ();
- }
-
- return_VOID;
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiExTruncateFor32bitTable
*
* PARAMETERS: ObjDesc - Object to be truncated
*
- * RETURN: none
+ * RETURN: TRUE if a truncation was performed, FALSE otherwise.
*
* DESCRIPTION: Truncate an ACPI Integer to 32 bits if the execution mode is
* 32-bit, as determined by the revision of the DSDT.
*
******************************************************************************/
-void
+BOOLEAN
AcpiExTruncateFor32bitTable (
ACPI_OPERAND_OBJECT *ObjDesc)
{
@@ -247,23 +174,27 @@ AcpiExTruncateFor32bitTable (
/*
* Object must be a valid number and we must be executing
- * a control method. NS node could be there for AML_INT_NAMEPATH_OP.
+ * a control method. Object could be NS node for AML_INT_NAMEPATH_OP.
*/
if ((!ObjDesc) ||
(ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) != ACPI_DESC_TYPE_OPERAND) ||
(ObjDesc->Common.Type != ACPI_TYPE_INTEGER))
{
- return;
+ return (FALSE);
}
- if (AcpiGbl_IntegerByteWidth == 4)
+ if ((AcpiGbl_IntegerByteWidth == 4) &&
+ (ObjDesc->Integer.Value > (UINT64) ACPI_UINT32_MAX))
{
/*
- * We are running a method that exists in a 32-bit ACPI table.
- * Truncate the value to 32 bits by zeroing out the upper 32-bit field
+ * We are executing in a 32-bit ACPI table. Truncate
+ * the value to 32 bits by zeroing out the upper 32-bit field
*/
ObjDesc->Integer.Value &= (UINT64) ACPI_UINT32_MAX;
+ return (TRUE);
}
+
+ return (FALSE);
}
@@ -301,7 +232,7 @@ AcpiExAcquireGlobalLock (
/* Attempt to get the global lock, wait forever */
Status = AcpiExAcquireMutexObject (ACPI_WAIT_FOREVER,
- AcpiGbl_GlobalLockMutex, AcpiOsGetThreadId ());
+ AcpiGbl_GlobalLockMutex, AcpiOsGetThreadId ());
if (ACPI_FAILURE (Status))
{
@@ -410,8 +341,8 @@ AcpiExDigitsNeeded (
*
* FUNCTION: AcpiExEisaIdToString
*
- * PARAMETERS: CompressedId - EISAID to be converted
- * OutString - Where to put the converted string (8 bytes)
+ * PARAMETERS: OutString - Where to put the converted string (8 bytes)
+ * CompressedId - EISAID to be converted
*
* RETURN: None
*
@@ -438,7 +369,8 @@ AcpiExEisaIdToString (
if (CompressedId > ACPI_UINT32_MAX)
{
ACPI_WARNING ((AE_INFO,
- "Expected EISAID is larger than 32 bits: 0x%8.8X%8.8X, truncating",
+ "Expected EISAID is larger than 32 bits: "
+ "0x%8.8X%8.8X, truncating",
ACPI_FORMAT_UINT64 (CompressedId)));
}
@@ -468,7 +400,7 @@ AcpiExEisaIdToString (
* possible 64-bit integer.
* Value - Value to be converted
*
- * RETURN: None, string
+ * RETURN: Converted string in OutString
*
* DESCRIPTION: Convert a 64-bit integer to decimal string representation.
* Assumes string buffer is large enough to hold the string. The
@@ -499,4 +431,70 @@ AcpiExIntegerToString (
}
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiExPciClsToString
+ *
+ * PARAMETERS: OutString - Where to put the converted string (7 bytes)
+ * ClassCode - PCI class code to be converted (3 bytes)
+ *
+ * RETURN: Converted string in OutString
+ *
+ * DESCRIPTION: Convert 3-bytes PCI class code to string representation.
+ * Return buffer must be large enough to hold the string. The
+ * string returned is always exactly of length
+ * ACPI_PCICLS_STRING_SIZE (includes null terminator).
+ *
+ ******************************************************************************/
+
+void
+AcpiExPciClsToString (
+ char *OutString,
+ UINT8 ClassCode[3])
+{
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /* All 3 bytes are hexadecimal */
+
+ OutString[0] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[0], 4);
+ OutString[1] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[0], 0);
+ OutString[2] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[1], 4);
+ OutString[3] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[1], 0);
+ OutString[4] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[2], 4);
+ OutString[5] = AcpiUtHexToAsciiChar ((UINT64) ClassCode[2], 0);
+ OutString[6] = 0;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiIsValidSpaceId
+ *
+ * PARAMETERS: SpaceId - ID to be validated
+ *
+ * RETURN: TRUE if SpaceId is a valid/supported ID.
+ *
+ * DESCRIPTION: Validate an operation region SpaceID.
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiIsValidSpaceId (
+ UINT8 SpaceId)
+{
+
+ if ((SpaceId >= ACPI_NUM_PREDEFINED_REGIONS) &&
+ (SpaceId < ACPI_USER_REGION_BEGIN) &&
+ (SpaceId != ACPI_ADR_SPACE_DATA_TABLE) &&
+ (SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE))
+ {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
#endif
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwacpi.c b/usr/src/uts/intel/io/acpica/hardware/hwacpi.c
index 577666100e..73512128d5 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwacpi.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwacpi.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __HWACPI_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -52,6 +49,7 @@
ACPI_MODULE_NAME ("hwacpi")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/******************************************************************************
*
* FUNCTION: AcpiHwSetMode
@@ -75,6 +73,14 @@ AcpiHwSetMode (
ACPI_FUNCTION_TRACE (HwSetMode);
+
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
/*
* ACPI 2.0 clarified that if SMI_CMD in FADT is zero,
* system does not support mode transition.
@@ -107,23 +113,23 @@ AcpiHwSetMode (
/* BIOS should have disabled ALL fixed and GP events */
Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand,
- (UINT32) AcpiGbl_FADT.AcpiEnable, 8);
+ (UINT32) AcpiGbl_FADT.AcpiEnable, 8);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Attempting to enable ACPI mode\n"));
break;
case ACPI_SYS_MODE_LEGACY:
-
/*
* BIOS should clear all fixed status bits and restore fixed event
* enable bits to default
*/
Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand,
- (UINT32) AcpiGbl_FADT.AcpiDisable, 8);
+ (UINT32) AcpiGbl_FADT.AcpiDisable, 8);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Attempting to enable Legacy (non-ACPI) mode\n"));
+ "Attempting to enable Legacy (non-ACPI) mode\n"));
break;
default:
+
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
@@ -141,13 +147,13 @@ AcpiHwSetMode (
Retry = 3000;
while (Retry)
{
- if (AcpiHwGetMode() == Mode)
+ if (AcpiHwGetMode () == Mode)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Mode %X successfully enabled\n",
- Mode));
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Mode %X successfully enabled\n", Mode));
return_ACPI_STATUS (AE_OK);
}
- AcpiOsStall(1000);
+ AcpiOsStall (ACPI_USEC_PER_MSEC);
Retry--;
}
@@ -164,7 +170,7 @@ AcpiHwSetMode (
*
* RETURN: SYS_MODE_ACPI or SYS_MODE_LEGACY
*
- * DESCRIPTION: Return current operating state of system. Determined by
+ * DESCRIPTION: Return current operating state of system. Determined by
* querying the SCI_EN bit.
*
******************************************************************************/
@@ -180,6 +186,13 @@ AcpiHwGetMode (
ACPI_FUNCTION_TRACE (HwGetMode);
+ /* If the Hardware Reduced flag is set, machine is always in acpi mode */
+
+ if (AcpiGbl_ReducedHardware)
+ {
+ return_UINT32 (ACPI_SYS_MODE_ACPI);
+ }
+
/*
* ACPI 2.0 clarified that if SMI_CMD in FADT is zero,
* system does not support mode transition.
@@ -204,3 +217,5 @@ AcpiHwGetMode (
return_UINT32 (ACPI_SYS_MODE_LEGACY);
}
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwesleep.c b/usr/src/uts/intel/io/acpica/hardware/hwesleep.c
new file mode 100644
index 0000000000..de914df853
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/hardware/hwesleep.c
@@ -0,0 +1,259 @@
+/******************************************************************************
+ *
+ * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * extended FADT-V5 sleep registers.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ ACPI_MODULE_NAME ("hwesleep")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwExecuteSleepMethod
+ *
+ * PARAMETERS: MethodPathname - Pathname of method to execute
+ * IntegerArgument - Argument to pass to the method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute a sleep/wake related method with one integer argument
+ * and no return value.
+ *
+ ******************************************************************************/
+
+void
+AcpiHwExecuteSleepMethod (
+ char *MethodPathname,
+ UINT32 IntegerArgument)
+{
+ ACPI_OBJECT_LIST ArgList;
+ ACPI_OBJECT Arg;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (HwExecuteSleepMethod);
+
+
+ /* One argument, IntegerArgument; No return value expected */
+
+ ArgList.Count = 1;
+ ArgList.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = (UINT64) IntegerArgument;
+
+ Status = AcpiEvaluateObject (NULL, MethodPathname, &ArgList, NULL);
+ if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "While executing method %s",
+ MethodPathname));
+ }
+
+ return_VOID;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwExtendedSleep
+ *
+ * PARAMETERS: SleepState - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state via the extended FADT sleep
+ * registers (V5 FADT).
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiHwExtendedSleep (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+ UINT8 SleepTypeValue;
+ UINT64 SleepStatus;
+
+
+ ACPI_FUNCTION_TRACE (HwExtendedSleep);
+
+
+ /* Extended sleep registers must be valid */
+
+ if (!AcpiGbl_FADT.SleepControl.Address ||
+ !AcpiGbl_FADT.SleepStatus.Address)
+ {
+ return_ACPI_STATUS (AE_NOT_EXIST);
+ }
+
+ /* Clear wake status (WAK_STS) */
+
+ Status = AcpiWrite ((UINT64) ACPI_X_WAKE_STATUS,
+ &AcpiGbl_FADT.SleepStatus);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ AcpiGbl_SystemAwakeAndRunning = FALSE;
+
+ /* Flush caches, as per ACPI specification */
+
+ ACPI_FLUSH_CPU_CACHE ();
+
+ /*
+ * Set the SLP_TYP and SLP_EN bits.
+ *
+ * Note: We only use the first value returned by the \_Sx method
+ * (AcpiGbl_SleepTypeA) - As per ACPI specification.
+ */
+ ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
+ "Entering sleep state [S%u]\n", SleepState));
+
+ SleepTypeValue = ((AcpiGbl_SleepTypeA << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK);
+
+ Status = AcpiWrite ((UINT64) (SleepTypeValue | ACPI_X_SLEEP_ENABLE),
+ &AcpiGbl_FADT.SleepControl);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Wait for transition back to Working State */
+
+ do
+ {
+ Status = AcpiRead (&SleepStatus, &AcpiGbl_FADT.SleepStatus);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ } while (!(((UINT8) SleepStatus) & ACPI_X_WAKE_STATUS));
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwExtendedWakePrep
+ *
+ * PARAMETERS: SleepState - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform first part of OS-independent ACPI cleanup after
+ * a sleep. Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiHwExtendedWakePrep (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+ UINT8 SleepTypeValue;
+
+
+ ACPI_FUNCTION_TRACE (HwExtendedWakePrep);
+
+
+ Status = AcpiGetSleepTypeData (ACPI_STATE_S0,
+ &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
+ if (ACPI_SUCCESS (Status))
+ {
+ SleepTypeValue = ((AcpiGbl_SleepTypeA << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK);
+
+ (void) AcpiWrite ((UINT64) (SleepTypeValue | ACPI_X_SLEEP_ENABLE),
+ &AcpiGbl_FADT.SleepControl);
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwExtendedWake
+ *
+ * PARAMETERS: SleepState - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiHwExtendedWake (
+ UINT8 SleepState)
+{
+ ACPI_FUNCTION_TRACE (HwExtendedWake);
+
+
+ /* Ensure EnterSleepStatePrep -> EnterSleepState ordering */
+
+ AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID;
+
+ /* Execute the wake methods */
+
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WAKING);
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__WAK, SleepState);
+
+ /*
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
+ */
+ (void) AcpiWrite ((UINT64) ACPI_X_WAKE_STATUS, &AcpiGbl_FADT.SleepStatus);
+ AcpiGbl_SystemAwakeAndRunning = TRUE;
+
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WORKING);
+ return_ACPI_STATUS (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwgpe.c b/usr/src/uts/intel/io/acpica/hardware/hwgpe.c
index 335814f942..3dbf10ba0a 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwgpe.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwgpe.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: hwgpe - Low level GPE enable/disable/clear functions
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +48,8 @@
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME ("hwgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
+
/* Local prototypes */
static ACPI_STATUS
@@ -57,13 +58,17 @@ AcpiHwEnableWakeupGpeBlock (
ACPI_GPE_BLOCK_INFO *GpeBlock,
void *Context);
+static ACPI_STATUS
+AcpiHwGpeEnableWrite (
+ UINT8 EnableMask,
+ ACPI_GPE_REGISTER_INFO *GpeRegisterInfo);
+
/******************************************************************************
*
* FUNCTION: AcpiHwGetGpeRegisterBit
*
* PARAMETERS: GpeEventInfo - Info block for the GPE
- * GpeRegisterInfo - Info block for the GPE register
*
* RETURN: Register mask with a one in the GPE bit position
*
@@ -74,12 +79,11 @@ AcpiHwEnableWakeupGpeBlock (
UINT32
AcpiHwGetGpeRegisterBit (
- ACPI_GPE_EVENT_INFO *GpeEventInfo,
- ACPI_GPE_REGISTER_INFO *GpeRegisterInfo)
+ ACPI_GPE_EVENT_INFO *GpeEventInfo)
{
return ((UINT32) 1 <<
- (GpeEventInfo->GpeNumber - GpeRegisterInfo->BaseGpeNumber));
+ (GpeEventInfo->GpeNumber - GpeEventInfo->RegisterInfo->BaseGpeNumber));
}
@@ -93,6 +97,8 @@ AcpiHwGetGpeRegisterBit (
* RETURN: Status
*
* DESCRIPTION: Enable or disable a single GPE in the parent enable register.
+ * The EnableMask field of the involved GPE register must be
+ * updated by the caller if necessary.
*
******************************************************************************/
@@ -128,14 +134,14 @@ AcpiHwLowSetGpe (
/* Set or clear just the bit that corresponds to this GPE */
- RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo, GpeRegisterInfo);
+ RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo);
switch (Action)
{
case ACPI_GPE_CONDITIONAL_ENABLE:
- /* Only enable if the EnableForRun bit is set */
+ /* Only enable if the corresponding EnableMask bit is set */
- if (!(RegisterBit & GpeRegisterInfo->EnableForRun))
+ if (!(RegisterBit & GpeRegisterInfo->EnableMask))
{
return (AE_BAD_PARAMETER);
}
@@ -143,15 +149,18 @@ AcpiHwLowSetGpe (
/*lint -fallthrough */
case ACPI_GPE_ENABLE:
+
ACPI_SET_BIT (EnableMask, RegisterBit);
break;
case ACPI_GPE_DISABLE:
+
ACPI_CLEAR_BIT (EnableMask, RegisterBit);
break;
default:
- ACPI_ERROR ((AE_INFO, "Invalid GPE Action, %u\n", Action));
+
+ ACPI_ERROR ((AE_INFO, "Invalid GPE Action, %u", Action));
return (AE_BAD_PARAMETER);
}
@@ -197,11 +206,9 @@ AcpiHwClearGpe (
* Write a one to the appropriate bit in the status register to
* clear this GPE.
*/
- RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo, GpeRegisterInfo);
-
- Status = AcpiHwWrite (RegisterBit,
- &GpeRegisterInfo->StatusAddress);
+ RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo);
+ Status = AcpiHwWrite (RegisterBit, &GpeRegisterInfo->StatusAddress);
return (Status);
}
@@ -239,13 +246,21 @@ AcpiHwGetGpeStatus (
return (AE_BAD_PARAMETER);
}
+ /* GPE currently handled? */
+
+ if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) !=
+ ACPI_GPE_DISPATCH_NONE)
+ {
+ LocalEventStatus |= ACPI_EVENT_FLAG_HAS_HANDLER;
+ }
+
/* Get the info block for the entire GPE register */
GpeRegisterInfo = GpeEventInfo->RegisterInfo;
/* Get the register bitmask for this GPE */
- RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo, GpeRegisterInfo);
+ RegisterBit = AcpiHwGetGpeRegisterBit (GpeEventInfo);
/* GPE currently enabled? (enabled for runtime?) */
@@ -261,6 +276,19 @@ AcpiHwGetGpeStatus (
LocalEventStatus |= ACPI_EVENT_FLAG_WAKE_ENABLED;
}
+ /* GPE currently enabled (enable bit == 1)? */
+
+ Status = AcpiHwRead (&InByte, &GpeRegisterInfo->EnableAddress);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ if (RegisterBit & InByte)
+ {
+ LocalEventStatus |= ACPI_EVENT_FLAG_ENABLE_SET;
+ }
+
/* GPE currently active (status bit == 1)? */
Status = AcpiHwRead (&InByte, &GpeRegisterInfo->StatusAddress);
@@ -271,7 +299,7 @@ AcpiHwGetGpeStatus (
if (RegisterBit & InByte)
{
- LocalEventStatus |= ACPI_EVENT_FLAG_SET;
+ LocalEventStatus |= ACPI_EVENT_FLAG_STATUS_SET;
}
/* Set return value */
@@ -283,6 +311,34 @@ AcpiHwGetGpeStatus (
/******************************************************************************
*
+ * FUNCTION: AcpiHwGpeEnableWrite
+ *
+ * PARAMETERS: EnableMask - Bit mask to write to the GPE register
+ * GpeRegisterInfo - Gpe Register info
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write the enable mask byte to the given GPE register.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiHwGpeEnableWrite (
+ UINT8 EnableMask,
+ ACPI_GPE_REGISTER_INFO *GpeRegisterInfo)
+{
+ ACPI_STATUS Status;
+
+
+ GpeRegisterInfo->EnableMask = EnableMask;
+
+ Status = AcpiHwWrite (EnableMask, &GpeRegisterInfo->EnableAddress);
+ return (Status);
+}
+
+
+/******************************************************************************
+ *
* FUNCTION: AcpiHwDisableGpeBlock
*
* PARAMETERS: GpeXruptInfo - GPE Interrupt info
@@ -310,7 +366,7 @@ AcpiHwDisableGpeBlock (
{
/* Disable all GPEs in this register */
- Status = AcpiHwWrite (0x00, &GpeBlock->RegisterInfo[i].EnableAddress);
+ Status = AcpiHwGpeEnableWrite (0x00, &GpeBlock->RegisterInfo[i]);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -383,6 +439,7 @@ AcpiHwEnableRuntimeGpeBlock (
{
UINT32 i;
ACPI_STATUS Status;
+ ACPI_GPE_REGISTER_INFO *GpeRegisterInfo;
/* NOTE: assumes that all GPEs are currently disabled */
@@ -391,15 +448,16 @@ AcpiHwEnableRuntimeGpeBlock (
for (i = 0; i < GpeBlock->RegisterCount; i++)
{
- if (!GpeBlock->RegisterInfo[i].EnableForRun)
+ GpeRegisterInfo = &GpeBlock->RegisterInfo[i];
+ if (!GpeRegisterInfo->EnableForRun)
{
continue;
}
/* Enable all "runtime" GPEs in this register */
- Status = AcpiHwWrite (GpeBlock->RegisterInfo[i].EnableForRun,
- &GpeBlock->RegisterInfo[i].EnableAddress);
+ Status = AcpiHwGpeEnableWrite (GpeRegisterInfo->EnableForRun,
+ GpeRegisterInfo);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -432,21 +490,21 @@ AcpiHwEnableWakeupGpeBlock (
{
UINT32 i;
ACPI_STATUS Status;
+ ACPI_GPE_REGISTER_INFO *GpeRegisterInfo;
/* Examine each GPE Register within the block */
for (i = 0; i < GpeBlock->RegisterCount; i++)
{
- if (!GpeBlock->RegisterInfo[i].EnableForWake)
- {
- continue;
- }
-
- /* Enable all "wake" GPEs in this register */
-
- Status = AcpiHwWrite (GpeBlock->RegisterInfo[i].EnableForWake,
- &GpeBlock->RegisterInfo[i].EnableAddress);
+ GpeRegisterInfo = &GpeBlock->RegisterInfo[i];
+
+ /*
+ * Enable all "wake" GPEs in this register and disable the
+ * remaining ones.
+ */
+ Status = AcpiHwGpeEnableWrite (GpeRegisterInfo->EnableForWake,
+ GpeRegisterInfo);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -538,3 +596,4 @@ AcpiHwEnableAllWakeupGpes (
return_ACPI_STATUS (Status);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwpci.c b/usr/src/uts/intel/io/acpica/hardware/hwpci.c
index 96a2dfcdbe..c2b707122d 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwpci.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwpci.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __HWPCI_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -142,7 +140,7 @@ AcpiHwDerivePciId (
ACPI_HANDLE PciRegion)
{
ACPI_STATUS Status;
- ACPI_PCI_DEVICE *ListHead = NULL;
+ ACPI_PCI_DEVICE *ListHead;
ACPI_FUNCTION_TRACE (HwDerivePciId);
@@ -161,11 +159,12 @@ AcpiHwDerivePciId (
/* Walk the list, updating the PCI device/function/bus numbers */
Status = AcpiHwProcessPciList (PciId, ListHead);
- }
- /* Always delete the list */
+ /* Delete the list */
+
+ AcpiHwDeletePciList (ListHead);
+ }
- AcpiHwDeletePciList (ListHead);
return_ACPI_STATUS (Status);
}
@@ -199,7 +198,6 @@ AcpiHwBuildPciList (
ACPI_HANDLE ParentDevice;
ACPI_STATUS Status;
ACPI_PCI_DEVICE *ListElement;
- ACPI_PCI_DEVICE *ListHead = NULL;
/*
@@ -207,12 +205,16 @@ AcpiHwBuildPciList (
* a list of device nodes. Loop will exit when either the PCI device is
* found, or the root of the namespace is reached.
*/
+ *ReturnListHead = NULL;
CurrentDevice = PciRegion;
while (1)
{
Status = AcpiGetParent (CurrentDevice, &ParentDevice);
if (ACPI_FAILURE (Status))
{
+ /* Must delete the list before exit */
+
+ AcpiHwDeletePciList (*ReturnListHead);
return (Status);
}
@@ -220,21 +222,23 @@ AcpiHwBuildPciList (
if (ParentDevice == RootPciDevice)
{
- *ReturnListHead = ListHead;
return (AE_OK);
}
ListElement = ACPI_ALLOCATE (sizeof (ACPI_PCI_DEVICE));
if (!ListElement)
{
+ /* Must delete the list before exit */
+
+ AcpiHwDeletePciList (*ReturnListHead);
return (AE_NO_MEMORY);
}
/* Put new element at the head of the list */
- ListElement->Next = ListHead;
+ ListElement->Next = *ReturnListHead;
ListElement->Device = ParentDevice;
- ListHead = ListElement;
+ *ReturnListHead = ListElement;
CurrentDevice = ParentDevice;
}
@@ -292,7 +296,7 @@ AcpiHwProcessPciList (
&BusNumber, &IsBridge);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ return (Status);
}
Info = Info->Next;
@@ -304,7 +308,7 @@ AcpiHwProcessPciList (
PciId->Segment, PciId->Bus, PciId->Device, PciId->Function,
Status, BusNumber, IsBridge));
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwregs.c b/usr/src/uts/intel/io/acpica/hardware/hwregs.c
index 61e61a1e6b..bc85c96b55 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwregs.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwregs.c
@@ -1,4 +1,3 @@
-
/*******************************************************************************
*
* Module Name: hwregs - Read/write access functions for the various ACPI
@@ -7,7 +6,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __HWREGS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acevents.h"
@@ -53,6 +50,8 @@
ACPI_MODULE_NAME ("hwregs")
+#if (!ACPI_REDUCED_HARDWARE)
+
/* Local Prototypes */
static ACPI_STATUS
@@ -67,6 +66,8 @@ AcpiHwWriteMultiple (
ACPI_GENERIC_ADDRESS *RegisterA,
ACPI_GENERIC_ADDRESS *RegisterB);
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/******************************************************************************
*
@@ -170,6 +171,7 @@ AcpiHwRead (
ACPI_GENERIC_ADDRESS *Reg)
{
UINT64 Address;
+ UINT64 Value64;
ACPI_STATUS Status;
@@ -195,12 +197,14 @@ AcpiHwRead (
if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
{
Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
- Address, Value, Reg->BitWidth);
+ Address, &Value64, Reg->BitWidth);
+
+ *Value = (UINT32) Value64;
}
else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
{
Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
- Address, Value, Reg->BitWidth);
+ Address, Value, Reg->BitWidth);
}
ACPI_DEBUG_PRINT ((ACPI_DB_IO,
@@ -254,12 +258,12 @@ AcpiHwWrite (
if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
{
Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
- Address, Value, Reg->BitWidth);
+ Address, (UINT64) Value, Reg->BitWidth);
}
else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
{
Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
- Address, Value, Reg->BitWidth);
+ Address, Value, Reg->BitWidth);
}
ACPI_DEBUG_PRINT ((ACPI_DB_IO,
@@ -271,6 +275,7 @@ AcpiHwWrite (
}
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: AcpiHwClearAcpiStatus
@@ -303,25 +308,27 @@ AcpiHwClearAcpiStatus (
/* Clear the fixed events in PM1 A/B */
Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_STATUS,
- ACPI_BITMASK_ALL_FIXED_STATUS);
+ ACPI_BITMASK_ALL_FIXED_STATUS);
+
+ AcpiOsReleaseLock (AcpiGbl_HardwareLock, LockFlags);
+
if (ACPI_FAILURE (Status))
{
- goto UnlockAndExit;
+ goto Exit;
}
/* Clear the GPE Bits in all GPE registers in all GPE blocks */
Status = AcpiEvWalkGpeList (AcpiHwClearGpeBlock, NULL);
-UnlockAndExit:
- AcpiOsReleaseLock (AcpiGbl_HardwareLock, LockFlags);
+Exit:
return_ACPI_STATUS (Status);
}
/*******************************************************************************
*
- * FUNCTION: AcpiHwGetRegisterBitMask
+ * FUNCTION: AcpiHwGetBitRegisterInfo
*
* PARAMETERS: RegisterId - Index of ACPI Register to access
*
@@ -420,24 +427,22 @@ AcpiHwRegisterRead (
case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */
Status = AcpiHwReadMultiple (&Value,
- &AcpiGbl_XPm1aStatus,
- &AcpiGbl_XPm1bStatus);
+ &AcpiGbl_XPm1aStatus,
+ &AcpiGbl_XPm1bStatus);
break;
-
case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
Status = AcpiHwReadMultiple (&Value,
- &AcpiGbl_XPm1aEnable,
- &AcpiGbl_XPm1bEnable);
+ &AcpiGbl_XPm1aEnable,
+ &AcpiGbl_XPm1bEnable);
break;
-
case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
Status = AcpiHwReadMultiple (&Value,
- &AcpiGbl_FADT.XPm1aControlBlock,
- &AcpiGbl_FADT.XPm1bControlBlock);
+ &AcpiGbl_FADT.XPm1aControlBlock,
+ &AcpiGbl_FADT.XPm1bControlBlock);
/*
* Zero the write-only bits. From the ACPI specification, "Hardware
@@ -447,26 +452,23 @@ AcpiHwRegisterRead (
Value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
break;
-
case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPm2ControlBlock);
break;
-
case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPmTimerBlock);
break;
-
case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
Status = AcpiHwReadPort (AcpiGbl_FADT.SmiCommand, &Value, 8);
break;
-
default:
+
ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
RegisterId));
Status = AE_BAD_PARAMETER;
@@ -536,28 +538,25 @@ AcpiHwRegisterWrite (
Value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
Status = AcpiHwWriteMultiple (Value,
- &AcpiGbl_XPm1aStatus,
- &AcpiGbl_XPm1bStatus);
+ &AcpiGbl_XPm1aStatus,
+ &AcpiGbl_XPm1bStatus);
break;
-
case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
Status = AcpiHwWriteMultiple (Value,
- &AcpiGbl_XPm1aEnable,
- &AcpiGbl_XPm1bEnable);
+ &AcpiGbl_XPm1aEnable,
+ &AcpiGbl_XPm1bEnable);
break;
-
case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
-
/*
* Perform a read first to preserve certain bits (per ACPI spec)
* Note: This includes SCI_EN, we never want to change this bit
*/
Status = AcpiHwReadMultiple (&ReadValue,
- &AcpiGbl_FADT.XPm1aControlBlock,
- &AcpiGbl_FADT.XPm1bControlBlock);
+ &AcpiGbl_FADT.XPm1aControlBlock,
+ &AcpiGbl_FADT.XPm1bControlBlock);
if (ACPI_FAILURE (Status))
{
goto Exit;
@@ -570,13 +569,11 @@ AcpiHwRegisterWrite (
/* Now we can write the data */
Status = AcpiHwWriteMultiple (Value,
- &AcpiGbl_FADT.XPm1aControlBlock,
- &AcpiGbl_FADT.XPm1bControlBlock);
+ &AcpiGbl_FADT.XPm1aControlBlock,
+ &AcpiGbl_FADT.XPm1bControlBlock);
break;
-
case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
-
/*
* For control registers, all reserved bits must be preserved,
* as per the ACPI spec.
@@ -594,13 +591,11 @@ AcpiHwRegisterWrite (
Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPm2ControlBlock);
break;
-
case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPmTimerBlock);
break;
-
case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
/* SMI_CMD is currently always in IO space */
@@ -608,8 +603,8 @@ AcpiHwRegisterWrite (
Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand, Value, 8);
break;
-
default:
+
ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
RegisterId));
Status = AE_BAD_PARAMETER;
@@ -731,3 +726,4 @@ AcpiHwWriteMultiple (
return (Status);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwsleep.c b/usr/src/uts/intel/io/acpica/hardware/hwsleep.c
index 1c8ca2440f..13cf255137 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwsleep.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwsleep.c
@@ -1,12 +1,12 @@
-
/******************************************************************************
*
- * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
+ * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * original/legacy sleep/PM registers.
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,215 +49,42 @@
ACPI_MODULE_NAME ("hwsleep")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
- * FUNCTION: AcpiSetFirmwareWakingVector
- *
- * PARAMETERS: PhysicalAddress - 32-bit physical address of ACPI real mode
- * entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 32-bit FirmwareWakingVector field of the FACS
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiSetFirmwareWakingVector (
- UINT32 PhysicalAddress)
-{
- ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector);
-
-
- /* Set the 32-bit vector */
-
- AcpiGbl_FACS->FirmwareWakingVector = PhysicalAddress;
-
- /* Clear the 64-bit vector if it exists */
-
- if ((AcpiGbl_FACS->Length > 32) && (AcpiGbl_FACS->Version >= 1))
- {
- AcpiGbl_FACS->XFirmwareWakingVector = 0;
- }
-
- return_ACPI_STATUS (AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiSetFirmwareWakingVector)
-
-
-#if ACPI_MACHINE_WIDTH == 64
-/*******************************************************************************
- *
- * FUNCTION: AcpiSetFirmwareWakingVector64
- *
- * PARAMETERS: PhysicalAddress - 64-bit physical address of ACPI protected
- * mode entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 64-bit X_FirmwareWakingVector field of the FACS, if
- * it exists in the table. This function is intended for use with
- * 64-bit host operating systems.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiSetFirmwareWakingVector64 (
- UINT64 PhysicalAddress)
-{
- ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector64);
-
-
- /* Determine if the 64-bit vector actually exists */
-
- if ((AcpiGbl_FACS->Length <= 32) || (AcpiGbl_FACS->Version < 1))
- {
- return_ACPI_STATUS (AE_NOT_EXIST);
- }
-
- /* Clear 32-bit vector, set the 64-bit X_ vector */
-
- AcpiGbl_FACS->FirmwareWakingVector = 0;
- AcpiGbl_FACS->XFirmwareWakingVector = PhysicalAddress;
- return_ACPI_STATUS (AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiSetFirmwareWakingVector64)
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEnterSleepStatePrep
- *
- * PARAMETERS: SleepState - Which sleep state to enter
- *
- * RETURN: Status
- *
- * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231)
- * This function must execute with interrupts enabled.
- * We break sleeping into 2 stages so that OSPM can handle
- * various OS-specific tasks between the two steps.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEnterSleepStatePrep (
- UINT8 SleepState)
-{
- ACPI_STATUS Status;
- ACPI_OBJECT_LIST ArgList;
- ACPI_OBJECT Arg;
-
-
- ACPI_FUNCTION_TRACE (AcpiEnterSleepStatePrep);
-
-
- /* _PSW methods could be run here to enable wake-on keyboard, LAN, etc. */
-
- Status = AcpiGetSleepTypeData (SleepState,
- &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Execute the _PTS method (Prepare To Sleep) */
-
- ArgList.Count = 1;
- ArgList.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = SleepState;
-
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__PTS, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Setup the argument to the _SST method (System STatus) */
-
- switch (SleepState)
- {
- case ACPI_STATE_S0:
- Arg.Integer.Value = ACPI_SST_WORKING;
- break;
-
- case ACPI_STATE_S1:
- case ACPI_STATE_S2:
- case ACPI_STATE_S3:
- Arg.Integer.Value = ACPI_SST_SLEEPING;
- break;
-
- case ACPI_STATE_S4:
- Arg.Integer.Value = ACPI_SST_SLEEP_CONTEXT;
- break;
-
- default:
- Arg.Integer.Value = ACPI_SST_INDICATOR_OFF; /* Default is off */
- break;
- }
-
- /*
- * Set the system indicators to show the desired sleep state.
- * _SST is an optional method (return no error if not found)
- */
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "While executing method _SST"));
- }
-
- return_ACPI_STATUS (AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiEnterSleepStatePrep)
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEnterSleepState
+ * FUNCTION: AcpiHwLegacySleep
*
* PARAMETERS: SleepState - Which sleep state to enter
*
* RETURN: Status
*
- * DESCRIPTION: Enter a system sleep state
+ * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
ACPI_STATUS
-AcpiEnterSleepState (
+AcpiHwLegacySleep (
UINT8 SleepState)
{
- UINT32 Pm1aControl;
- UINT32 Pm1bControl;
ACPI_BIT_REGISTER_INFO *SleepTypeRegInfo;
ACPI_BIT_REGISTER_INFO *SleepEnableRegInfo;
+ UINT32 Pm1aControl;
+ UINT32 Pm1bControl;
UINT32 InValue;
- ACPI_OBJECT_LIST ArgList;
- ACPI_OBJECT Arg;
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (AcpiEnterSleepState);
+ ACPI_FUNCTION_TRACE (HwLegacySleep);
- if ((AcpiGbl_SleepTypeA > ACPI_SLEEP_TYPE_MAX) ||
- (AcpiGbl_SleepTypeB > ACPI_SLEEP_TYPE_MAX))
- {
- ACPI_ERROR ((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
- AcpiGbl_SleepTypeA, AcpiGbl_SleepTypeB));
- return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
- }
-
- SleepTypeRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE);
+ SleepTypeRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE);
SleepEnableRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE);
/* Clear wake status */
- Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+ Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS,
+ ACPI_CLEAR_STATUS);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -271,20 +98,6 @@ AcpiEnterSleepState (
return_ACPI_STATUS (Status);
}
- if (SleepState != ACPI_STATE_S5)
- {
- /*
- * Disable BM arbitration. This feature is contained within an
- * optional register (PM2 Control), so ignore a BAD_ADDRESS
- * exception.
- */
- Status = AcpiWriteBitRegister (ACPI_BITREG_ARB_DISABLE, 1);
- if (ACPI_FAILURE (Status) && (Status != AE_BAD_ADDRESS))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
/*
* 1) Disable/Clear all GPEs
* 2) Enable all wakeup GPEs
@@ -302,23 +115,10 @@ AcpiEnterSleepState (
return_ACPI_STATUS (Status);
}
- /* Execute the _GTS method (Going To Sleep) */
-
- ArgList.Count = 1;
- ArgList.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = SleepState;
-
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__GTS, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- return_ACPI_STATUS (Status);
- }
-
/* Get current value of PM1A control */
Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
- &Pm1aControl);
+ &Pm1aControl);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -329,7 +129,7 @@ AcpiEnterSleepState (
/* Clear the SLP_EN and SLP_TYP fields */
Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask |
- SleepEnableRegInfo->AccessBitMask);
+ SleepEnableRegInfo->AccessBitMask);
Pm1bControl = Pm1aControl;
/* Insert the SLP_TYP bits */
@@ -380,17 +180,17 @@ AcpiEnterSleepState (
* to still read the right value. Ideally, this block would go
* away entirely.
*/
- AcpiOsStall (10000000);
+ AcpiOsStall (10 * ACPI_USEC_PER_SEC);
Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_CONTROL,
- SleepEnableRegInfo->AccessBitMask);
+ SleepEnableRegInfo->AccessBitMask);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
}
- /* Wait until we enter sleep state */
+ /* Wait for transition back to Working State */
do
{
@@ -400,110 +200,30 @@ AcpiEnterSleepState (
return_ACPI_STATUS (Status);
}
- /* Spin until we wake */
-
- } while (!InValue);
-
- return_ACPI_STATUS (AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiEnterSleepState)
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEnterSleepStateS4bios
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Perform a S4 bios request.
- * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEnterSleepStateS4bios (
- void)
-{
- UINT32 InValue;
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (AcpiEnterSleepStateS4bios);
-
-
- /* Clear the wake status bit (PM1) */
-
- Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- Status = AcpiHwClearAcpiStatus ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /*
- * 1) Disable/Clear all GPEs
- * 2) Enable all wakeup GPEs
- */
- Status = AcpiHwDisableAllGpes ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- AcpiGbl_SystemAwakeAndRunning = FALSE;
-
- Status = AcpiHwEnableAllWakeupGpes ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- ACPI_FLUSH_CPU_CACHE ();
-
- Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand,
- (UINT32) AcpiGbl_FADT.S4BiosRequest, 8);
-
- do {
- AcpiOsStall(1000);
- Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
} while (!InValue);
return_ACPI_STATUS (AE_OK);
}
-ACPI_EXPORT_SYMBOL (AcpiEnterSleepStateS4bios)
-
/*******************************************************************************
*
- * FUNCTION: AcpiLeaveSleepState
+ * FUNCTION: AcpiHwLegacyWakePrep
*
* PARAMETERS: SleepState - Which sleep state we just exited
*
* RETURN: Status
*
- * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ * sleep.
* Called with interrupts ENABLED.
*
******************************************************************************/
ACPI_STATUS
-AcpiLeaveSleepState (
+AcpiHwLegacyWakePrep (
UINT8 SleepState)
{
- ACPI_OBJECT_LIST ArgList;
- ACPI_OBJECT Arg;
ACPI_STATUS Status;
ACPI_BIT_REGISTER_INFO *SleepTypeRegInfo;
ACPI_BIT_REGISTER_INFO *SleepEnableRegInfo;
@@ -511,8 +231,7 @@ AcpiLeaveSleepState (
UINT32 Pm1bControl;
- ACPI_FUNCTION_TRACE (AcpiLeaveSleepState);
-
+ ACPI_FUNCTION_TRACE (HwLegacyWakePrep);
/*
* Set SLP_TYPE and SLP_EN to state S0.
@@ -520,7 +239,7 @@ AcpiLeaveSleepState (
* by some machines.
*/
Status = AcpiGetSleepTypeData (ACPI_STATE_S0,
- &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
+ &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
if (ACPI_SUCCESS (Status))
{
SleepTypeRegInfo =
@@ -531,7 +250,7 @@ AcpiLeaveSleepState (
/* Get current value of PM1A control */
Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
- &Pm1aControl);
+ &Pm1aControl);
if (ACPI_SUCCESS (Status))
{
/* Clear the SLP_EN and SLP_TYP fields */
@@ -553,40 +272,42 @@ AcpiLeaveSleepState (
}
}
- /* Ensure EnterSleepStatePrep -> EnterSleepState ordering */
+ return_ACPI_STATUS (Status);
+}
- AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID;
- /* Setup parameter object */
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwLegacyWake
+ *
+ * PARAMETERS: SleepState - Which sleep state we just exited
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiHwLegacyWake (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
- ArgList.Count = 1;
- ArgList.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- /* Ignore any errors from these methods */
+ ACPI_FUNCTION_TRACE (HwLegacyWake);
- Arg.Integer.Value = ACPI_SST_WAKING;
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Method _SST"));
- }
- Arg.Integer.Value = SleepState;
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__BFS, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Method _BFS"));
- }
+ /* Ensure EnterSleepStatePrep -> EnterSleepState ordering */
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__WAK, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Method _WAK"));
- }
- /* TBD: _WAK "sometimes" returns stuff - do we want to look at it? */
+ AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID;
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WAKING);
/*
+ * GPEs must be enabled before _WAK is called as GPEs
+ * might get fired there
+ *
* Restore the GPEs:
* 1) Disable/Clear all GPEs
* 2) Enable all runtime GPEs
@@ -596,7 +317,6 @@ AcpiLeaveSleepState (
{
return_ACPI_STATUS (Status);
}
- AcpiGbl_SystemAwakeAndRunning = TRUE;
Status = AcpiHwEnableAllRuntimeGpes ();
if (ACPI_FAILURE (Status))
@@ -604,6 +324,21 @@ AcpiLeaveSleepState (
return_ACPI_STATUS (Status);
}
+ /*
+ * Now we can execute _WAK, etc. Some machines require that the GPEs
+ * are enabled before the wake methods are executed.
+ */
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__WAK, SleepState);
+
+ /*
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
+ */
+ (void) AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS,
+ ACPI_CLEAR_STATUS);
+ AcpiGbl_SystemAwakeAndRunning = TRUE;
+
/* Enable power button */
(void) AcpiWriteBitRegister(
@@ -614,26 +349,8 @@ AcpiLeaveSleepState (
AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].StatusRegisterId,
ACPI_CLEAR_STATUS);
- /*
- * Enable BM arbitration. This feature is contained within an
- * optional register (PM2 Control), so ignore a BAD_ADDRESS
- * exception.
- */
- Status = AcpiWriteBitRegister (ACPI_BITREG_ARB_DISABLE, 0);
- if (ACPI_FAILURE (Status) && (Status != AE_BAD_ADDRESS))
- {
- return_ACPI_STATUS (Status);
- }
-
- Arg.Integer.Value = ACPI_SST_WORKING;
- Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
- if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Method _SST"));
- }
-
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WORKING);
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiLeaveSleepState)
-
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwtimer.c b/usr/src/uts/intel/io/acpica/hardware/hwtimer.c
index faf6770283..64b2a8aaf5 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwtimer.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwtimer.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Name: hwtimer.c - ACPI Power Management Timer Interface
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +41,8 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#define EXPORT_ACPI_INTERFACES
+
#include "acpi.h"
#include "accommon.h"
@@ -49,6 +50,7 @@
ACPI_MODULE_NAME ("hwtimer")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/******************************************************************************
*
* FUNCTION: AcpiGetTimerResolution
@@ -115,8 +117,14 @@ AcpiGetTimer (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- Status = AcpiHwRead (Ticks, &AcpiGbl_FADT.XPmTimerBlock);
+ /* ACPI 5.0A: PM Timer is optional */
+
+ if (!AcpiGbl_FADT.XPmTimerBlock.Address)
+ {
+ return_ACPI_STATUS (AE_SUPPORT);
+ }
+ Status = AcpiHwRead (Ticks, &AcpiGbl_FADT.XPmTimerBlock);
return_ACPI_STATUS (Status);
}
@@ -143,7 +151,7 @@ ACPI_EXPORT_SYMBOL (AcpiGetTimer)
* a versatile and accurate timer.
*
* Note that this function accommodates only a single timer
- * rollover. Thus for 24-bit timers, this function should only
+ * rollover. Thus for 24-bit timers, this function should only
* be used for calculating durations less than ~4.6 seconds
* (~20 minutes for 32-bit timers) -- calculations below:
*
@@ -171,6 +179,13 @@ AcpiGetTimerDuration (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
+ /* ACPI 5.0A: PM Timer is optional */
+
+ if (!AcpiGbl_FADT.XPmTimerBlock.Address)
+ {
+ return_ACPI_STATUS (AE_SUPPORT);
+ }
+
/*
* Compute Tick Delta:
* Handle (max one) timer rollovers on 24-bit versus 32-bit timers.
@@ -203,10 +218,11 @@ AcpiGetTimerDuration (
/*
* Compute Duration (Requires a 64-bit multiply and divide):
*
- * TimeElapsed = (DeltaTicks * 1000000) / PM_TIMER_FREQUENCY;
+ * TimeElapsed (microseconds) =
+ * (DeltaTicks * ACPI_USEC_PER_SEC) / ACPI_PM_TIMER_FREQUENCY;
*/
- Status = AcpiUtShortDivide (((UINT64) DeltaTicks) * 1000000,
- PM_TIMER_FREQUENCY, &Quotient, NULL);
+ Status = AcpiUtShortDivide (((UINT64) DeltaTicks) * ACPI_USEC_PER_SEC,
+ ACPI_PM_TIMER_FREQUENCY, &Quotient, NULL);
*TimeElapsed = (UINT32) Quotient;
return_ACPI_STATUS (Status);
@@ -214,3 +230,4 @@ AcpiGetTimerDuration (
ACPI_EXPORT_SYMBOL (AcpiGetTimerDuration)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwvalid.c b/usr/src/uts/intel/io/acpica/hardware/hwvalid.c
index 869c9f18fd..64507d0418 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwvalid.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwvalid.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: hwvalid - I/O request validation
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __HWVALID_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -149,6 +146,8 @@ AcpiHwValidateIoRequest (
(BitWidth != 16) &&
(BitWidth != 32))
{
+ ACPI_ERROR ((AE_INFO,
+ "Bad BitWidth parameter: %8.8X", BitWidth));
return (AE_BAD_PARAMETER);
}
@@ -156,8 +155,8 @@ AcpiHwValidateIoRequest (
ByteWidth = ACPI_DIV_8 (BitWidth);
LastAddress = Address + ByteWidth - 1;
- ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Address %p LastAddress %p Length %X",
- ACPI_CAST_PTR (void, Address), ACPI_CAST_PTR (void, LastAddress),
+ ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X",
+ ACPI_FORMAT_UINT64 (Address), ACPI_FORMAT_UINT64 (LastAddress),
ByteWidth));
/* Maximum 16-bit address in I/O space */
@@ -165,8 +164,8 @@ AcpiHwValidateIoRequest (
if (LastAddress > ACPI_UINT16_MAX)
{
ACPI_ERROR ((AE_INFO,
- "Illegal I/O port address/length above 64K: %p/0x%X",
- ACPI_CAST_PTR (void, Address), ByteWidth));
+ "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X",
+ ACPI_FORMAT_UINT64 (Address), ByteWidth));
return_ACPI_STATUS (AE_LIMIT);
}
@@ -197,8 +196,8 @@ AcpiHwValidateIoRequest (
if (AcpiGbl_OsiData >= PortInfo->OsiDependency)
{
ACPI_DEBUG_PRINT ((ACPI_DB_IO,
- "Denied AML access to port 0x%p/%X (%s 0x%.4X-0x%.4X)",
- ACPI_CAST_PTR (void, Address), ByteWidth, PortInfo->Name,
+ "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)",
+ ACPI_FORMAT_UINT64 (Address), ByteWidth, PortInfo->Name,
PortInfo->Start, PortInfo->End));
return_ACPI_STATUS (AE_AML_ILLEGAL_ADDRESS);
@@ -362,5 +361,3 @@ AcpiHwWritePort (
return (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwxface.c b/usr/src/uts/intel/io/acpica/hardware/hwxface.c
index f26c511ec8..f5d4fca6b9 100644
--- a/usr/src/uts/intel/io/acpica/hardware/hwxface.c
+++ b/usr/src/uts/intel/io/acpica/hardware/hwxface.c
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: hwxface - Public ACPICA hardware interfaces
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +41,8 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
+#define EXPORT_ACPI_INTERFACES
+
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -91,9 +92,15 @@ AcpiReset (
* For I/O space, write directly to the OSL. This bypasses the port
* validation mechanism, which may block a valid write to the reset
* register.
+ *
+ * NOTE:
+ * The ACPI spec requires the reset register width to be 8, so we
+ * hardcode it here and ignore the FADT value. This maintains
+ * compatibility with other ACPI implementations that have allowed
+ * BIOS code with bad register width values to go unnoticed.
*/
Status = AcpiOsWritePort ((ACPI_IO_ADDRESS) ResetReg->Address,
- AcpiGbl_FADT.ResetValue, ResetReg->BitWidth);
+ AcpiGbl_FADT.ResetValue, ACPI_RESET_REGISTER_WIDTH);
}
else
{
@@ -132,7 +139,8 @@ AcpiRead (
UINT64 *ReturnValue,
ACPI_GENERIC_ADDRESS *Reg)
{
- UINT32 Value;
+ UINT32 ValueLo;
+ UINT32 ValueHi;
UINT32 Width;
UINT64 Address;
ACPI_STATUS Status;
@@ -154,66 +162,52 @@ AcpiRead (
return (Status);
}
- Width = Reg->BitWidth;
- if (Width == 64)
- {
- Width = 32; /* Break into two 32-bit transfers */
- }
-
- /* Initialize entire 64-bit return value to zero */
-
- *ReturnValue = 0;
- Value = 0;
-
/*
- * Two address spaces supported: Memory or IO. PCI_Config is
+ * Two address spaces supported: Memory or I/O. PCI_Config is
* not supported here because the GAS structure is insufficient
*/
if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
{
Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
- Address, &Value, Width);
+ Address, ReturnValue, Reg->BitWidth);
if (ACPI_FAILURE (Status))
{
return (Status);
}
- *ReturnValue = Value;
-
- if (Reg->BitWidth == 64)
- {
- /* Read the top 32 bits */
-
- Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
- (Address + 4), &Value, 32);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- *ReturnValue |= ((UINT64) Value << 32);
- }
}
else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
{
+ ValueLo = 0;
+ ValueHi = 0;
+
+ Width = Reg->BitWidth;
+ if (Width == 64)
+ {
+ Width = 32; /* Break into two 32-bit transfers */
+ }
+
Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
- Address, &Value, Width);
+ Address, &ValueLo, Width);
if (ACPI_FAILURE (Status))
{
return (Status);
}
- *ReturnValue = Value;
if (Reg->BitWidth == 64)
{
/* Read the top 32 bits */
Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
- (Address + 4), &Value, 32);
+ (Address + 4), &ValueHi, 32);
if (ACPI_FAILURE (Status))
{
return (Status);
}
- *ReturnValue |= ((UINT64) Value << 32);
}
+
+ /* Set the return value only if status is AE_OK */
+
+ *ReturnValue = (ValueLo | ((UINT64) ValueHi << 32));
}
ACPI_DEBUG_PRINT ((ACPI_DB_IO,
@@ -222,7 +216,7 @@ AcpiRead (
ACPI_FORMAT_UINT64 (Address),
AcpiUtGetRegionName (Reg->SpaceId)));
- return (Status);
+ return (AE_OK);
}
ACPI_EXPORT_SYMBOL (AcpiRead)
@@ -262,12 +256,6 @@ AcpiWrite (
return (Status);
}
- Width = Reg->BitWidth;
- if (Width == 64)
- {
- Width = 32; /* Break into two 32-bit transfers */
- }
-
/*
* Two address spaces supported: Memory or IO. PCI_Config is
* not supported here because the GAS structure is insufficient
@@ -275,26 +263,22 @@ AcpiWrite (
if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
{
Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
- Address, ACPI_LODWORD (Value), Width);
+ Address, Value, Reg->BitWidth);
if (ACPI_FAILURE (Status))
{
return (Status);
}
-
- if (Reg->BitWidth == 64)
- {
- Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
- (Address + 4), ACPI_HIDWORD (Value), 32);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
}
else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
{
+ Width = Reg->BitWidth;
+ if (Width == 64)
+ {
+ Width = 32; /* Break into two 32-bit transfers */
+ }
+
Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
- Address, ACPI_LODWORD (Value), Width);
+ Address, ACPI_LODWORD (Value), Width);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -303,7 +287,7 @@ AcpiWrite (
if (Reg->BitWidth == 64)
{
Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
- (Address + 4), ACPI_HIDWORD (Value), 32);
+ (Address + 4), ACPI_HIDWORD (Value), 32);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -323,6 +307,7 @@ AcpiWrite (
ACPI_EXPORT_SYMBOL (AcpiWrite)
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: AcpiReadBitRegister
@@ -373,7 +358,7 @@ AcpiReadBitRegister (
/* Read the entire parent register */
Status = AcpiHwRegisterRead (BitRegInfo->ParentRegister,
- &RegisterValue);
+ &RegisterValue);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -382,7 +367,7 @@ AcpiReadBitRegister (
/* Normalize the value that was read, mask off other bits */
Value = ((RegisterValue & BitRegInfo->AccessBitMask)
- >> BitRegInfo->BitPosition);
+ >> BitRegInfo->BitPosition);
ACPI_DEBUG_PRINT ((ACPI_DB_IO,
"BitReg %X, ParentReg %X, Actual %8.8X, ReturnValue %8.8X\n",
@@ -401,7 +386,7 @@ ACPI_EXPORT_SYMBOL (AcpiReadBitRegister)
*
* PARAMETERS: RegisterId - ID of ACPI Bit Register to access
* Value - Value to write to the register, in bit
- * position zero. The bit is automaticallly
+ * position zero. The bit is automatically
* shifted to the correct position.
*
* RETURN: Status
@@ -454,7 +439,7 @@ AcpiWriteBitRegister (
* interested in
*/
Status = AcpiHwRegisterRead (BitRegInfo->ParentRegister,
- &RegisterValue);
+ &RegisterValue);
if (ACPI_FAILURE (Status))
{
goto UnlockAndExit;
@@ -468,7 +453,7 @@ AcpiWriteBitRegister (
BitRegInfo->AccessBitMask, Value);
Status = AcpiHwRegisterWrite (BitRegInfo->ParentRegister,
- RegisterValue);
+ RegisterValue);
}
else
{
@@ -488,7 +473,7 @@ AcpiWriteBitRegister (
if (RegisterValue)
{
Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_STATUS,
- RegisterValue);
+ RegisterValue);
}
}
@@ -505,6 +490,8 @@ UnlockAndExit:
ACPI_EXPORT_SYMBOL (AcpiWriteBitRegister)
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/*******************************************************************************
*
@@ -514,10 +501,33 @@ ACPI_EXPORT_SYMBOL (AcpiWriteBitRegister)
* *SleepTypeA - Where SLP_TYPa is returned
* *SleepTypeB - Where SLP_TYPb is returned
*
- * RETURN: Status - ACPI status
+ * RETURN: Status
+ *
+ * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested
+ * sleep state via the appropriate \_Sx object.
+ *
+ * The sleep state package returned from the corresponding \_Sx_ object
+ * must contain at least one integer.
+ *
+ * March 2005:
+ * Added support for a package that contains two integers. This
+ * goes against the ACPI specification which defines this object as a
+ * package with one encoded DWORD integer. However, existing practice
+ * by many BIOS vendors is to return a package with 2 or more integer
+ * elements, at least one per sleep type (A/B).
+ *
+ * January 2013:
+ * Therefore, we must be prepared to accept a package with either a
+ * single integer or multiple integers.
+ *
+ * The single integer DWORD format is as follows:
+ * BYTE 0 - Value for the PM1A SLP_TYP register
+ * BYTE 1 - Value for the PM1B SLP_TYP register
+ * BYTE 2-3 - Reserved
*
- * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested sleep
- * state.
+ * The dual integer format is as follows:
+ * Integer 0 - Value for the PM1A SLP_TYP register
+ * Integer 1 - Value for the PM1A SLP_TYP register
*
******************************************************************************/
@@ -527,8 +537,9 @@ AcpiGetSleepTypeData (
UINT8 *SleepTypeA,
UINT8 *SleepTypeB)
{
- ACPI_STATUS Status = AE_OK;
+ ACPI_STATUS Status;
ACPI_EVALUATE_INFO *Info;
+ ACPI_OPERAND_OBJECT **Elements;
ACPI_FUNCTION_TRACE (AcpiGetSleepTypeData);
@@ -537,8 +548,7 @@ AcpiGetSleepTypeData (
/* Validate parameters */
if ((SleepState > ACPI_S_STATES_MAX) ||
- !SleepTypeA ||
- !SleepTypeB)
+ !SleepTypeA || !SleepTypeB)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
@@ -551,18 +561,23 @@ AcpiGetSleepTypeData (
return_ACPI_STATUS (AE_NO_MEMORY);
}
- Info->Pathname = ACPI_CAST_PTR (char, AcpiGbl_SleepStateNames[SleepState]);
-
- /* Evaluate the namespace object containing the values for this state */
+ /*
+ * Evaluate the \_Sx namespace object containing the register values
+ * for this state
+ */
+ Info->RelativePathname = AcpiGbl_SleepStateNames[SleepState];
Status = AcpiNsEvaluate (Info);
if (ACPI_FAILURE (Status))
{
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "%s while evaluating SleepState [%s]\n",
- AcpiFormatException (Status), Info->Pathname));
+ if (Status == AE_NOT_FOUND)
+ {
+ /* The _Sx states are optional, ignore NOT_FOUND */
+
+ goto FinalCleanup;
+ }
- goto Cleanup;
+ goto WarningCleanup;
}
/* Must have a return object */
@@ -570,67 +585,76 @@ AcpiGetSleepTypeData (
if (!Info->ReturnObject)
{
ACPI_ERROR ((AE_INFO, "No Sleep State object returned from [%s]",
- Info->Pathname));
- Status = AE_NOT_EXIST;
+ Info->RelativePathname));
+ Status = AE_AML_NO_RETURN_VALUE;
+ goto WarningCleanup;
}
- /* It must be of type Package */
+ /* Return object must be of type Package */
- else if (Info->ReturnObject->Common.Type != ACPI_TYPE_PACKAGE)
+ if (Info->ReturnObject->Common.Type != ACPI_TYPE_PACKAGE)
{
ACPI_ERROR ((AE_INFO, "Sleep State return object is not a Package"));
Status = AE_AML_OPERAND_TYPE;
+ goto ReturnValueCleanup;
}
/*
- * The package must have at least two elements. NOTE (March 2005): This
- * goes against the current ACPI spec which defines this object as a
- * package with one encoded DWORD element. However, existing practice
- * by BIOS vendors seems to be to have 2 or more elements, at least
- * one per sleep type (A/B).
+ * Any warnings about the package length or the object types have
+ * already been issued by the predefined name module -- there is no
+ * need to repeat them here.
*/
- else if (Info->ReturnObject->Package.Count < 2)
+ Elements = Info->ReturnObject->Package.Elements;
+ switch (Info->ReturnObject->Package.Count)
{
- ACPI_ERROR ((AE_INFO,
- "Sleep State return package does not have at least two elements"));
- Status = AE_AML_NO_OPERAND;
- }
+ case 0:
- /* The first two elements must both be of type Integer */
+ Status = AE_AML_PACKAGE_LIMIT;
+ break;
- else if (((Info->ReturnObject->Package.Elements[0])->Common.Type
- != ACPI_TYPE_INTEGER) ||
- ((Info->ReturnObject->Package.Elements[1])->Common.Type
- != ACPI_TYPE_INTEGER))
- {
- ACPI_ERROR ((AE_INFO,
- "Sleep State return package elements are not both Integers "
- "(%s, %s)",
- AcpiUtGetObjectTypeName (Info->ReturnObject->Package.Elements[0]),
- AcpiUtGetObjectTypeName (Info->ReturnObject->Package.Elements[1])));
- Status = AE_AML_OPERAND_TYPE;
- }
- else
- {
- /* Valid _Sx_ package size, type, and value */
+ case 1:
+
+ if (Elements[0]->Common.Type != ACPI_TYPE_INTEGER)
+ {
+ Status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ /* A valid _Sx_ package with one integer */
+
+ *SleepTypeA = (UINT8) Elements[0]->Integer.Value;
+ *SleepTypeB = (UINT8) (Elements[0]->Integer.Value >> 8);
+ break;
- *SleepTypeA = (UINT8)
- (Info->ReturnObject->Package.Elements[0])->Integer.Value;
- *SleepTypeB = (UINT8)
- (Info->ReturnObject->Package.Elements[1])->Integer.Value;
+ case 2:
+ default:
+
+ if ((Elements[0]->Common.Type != ACPI_TYPE_INTEGER) ||
+ (Elements[1]->Common.Type != ACPI_TYPE_INTEGER))
+ {
+ Status = AE_AML_OPERAND_TYPE;
+ break;
+ }
+
+ /* A valid _Sx_ package with two integers */
+
+ *SleepTypeA = (UINT8) Elements[0]->Integer.Value;
+ *SleepTypeB = (UINT8) Elements[1]->Integer.Value;
+ break;
}
+ReturnValueCleanup:
+ AcpiUtRemoveReference (Info->ReturnObject);
+
+WarningCleanup:
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "While evaluating SleepState [%s], bad Sleep object %p type %s",
- Info->Pathname, Info->ReturnObject,
- AcpiUtGetObjectTypeName (Info->ReturnObject)));
+ "While evaluating Sleep State [%s]",
+ Info->RelativePathname));
}
- AcpiUtRemoveReference (Info->ReturnObject);
-
-Cleanup:
+FinalCleanup:
ACPI_FREE (Info);
return_ACPI_STATUS (Status);
}
diff --git a/usr/src/uts/intel/io/acpica/hardware/hwxfsleep.c b/usr/src/uts/intel/io/acpica/hardware/hwxfsleep.c
new file mode 100644
index 0000000000..7a0eaf5061
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/hardware/hwxfsleep.c
@@ -0,0 +1,501 @@
+/******************************************************************************
+ *
+ * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define EXPORT_ACPI_INTERFACES
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ ACPI_MODULE_NAME ("hwxfsleep")
+
+/* Local prototypes */
+
+#if (!ACPI_REDUCED_HARDWARE)
+static ACPI_STATUS
+AcpiHwSetFirmwareWakingVector (
+ ACPI_TABLE_FACS *Facs,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress64);
+#endif
+
+static ACPI_STATUS
+AcpiHwSleepDispatch (
+ UINT8 SleepState,
+ UINT32 FunctionId);
+
+/*
+ * Dispatch table used to efficiently branch to the various sleep
+ * functions.
+ */
+#define ACPI_SLEEP_FUNCTION_ID 0
+#define ACPI_WAKE_PREP_FUNCTION_ID 1
+#define ACPI_WAKE_FUNCTION_ID 2
+
+/* Legacy functions are optional, based upon ACPI_REDUCED_HARDWARE */
+
+static ACPI_SLEEP_FUNCTIONS AcpiSleepDispatch[] =
+{
+ {ACPI_HW_OPTIONAL_FUNCTION (AcpiHwLegacySleep), AcpiHwExtendedSleep},
+ {ACPI_HW_OPTIONAL_FUNCTION (AcpiHwLegacyWakePrep), AcpiHwExtendedWakePrep},
+ {ACPI_HW_OPTIONAL_FUNCTION (AcpiHwLegacyWake), AcpiHwExtendedWake}
+};
+
+
+/*
+ * These functions are removed for the ACPI_REDUCED_HARDWARE case:
+ * AcpiSetFirmwareWakingVector
+ * AcpiEnterSleepStateS4bios
+ */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwSetFirmwareWakingVector
+ *
+ * PARAMETERS: Facs - Pointer to FACS table
+ * PhysicalAddress - 32-bit physical address of ACPI real mode
+ * entry point
+ * PhysicalAddress64 - 64-bit physical address of ACPI protected
+ * mode entry point
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the FirmwareWakingVector fields of the FACS
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiHwSetFirmwareWakingVector (
+ ACPI_TABLE_FACS *Facs,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress64)
+{
+ ACPI_FUNCTION_TRACE (AcpiHwSetFirmwareWakingVector);
+
+
+ /*
+ * According to the ACPI specification 2.0c and later, the 64-bit
+ * waking vector should be cleared and the 32-bit waking vector should
+ * be used, unless we want the wake-up code to be called by the BIOS in
+ * Protected Mode. Some systems (for example HP dv5-1004nr) are known
+ * to fail to resume if the 64-bit vector is used.
+ */
+
+ /* Set the 32-bit vector */
+
+ Facs->FirmwareWakingVector = (UINT32) PhysicalAddress;
+
+ if (Facs->Length > 32)
+ {
+ if (Facs->Version >= 1)
+ {
+ /* Set the 64-bit vector */
+
+ Facs->XFirmwareWakingVector = PhysicalAddress64;
+ }
+ else
+ {
+ /* Clear the 64-bit vector if it exists */
+
+ Facs->XFirmwareWakingVector = 0;
+ }
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiSetFirmwareWakingVector
+ *
+ * PARAMETERS: PhysicalAddress - 32-bit physical address of ACPI real mode
+ * entry point
+ * PhysicalAddress64 - 64-bit physical address of ACPI protected
+ * mode entry point
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the FirmwareWakingVector fields of the FACS
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiSetFirmwareWakingVector (
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress64)
+{
+
+ ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector);
+
+ if (AcpiGbl_FACS)
+ {
+ (void) AcpiHwSetFirmwareWakingVector (AcpiGbl_FACS,
+ PhysicalAddress, PhysicalAddress64);
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiSetFirmwareWakingVector)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEnterSleepStateS4bios
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform a S4 bios request.
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnterSleepStateS4bios (
+ void)
+{
+ UINT32 InValue;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiEnterSleepStateS4bios);
+
+
+ /* Clear the wake status bit (PM1) */
+
+ Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Status = AcpiHwClearAcpiStatus ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /*
+ * 1) Disable/Clear all GPEs
+ * 2) Enable all wakeup GPEs
+ */
+ Status = AcpiHwDisableAllGpes ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ AcpiGbl_SystemAwakeAndRunning = FALSE;
+
+ Status = AcpiHwEnableAllWakeupGpes ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ ACPI_FLUSH_CPU_CACHE ();
+
+ Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand,
+ (UINT32) AcpiGbl_FADT.S4BiosRequest, 8);
+
+ do {
+ AcpiOsStall (ACPI_USEC_PER_MSEC);
+ Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ } while (!InValue);
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiEnterSleepStateS4bios)
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiHwSleepDispatch
+ *
+ * PARAMETERS: SleepState - Which sleep state to enter/exit
+ * FunctionId - Sleep, WakePrep, or Wake
+ *
+ * RETURN: Status from the invoked sleep handling function.
+ *
+ * DESCRIPTION: Dispatch a sleep/wake request to the appropriate handling
+ * function.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiHwSleepDispatch (
+ UINT8 SleepState,
+ UINT32 FunctionId)
+{
+ ACPI_STATUS Status;
+ ACPI_SLEEP_FUNCTIONS *SleepFunctions = &AcpiSleepDispatch[FunctionId];
+
+
+#if (!ACPI_REDUCED_HARDWARE)
+ /*
+ * If the Hardware Reduced flag is set (from the FADT), we must
+ * use the extended sleep registers (FADT). Note: As per the ACPI
+ * specification, these extended registers are to be used for HW-reduced
+ * platforms only. They are not general-purpose replacements for the
+ * legacy PM register sleep support.
+ */
+ if (AcpiGbl_ReducedHardware)
+ {
+ Status = SleepFunctions->ExtendedFunction (SleepState);
+ }
+ else
+ {
+ /* Legacy sleep */
+
+ Status = SleepFunctions->LegacyFunction (SleepState);
+ }
+
+ return (Status);
+
+#else
+ /*
+ * For the case where reduced-hardware-only code is being generated,
+ * we know that only the extended sleep registers are available
+ */
+ Status = SleepFunctions->ExtendedFunction (SleepState);
+ return (Status);
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEnterSleepStatePrep
+ *
+ * PARAMETERS: SleepState - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Prepare to enter a system sleep state.
+ * This function must execute with interrupts enabled.
+ * We break sleeping into 2 stages so that OSPM can handle
+ * various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnterSleepStatePrep (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+ ACPI_OBJECT_LIST ArgList;
+ ACPI_OBJECT Arg;
+ UINT32 SstValue;
+
+
+ ACPI_FUNCTION_TRACE (AcpiEnterSleepStatePrep);
+
+
+ Status = AcpiGetSleepTypeData (SleepState,
+ &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Execute the _PTS method (Prepare To Sleep) */
+
+ ArgList.Count = 1;
+ ArgList.Pointer = &Arg;
+ Arg.Type = ACPI_TYPE_INTEGER;
+ Arg.Integer.Value = SleepState;
+
+ Status = AcpiEvaluateObject (NULL, METHOD_PATHNAME__PTS, &ArgList, NULL);
+ if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Setup the argument to the _SST method (System STatus) */
+
+ switch (SleepState)
+ {
+ case ACPI_STATE_S0:
+
+ SstValue = ACPI_SST_WORKING;
+ break;
+
+ case ACPI_STATE_S1:
+ case ACPI_STATE_S2:
+ case ACPI_STATE_S3:
+
+ SstValue = ACPI_SST_SLEEPING;
+ break;
+
+ case ACPI_STATE_S4:
+
+ SstValue = ACPI_SST_SLEEP_CONTEXT;
+ break;
+
+ default:
+
+ SstValue = ACPI_SST_INDICATOR_OFF; /* Default is off */
+ break;
+ }
+
+ /*
+ * Set the system indicators to show the desired sleep state.
+ * _SST is an optional method (return no error if not found)
+ */
+ AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, SstValue);
+ return_ACPI_STATUS (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiEnterSleepStatePrep)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEnterSleepState
+ *
+ * PARAMETERS: SleepState - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnterSleepState (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiEnterSleepState);
+
+
+ if ((AcpiGbl_SleepTypeA > ACPI_SLEEP_TYPE_MAX) ||
+ (AcpiGbl_SleepTypeB > ACPI_SLEEP_TYPE_MAX))
+ {
+ ACPI_ERROR ((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
+ AcpiGbl_SleepTypeA, AcpiGbl_SleepTypeB));
+ return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
+ }
+
+ Status = AcpiHwSleepDispatch (SleepState, ACPI_SLEEP_FUNCTION_ID);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiEnterSleepState)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiLeaveSleepStatePrep
+ *
+ * PARAMETERS: SleepState - Which sleep state we are exiting
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ * sleep. Called with interrupts DISABLED.
+ * We break wake/resume into 2 stages so that OSPM can handle
+ * various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiLeaveSleepStatePrep (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiLeaveSleepStatePrep);
+
+
+ Status = AcpiHwSleepDispatch (SleepState, ACPI_WAKE_PREP_FUNCTION_ID);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiLeaveSleepStatePrep)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiLeaveSleepState
+ *
+ * PARAMETERS: SleepState - Which sleep state we are exiting
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiLeaveSleepState (
+ UINT8 SleepState)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiLeaveSleepState);
+
+
+ Status = AcpiHwSleepDispatch (SleepState, ACPI_WAKE_FUNCTION_ID);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiLeaveSleepState)
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsaccess.c b/usr/src/uts/intel/io/acpica/namespace/nsaccess.c
index 1bb2b4b6e6..eb6fed6230 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsaccess.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsaccess.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSACCESS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -113,20 +111,20 @@ AcpiNsRootInitialize (
{
/* _OSI is optional for now, will be permanent later */
- if (!ACPI_STRCMP (InitVal->Name, "_OSI") && !AcpiGbl_CreateOsiMethod)
+ if (!strcmp (InitVal->Name, "_OSI") && !AcpiGbl_CreateOsiMethod)
{
continue;
}
- Status = AcpiNsLookup (NULL, InitVal->Name, InitVal->Type,
- ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH,
- NULL, &NewNode);
-
- if (ACPI_FAILURE (Status) || (!NewNode)) /* Must be on same line for code converter */
+ Status = AcpiNsLookup (NULL, ACPI_CAST_PTR (char, InitVal->Name),
+ InitVal->Type, ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH,
+ NULL, &NewNode);
+ if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
"Could not create predefined name %s",
InitVal->Name));
+ continue;
}
/*
@@ -167,6 +165,7 @@ AcpiNsRootInitialize (
switch (InitVal->Type)
{
case ACPI_TYPE_METHOD:
+
ObjDesc->Method.ParamCount = (UINT8) ACPI_TO_INTEGER (Val);
ObjDesc->Common.Flags |= AOPOBJ_DATA_VALID;
@@ -188,17 +187,15 @@ AcpiNsRootInitialize (
ObjDesc->Integer.Value = ACPI_TO_INTEGER (Val);
break;
-
case ACPI_TYPE_STRING:
/* Build an object around the static string */
- ObjDesc->String.Length = (UINT32) ACPI_STRLEN (Val);
+ ObjDesc->String.Length = (UINT32) strlen (Val);
ObjDesc->String.Pointer = Val;
ObjDesc->Common.Flags |= AOPOBJ_STATIC_POINTER;
break;
-
case ACPI_TYPE_MUTEX:
ObjDesc->Mutex.Node = NewNode;
@@ -215,14 +212,14 @@ AcpiNsRootInitialize (
/* Special case for ACPI Global Lock */
- if (ACPI_STRCMP (InitVal->Name, "_GL_") == 0)
+ if (strcmp (InitVal->Name, "_GL_") == 0)
{
AcpiGbl_GlobalLockMutex = ObjDesc;
/* Create additional counting semaphore for global lock */
Status = AcpiOsCreateSemaphore (
- 1, 0, &AcpiGbl_GlobalLockSemaphore);
+ 1, 0, &AcpiGbl_GlobalLockSemaphore);
if (ACPI_FAILURE (Status))
{
AcpiUtRemoveReference (ObjDesc);
@@ -231,7 +228,6 @@ AcpiNsRootInitialize (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Unsupported initial type value 0x%X",
@@ -244,7 +240,7 @@ AcpiNsRootInitialize (
/* Store pointer to value descriptor in the Node */
Status = AcpiNsAttachObject (NewNode, ObjDesc,
- ObjDesc->Common.Type);
+ ObjDesc->Common.Type);
/* Remove local reference to the object */
@@ -261,7 +257,7 @@ UnlockAndExit:
if (ACPI_SUCCESS (Status))
{
Status = AcpiNsGetNode (NULL, "\\_GPE", ACPI_NS_NO_UPSEARCH,
- &AcpiGbl_FadtGpeDevice);
+ &AcpiGbl_FadtGpeDevice);
}
return_ACPI_STATUS (Status);
@@ -323,7 +319,9 @@ AcpiNsLookup (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- LocalFlags = Flags & ~(ACPI_NS_ERROR_IF_FOUND | ACPI_NS_SEARCH_PARENT);
+ LocalFlags = Flags &
+ ~(ACPI_NS_ERROR_IF_FOUND | ACPI_NS_OVERRIDE_IF_FOUND |
+ ACPI_NS_SEARCH_PARENT);
*ReturnNode = ACPI_ENTRY_NOT_FOUND;
AcpiGbl_NsLookupCount++;
@@ -450,8 +448,8 @@ AcpiNsLookup (
/* Current scope has no parent scope */
ACPI_ERROR ((AE_INFO,
- "ACPI path has too many parent prefixes (^) "
- "- reached beyond root node"));
+ "%s: Path has too many parent prefixes (^) "
+ "- reached beyond root node", Pathname));
return_ACPI_STATUS (AE_NOT_FOUND);
}
}
@@ -575,6 +573,13 @@ AcpiNsLookup (
{
LocalFlags |= ACPI_NS_ERROR_IF_FOUND;
}
+
+ /* Set override flag according to caller */
+
+ if (Flags & ACPI_NS_OVERRIDE_IF_FOUND)
+ {
+ LocalFlags |= ACPI_NS_OVERRIDE_IF_FOUND;
+ }
}
/* Extract one ACPI name from the front of the pathname */
@@ -584,7 +589,7 @@ AcpiNsLookup (
/* Try to find the single (4 character) ACPI name */
Status = AcpiNsSearchAndEnter (SimpleName, WalkState, CurrentNode,
- InterpreterMode, ThisSearchType, LocalFlags, &ThisNode);
+ InterpreterMode, ThisSearchType, LocalFlags, &ThisNode);
if (ACPI_FAILURE (Status))
{
if (Status == AE_NOT_FOUND)
@@ -697,4 +702,3 @@ AcpiNsLookup (
*ReturnNode = ThisNode;
return_ACPI_STATUS (AE_OK);
}
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsalloc.c b/usr/src/uts/intel/io/acpica/namespace/nsalloc.c
index ac12ba4faf..72a974f5b4 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsalloc.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsalloc.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSALLOC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -88,7 +85,7 @@ AcpiNsCreateNode (
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
Temp = AcpiGbl_NsNodeList->TotalAllocated -
- AcpiGbl_NsNodeList->TotalFreed;
+ AcpiGbl_NsNodeList->TotalFreed;
if (Temp > AcpiGbl_NsNodeList->MaxOccupied)
{
AcpiGbl_NsNodeList->MaxOccupied = Temp;
@@ -121,6 +118,7 @@ AcpiNsDeleteNode (
ACPI_NAMESPACE_NODE *Node)
{
ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *NextDesc;
ACPI_FUNCTION_NAME (NsDeleteNode);
@@ -131,12 +129,13 @@ AcpiNsDeleteNode (
AcpiNsDetachObject (Node);
/*
- * Delete an attached data object if present (an object that was created
- * and attached via AcpiAttachData). Note: After any normal object is
- * detached above, the only possible remaining object is a data object.
+ * Delete an attached data object list if present (objects that were
+ * attached via AcpiAttachData). Note: After any normal object is
+ * detached above, the only possible remaining object(s) are data
+ * objects, in a linked list.
*/
ObjDesc = Node->Object;
- if (ObjDesc &&
+ while (ObjDesc &&
(ObjDesc->Common.Type == ACPI_TYPE_LOCAL_DATA))
{
/* Invoke the attached data deletion handler if present */
@@ -146,7 +145,16 @@ AcpiNsDeleteNode (
ObjDesc->Data.Handler (Node, ObjDesc->Data.Pointer);
}
+ NextDesc = ObjDesc->Common.NextObject;
AcpiUtRemoveReference (ObjDesc);
+ ObjDesc = NextDesc;
+ }
+
+ /* Special case for the statically allocated root node */
+
+ if (Node == AcpiGbl_RootNode)
+ {
+ return;
}
/* Now we can delete the node */
@@ -269,7 +277,8 @@ AcpiNsInstallNode (
* modified the namespace. This is used for cleanup when the
* method exits.
*/
- WalkState->MethodDesc->Method.InfoFlags |= ACPI_METHOD_MODIFIED_NAMESPACE;
+ WalkState->MethodDesc->Method.InfoFlags |=
+ ACPI_METHOD_MODIFIED_NAMESPACE;
}
}
@@ -376,7 +385,7 @@ AcpiNsDeleteChildren (
*
* RETURN: None.
*
- * DESCRIPTION: Delete a subtree of the namespace. This includes all objects
+ * DESCRIPTION: Delete a subtree of the namespace. This includes all objects
* stored within the subtree.
*
******************************************************************************/
@@ -472,7 +481,7 @@ AcpiNsDeleteNamespaceSubtree (
* RETURN: Status
*
* DESCRIPTION: Delete entries within the namespace that are owned by a
- * specific ID. Used to delete entire ACPI tables. All
+ * specific ID. Used to delete entire ACPI tables. All
* reference counts are updated.
*
* MUTEX: Locks namespace during deletion walk.
@@ -584,5 +593,3 @@ AcpiNsDeleteNamespaceByOwner (
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return_VOID;
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsarguments.c b/usr/src/uts/intel/io/acpica/namespace/nsarguments.c
new file mode 100644
index 0000000000..1483e8121b
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/namespace/nsarguments.c
@@ -0,0 +1,305 @@
+/******************************************************************************
+ *
+ * Module Name: nsarguments - Validation of args for ACPI predefined methods
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+
+
+#define _COMPONENT ACPI_NAMESPACE
+ ACPI_MODULE_NAME ("nsarguments")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckArgumentTypes
+ *
+ * PARAMETERS: Info - Method execution information block
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check the incoming argument count and all argument types
+ * against the argument type list for a predefined name.
+ *
+ ******************************************************************************/
+
+void
+AcpiNsCheckArgumentTypes (
+ ACPI_EVALUATE_INFO *Info)
+{
+ UINT16 ArgTypeList;
+ UINT8 ArgCount;
+ UINT8 ArgType;
+ UINT8 UserArgType;
+ UINT32 i;
+
+
+ /* If not a predefined name, cannot typecheck args */
+
+ if (!Info->Predefined)
+ {
+ return;
+ }
+
+ ArgTypeList = Info->Predefined->Info.ArgumentList;
+ ArgCount = METHOD_GET_ARG_COUNT (ArgTypeList);
+
+ /* Typecheck all arguments */
+
+ for (i = 0; ((i < ArgCount) && (i < Info->ParamCount)); i++)
+ {
+ ArgType = METHOD_GET_NEXT_TYPE (ArgTypeList);
+ UserArgType = Info->Parameters[i]->Common.Type;
+
+ if (UserArgType != ArgType)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, ACPI_WARN_ALWAYS,
+ "Argument #%u type mismatch - "
+ "Found [%s], ACPI requires [%s]", (i + 1),
+ AcpiUtGetTypeName (UserArgType),
+ AcpiUtGetTypeName (ArgType)));
+ }
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckAcpiCompliance
+ *
+ * PARAMETERS: Pathname - Full pathname to the node (for error msgs)
+ * Node - Namespace node for the method/object
+ * Predefined - Pointer to entry in predefined name table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check that the declared parameter count (in ASL/AML) for a
+ * predefined name is what is expected (matches what is defined in
+ * the ACPI specification for this predefined name.)
+ *
+ ******************************************************************************/
+
+void
+AcpiNsCheckAcpiCompliance (
+ char *Pathname,
+ ACPI_NAMESPACE_NODE *Node,
+ const ACPI_PREDEFINED_INFO *Predefined)
+{
+ UINT32 AmlParamCount;
+ UINT32 RequiredParamCount;
+
+
+ if (!Predefined)
+ {
+ return;
+ }
+
+ /* Get the ACPI-required arg count from the predefined info table */
+
+ RequiredParamCount =
+ METHOD_GET_ARG_COUNT (Predefined->Info.ArgumentList);
+
+ /*
+ * If this object is not a control method, we can check if the ACPI
+ * spec requires that it be a method.
+ */
+ if (Node->Type != ACPI_TYPE_METHOD)
+ {
+ if (RequiredParamCount > 0)
+ {
+ /* Object requires args, must be implemented as a method */
+
+ ACPI_BIOS_ERROR_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Object (%s) must be a control method with %u arguments",
+ AcpiUtGetTypeName (Node->Type), RequiredParamCount));
+ }
+ else if (!RequiredParamCount && !Predefined->Info.ExpectedBtypes)
+ {
+ /* Object requires no args and no return value, must be a method */
+
+ ACPI_BIOS_ERROR_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Object (%s) must be a control method "
+ "with no arguments and no return value",
+ AcpiUtGetTypeName (Node->Type)));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a control method.
+ * Check that the ASL/AML-defined parameter count for this method
+ * matches the ACPI-required parameter count
+ *
+ * Some methods are allowed to have a "minimum" number of args (_SCP)
+ * because their definition in ACPI has changed over time.
+ *
+ * Note: These are BIOS errors in the declaration of the object
+ */
+ AmlParamCount = Node->Object->Method.ParamCount;
+
+ if (AmlParamCount < RequiredParamCount)
+ {
+ ACPI_BIOS_ERROR_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "ASL declared %u, ACPI requires %u",
+ AmlParamCount, RequiredParamCount));
+ }
+ else if ((AmlParamCount > RequiredParamCount) &&
+ !(Predefined->Info.ArgumentList & ARG_COUNT_IS_MINIMUM))
+ {
+ ACPI_BIOS_ERROR_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "ASL declared %u, ACPI requires %u",
+ AmlParamCount, RequiredParamCount));
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckArgumentCount
+ *
+ * PARAMETERS: Pathname - Full pathname to the node (for error msgs)
+ * Node - Namespace node for the method/object
+ * UserParamCount - Number of args passed in by the caller
+ * Predefined - Pointer to entry in predefined name table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Check that incoming argument count matches the declared
+ * parameter count (in the ASL/AML) for an object.
+ *
+ ******************************************************************************/
+
+void
+AcpiNsCheckArgumentCount (
+ char *Pathname,
+ ACPI_NAMESPACE_NODE *Node,
+ UINT32 UserParamCount,
+ const ACPI_PREDEFINED_INFO *Predefined)
+{
+ UINT32 AmlParamCount;
+ UINT32 RequiredParamCount;
+
+
+ if (!Predefined)
+ {
+ /*
+ * Not a predefined name. Check the incoming user argument count
+ * against the count that is specified in the method/object.
+ */
+ if (Node->Type != ACPI_TYPE_METHOD)
+ {
+ if (UserParamCount)
+ {
+ ACPI_INFO_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "%u arguments were passed to a non-method ACPI object (%s)",
+ UserParamCount, AcpiUtGetTypeName (Node->Type)));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a control method. Check the parameter count.
+ * We can only check the incoming argument count against the
+ * argument count declared for the method in the ASL/AML.
+ *
+ * Emit a message if too few or too many arguments have been passed
+ * by the caller.
+ *
+ * Note: Too many arguments will not cause the method to
+ * fail. However, the method will fail if there are too few
+ * arguments and the method attempts to use one of the missing ones.
+ */
+ AmlParamCount = Node->Object->Method.ParamCount;
+
+ if (UserParamCount < AmlParamCount)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "Caller passed %u, method requires %u",
+ UserParamCount, AmlParamCount));
+ }
+ else if (UserParamCount > AmlParamCount)
+ {
+ ACPI_INFO_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "Caller passed %u, method requires %u",
+ UserParamCount, AmlParamCount));
+ }
+
+ return;
+ }
+
+ /*
+ * This is a predefined name. Validate the user-supplied parameter
+ * count against the ACPI specification. We don't validate against
+ * the method itself because what is important here is that the
+ * caller is in conformance with the spec. (The arg count for the
+ * method was checked against the ACPI spec earlier.)
+ *
+ * Some methods are allowed to have a "minimum" number of args (_SCP)
+ * because their definition in ACPI has changed over time.
+ */
+ RequiredParamCount =
+ METHOD_GET_ARG_COUNT (Predefined->Info.ArgumentList);
+
+ if (UserParamCount < RequiredParamCount)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Insufficient arguments - "
+ "Caller passed %u, ACPI requires %u",
+ UserParamCount, RequiredParamCount));
+ }
+ else if ((UserParamCount > RequiredParamCount) &&
+ !(Predefined->Info.ArgumentList & ARG_COUNT_IS_MINIMUM))
+ {
+ ACPI_INFO_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments - "
+ "Caller passed %u, ACPI requires %u",
+ UserParamCount, RequiredParamCount));
+ }
+}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsconvert.c b/usr/src/uts/intel/io/acpica/namespace/nsconvert.c
new file mode 100644
index 0000000000..2fa182eddc
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/namespace/nsconvert.c
@@ -0,0 +1,566 @@
+/******************************************************************************
+ *
+ * Module Name: nsconvert - Object conversions for objects returned by
+ * predefined methods
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+#include "acpredef.h"
+#include "amlresrc.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ ACPI_MODULE_NAME ("nsconvert")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToInteger
+ *
+ * PARAMETERS: OriginalObject - Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToInteger (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject;
+ ACPI_STATUS Status;
+ UINT64 Value = 0;
+ UINT32 i;
+
+
+ switch (OriginalObject->Common.Type)
+ {
+ case ACPI_TYPE_STRING:
+
+ /* String-to-Integer conversion */
+
+ Status = AcpiUtStrtoul64 (OriginalObject->String.Pointer,
+ ACPI_ANY_BASE, AcpiGbl_IntegerByteWidth, &Value);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */
+
+ if (OriginalObject->Buffer.Length > 8)
+ {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Extract each buffer byte to create the integer */
+
+ for (i = 0; i < OriginalObject->Buffer.Length; i++)
+ {
+ Value |= ((UINT64)
+ OriginalObject->Buffer.Pointer[i] << (i * 8));
+ }
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ NewObject = AcpiUtCreateIntegerObject (Value);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToString
+ *
+ * PARAMETERS: OriginalObject - Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToString (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject;
+ ACPI_SIZE Length;
+ ACPI_STATUS Status;
+
+
+ switch (OriginalObject->Common.Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-String conversion. Commonly, convert
+ * an integer of value 0 to a NULL string. The last element of
+ * _BIF and _BIX packages occasionally need this fix.
+ */
+ if (OriginalObject->Integer.Value == 0)
+ {
+ /* Allocate a new NULL string object */
+
+ NewObject = AcpiUtCreateStringObject (0);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+ }
+ else
+ {
+ Status = AcpiExConvertToString (OriginalObject,
+ &NewObject, ACPI_IMPLICIT_CONVERT_HEX);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+ /*
+ * Buffer-to-String conversion. Use a ToString
+ * conversion, no transform performed on the buffer data. The best
+ * example of this is the _BIF method, where the string data from
+ * the battery is often (incorrectly) returned as buffer object(s).
+ */
+ Length = 0;
+ while ((Length < OriginalObject->Buffer.Length) &&
+ (OriginalObject->Buffer.Pointer[Length]))
+ {
+ Length++;
+ }
+
+ /* Allocate a new string object */
+
+ NewObject = AcpiUtCreateStringObject (Length);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ /*
+ * Copy the raw buffer data with no transform. String is already NULL
+ * terminated at Length+1.
+ */
+ memcpy (NewObject->String.Pointer,
+ OriginalObject->Buffer.Pointer, Length);
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToBuffer
+ *
+ * PARAMETERS: OriginalObject - Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToBuffer (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject;
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT **Elements;
+ UINT32 *DwordBuffer;
+ UINT32 Count;
+ UINT32 i;
+
+
+ switch (OriginalObject->Common.Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-Buffer conversion.
+ * Convert the Integer to a packed-byte buffer. _MAT and other
+ * objects need this sometimes, if a read has been performed on a
+ * Field object that is less than or equal to the global integer
+ * size (32 or 64 bits).
+ */
+ Status = AcpiExConvertToBuffer (OriginalObject, &NewObject);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ /* String-to-Buffer conversion. Simple data copy */
+
+ NewObject = AcpiUtCreateBufferObject
+ (OriginalObject->String.Length);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ memcpy (NewObject->Buffer.Pointer,
+ OriginalObject->String.Pointer, OriginalObject->String.Length);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * This case is often seen for predefined names that must return a
+ * Buffer object with multiple DWORD integers within. For example,
+ * _FDE and _GTM. The Package can be converted to a Buffer.
+ */
+
+ /* All elements of the Package must be integers */
+
+ Elements = OriginalObject->Package.Elements;
+ Count = OriginalObject->Package.Count;
+
+ for (i = 0; i < Count; i++)
+ {
+ if ((!*Elements) ||
+ ((*Elements)->Common.Type != ACPI_TYPE_INTEGER))
+ {
+ return (AE_AML_OPERAND_TYPE);
+ }
+ Elements++;
+ }
+
+ /* Create the new buffer object to replace the Package */
+
+ NewObject = AcpiUtCreateBufferObject (ACPI_MUL_4 (Count));
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Copy the package elements (integers) to the buffer as DWORDs */
+
+ Elements = OriginalObject->Package.Elements;
+ DwordBuffer = ACPI_CAST_PTR (UINT32, NewObject->Buffer.Pointer);
+
+ for (i = 0; i < Count; i++)
+ {
+ *DwordBuffer = (UINT32) (*Elements)->Integer.Value;
+ DwordBuffer++;
+ Elements++;
+ }
+ break;
+
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToUnicode
+ *
+ * PARAMETERS: Scope - Namespace node for the method/object
+ * OriginalObject - ASCII String Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String object to a Unicode string Buffer.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToUnicode (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject;
+ char *AsciiString;
+ UINT16 *UnicodeBuffer;
+ UINT32 UnicodeLength;
+ UINT32 i;
+
+
+ if (!OriginalObject)
+ {
+ return (AE_OK);
+ }
+
+ /* If a Buffer was returned, it must be at least two bytes long */
+
+ if (OriginalObject->Common.Type == ACPI_TYPE_BUFFER)
+ {
+ if (OriginalObject->Buffer.Length < 2)
+ {
+ return (AE_AML_OPERAND_VALUE);
+ }
+
+ *ReturnObject = NULL;
+ return (AE_OK);
+ }
+
+ /*
+ * The original object is an ASCII string. Convert this string to
+ * a unicode buffer.
+ */
+ AsciiString = OriginalObject->String.Pointer;
+ UnicodeLength = (OriginalObject->String.Length * 2) + 2;
+
+ /* Create a new buffer object for the Unicode data */
+
+ NewObject = AcpiUtCreateBufferObject (UnicodeLength);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ UnicodeBuffer = ACPI_CAST_PTR (UINT16, NewObject->Buffer.Pointer);
+
+ /* Convert ASCII to Unicode */
+
+ for (i = 0; i < OriginalObject->String.Length; i++)
+ {
+ UnicodeBuffer[i] = (UINT16) AsciiString[i];
+ }
+
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToResource
+ *
+ * PARAMETERS: Scope - Namespace node for the method/object
+ * OriginalObject - Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful
+ *
+ * DESCRIPTION: Attempt to convert a Integer object to a ResourceTemplate
+ * Buffer.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToResource (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject;
+ UINT8 *Buffer;
+
+
+ /*
+ * We can fix the following cases for an expected resource template:
+ * 1. No return value (interpreter slack mode is disabled)
+ * 2. A "Return (Zero)" statement
+ * 3. A "Return empty buffer" statement
+ *
+ * We will return a buffer containing a single EndTag
+ * resource descriptor.
+ */
+ if (OriginalObject)
+ {
+ switch (OriginalObject->Common.Type)
+ {
+ case ACPI_TYPE_INTEGER:
+
+ /* We can only repair an Integer==0 */
+
+ if (OriginalObject->Integer.Value)
+ {
+ return (AE_AML_OPERAND_TYPE);
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ if (OriginalObject->Buffer.Length)
+ {
+ /* Additional checks can be added in the future */
+
+ *ReturnObject = NULL;
+ return (AE_OK);
+ }
+ break;
+
+ case ACPI_TYPE_STRING:
+ default:
+
+ return (AE_AML_OPERAND_TYPE);
+ }
+ }
+
+ /* Create the new buffer object for the resource descriptor */
+
+ NewObject = AcpiUtCreateBufferObject (2);
+ if (!NewObject)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ Buffer = ACPI_CAST_PTR (UINT8, NewObject->Buffer.Pointer);
+
+ /* Initialize the Buffer with a single EndTag descriptor */
+
+ Buffer[0] = (ACPI_RESOURCE_NAME_END_TAG | ASL_RDESC_END_TAG_SIZE);
+ Buffer[1] = 0x00;
+
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsConvertToReference
+ *
+ * PARAMETERS: Scope - Namespace node for the method/object
+ * OriginalObject - Object to be converted
+ * ReturnObject - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful
+ *
+ * DESCRIPTION: Attempt to convert a Integer object to a ObjectReference.
+ * Buffer.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsConvertToReference (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject)
+{
+ ACPI_OPERAND_OBJECT *NewObject = NULL;
+ ACPI_STATUS Status;
+ ACPI_NAMESPACE_NODE *Node;
+ ACPI_GENERIC_STATE ScopeInfo;
+ char *Name;
+
+
+ ACPI_FUNCTION_NAME (NsConvertToReference);
+
+
+ /* Convert path into internal presentation */
+
+ Status = AcpiNsInternalizeName (OriginalObject->String.Pointer, &Name);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Find the namespace node */
+
+ ScopeInfo.Scope.Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Scope);
+ Status = AcpiNsLookup (&ScopeInfo, Name,
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, &Node);
+ if (ACPI_FAILURE (Status))
+ {
+ /* Check if we are resolving a named reference within a package */
+
+ ACPI_ERROR_NAMESPACE (OriginalObject->String.Pointer, Status);
+ goto ErrorExit;
+ }
+
+ /* Create and init a new internal ACPI object */
+
+ NewObject = AcpiUtCreateInternalObject (ACPI_TYPE_LOCAL_REFERENCE);
+ if (!NewObject)
+ {
+ Status = AE_NO_MEMORY;
+ goto ErrorExit;
+ }
+ NewObject->Reference.Node = Node;
+ NewObject->Reference.Object = Node->Object;
+ NewObject->Reference.Class = ACPI_REFCLASS_NAME;
+
+ /*
+ * Increase reference of the object if needed (the object is likely a
+ * null for device nodes).
+ */
+ AcpiUtAddReference (Node->Object);
+
+ErrorExit:
+ ACPI_FREE (Name);
+ *ReturnObject = NewObject;
+ return (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsdump.c b/usr/src/uts/intel/io/acpica/namespace/nsdump.c
index 58137abbc5..32ada86001 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsdump.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsdump.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,11 +41,10 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSDUMP_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
+#include "acoutput.h"
#define _COMPONENT ACPI_NAMESPACE
@@ -68,6 +67,22 @@ AcpiNsDumpOneDevice (
#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+static ACPI_STATUS
+AcpiNsDumpOneObjectPath (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue);
+
+static ACPI_STATUS
+AcpiNsGetMaxDepth (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue);
+
+
/*******************************************************************************
*
* FUNCTION: AcpiNsPrintPathname
@@ -84,7 +99,7 @@ AcpiNsDumpOneDevice (
void
AcpiNsPrintPathname (
UINT32 NumSegments,
- char *Pathname)
+ const char *Pathname)
{
UINT32 i;
@@ -92,7 +107,9 @@ AcpiNsPrintPathname (
ACPI_FUNCTION_NAME (NsPrintPathname);
- if (!(AcpiDbgLevel & ACPI_LV_NAMES) || !(AcpiDbgLayer & ACPI_NAMESPACE))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_NAMES, ACPI_NAMESPACE))
{
return;
}
@@ -105,7 +122,7 @@ AcpiNsPrintPathname (
{
for (i = 0; i < 4; i++)
{
- ACPI_IS_PRINT (Pathname[i]) ?
+ isprint ((int) Pathname[i]) ?
AcpiOsPrintf ("%c", Pathname[i]) :
AcpiOsPrintf ("?");
}
@@ -122,6 +139,9 @@ AcpiNsPrintPathname (
}
+#ifdef ACPI_OBSOLETE_FUNCTIONS
+/* Not used at this time, perhaps later */
+
/*******************************************************************************
*
* FUNCTION: AcpiNsDumpPathname
@@ -141,7 +161,7 @@ AcpiNsPrintPathname (
void
AcpiNsDumpPathname (
ACPI_HANDLE Handle,
- char *Msg,
+ const char *Msg,
UINT32 Level,
UINT32 Component)
{
@@ -151,7 +171,7 @@ AcpiNsDumpPathname (
/* Do this only if the requested debug level and component are enabled */
- if (!(AcpiDbgLevel & Level) || !(AcpiDbgLayer & Component))
+ if (!ACPI_IS_DEBUG_ENABLED (Level, Component))
{
return_VOID;
}
@@ -162,7 +182,7 @@ AcpiNsDumpPathname (
AcpiOsPrintf ("\n");
return_VOID;
}
-
+#endif
/*******************************************************************************
*
@@ -241,7 +261,8 @@ AcpiNsDumpOneObject (
if (Type > ACPI_TYPE_LOCAL_MAX)
{
- ACPI_WARNING ((AE_INFO, "Invalid ACPI Object Type 0x%08X", Type));
+ ACPI_WARNING ((AE_INFO,
+ "Invalid ACPI Object Type 0x%08X", Type));
}
AcpiOsPrintf ("%4.4s", AcpiUtGetNodeName (ThisNode));
@@ -250,7 +271,7 @@ AcpiNsDumpOneObject (
/* Now we can print out the pertinent information */
AcpiOsPrintf (" %-12s %p %2.2X ",
- AcpiUtGetTypeName (Type), ThisNode, ThisNode->OwnerId);
+ AcpiUtGetTypeName (Type), ThisNode, ThisNode->OwnerId);
DbgLevel = AcpiDbgLevel;
AcpiDbgLevel = 0;
@@ -270,7 +291,23 @@ AcpiNsDumpOneObject (
if (!ObjDesc)
{
- /* No attached object, we are done */
+ /* No attached object. Some types should always have an object */
+
+ switch (Type)
+ {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_METHOD:
+
+ AcpiOsPrintf ("<No attached object>");
+ break;
+
+ default:
+
+ break;
+ }
AcpiOsPrintf ("\n");
return (AE_OK);
@@ -280,18 +317,16 @@ AcpiNsDumpOneObject (
{
case ACPI_TYPE_PROCESSOR:
- AcpiOsPrintf ("ID %X Len %.4X Addr %p\n",
+ AcpiOsPrintf ("ID %02X Len %02X Addr %8.8X%8.8X\n",
ObjDesc->Processor.ProcId, ObjDesc->Processor.Length,
- ACPI_CAST_PTR (void, ObjDesc->Processor.Address));
+ ACPI_FORMAT_UINT64 (ObjDesc->Processor.Address));
break;
-
case ACPI_TYPE_DEVICE:
AcpiOsPrintf ("Notify Object: %p\n", ObjDesc);
break;
-
case ACPI_TYPE_METHOD:
AcpiOsPrintf ("Args %X Len %.4X Aml %p\n",
@@ -299,14 +334,12 @@ AcpiNsDumpOneObject (
ObjDesc->Method.AmlLength, ObjDesc->Method.AmlStart);
break;
-
case ACPI_TYPE_INTEGER:
AcpiOsPrintf ("= %8.8X%8.8X\n",
ACPI_FORMAT_UINT64 (ObjDesc->Integer.Value));
break;
-
case ACPI_TYPE_PACKAGE:
if (ObjDesc->Common.Flags & AOPOBJ_DATA_VALID)
@@ -320,13 +353,12 @@ AcpiNsDumpOneObject (
}
break;
-
case ACPI_TYPE_BUFFER:
if (ObjDesc->Common.Flags & AOPOBJ_DATA_VALID)
{
AcpiOsPrintf ("Len %.2X",
- ObjDesc->Buffer.Length);
+ ObjDesc->Buffer.Length);
/* Dump some of the buffer */
@@ -346,15 +378,13 @@ AcpiNsDumpOneObject (
}
break;
-
case ACPI_TYPE_STRING:
AcpiOsPrintf ("Len %.2X ", ObjDesc->String.Length);
- AcpiUtPrintString (ObjDesc->String.Pointer, 32);
+ AcpiUtPrintString (ObjDesc->String.Pointer, 80);
AcpiOsPrintf ("\n");
break;
-
case ACPI_TYPE_REGION:
AcpiOsPrintf ("[%s]",
@@ -362,7 +392,7 @@ AcpiNsDumpOneObject (
if (ObjDesc->Region.Flags & AOPOBJ_DATA_VALID)
{
AcpiOsPrintf (" Addr %8.8X%8.8X Len %.4X\n",
- ACPI_FORMAT_NATIVE_UINT (ObjDesc->Region.Address),
+ ACPI_FORMAT_UINT64 (ObjDesc->Region.Address),
ObjDesc->Region.Length);
}
else
@@ -371,13 +401,11 @@ AcpiNsDumpOneObject (
}
break;
-
case ACPI_TYPE_LOCAL_REFERENCE:
AcpiOsPrintf ("[%s]\n", AcpiUtGetReferenceName (ObjDesc));
break;
-
case ACPI_TYPE_BUFFER_FIELD:
if (ObjDesc->BufferField.BufferObj &&
@@ -389,7 +417,6 @@ AcpiNsDumpOneObject (
}
break;
-
case ACPI_TYPE_LOCAL_REGION_FIELD:
AcpiOsPrintf ("Rgn [%4.4s]",
@@ -397,7 +424,6 @@ AcpiNsDumpOneObject (
ObjDesc->CommonField.RegionObj->Region.Node));
break;
-
case ACPI_TYPE_LOCAL_BANK_FIELD:
AcpiOsPrintf ("Rgn [%4.4s] Bnk [%4.4s]",
@@ -407,7 +433,6 @@ AcpiNsDumpOneObject (
ObjDesc->BankField.BankObj->CommonField.Node));
break;
-
case ACPI_TYPE_LOCAL_INDEX_FIELD:
AcpiOsPrintf ("Idx [%4.4s] Dat [%4.4s]",
@@ -417,7 +442,6 @@ AcpiNsDumpOneObject (
ObjDesc->IndexField.DataObj->CommonField.Node));
break;
-
case ACPI_TYPE_LOCAL_ALIAS:
case ACPI_TYPE_LOCAL_METHOD_ALIAS:
@@ -448,11 +472,11 @@ AcpiNsDumpOneObject (
break;
default:
+
break;
}
break;
-
case ACPI_DISPLAY_OBJECTS:
AcpiOsPrintf ("O:%p", ObjDesc);
@@ -501,7 +525,6 @@ AcpiNsDumpOneObject (
}
break;
-
default:
AcpiOsPrintf ("\n");
break;
@@ -516,9 +539,9 @@ AcpiNsDumpOneObject (
/* If there is an attached object, display it */
- DbgLevel = AcpiDbgLevel;
+ DbgLevel = AcpiDbgLevel;
AcpiDbgLevel = 0;
- ObjDesc = AcpiNsGetAttachedObject (ThisNode);
+ ObjDesc = AcpiNsGetAttachedObject (ThisNode);
AcpiDbgLevel = DbgLevel;
/* Dump attached objects */
@@ -545,14 +568,18 @@ AcpiNsDumpOneObject (
if (ObjType > ACPI_TYPE_LOCAL_MAX)
{
- AcpiOsPrintf ("(Pointer to ACPI Object type %.2X [UNKNOWN])\n",
+ AcpiOsPrintf (
+ "(Pointer to ACPI Object type %.2X [UNKNOWN])\n",
ObjType);
+
BytesToDump = 32;
}
else
{
- AcpiOsPrintf ("(Pointer to ACPI Object type %.2X [%s])\n",
+ AcpiOsPrintf (
+ "(Pointer to ACPI Object type %.2X [%s])\n",
ObjType, AcpiUtGetTypeName (ObjType));
+
BytesToDump = sizeof (ACPI_OPERAND_OBJECT);
}
@@ -582,36 +609,44 @@ AcpiNsDumpOneObject (
*/
BytesToDump = ObjDesc->String.Length;
ObjDesc = (void *) ObjDesc->String.Pointer;
- AcpiOsPrintf ( "(Buffer/String pointer %p length %X)\n",
+
+ AcpiOsPrintf ("(Buffer/String pointer %p length %X)\n",
ObjDesc, BytesToDump);
ACPI_DUMP_BUFFER (ObjDesc, BytesToDump);
goto Cleanup;
case ACPI_TYPE_BUFFER_FIELD:
+
ObjDesc = (ACPI_OPERAND_OBJECT *) ObjDesc->BufferField.BufferObj;
break;
case ACPI_TYPE_PACKAGE:
+
ObjDesc = (void *) ObjDesc->Package.Elements;
break;
case ACPI_TYPE_METHOD:
+
ObjDesc = (void *) ObjDesc->Method.AmlStart;
break;
case ACPI_TYPE_LOCAL_REGION_FIELD:
+
ObjDesc = (void *) ObjDesc->Field.RegionObj;
break;
case ACPI_TYPE_LOCAL_BANK_FIELD:
+
ObjDesc = (void *) ObjDesc->BankField.RegionObj;
break;
case ACPI_TYPE_LOCAL_INDEX_FIELD:
+
ObjDesc = (void *) ObjDesc->IndexField.IndexObj;
break;
default:
+
goto Cleanup;
}
@@ -676,8 +711,151 @@ AcpiNsDumpObjects (
Info.DisplayType = DisplayType;
(void) AcpiNsWalkNamespace (Type, StartHandle, MaxDepth,
- ACPI_NS_WALK_NO_UNLOCK | ACPI_NS_WALK_TEMP_NODES,
- AcpiNsDumpOneObject, NULL, (void *) &Info, NULL);
+ ACPI_NS_WALK_NO_UNLOCK | ACPI_NS_WALK_TEMP_NODES,
+ AcpiNsDumpOneObject, NULL, (void *) &Info, NULL);
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsDumpOneObjectPath, AcpiNsGetMaxDepth
+ *
+ * PARAMETERS: ObjHandle - Node to be dumped
+ * Level - Nesting level of the handle
+ * Context - Passed into WalkNamespace
+ * ReturnValue - Not used
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dump the full pathname to a namespace object. AcpNsGetMaxDepth
+ * computes the maximum nesting depth in the namespace tree, in
+ * order to simplify formatting in AcpiNsDumpOneObjectPath.
+ * These procedures are UserFunctions called by AcpiNsWalkNamespace.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiNsDumpOneObjectPath (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue)
+{
+ UINT32 MaxLevel = *((UINT32 *) Context);
+ char *Pathname;
+ ACPI_NAMESPACE_NODE *Node;
+ int PathIndent;
+
+
+ if (!ObjHandle)
+ {
+ return (AE_OK);
+ }
+
+ Node = AcpiNsValidateHandle (ObjHandle);
+ if (!Node)
+ {
+ /* Ignore bad node during namespace walk */
+
+ return (AE_OK);
+ }
+
+ Pathname = AcpiNsGetNormalizedPathname (Node, TRUE);
+
+ PathIndent = 1;
+ if (Level <= MaxLevel)
+ {
+ PathIndent = MaxLevel - Level + 1;
+ }
+
+ AcpiOsPrintf ("%2d%*s%-12s%*s",
+ Level, Level, " ", AcpiUtGetTypeName (Node->Type),
+ PathIndent, " ");
+
+ AcpiOsPrintf ("%s\n", &Pathname[1]);
+ ACPI_FREE (Pathname);
+ return (AE_OK);
+}
+
+
+static ACPI_STATUS
+AcpiNsGetMaxDepth (
+ ACPI_HANDLE ObjHandle,
+ UINT32 Level,
+ void *Context,
+ void **ReturnValue)
+{
+ UINT32 *MaxLevel = (UINT32 *) Context;
+
+
+ if (Level > *MaxLevel)
+ {
+ *MaxLevel = Level;
+ }
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsDumpObjectPaths
+ *
+ * PARAMETERS: Type - Object type to be dumped
+ * DisplayType - 0 or ACPI_DISPLAY_SUMMARY
+ * MaxDepth - Maximum depth of dump. Use ACPI_UINT32_MAX
+ * for an effectively unlimited depth.
+ * OwnerId - Dump only objects owned by this ID. Use
+ * ACPI_UINT32_MAX to match all owners.
+ * StartHandle - Where in namespace to start/end search
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump full object pathnames within the loaded namespace. Uses
+ * AcpiNsWalkNamespace in conjunction with AcpiNsDumpOneObjectPath.
+ *
+ ******************************************************************************/
+
+void
+AcpiNsDumpObjectPaths (
+ ACPI_OBJECT_TYPE Type,
+ UINT8 DisplayType,
+ UINT32 MaxDepth,
+ ACPI_OWNER_ID OwnerId,
+ ACPI_HANDLE StartHandle)
+{
+ ACPI_STATUS Status;
+ UINT32 MaxLevel = 0;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /*
+ * Just lock the entire namespace for the duration of the dump.
+ * We don't want any changes to the namespace during this time,
+ * especially the temporary nodes since we are going to display
+ * them also.
+ */
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiOsPrintf ("Could not acquire namespace mutex\n");
+ return;
+ }
+
+ /* Get the max depth of the namespace tree, for formatting later */
+
+ (void) AcpiNsWalkNamespace (Type, StartHandle, MaxDepth,
+ ACPI_NS_WALK_NO_UNLOCK | ACPI_NS_WALK_TEMP_NODES,
+ AcpiNsGetMaxDepth, NULL, (void *) &MaxLevel, NULL);
+
+ /* Now dump the entire namespace */
+
+ (void) AcpiNsWalkNamespace (Type, StartHandle, MaxDepth,
+ ACPI_NS_WALK_NO_UNLOCK | ACPI_NS_WALK_TEMP_NODES,
+ AcpiNsDumpOneObjectPath, NULL, (void *) &MaxLevel, NULL);
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
}
@@ -722,7 +900,7 @@ AcpiNsDumpEntry (
*
* PARAMETERS: SearchBase - Root of subtree to be dumped, or
* NS_ALL to dump the entire namespace
- * MaxDepth - Maximum depth of dump. Use INT_MAX
+ * MaxDepth - Maximum depth of dump. Use INT_MAX
* for an effectively unlimited depth.
*
* RETURN: None
@@ -748,7 +926,8 @@ AcpiNsDumpTables (
* If the name space has not been initialized,
* there is nothing to dump.
*/
- ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, "namespace not initialized!\n"));
+ ACPI_DEBUG_PRINT ((ACPI_DB_TABLES,
+ "namespace not initialized!\n"));
return_VOID;
}
@@ -761,9 +940,8 @@ AcpiNsDumpTables (
}
AcpiNsDumpObjects (ACPI_TYPE_ANY, ACPI_DISPLAY_OBJECTS, MaxDepth,
- ACPI_OWNER_ID_MAX, SearchHandle);
+ ACPI_OWNER_ID_MAX, SearchHandle);
return_VOID;
}
#endif
#endif
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c b/usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c
index 11aab7a3a9..ea143fb5b4 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsdumpdv.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSDUMPDV_C__
-
#include "acpi.h"
@@ -141,7 +139,7 @@ AcpiNsDumpRootDevices (
return;
}
- Status = AcpiGetHandle (NULL, ACPI_NS_SYSTEM_BUS, &SysBusHandle);
+ Status = AcpiGetHandle (NULL, METHOD_NAME__SB_, &SysBusHandle);
if (ACPI_FAILURE (Status))
{
return;
@@ -151,11 +149,9 @@ AcpiNsDumpRootDevices (
"Display of all devices in the namespace:\n"));
Status = AcpiNsWalkNamespace (ACPI_TYPE_DEVICE, SysBusHandle,
- ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
- AcpiNsDumpOneDevice, NULL, NULL, NULL);
+ ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
+ AcpiNsDumpOneDevice, NULL, NULL, NULL);
}
#endif
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nseval.c b/usr/src/uts/intel/io/acpica/namespace/nseval.c
index c376ac7213..63051b8a52 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nseval.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nseval.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSEVAL_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -65,15 +63,14 @@ AcpiNsExecModuleCode (
*
* FUNCTION: AcpiNsEvaluate
*
- * PARAMETERS: Info - Evaluation info block, contains:
+ * PARAMETERS: Info - Evaluation info block, contains these fields
+ * and more:
* PrefixNode - Prefix or Method/Object Node to execute
- * Pathname - Name of method to execute, If NULL, the
+ * RelativePath - Name of method to execute, If NULL, the
* Node is the object to execute
* Parameters - List of parameters to pass to the method,
* terminated by NULL. Params itself may be
* NULL if no parameters are being passed.
- * ReturnObject - Where to put method's return value (if
- * any). If NULL, no value is returned.
* ParameterType - Type of Parameter list
* ReturnObject - Where to put method's return value (if
* any). If NULL, no value is returned.
@@ -93,7 +90,6 @@ AcpiNsEvaluate (
ACPI_EVALUATE_INFO *Info)
{
ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *Node;
ACPI_FUNCTION_TRACE (NsEvaluate);
@@ -104,80 +100,141 @@ AcpiNsEvaluate (
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- /* Initialize the return value to an invalid object */
+ if (!Info->Node)
+ {
+ /*
+ * Get the actual namespace node for the target object if we
+ * need to. Handles these cases:
+ *
+ * 1) Null node, valid pathname from root (absolute path)
+ * 2) Node and valid pathname (path relative to Node)
+ * 3) Node, Null pathname
+ */
+ Status = AcpiNsGetNode (Info->PrefixNode, Info->RelativePathname,
+ ACPI_NS_NO_UPSEARCH, &Info->Node);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ /*
+ * For a method alias, we must grab the actual method node so that
+ * proper scoping context will be established before execution.
+ */
+ if (AcpiNsGetType (Info->Node) == ACPI_TYPE_LOCAL_METHOD_ALIAS)
+ {
+ Info->Node = ACPI_CAST_PTR (
+ ACPI_NAMESPACE_NODE, Info->Node->Object);
+ }
+
+ /* Complete the info block initialization */
Info->ReturnObject = NULL;
+ Info->NodeFlags = Info->Node->Flags;
+ Info->ObjDesc = AcpiNsGetAttachedObject (Info->Node);
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n",
+ Info->RelativePathname, Info->Node,
+ AcpiNsGetAttachedObject (Info->Node)));
+
+ /* Get info if we have a predefined name (_HID, etc.) */
+
+ Info->Predefined = AcpiUtMatchPredefinedMethod (Info->Node->Name.Ascii);
+
+ /* Get the full pathname to the object, for use in warning messages */
+
+ Info->FullPathname = AcpiNsGetNormalizedPathname (Info->Node, TRUE);
+ if (!Info->FullPathname)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ /* Count the number of arguments being passed in */
+
Info->ParamCount = 0;
+ if (Info->Parameters)
+ {
+ while (Info->Parameters[Info->ParamCount])
+ {
+ Info->ParamCount++;
+ }
+
+ /* Warn on impossible argument count */
+
+ if (Info->ParamCount > ACPI_METHOD_NUM_ARGS)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, ACPI_WARN_ALWAYS,
+ "Excess arguments (%u) - using only %u",
+ Info->ParamCount, ACPI_METHOD_NUM_ARGS));
+
+ Info->ParamCount = ACPI_METHOD_NUM_ARGS;
+ }
+ }
/*
- * Get the actual namespace node for the target object. Handles these cases:
- *
- * 1) Null node, Pathname (absolute path)
- * 2) Node, Pathname (path relative to Node)
- * 3) Node, Null Pathname
+ * For predefined names: Check that the declared argument count
+ * matches the ACPI spec -- otherwise this is a BIOS error.
*/
- Status = AcpiNsGetNode (Info->PrefixNode, Info->Pathname,
- ACPI_NS_NO_UPSEARCH, &Info->ResolvedNode);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
+ AcpiNsCheckAcpiCompliance (Info->FullPathname, Info->Node,
+ Info->Predefined);
/*
- * For a method alias, we must grab the actual method node so that proper
- * scoping context will be established before execution.
+ * For all names: Check that the incoming argument count for
+ * this method/object matches the actual ASL/AML definition.
*/
- if (AcpiNsGetType (Info->ResolvedNode) == ACPI_TYPE_LOCAL_METHOD_ALIAS)
- {
- Info->ResolvedNode =
- ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Info->ResolvedNode->Object);
- }
+ AcpiNsCheckArgumentCount (Info->FullPathname, Info->Node,
+ Info->ParamCount, Info->Predefined);
- ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n", Info->Pathname,
- Info->ResolvedNode, AcpiNsGetAttachedObject (Info->ResolvedNode)));
+ /* For predefined names: Typecheck all incoming arguments */
- Node = Info->ResolvedNode;
+ AcpiNsCheckArgumentTypes (Info);
/*
- * Two major cases here:
+ * Three major evaluation cases:
*
- * 1) The object is a control method -- execute it
- * 2) The object is not a method -- just return it's current value
+ * 1) Object types that cannot be evaluated by definition
+ * 2) The object is a control method -- execute it
+ * 3) The object is not a method -- just return it's current value
*/
- if (AcpiNsGetType (Info->ResolvedNode) == ACPI_TYPE_METHOD)
+ switch (AcpiNsGetType (Info->Node))
{
+ case ACPI_TYPE_DEVICE:
+ case ACPI_TYPE_EVENT:
+ case ACPI_TYPE_MUTEX:
+ case ACPI_TYPE_REGION:
+ case ACPI_TYPE_THERMAL:
+ case ACPI_TYPE_LOCAL_SCOPE:
/*
- * 1) Object is a control method - execute it
+ * 1) Disallow evaluation of certain object types. For these,
+ * object evaluation is undefined and not supported.
*/
+ ACPI_ERROR ((AE_INFO,
+ "%s: Evaluation of object type [%s] is not supported",
+ Info->FullPathname,
+ AcpiUtGetTypeName (Info->Node->Type)));
- /* Verify that there is a method object associated with this node */
+ Status = AE_TYPE;
+ goto Cleanup;
- Info->ObjDesc = AcpiNsGetAttachedObject (Info->ResolvedNode);
- if (!Info->ObjDesc)
- {
- ACPI_ERROR ((AE_INFO, "Control method has no attached sub-object"));
- return_ACPI_STATUS (AE_NULL_OBJECT);
- }
+ case ACPI_TYPE_METHOD:
+ /*
+ * 2) Object is a control method - execute it
+ */
- /* Count the number of arguments being passed to the method */
+ /* Verify that there is a method object associated with this node */
- if (Info->Parameters)
+ if (!Info->ObjDesc)
{
- while (Info->Parameters[Info->ParamCount])
- {
- if (Info->ParamCount > ACPI_METHOD_MAX_ARG)
- {
- return_ACPI_STATUS (AE_LIMIT);
- }
- Info->ParamCount++;
- }
+ ACPI_ERROR ((AE_INFO, "%s: Method has no attached sub-object",
+ Info->FullPathname));
+ Status = AE_NULL_OBJECT;
+ goto Cleanup;
}
- ACPI_DUMP_PATHNAME (Info->ResolvedNode, "ACPI: Execute Method",
- ACPI_LV_INFO, _COMPONENT);
-
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "Method at AML address %p Length %X\n",
+ "**** Execute method [%s] at AML address %p length %X\n",
+ Info->FullPathname,
Info->ObjDesc->Method.AmlStart + 1,
Info->ObjDesc->Method.AmlLength - 1));
@@ -192,80 +249,59 @@ AcpiNsEvaluate (
AcpiExEnterInterpreter ();
Status = AcpiPsExecuteMethod (Info);
AcpiExExitInterpreter ();
- }
- else
- {
+ break;
+
+ default:
/*
- * 2) Object is not a method, return its current value
- *
- * Disallow certain object types. For these, "evaluation" is undefined.
+ * 3) All other non-method objects -- get the current object value
*/
- switch (Info->ResolvedNode->Type)
- {
- case ACPI_TYPE_DEVICE:
- case ACPI_TYPE_EVENT:
- case ACPI_TYPE_MUTEX:
- case ACPI_TYPE_REGION:
- case ACPI_TYPE_THERMAL:
- case ACPI_TYPE_LOCAL_SCOPE:
-
- ACPI_ERROR ((AE_INFO,
- "[%4.4s] Evaluation of object type [%s] is not supported",
- Info->ResolvedNode->Name.Ascii,
- AcpiUtGetTypeName (Info->ResolvedNode->Type)));
-
- return_ACPI_STATUS (AE_TYPE);
-
- default:
- break;
- }
/*
- * Objects require additional resolution steps (e.g., the Node may be
- * a field that must be read, etc.) -- we can't just grab the object
- * out of the node.
+ * Some objects require additional resolution steps (e.g., the Node
+ * may be a field that must be read, etc.) -- we can't just grab
+ * the object out of the node.
*
* Use ResolveNodeToValue() to get the associated value.
*
* NOTE: we can get away with passing in NULL for a walk state because
- * ResolvedNode is guaranteed to not be a reference to either a method
+ * the Node is guaranteed to not be a reference to either a method
* local or a method argument (because this interface is never called
* from a running method.)
*
* Even though we do not directly invoke the interpreter for object
- * resolution, we must lock it because we could access an opregion.
- * The opregion access code assumes that the interpreter is locked.
+ * resolution, we must lock it because we could access an OpRegion.
+ * The OpRegion access code assumes that the interpreter is locked.
*/
AcpiExEnterInterpreter ();
- /* Function has a strange interface */
+ /* TBD: ResolveNodeToValue has a strange interface, fix */
- Status = AcpiExResolveNodeToValue (&Info->ResolvedNode, NULL);
+ Info->ReturnObject = ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, Info->Node);
+
+ Status = AcpiExResolveNodeToValue (ACPI_CAST_INDIRECT_PTR (
+ ACPI_NAMESPACE_NODE, &Info->ReturnObject), NULL);
AcpiExExitInterpreter ();
- /*
- * If AcpiExResolveNodeToValue() succeeded, the return value was placed
- * in ResolvedNode.
- */
- if (ACPI_SUCCESS (Status))
+ if (ACPI_FAILURE (Status))
{
- Status = AE_CTRL_RETURN_VALUE;
- Info->ReturnObject =
- ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, Info->ResolvedNode);
-
- ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Returning object %p [%s]\n",
- Info->ReturnObject,
- AcpiUtGetObjectTypeName (Info->ReturnObject)));
+ Info->ReturnObject = NULL;
+ goto Cleanup;
}
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Returned object %p [%s]\n",
+ Info->ReturnObject,
+ AcpiUtGetObjectTypeName (Info->ReturnObject)));
+
+ Status = AE_CTRL_RETURN_VALUE; /* Always has a "return value" */
+ break;
}
/*
- * Check input argument count against the ASL-defined count for a method.
- * Also check predefined names: argument count and return value against
- * the ACPI specification. Some incorrect return value types are repaired.
+ * For predefined names, check the return value against the ACPI
+ * specification. Some incorrect return value types are repaired.
*/
- (void) AcpiNsCheckPredefinedNames (Node, Info->ParamCount,
- Status, &Info->ReturnObject);
+ (void) AcpiNsCheckReturnValue (Info->Node, Info, Info->ParamCount,
+ Status, &Info->ReturnObject);
/* Check if there is a return value that must be dealt with */
@@ -285,12 +321,16 @@ AcpiNsEvaluate (
}
ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
- "*** Completed evaluation of object %s ***\n", Info->Pathname));
+ "*** Completed evaluation of object %s ***\n",
+ Info->RelativePathname));
+Cleanup:
/*
* Namespace was unlocked by the handling AcpiNs* function, so we
- * just return
+ * just free the pathname and return
*/
+ ACPI_FREE (Info->FullPathname);
+ Info->FullPathname = NULL;
return_ACPI_STATUS (Status);
}
@@ -356,7 +396,7 @@ AcpiNsExecModuleCodeList (
AcpiUtRemoveReference (Prev);
}
- ACPI_INFO ((AE_INFO,
+ ACPI_INFO ((
"Executed %u blocks of module-level executable AML code",
MethodCount));
@@ -400,8 +440,8 @@ AcpiNsExecModuleCode (
* Get the parent node. We cheat by using the NextObject field
* of the method object descriptor.
*/
- ParentNode = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
- MethodObj->Method.NextObject);
+ ParentNode = ACPI_CAST_PTR (
+ ACPI_NAMESPACE_NODE, MethodObj->Method.NextObject);
Type = AcpiNsGetType (ParentNode);
/*
@@ -423,13 +463,13 @@ AcpiNsExecModuleCode (
/* Initialize the evaluation information block */
- ACPI_MEMSET (Info, 0, sizeof (ACPI_EVALUATE_INFO));
+ memset (Info, 0, sizeof (ACPI_EVALUATE_INFO));
Info->PrefixNode = ParentNode;
/*
- * Get the currently attached parent object. Add a reference, because the
- * ref count will be decreased when the method object is installed to
- * the parent node.
+ * Get the currently attached parent object. Add a reference,
+ * because the ref count will be decreased when the method object
+ * is installed to the parent node.
*/
ParentObj = AcpiNsGetAttachedObject (ParentNode);
if (ParentObj)
@@ -439,8 +479,7 @@ AcpiNsExecModuleCode (
/* Install the method (module-level code) in the parent node */
- Status = AcpiNsAttachObject (ParentNode, MethodObj,
- ACPI_TYPE_METHOD);
+ Status = AcpiNsAttachObject (ParentNode, MethodObj, ACPI_TYPE_METHOD);
if (ACPI_FAILURE (Status))
{
goto Exit;
@@ -450,7 +489,8 @@ AcpiNsExecModuleCode (
Status = AcpiNsEvaluate (Info);
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "Executed module-level code at %p\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_INIT_NAMES,
+ "Executed module-level code at %p\n",
MethodObj->Method.AmlStart));
/* Delete a possible implicit return value (in slack mode) */
@@ -482,4 +522,3 @@ Exit:
}
return_VOID;
}
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsinit.c b/usr/src/uts/intel/io/acpica/namespace/nsinit.c
index 1822bf519f..dbe6c7c09f 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsinit.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsinit.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,14 +41,12 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSXFINIT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acevents.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME ("nsinit")
@@ -101,27 +99,29 @@ AcpiNsInitializeObjects (
ACPI_FUNCTION_TRACE (NsInitializeObjects);
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[Init] Completing Initialization of ACPI Objects\n"));
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
"**** Starting initialization of namespace objects ****\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
- "Completing Region/Field/Buffer/Package initialization:"));
+ "Completing Region/Field/Buffer/Package initialization:\n"));
/* Set all init info to zero */
- ACPI_MEMSET (&Info, 0, sizeof (ACPI_INIT_WALK_INFO));
+ memset (&Info, 0, sizeof (ACPI_INIT_WALK_INFO));
/* Walk entire namespace from the supplied root */
Status = AcpiWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, AcpiNsInitOneObject, NULL,
- &Info, NULL);
+ ACPI_UINT32_MAX, AcpiNsInitOneObject, NULL,
+ &Info, NULL);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status, "During WalkNamespace"));
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
- "\nInitialized %u/%u Regions %u/%u Fields %u/%u "
+ " Initialized %u/%u Regions %u/%u Fields %u/%u "
"Buffers %u/%u Packages (%u nodes)\n",
Info.OpRegionInit, Info.OpRegionCount,
Info.FieldInit, Info.FieldCount,
@@ -129,9 +129,8 @@ AcpiNsInitializeObjects (
Info.PackageInit, Info.PackageCount, Info.ObjectCount));
ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
- "%u Control Methods found\n", Info.MethodCount));
- ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
- "%u Op Regions found\n", Info.OpRegionCount));
+ "%u Control Methods found\n%u Op Regions found\n",
+ Info.MethodCount, Info.OpRegionCount));
return_ACPI_STATUS (AE_OK);
}
@@ -155,84 +154,140 @@ AcpiNsInitializeObjects (
ACPI_STATUS
AcpiNsInitializeDevices (
- void)
+ UINT32 Flags)
{
- ACPI_STATUS Status;
+ ACPI_STATUS Status = AE_OK;
ACPI_DEVICE_WALK_INFO Info;
+ ACPI_HANDLE Handle;
ACPI_FUNCTION_TRACE (NsInitializeDevices);
- /* Init counters */
+ if (!(Flags & ACPI_NO_DEVICE_INIT))
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[Init] Initializing ACPI Devices\n"));
- Info.DeviceCount = 0;
- Info.Num_STA = 0;
- Info.Num_INI = 0;
+ /* Init counters */
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
- "Initializing Device/Processor/Thermal objects "
- "by executing _INI methods:"));
+ Info.DeviceCount = 0;
+ Info.Num_STA = 0;
+ Info.Num_INI = 0;
- /* Tree analysis: find all subtrees that contain _INI methods */
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ "Initializing Device/Processor/Thermal objects "
+ "and executing _INI/_STA methods:\n"));
- Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, FALSE, AcpiNsFindIniMethods, NULL, &Info, NULL);
- if (ACPI_FAILURE (Status))
- {
- goto ErrorExit;
- }
+ /* Tree analysis: find all subtrees that contain _INI methods */
- /* Allocate the evaluation information block */
-
- Info.EvaluateInfo = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
- if (!Info.EvaluateInfo)
- {
- Status = AE_NO_MEMORY;
- goto ErrorExit;
- }
+ Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, FALSE, AcpiNsFindIniMethods, NULL, &Info, NULL);
+ if (ACPI_FAILURE (Status))
+ {
+ goto ErrorExit;
+ }
- /*
- * Execute the "global" _INI method that may appear at the root. This
- * support is provided for Windows compatibility (Vista+) and is not
- * part of the ACPI specification.
- */
- Info.EvaluateInfo->PrefixNode = AcpiGbl_RootNode;
- Info.EvaluateInfo->Pathname = METHOD_NAME__INI;
- Info.EvaluateInfo->Parameters = NULL;
- Info.EvaluateInfo->Flags = ACPI_IGNORE_RETURN_VALUE;
+ /* Allocate the evaluation information block */
- Status = AcpiNsEvaluate (Info.EvaluateInfo);
- if (ACPI_SUCCESS (Status))
- {
- Info.Num_INI++;
- }
+ Info.EvaluateInfo = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
+ if (!Info.EvaluateInfo)
+ {
+ Status = AE_NO_MEMORY;
+ goto ErrorExit;
+ }
- /* Walk namespace to execute all _INIs on present devices */
+ /*
+ * Execute the "global" _INI method that may appear at the root.
+ * This support is provided for Windows compatibility (Vista+) and
+ * is not part of the ACPI specification.
+ */
+ Info.EvaluateInfo->PrefixNode = AcpiGbl_RootNode;
+ Info.EvaluateInfo->RelativePathname = METHOD_NAME__INI;
+ Info.EvaluateInfo->Parameters = NULL;
+ Info.EvaluateInfo->Flags = ACPI_IGNORE_RETURN_VALUE;
+
+ Status = AcpiNsEvaluate (Info.EvaluateInfo);
+ if (ACPI_SUCCESS (Status))
+ {
+ Info.Num_INI++;
+ }
- Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, FALSE, AcpiNsInitOneDevice, NULL, &Info, NULL);
+ /*
+ * Execute \_SB._INI.
+ * There appears to be a strict order requirement for \_SB._INI,
+ * which should be evaluated before any _REG evaluations.
+ */
+ Status = AcpiGetHandle (NULL, "\\_SB", &Handle);
+ if (ACPI_SUCCESS (Status))
+ {
+ memset (Info.EvaluateInfo, 0, sizeof (ACPI_EVALUATE_INFO));
+ Info.EvaluateInfo->PrefixNode = Handle;
+ Info.EvaluateInfo->RelativePathname = METHOD_NAME__INI;
+ Info.EvaluateInfo->Parameters = NULL;
+ Info.EvaluateInfo->Flags = ACPI_IGNORE_RETURN_VALUE;
+
+ Status = AcpiNsEvaluate (Info.EvaluateInfo);
+ if (ACPI_SUCCESS (Status))
+ {
+ Info.Num_INI++;
+ }
+ }
+ }
/*
- * Any _OSI requests should be completed by now. If the BIOS has
- * requested any Windows OSI strings, we will always truncate
- * I/O addresses to 16 bits -- for Windows compatibility.
+ * Run all _REG methods
+ *
+ * Note: Any objects accessed by the _REG methods will be automatically
+ * initialized, even if they contain executable AML (see the call to
+ * AcpiNsInitializeObjects below).
+ *
+ * Note: According to the ACPI specification, we actually needn't execute
+ * _REG for SystemMemory/SystemIo operation regions, but for PCI_Config
+ * operation regions, it is required to evaluate _REG for those on a PCI
+ * root bus that doesn't contain _BBN object. So this code is kept here
+ * in order not to break things.
*/
- if (AcpiGbl_OsiData >= ACPI_OSI_WIN_2000)
+ if (!(Flags & ACPI_NO_ADDRESS_SPACE_INIT))
{
- AcpiGbl_TruncateIoAddresses = TRUE;
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[Init] Executing _REG OpRegion methods\n"));
+
+ Status = AcpiEvInitializeOpRegions ();
+ if (ACPI_FAILURE (Status))
+ {
+ goto ErrorExit;
+ }
}
- ACPI_FREE (Info.EvaluateInfo);
- if (ACPI_FAILURE (Status))
+ if (!(Flags & ACPI_NO_DEVICE_INIT))
{
- goto ErrorExit;
- }
+ /* Walk namespace to execute all _INIs on present devices */
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
- "\nExecuted %u _INI methods requiring %u _STA executions "
- "(examined %u objects)\n",
- Info.Num_INI, Info.Num_STA, Info.DeviceCount));
+ Status = AcpiNsWalkNamespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, FALSE, AcpiNsInitOneDevice, NULL, &Info, NULL);
+
+ /*
+ * Any _OSI requests should be completed by now. If the BIOS has
+ * requested any Windows OSI strings, we will always truncate
+ * I/O addresses to 16 bits -- for Windows compatibility.
+ */
+ if (AcpiGbl_OsiData >= ACPI_OSI_WIN_2000)
+ {
+ AcpiGbl_TruncateIoAddresses = TRUE;
+ }
+
+ ACPI_FREE (Info.EvaluateInfo);
+ if (ACPI_FAILURE (Status))
+ {
+ goto ErrorExit;
+ }
+
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ " Executed %u _INI methods requiring %u _STA executions "
+ "(examined %u objects)\n",
+ Info.Num_INI, Info.Num_STA, Info.DeviceCount));
+ }
return_ACPI_STATUS (Status);
@@ -254,7 +309,7 @@ ErrorExit:
*
* RETURN: Status
*
- * DESCRIPTION: Callback from AcpiWalkNamespace. Invoked for every object
+ * DESCRIPTION: Callback from AcpiWalkNamespace. Invoked for every object
* within the namespace.
*
* Currently, the only objects that require initialization are:
@@ -296,28 +351,34 @@ AcpiNsInitOneObject (
switch (Type)
{
case ACPI_TYPE_REGION:
+
Info->OpRegionCount++;
break;
case ACPI_TYPE_BUFFER_FIELD:
+
Info->FieldCount++;
break;
case ACPI_TYPE_LOCAL_BANK_FIELD:
+
Info->FieldCount++;
break;
case ACPI_TYPE_BUFFER:
+
Info->BufferCount++;
break;
case ACPI_TYPE_PACKAGE:
+
Info->PackageCount++;
break;
default:
/* No init required, just exit now */
+
return (AE_OK);
}
@@ -369,7 +430,9 @@ AcpiNsInitOneObject (
break;
default:
+
/* No other types can get here */
+
break;
}
@@ -381,15 +444,6 @@ AcpiNsInitOneObject (
}
/*
- * Print a dot for each object unless we are going to print the entire
- * pathname
- */
- if (!(AcpiDbgLevel & ACPI_LV_INIT_NAMES))
- {
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, "."));
- }
-
- /*
* We ignore errors from above, and always return OK, since we don't want
* to abort the walk on any single error.
*/
@@ -465,6 +519,7 @@ AcpiNsFindIniMethods (
break;
default:
+
break;
}
@@ -615,38 +670,37 @@ AcpiNsInitOneDevice (
* Note: We know there is an _INI within this subtree, but it may not be
* under this particular device, it may be lower in the branch.
*/
- ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (
- ACPI_TYPE_METHOD, DeviceNode, METHOD_NAME__INI));
-
- Info->PrefixNode = DeviceNode;
- Info->Pathname = METHOD_NAME__INI;
- Info->Parameters = NULL;
- Info->Flags = ACPI_IGNORE_RETURN_VALUE;
-
- Status = AcpiNsEvaluate (Info);
- if (ACPI_SUCCESS (Status))
+ if (!ACPI_COMPARE_NAME (DeviceNode->Name.Ascii, "_SB_") ||
+ DeviceNode->Parent != AcpiGbl_RootNode)
{
- WalkInfo->Num_INI++;
+ ACPI_DEBUG_EXEC (AcpiUtDisplayInitPathname (
+ ACPI_TYPE_METHOD, DeviceNode, METHOD_NAME__INI));
- if ((AcpiDbgLevel <= ACPI_LV_ALL_EXCEPTIONS) &&
- (!(AcpiDbgLevel & ACPI_LV_INFO)))
+ memset (Info, 0, sizeof (ACPI_EVALUATE_INFO));
+ Info->PrefixNode = DeviceNode;
+ Info->RelativePathname = METHOD_NAME__INI;
+ Info->Parameters = NULL;
+ Info->Flags = ACPI_IGNORE_RETURN_VALUE;
+
+ Status = AcpiNsEvaluate (Info);
+ if (ACPI_SUCCESS (Status))
{
- ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, "."));
+ WalkInfo->Num_INI++;
}
- }
#ifdef ACPI_DEBUG_OUTPUT
- else if (Status != AE_NOT_FOUND)
- {
- /* Ignore error and move on to next device */
+ else if (Status != AE_NOT_FOUND)
+ {
+ /* Ignore error and move on to next device */
- char *ScopeName = AcpiNsGetExternalPathname (Info->ResolvedNode);
+ char *ScopeName = AcpiNsGetNormalizedPathname (DeviceNode, TRUE);
- ACPI_EXCEPTION ((AE_INFO, Status, "during %s._INI execution",
- ScopeName));
- ACPI_FREE (ScopeName);
- }
+ ACPI_EXCEPTION ((AE_INFO, Status, "during %s._INI execution",
+ ScopeName));
+ ACPI_FREE (ScopeName);
+ }
#endif
+ }
/* Ignore errors from above */
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsload.c b/usr/src/uts/intel/io/acpica/namespace/nsload.c
index 5825f6bc9d..9c899e7846 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsload.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsload.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSLOAD_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -93,8 +91,8 @@ AcpiNsLoadTable (
/*
* Parse the table and load the namespace with all named
- * objects found within. Control methods are NOT parsed
- * at this time. In fact, the control methods cannot be
+ * objects found within. Control methods are NOT parsed
+ * at this time. In fact, the control methods cannot be
* parsed until the entire namespace is loaded, because
* if a control method makes a forward reference (call)
* to another control method, we can't continue parsing
@@ -130,7 +128,21 @@ AcpiNsLoadTable (
}
else
{
- (void) AcpiTbReleaseOwnerId (TableIndex);
+ /*
+ * On error, delete any namespace objects created by this table.
+ * We cannot initialize these objects, so delete them. There are
+ * a couple of expecially bad cases:
+ * AE_ALREADY_EXISTS - namespace collision.
+ * AE_NOT_FOUND - the target of a Scope operator does not
+ * exist. This target of Scope must already exist in the
+ * namespace, as per the ACPI specification.
+ */
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ AcpiNsDeleteNamespaceByOwner (
+ AcpiGbl_RootTableList.Tables[TableIndex].OwnerId);
+
+ AcpiTbReleaseOwnerId (TableIndex);
+ return_ACPI_STATUS (Status);
}
Unlock:
@@ -142,18 +154,36 @@ Unlock:
}
/*
- * Now we can parse the control methods. We always parse
+ * Now we can parse the control methods. We always parse
* them here for a sanity check, and if configured for
* just-in-time parsing, we delete the control method
* parse trees.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "**** Begin Table Method Parsing and Object Initialization\n"));
+ "**** Begin Table Object Initialization\n"));
Status = AcpiDsInitializeObjects (TableIndex, Node);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "**** Completed Table Method Parsing and Object Initialization\n"));
+ "**** Completed Table Object Initialization\n"));
+
+ /*
+ * Execute any module-level code that was detected during the table load
+ * phase. Although illegal since ACPI 2.0, there are many machines that
+ * contain this type of code. Each block of detected executable AML code
+ * outside of any control method is wrapped with a temporary control
+ * method object and placed on a global list. The methods on this list
+ * are executed below.
+ *
+ * This case executes the module-level code for each table immediately
+ * after the table has been loaded. This provides compatibility with
+ * other ACPI implementations. Optionally, the execution can be deferred
+ * until later, see AcpiInitializeObjects.
+ */
+ if (!AcpiGbl_GroupModuleLevelCode)
+ {
+ AcpiNsExecModuleCodeList ();
+ }
return_ACPI_STATUS (Status);
}
@@ -192,7 +222,7 @@ AcpiNsLoadNamespace (
}
/*
- * Load the namespace. The DSDT is required,
+ * Load the namespace. The DSDT is required,
* but the SSDT and PSDT tables are optional.
*/
Status = AcpiNsLoadTableByType (ACPI_TABLE_ID_DSDT);
@@ -247,8 +277,8 @@ AcpiNsDeleteSubtree (
ParentHandle = StartHandle;
- ChildHandle = NULL;
- Level = 1;
+ ChildHandle = NULL;
+ Level = 1;
/*
* Traverse the tree of objects until we bubble back up
@@ -259,7 +289,7 @@ AcpiNsDeleteSubtree (
/* Attempt to get the next object in this scope */
Status = AcpiGetNextObject (ACPI_TYPE_ANY, ParentHandle,
- ChildHandle, &NextChildHandle);
+ ChildHandle, &NextChildHandle);
ChildHandle = NextChildHandle;
@@ -270,7 +300,7 @@ AcpiNsDeleteSubtree (
/* Check if this object has any children */
if (ACPI_SUCCESS (AcpiGetNextObject (ACPI_TYPE_ANY, ChildHandle,
- NULL, &Dummy)))
+ NULL, &Dummy)))
{
/*
* There is at least one child of this object,
@@ -318,7 +348,7 @@ AcpiNsDeleteSubtree (
* RETURN: Status
*
* DESCRIPTION: Shrinks the namespace, typically in response to an undocking
- * event. Deletes an entire subtree starting from (and
+ * event. Deletes an entire subtree starting from (and
* including) the given handle.
*
******************************************************************************/
@@ -348,9 +378,7 @@ AcpiNsUnloadNamespace (
/* This function does the real work */
Status = AcpiNsDeleteSubtree (Handle);
-
return_ACPI_STATUS (Status);
}
#endif
#endif
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsnames.c b/usr/src/uts/intel/io/acpica/namespace/nsnames.c
index e59b2e9569..95f8c8db97 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsnames.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsnames.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSNAMES_C__
-
#include "acpi.h"
#include "accommon.h"
#include "amlcode.h"
@@ -55,82 +53,6 @@
/*******************************************************************************
*
- * FUNCTION: AcpiNsBuildExternalPath
- *
- * PARAMETERS: Node - NS node whose pathname is needed
- * Size - Size of the pathname
- * *NameBuffer - Where to return the pathname
- *
- * RETURN: Status
- * Places the pathname into the NameBuffer, in external format
- * (name segments separated by path separators)
- *
- * DESCRIPTION: Generate a full pathaname
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiNsBuildExternalPath (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_SIZE Size,
- char *NameBuffer)
-{
- ACPI_SIZE Index;
- ACPI_NAMESPACE_NODE *ParentNode;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- /* Special case for root */
-
- Index = Size - 1;
- if (Index < ACPI_NAME_SIZE)
- {
- NameBuffer[0] = AML_ROOT_PREFIX;
- NameBuffer[1] = 0;
- return (AE_OK);
- }
-
- /* Store terminator byte, then build name backwards */
-
- ParentNode = Node;
- NameBuffer[Index] = 0;
-
- while ((Index > ACPI_NAME_SIZE) && (ParentNode != AcpiGbl_RootNode))
- {
- Index -= ACPI_NAME_SIZE;
-
- /* Put the name into the buffer */
-
- ACPI_MOVE_32_TO_32 ((NameBuffer + Index), &ParentNode->Name);
- ParentNode = ParentNode->Parent;
-
- /* Prefix name with the path separator */
-
- Index--;
- NameBuffer[Index] = ACPI_PATH_SEPARATOR;
- }
-
- /* Overwrite final separator with the root prefix character */
-
- NameBuffer[Index] = AML_ROOT_PREFIX;
-
- if (Index != 0)
- {
- ACPI_ERROR ((AE_INFO,
- "Could not construct external pathname; index=%u, size=%u, Path=%s",
- (UINT32) Index, (UINT32) Size, &NameBuffer[Size]));
-
- return (AE_BAD_PARAMETER);
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiNsGetExternalPathname
*
* PARAMETERS: Node - Namespace node whose pathname is needed
@@ -148,40 +70,13 @@ char *
AcpiNsGetExternalPathname (
ACPI_NAMESPACE_NODE *Node)
{
- ACPI_STATUS Status;
char *NameBuffer;
- ACPI_SIZE Size;
ACPI_FUNCTION_TRACE_PTR (NsGetExternalPathname, Node);
- /* Calculate required buffer size based on depth below root */
-
- Size = AcpiNsGetPathnameLength (Node);
- if (!Size)
- {
- return_PTR (NULL);
- }
-
- /* Allocate a buffer to be returned to caller */
-
- NameBuffer = ACPI_ALLOCATE_ZEROED (Size);
- if (!NameBuffer)
- {
- ACPI_ERROR ((AE_INFO, "Could not allocate %u bytes", (UINT32) Size));
- return_PTR (NULL);
- }
-
- /* Build the path in the allocated buffer */
-
- Status = AcpiNsBuildExternalPath (Node, Size, NameBuffer);
- if (ACPI_FAILURE (Status))
- {
- ACPI_FREE (NameBuffer);
- return_PTR (NULL);
- }
-
+ NameBuffer = AcpiNsGetNormalizedPathname (Node, FALSE);
return_PTR (NameBuffer);
}
@@ -203,38 +98,13 @@ AcpiNsGetPathnameLength (
ACPI_NAMESPACE_NODE *Node)
{
ACPI_SIZE Size;
- ACPI_NAMESPACE_NODE *NextNode;
ACPI_FUNCTION_ENTRY ();
- /*
- * Compute length of pathname as 5 * number of name segments.
- * Go back up the parent tree to the root
- */
- Size = 0;
- NextNode = Node;
-
- while (NextNode && (NextNode != AcpiGbl_RootNode))
- {
- if (ACPI_GET_DESCRIPTOR_TYPE (NextNode) != ACPI_DESC_TYPE_NAMED)
- {
- ACPI_ERROR ((AE_INFO,
- "Invalid Namespace Node (%p) while traversing namespace",
- NextNode));
- return 0;
- }
- Size += ACPI_PATH_SEGMENT_LENGTH;
- NextNode = NextNode->Parent;
- }
-
- if (!Size)
- {
- Size = 1; /* Root node case */
- }
-
- return (Size + 1); /* +1 for null string terminator */
+ Size = AcpiNsBuildNormalizedPath (Node, NULL, 0, FALSE);
+ return (Size);
}
@@ -245,6 +115,8 @@ AcpiNsGetPathnameLength (
* PARAMETERS: TargetHandle - Handle of named object whose name is
* to be found
* Buffer - Where the pathname is returned
+ * NoTrailing - Remove trailing '_' for each name
+ * segment
*
* RETURN: Status, Buffer is filled with pathname if status is AE_OK
*
@@ -255,7 +127,8 @@ AcpiNsGetPathnameLength (
ACPI_STATUS
AcpiNsHandleToPathname (
ACPI_HANDLE TargetHandle,
- ACPI_BUFFER *Buffer)
+ ACPI_BUFFER *Buffer,
+ BOOLEAN NoTrailing)
{
ACPI_STATUS Status;
ACPI_NAMESPACE_NODE *Node;
@@ -273,7 +146,7 @@ AcpiNsHandleToPathname (
/* Determine size required for the caller buffer */
- RequiredSize = AcpiNsGetPathnameLength (Node);
+ RequiredSize = AcpiNsBuildNormalizedPath (Node, NULL, 0, NoTrailing);
if (!RequiredSize)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
@@ -289,7 +162,8 @@ AcpiNsHandleToPathname (
/* Build the path in the caller buffer */
- Status = AcpiNsBuildExternalPath (Node, RequiredSize, Buffer->Pointer);
+ (void) AcpiNsBuildNormalizedPath (Node, Buffer->Pointer,
+ RequiredSize, NoTrailing);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -301,3 +175,172 @@ AcpiNsHandleToPathname (
}
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsBuildNormalizedPath
+ *
+ * PARAMETERS: Node - Namespace node
+ * FullPath - Where the path name is returned
+ * PathSize - Size of returned path name buffer
+ * NoTrailing - Remove trailing '_' from each name segment
+ *
+ * RETURN: Return 1 if the AML path is empty, otherwise returning (length
+ * of pathname + 1) which means the 'FullPath' contains a trailing
+ * null.
+ *
+ * DESCRIPTION: Build and return a full namespace pathname.
+ * Note that if the size of 'FullPath' isn't large enough to
+ * contain the namespace node's path name, the actual required
+ * buffer length is returned, and it should be greater than
+ * 'PathSize'. So callers are able to check the returning value
+ * to determine the buffer size of 'FullPath'.
+ *
+ ******************************************************************************/
+
+UINT32
+AcpiNsBuildNormalizedPath (
+ ACPI_NAMESPACE_NODE *Node,
+ char *FullPath,
+ UINT32 PathSize,
+ BOOLEAN NoTrailing)
+{
+ UINT32 Length = 0, i;
+ char Name[ACPI_NAME_SIZE];
+ BOOLEAN DoNoTrailing;
+ char c, *Left, *Right;
+ ACPI_NAMESPACE_NODE *NextNode;
+
+
+ ACPI_FUNCTION_TRACE_PTR (NsBuildNormalizedPath, Node);
+
+
+#define ACPI_PATH_PUT8(Path, Size, Byte, Length) \
+ do { \
+ if ((Length) < (Size)) \
+ { \
+ (Path)[(Length)] = (Byte); \
+ } \
+ (Length)++; \
+ } while (0)
+
+ /*
+ * Make sure the PathSize is correct, so that we don't need to
+ * validate both FullPath and PathSize.
+ */
+ if (!FullPath)
+ {
+ PathSize = 0;
+ }
+
+ if (!Node)
+ {
+ goto BuildTrailingNull;
+ }
+
+ NextNode = Node;
+ while (NextNode && NextNode != AcpiGbl_RootNode)
+ {
+ if (NextNode != Node)
+ {
+ ACPI_PATH_PUT8(FullPath, PathSize, AML_DUAL_NAME_PREFIX, Length);
+ }
+
+ ACPI_MOVE_32_TO_32 (Name, &NextNode->Name);
+ DoNoTrailing = NoTrailing;
+ for (i = 0; i < 4; i++)
+ {
+ c = Name[4-i-1];
+ if (DoNoTrailing && c != '_')
+ {
+ DoNoTrailing = FALSE;
+ }
+ if (!DoNoTrailing)
+ {
+ ACPI_PATH_PUT8(FullPath, PathSize, c, Length);
+ }
+ }
+
+ NextNode = NextNode->Parent;
+ }
+
+ ACPI_PATH_PUT8(FullPath, PathSize, AML_ROOT_PREFIX, Length);
+
+ /* Reverse the path string */
+
+ if (Length <= PathSize)
+ {
+ Left = FullPath;
+ Right = FullPath+Length - 1;
+
+ while (Left < Right)
+ {
+ c = *Left;
+ *Left++ = *Right;
+ *Right-- = c;
+ }
+ }
+
+ /* Append the trailing null */
+
+BuildTrailingNull:
+ ACPI_PATH_PUT8 (FullPath, PathSize, '\0', Length);
+
+#undef ACPI_PATH_PUT8
+
+ return_UINT32 (Length);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsGetNormalizedPathname
+ *
+ * PARAMETERS: Node - Namespace node whose pathname is needed
+ * NoTrailing - Remove trailing '_' from each name segment
+ *
+ * RETURN: Pointer to storage containing the fully qualified name of
+ * the node, In external format (name segments separated by path
+ * separators.)
+ *
+ * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually
+ * for error and debug statements. All trailing '_' will be
+ * removed from the full pathname if 'NoTrailing' is specified..
+ *
+ ******************************************************************************/
+
+char *
+AcpiNsGetNormalizedPathname (
+ ACPI_NAMESPACE_NODE *Node,
+ BOOLEAN NoTrailing)
+{
+ char *NameBuffer;
+ ACPI_SIZE Size;
+
+
+ ACPI_FUNCTION_TRACE_PTR (NsGetNormalizedPathname, Node);
+
+
+ /* Calculate required buffer size based on depth below root */
+
+ Size = AcpiNsBuildNormalizedPath (Node, NULL, 0, NoTrailing);
+ if (!Size)
+ {
+ return_PTR (NULL);
+ }
+
+ /* Allocate a buffer to be returned to caller */
+
+ NameBuffer = ACPI_ALLOCATE_ZEROED (Size);
+ if (!NameBuffer)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Could not allocate %u bytes", (UINT32) Size));
+ return_PTR (NULL);
+ }
+
+ /* Build the path in the allocated buffer */
+
+ (void) AcpiNsBuildNormalizedPath (Node, NameBuffer, Size, NoTrailing);
+
+ return_PTR (NameBuffer);
+}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsobject.c b/usr/src/uts/intel/io/acpica/namespace/nsobject.c
index dadd2e8d98..cf2436155b 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsobject.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsobject.c
@@ -6,7 +6,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,9 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSOBJECT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -66,7 +63,7 @@
* RETURN: Status
*
* DESCRIPTION: Record the given object as the value associated with the
- * name whose ACPI_HANDLE is passed. If Object is NULL
+ * name whose ACPI_HANDLE is passed. If Object is NULL
* and Type is ACPI_TYPE_ANY, set the name as having no value.
* Note: Future may require that the Node->Flags field be passed
* as a parameter.
@@ -146,9 +143,9 @@ AcpiNsAttachObject (
{
/*
* Value passed is a name handle and that name has a
- * non-null value. Use that name's value and type.
+ * non-null value. Use that name's value and type.
*/
- ObjDesc = ((ACPI_NAMESPACE_NODE *) Object)->Object;
+ ObjDesc = ((ACPI_NAMESPACE_NODE *) Object)->Object;
ObjectType = ((ACPI_NAMESPACE_NODE *) Object)->Type;
}
@@ -198,8 +195,8 @@ AcpiNsAttachObject (
LastObjDesc->Common.NextObject = Node->Object;
}
- Node->Type = (UINT8) ObjectType;
- Node->Object = ObjDesc;
+ Node->Type = (UINT8) ObjectType;
+ Node->Object = ObjDesc;
return_ACPI_STATUS (AE_OK);
}
@@ -247,17 +244,32 @@ AcpiNsDetachObject (
}
}
- /* Clear the entry in all cases */
+ /* Clear the Node entry in all cases */
Node->Object = NULL;
if (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) == ACPI_DESC_TYPE_OPERAND)
{
+ /* Unlink object from front of possible object list */
+
Node->Object = ObjDesc->Common.NextObject;
+
+ /* Handle possible 2-descriptor object */
+
if (Node->Object &&
- ((Node->Object)->Common.Type != ACPI_TYPE_LOCAL_DATA))
+ (Node->Object->Common.Type != ACPI_TYPE_LOCAL_DATA))
{
Node->Object = Node->Object->Common.NextObject;
}
+
+ /*
+ * Detach the object from any data objects (which are still held by
+ * the namespace node)
+ */
+ if (ObjDesc->Common.NextObject &&
+ ((ObjDesc->Common.NextObject)->Common.Type == ACPI_TYPE_LOCAL_DATA))
+ {
+ ObjDesc->Common.NextObject = NULL;
+ }
}
/* Reset the node type to untyped */
@@ -354,7 +366,7 @@ AcpiNsGetSecondaryObject (
*
* RETURN: Status
*
- * DESCRIPTION: Low-level attach data. Create and attach a Data object.
+ * DESCRIPTION: Low-level attach data. Create and attach a Data object.
*
******************************************************************************/
@@ -420,7 +432,7 @@ AcpiNsAttachData (
*
* RETURN: Status
*
- * DESCRIPTION: Low-level detach data. Delete the data node, but the caller
+ * DESCRIPTION: Low-level detach data. Delete the data node, but the caller
* is responsible for the actual data.
*
******************************************************************************/
@@ -501,5 +513,3 @@ AcpiNsGetAttachedData (
return (AE_NOT_FOUND);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsparse.c b/usr/src/uts/intel/io/acpica/namespace/nsparse.c
index 983697eaaf..1ff33362ed 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsparse.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsparse.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSPARSE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -86,6 +84,22 @@ AcpiNsOneCompleteParse (
ACPI_FUNCTION_TRACE (NsOneCompleteParse);
+ Status = AcpiGetTableByIndex (TableIndex, &Table);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Table must consist of at least a complete header */
+
+ if (Table->Length < sizeof (ACPI_TABLE_HEADER))
+ {
+ return_ACPI_STATUS (AE_BAD_HEADER);
+ }
+
+ AmlStart = (UINT8 *) Table + sizeof (ACPI_TABLE_HEADER);
+ AmlLength = Table->Length - sizeof (ACPI_TABLE_HEADER);
+
Status = AcpiTbGetOwnerId (TableIndex, &OwnerId);
if (ACPI_FAILURE (Status))
{
@@ -94,7 +108,7 @@ AcpiNsOneCompleteParse (
/* Create and init a Root Node */
- ParseRoot = AcpiPsCreateScopeOp ();
+ ParseRoot = AcpiPsCreateScopeOp (AmlStart);
if (!ParseRoot)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -109,39 +123,28 @@ AcpiNsOneCompleteParse (
return_ACPI_STATUS (AE_NO_MEMORY);
}
- Status = AcpiGetTableByIndex (TableIndex, &Table);
+ Status = AcpiDsInitAmlWalk (WalkState, ParseRoot, NULL,
+ AmlStart, AmlLength, NULL, (UINT8) PassNumber);
if (ACPI_FAILURE (Status))
{
AcpiDsDeleteWalkState (WalkState);
- AcpiPsFreeOp (ParseRoot);
- return_ACPI_STATUS (Status);
+ goto Cleanup;
}
- /* Table must consist of at least a complete header */
+ /* Found OSDT table, enable the namespace override feature */
- if (Table->Length < sizeof (ACPI_TABLE_HEADER))
+ if (ACPI_COMPARE_NAME(Table->Signature, ACPI_SIG_OSDT) &&
+ PassNumber == ACPI_IMODE_LOAD_PASS1)
{
- Status = AE_BAD_HEADER;
- }
- else
- {
- AmlStart = (UINT8 *) Table + sizeof (ACPI_TABLE_HEADER);
- AmlLength = Table->Length - sizeof (ACPI_TABLE_HEADER);
- Status = AcpiDsInitAmlWalk (WalkState, ParseRoot, NULL,
- AmlStart, AmlLength, NULL, (UINT8) PassNumber);
- }
-
- if (ACPI_FAILURE (Status))
- {
- AcpiDsDeleteWalkState (WalkState);
- goto Cleanup;
+ WalkState->NamespaceOverride = TRUE;
}
/* StartNode is the default location to load the table */
if (StartNode && StartNode != AcpiGbl_RootNode)
{
- Status = AcpiDsScopeStackPush (StartNode, ACPI_TYPE_METHOD, WalkState);
+ Status = AcpiDsScopeStackPush (
+ StartNode, ACPI_TYPE_METHOD, WalkState);
if (ACPI_FAILURE (Status))
{
AcpiDsDeleteWalkState (WalkState);
@@ -151,7 +154,8 @@ AcpiNsOneCompleteParse (
/* Parse the AML */
- ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "*PARSE* pass %u parse\n", PassNumber));
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
+ "*PARSE* pass %u parse\n", PassNumber));
Status = AcpiPsParseAml (WalkState);
Cleanup:
@@ -187,16 +191,17 @@ AcpiNsParseTable (
/*
* AML Parse, pass 1
*
- * In this pass, we load most of the namespace. Control methods
- * are not parsed until later. A parse tree is not created. Instead,
- * each Parser Op subtree is deleted when it is finished. This saves
+ * In this pass, we load most of the namespace. Control methods
+ * are not parsed until later. A parse tree is not created. Instead,
+ * each Parser Op subtree is deleted when it is finished. This saves
* a great deal of memory, and allows a small cache of parse objects
- * to service the entire parse. The second pass of the parse then
+ * to service the entire parse. The second pass of the parse then
* performs another complete parse of the AML.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "**** Start pass 1\n"));
+
Status = AcpiNsOneCompleteParse (ACPI_IMODE_LOAD_PASS1,
- TableIndex, StartNode);
+ TableIndex, StartNode);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -213,7 +218,7 @@ AcpiNsParseTable (
*/
ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "**** Start pass 2\n"));
Status = AcpiNsOneCompleteParse (ACPI_IMODE_LOAD_PASS2,
- TableIndex, StartNode);
+ TableIndex, StartNode);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -221,5 +226,3 @@ AcpiNsParseTable (
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nspredef.c b/usr/src/uts/intel/io/acpica/namespace/nspredef.c
index 95c3edf04d..ccd4aba8f2 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nspredef.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nspredef.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -63,12 +63,12 @@
* There are several areas that are validated:
*
* 1) The number of input arguments as defined by the method/object in the
- * ASL is validated against the ACPI specification.
+ * ASL is validated against the ACPI specification.
* 2) The type of the return object (if any) is validated against the ACPI
- * specification.
+ * specification.
* 3) For returned package objects, the count of package elements is
- * validated, as well as the type of each package element. Nested
- * packages are supported.
+ * validated, as well as the type of each package element. Nested
+ * packages are supported.
*
* For any problems found, a warning message is issued.
*
@@ -78,63 +78,21 @@
/* Local prototypes */
static ACPI_STATUS
-AcpiNsCheckPackage (
- ACPI_PREDEFINED_DATA *Data,
- ACPI_OPERAND_OBJECT **ReturnObjectPtr);
-
-static ACPI_STATUS
-AcpiNsCheckPackageList (
- ACPI_PREDEFINED_DATA *Data,
- const ACPI_PREDEFINED_INFO *Package,
- ACPI_OPERAND_OBJECT **Elements,
- UINT32 Count);
-
-static ACPI_STATUS
-AcpiNsCheckPackageElements (
- ACPI_PREDEFINED_DATA *Data,
- ACPI_OPERAND_OBJECT **Elements,
- UINT8 Type1,
- UINT32 Count1,
- UINT8 Type2,
- UINT32 Count2,
- UINT32 StartIndex);
-
-static ACPI_STATUS
-AcpiNsCheckObjectType (
- ACPI_PREDEFINED_DATA *Data,
- ACPI_OPERAND_OBJECT **ReturnObjectPtr,
- UINT32 ExpectedBtypes,
- UINT32 PackageIndex);
-
-static ACPI_STATUS
AcpiNsCheckReference (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT *ReturnObject);
-static void
-AcpiNsGetExpectedTypes (
- char *Buffer,
- UINT32 ExpectedBtypes);
-
-/*
- * Names for the types that can be returned by the predefined objects.
- * Used for warning messages. Must be in the same order as the ACPI_RTYPEs
- */
-static const char *AcpiRtypeNames[] =
-{
- "/Integer",
- "/String",
- "/Buffer",
- "/Package",
- "/Reference",
-};
+static UINT32
+AcpiNsGetBitmappedType (
+ ACPI_OPERAND_OBJECT *ReturnObject);
/*******************************************************************************
*
- * FUNCTION: AcpiNsCheckPredefinedNames
+ * FUNCTION: AcpiNsCheckReturnValue
*
* PARAMETERS: Node - Namespace node for the method/object
+ * Info - Method execution information block
* UserParamCount - Number of parameters actually passed
* ReturnStatus - Status from the object evaluation
* ReturnObjectPtr - Pointer to the object returned from the
@@ -142,126 +100,98 @@ static const char *AcpiRtypeNames[] =
*
* RETURN: Status
*
- * DESCRIPTION: Check an ACPI name for a match in the predefined name list.
+ * DESCRIPTION: Check the value returned from a predefined name.
*
******************************************************************************/
ACPI_STATUS
-AcpiNsCheckPredefinedNames (
+AcpiNsCheckReturnValue (
ACPI_NAMESPACE_NODE *Node,
+ ACPI_EVALUATE_INFO *Info,
UINT32 UserParamCount,
ACPI_STATUS ReturnStatus,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
- ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
- ACPI_STATUS Status = AE_OK;
+ ACPI_STATUS Status;
const ACPI_PREDEFINED_INFO *Predefined;
- char *Pathname;
- ACPI_PREDEFINED_DATA *Data;
-
-
- /* Match the name for this method/object against the predefined list */
- Predefined = AcpiNsCheckForPredefinedName (Node);
-
- /* Get the full pathname to the object, for use in warning messages */
-
- Pathname = AcpiNsGetExternalPathname (Node);
- if (!Pathname)
- {
- return (AE_OK); /* Could not get pathname, ignore */
- }
-
- /*
- * Check that the parameter count for this method matches the ASL
- * definition. For predefined names, ensure that both the caller and
- * the method itself are in accordance with the ACPI specification.
- */
- AcpiNsCheckParameterCount (Pathname, Node, UserParamCount, Predefined);
/* If not a predefined name, we cannot validate the return object */
+ Predefined = Info->Predefined;
if (!Predefined)
{
- goto Cleanup;
+ return (AE_OK);
}
/*
* If the method failed or did not actually return an object, we cannot
* validate the return object
*/
- if ((ReturnStatus != AE_OK) && (ReturnStatus != AE_CTRL_RETURN_VALUE))
+ if ((ReturnStatus != AE_OK) &&
+ (ReturnStatus != AE_CTRL_RETURN_VALUE))
{
- goto Cleanup;
+ return (AE_OK);
}
/*
- * If there is no return value, check if we require a return value for
- * this predefined name. Either one return value is expected, or none,
- * for both methods and other objects.
+ * Return value validation and possible repair.
*
- * Exit now if there is no return object. Warning if one was expected.
- */
- if (!ReturnObject)
- {
- if ((Predefined->Info.ExpectedBtypes) &&
- (!(Predefined->Info.ExpectedBtypes & ACPI_RTYPE_NONE)))
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
- "Missing expected return value"));
-
- Status = AE_AML_NO_RETURN_VALUE;
- }
- goto Cleanup;
- }
-
- /*
- * 1) We have a return value, but if one wasn't expected, just exit, this is
- * not a problem. For example, if the "Implicit Return" feature is
- * enabled, methods will always return a value.
+ * 1) Don't perform return value validation/repair if this feature
+ * has been disabled via a global option.
*
- * 2) If the return value can be of any type, then we cannot perform any
- * validation, exit.
+ * 2) We have a return value, but if one wasn't expected, just exit,
+ * this is not a problem. For example, if the "Implicit Return"
+ * feature is enabled, methods will always return a value.
+ *
+ * 3) If the return value can be of any type, then we cannot perform
+ * any validation, just exit.
*/
- if ((!Predefined->Info.ExpectedBtypes) ||
+ if (AcpiGbl_DisableAutoRepair ||
+ (!Predefined->Info.ExpectedBtypes) ||
(Predefined->Info.ExpectedBtypes == ACPI_RTYPE_ALL))
{
- goto Cleanup;
- }
-
- /* Create the parameter data block for object validation */
-
- Data = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_PREDEFINED_DATA));
- if (!Data)
- {
- goto Cleanup;
+ return (AE_OK);
}
- Data->Predefined = Predefined;
- Data->NodeFlags = Node->Flags;
- Data->Pathname = Pathname;
/*
* Check that the type of the main return object is what is expected
* for this predefined name
*/
- Status = AcpiNsCheckObjectType (Data, ReturnObjectPtr,
- Predefined->Info.ExpectedBtypes, ACPI_NOT_PACKAGE_ELEMENT);
+ Status = AcpiNsCheckObjectType (Info, ReturnObjectPtr,
+ Predefined->Info.ExpectedBtypes, ACPI_NOT_PACKAGE_ELEMENT);
if (ACPI_FAILURE (Status))
{
goto Exit;
}
/*
+ *
+ * 4) If there is no return value and it is optional, just return
+ * AE_OK (_WAK).
+ */
+ if (!(*ReturnObjectPtr))
+ {
+ goto Exit;
+ }
+
+ /*
* For returned Package objects, check the type of all sub-objects.
* Note: Package may have been newly created by call above.
*/
if ((*ReturnObjectPtr)->Common.Type == ACPI_TYPE_PACKAGE)
{
- Data->ParentPackage = *ReturnObjectPtr;
- Status = AcpiNsCheckPackage (Data, ReturnObjectPtr);
+ Info->ParentPackage = *ReturnObjectPtr;
+ Status = AcpiNsCheckPackage (Info, ReturnObjectPtr);
if (ACPI_FAILURE (Status))
{
- goto Exit;
+ /* We might be able to fix some errors */
+
+ if ((Status != AE_AML_OPERAND_TYPE) &&
+ (Status != AE_AML_OPERAND_VALUE))
+ {
+ goto Exit;
+ }
}
}
@@ -273,7 +203,7 @@ AcpiNsCheckPredefinedNames (
* performed on a per-name basis, i.e., the code is specific to
* particular predefined names.
*/
- Status = AcpiNsComplexRepairs (Data, Node, Status, ReturnObjectPtr);
+ Status = AcpiNsComplexRepairs (Info, Node, Status, ReturnObjectPtr);
Exit:
/*
@@ -281,715 +211,21 @@ Exit:
* or more objects, mark the parent node to suppress further warning
* messages during the next evaluation of the same method/object.
*/
- if (ACPI_FAILURE (Status) || (Data->Flags & ACPI_OBJECT_REPAIRED))
+ if (ACPI_FAILURE (Status) ||
+ (Info->ReturnFlags & ACPI_OBJECT_REPAIRED))
{
Node->Flags |= ANOBJ_EVALUATED;
}
- ACPI_FREE (Data);
-Cleanup:
- ACPI_FREE (Pathname);
return (Status);
}
/*******************************************************************************
*
- * FUNCTION: AcpiNsCheckParameterCount
- *
- * PARAMETERS: Pathname - Full pathname to the node (for error msgs)
- * Node - Namespace node for the method/object
- * UserParamCount - Number of args passed in by the caller
- * Predefined - Pointer to entry in predefined name table
- *
- * RETURN: None
- *
- * DESCRIPTION: Check that the declared (in ASL/AML) parameter count for a
- * predefined name is what is expected (i.e., what is defined in
- * the ACPI specification for this predefined name.)
- *
- ******************************************************************************/
-
-void
-AcpiNsCheckParameterCount (
- char *Pathname,
- ACPI_NAMESPACE_NODE *Node,
- UINT32 UserParamCount,
- const ACPI_PREDEFINED_INFO *Predefined)
-{
- UINT32 ParamCount;
- UINT32 RequiredParamsCurrent;
- UINT32 RequiredParamsOld;
-
-
- /* Methods have 0-7 parameters. All other types have zero. */
-
- ParamCount = 0;
- if (Node->Type == ACPI_TYPE_METHOD)
- {
- ParamCount = Node->Object->Method.ParamCount;
- }
-
- if (!Predefined)
- {
- /*
- * Check the parameter count for non-predefined methods/objects.
- *
- * Warning if too few or too many arguments have been passed by the
- * caller. An incorrect number of arguments may not cause the method
- * to fail. However, the method will fail if there are too few
- * arguments and the method attempts to use one of the missing ones.
- */
- if (UserParamCount < ParamCount)
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
- "Insufficient arguments - needs %u, found %u",
- ParamCount, UserParamCount));
- }
- else if (UserParamCount > ParamCount)
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
- "Excess arguments - needs %u, found %u",
- ParamCount, UserParamCount));
- }
- return;
- }
-
- /*
- * Validate the user-supplied parameter count.
- * Allow two different legal argument counts (_SCP, etc.)
- */
- RequiredParamsCurrent = Predefined->Info.ParamCount & 0x0F;
- RequiredParamsOld = Predefined->Info.ParamCount >> 4;
-
- if (UserParamCount != ACPI_UINT32_MAX)
- {
- if ((UserParamCount != RequiredParamsCurrent) &&
- (UserParamCount != RequiredParamsOld))
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
- "Parameter count mismatch - "
- "caller passed %u, ACPI requires %u",
- UserParamCount, RequiredParamsCurrent));
- }
- }
-
- /*
- * Check that the ASL-defined parameter count is what is expected for
- * this predefined name (parameter count as defined by the ACPI
- * specification)
- */
- if ((ParamCount != RequiredParamsCurrent) &&
- (ParamCount != RequiredParamsOld))
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, Node->Flags,
- "Parameter count mismatch - ASL declared %u, ACPI requires %u",
- ParamCount, RequiredParamsCurrent));
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsCheckForPredefinedName
- *
- * PARAMETERS: Node - Namespace node for the method/object
- *
- * RETURN: Pointer to entry in predefined table. NULL indicates not found.
- *
- * DESCRIPTION: Check an object name against the predefined object list.
- *
- ******************************************************************************/
-
-const ACPI_PREDEFINED_INFO *
-AcpiNsCheckForPredefinedName (
- ACPI_NAMESPACE_NODE *Node)
-{
- const ACPI_PREDEFINED_INFO *ThisName;
-
-
- /* Quick check for a predefined name, first character must be underscore */
-
- if (Node->Name.Ascii[0] != '_')
- {
- return (NULL);
- }
-
- /* Search info table for a predefined method/object name */
-
- ThisName = PredefinedNames;
- while (ThisName->Info.Name[0])
- {
- if (ACPI_COMPARE_NAME (Node->Name.Ascii, ThisName->Info.Name))
- {
- return (ThisName);
- }
-
- /*
- * Skip next entry in the table if this name returns a Package
- * (next entry contains the package info)
- */
- if (ThisName->Info.ExpectedBtypes & ACPI_RTYPE_PACKAGE)
- {
- ThisName++;
- }
-
- ThisName++;
- }
-
- return (NULL); /* Not found */
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsCheckPackage
- *
- * PARAMETERS: Data - Pointer to validation data structure
- * ReturnObjectPtr - Pointer to the object returned from the
- * evaluation of a method or object
- *
- * RETURN: Status
- *
- * DESCRIPTION: Check a returned package object for the correct count and
- * correct type of all sub-objects.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsCheckPackage (
- ACPI_PREDEFINED_DATA *Data,
- ACPI_OPERAND_OBJECT **ReturnObjectPtr)
-{
- ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
- const ACPI_PREDEFINED_INFO *Package;
- ACPI_OPERAND_OBJECT **Elements;
- ACPI_STATUS Status = AE_OK;
- UINT32 ExpectedCount;
- UINT32 Count;
- UINT32 i;
-
-
- ACPI_FUNCTION_NAME (NsCheckPackage);
-
-
- /* The package info for this name is in the next table entry */
-
- Package = Data->Predefined + 1;
-
- ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
- "%s Validating return Package of Type %X, Count %X\n",
- Data->Pathname, Package->RetInfo.Type, ReturnObject->Package.Count));
-
- /*
- * For variable-length Packages, we can safely remove all embedded
- * and trailing NULL package elements
- */
- AcpiNsRemoveNullElements (Data, Package->RetInfo.Type, ReturnObject);
-
- /* Extract package count and elements array */
-
- Elements = ReturnObject->Package.Elements;
- Count = ReturnObject->Package.Count;
-
- /* The package must have at least one element, else invalid */
-
- if (!Count)
- {
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
- "Return Package has no elements (empty)"));
-
- return (AE_AML_OPERAND_VALUE);
- }
-
- /*
- * Decode the type of the expected package contents
- *
- * PTYPE1 packages contain no subpackages
- * PTYPE2 packages contain sub-packages
- */
- switch (Package->RetInfo.Type)
- {
- case ACPI_PTYPE1_FIXED:
-
- /*
- * The package count is fixed and there are no sub-packages
- *
- * If package is too small, exit.
- * If package is larger than expected, issue warning but continue
- */
- ExpectedCount = Package->RetInfo.Count1 + Package->RetInfo.Count2;
- if (Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
- else if (Count > ExpectedCount)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
- "%s: Return Package is larger than needed - "
- "found %u, expected %u\n",
- Data->Pathname, Count, ExpectedCount));
- }
-
- /* Validate all elements of the returned package */
-
- Status = AcpiNsCheckPackageElements (Data, Elements,
- Package->RetInfo.ObjectType1, Package->RetInfo.Count1,
- Package->RetInfo.ObjectType2, Package->RetInfo.Count2, 0);
- break;
-
-
- case ACPI_PTYPE1_VAR:
-
- /*
- * The package count is variable, there are no sub-packages, and all
- * elements must be of the same type
- */
- for (i = 0; i < Count; i++)
- {
- Status = AcpiNsCheckObjectType (Data, Elements,
- Package->RetInfo.ObjectType1, i);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- Elements++;
- }
- break;
-
-
- case ACPI_PTYPE1_OPTION:
-
- /*
- * The package count is variable, there are no sub-packages. There are
- * a fixed number of required elements, and a variable number of
- * optional elements.
- *
- * Check if package is at least as large as the minimum required
- */
- ExpectedCount = Package->RetInfo3.Count;
- if (Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
-
- /* Variable number of sub-objects */
-
- for (i = 0; i < Count; i++)
- {
- if (i < Package->RetInfo3.Count)
- {
- /* These are the required package elements (0, 1, or 2) */
-
- Status = AcpiNsCheckObjectType (Data, Elements,
- Package->RetInfo3.ObjectType[i], i);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- else
- {
- /* These are the optional package elements */
-
- Status = AcpiNsCheckObjectType (Data, Elements,
- Package->RetInfo3.TailObjectType, i);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- Elements++;
- }
- break;
-
-
- case ACPI_PTYPE2_REV_FIXED:
-
- /* First element is the (Integer) revision */
-
- Status = AcpiNsCheckObjectType (Data, Elements,
- ACPI_RTYPE_INTEGER, 0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- Elements++;
- Count--;
-
- /* Examine the sub-packages */
-
- Status = AcpiNsCheckPackageList (Data, Package, Elements, Count);
- break;
-
-
- case ACPI_PTYPE2_PKG_COUNT:
-
- /* First element is the (Integer) count of sub-packages to follow */
-
- Status = AcpiNsCheckObjectType (Data, Elements,
- ACPI_RTYPE_INTEGER, 0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /*
- * Count cannot be larger than the parent package length, but allow it
- * to be smaller. The >= accounts for the Integer above.
- */
- ExpectedCount = (UINT32) (*Elements)->Integer.Value;
- if (ExpectedCount >= Count)
- {
- goto PackageTooSmall;
- }
-
- Count = ExpectedCount;
- Elements++;
-
- /* Examine the sub-packages */
-
- Status = AcpiNsCheckPackageList (Data, Package, Elements, Count);
- break;
-
-
- case ACPI_PTYPE2:
- case ACPI_PTYPE2_FIXED:
- case ACPI_PTYPE2_MIN:
- case ACPI_PTYPE2_COUNT:
-
- /*
- * These types all return a single Package that consists of a
- * variable number of sub-Packages.
- *
- * First, ensure that the first element is a sub-Package. If not,
- * the BIOS may have incorrectly returned the object as a single
- * package instead of a Package of Packages (a common error if
- * there is only one entry). We may be able to repair this by
- * wrapping the returned Package with a new outer Package.
- */
- if (*Elements && ((*Elements)->Common.Type != ACPI_TYPE_PACKAGE))
- {
- /* Create the new outer package and populate it */
-
- Status = AcpiNsRepairPackageList (Data, ReturnObjectPtr);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /* Update locals to point to the new package (of 1 element) */
-
- ReturnObject = *ReturnObjectPtr;
- Elements = ReturnObject->Package.Elements;
- Count = 1;
- }
-
- /* Examine the sub-packages */
-
- Status = AcpiNsCheckPackageList (Data, Package, Elements, Count);
- break;
-
-
- default:
-
- /* Should not get here if predefined info table is correct */
-
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
- "Invalid internal return type in table entry: %X",
- Package->RetInfo.Type));
-
- return (AE_AML_INTERNAL);
- }
-
- return (Status);
-
-
-PackageTooSmall:
-
- /* Error exit for the case with an incorrect package count */
-
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
- "Return Package is too small - found %u elements, expected %u",
- Count, ExpectedCount));
-
- return (AE_AML_OPERAND_VALUE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsCheckPackageList
- *
- * PARAMETERS: Data - Pointer to validation data structure
- * Package - Pointer to package-specific info for method
- * Elements - Element list of parent package. All elements
- * of this list should be of type Package.
- * Count - Count of subpackages
- *
- * RETURN: Status
- *
- * DESCRIPTION: Examine a list of subpackages
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsCheckPackageList (
- ACPI_PREDEFINED_DATA *Data,
- const ACPI_PREDEFINED_INFO *Package,
- ACPI_OPERAND_OBJECT **Elements,
- UINT32 Count)
-{
- ACPI_OPERAND_OBJECT *SubPackage;
- ACPI_OPERAND_OBJECT **SubElements;
- ACPI_STATUS Status;
- UINT32 ExpectedCount;
- UINT32 i;
- UINT32 j;
-
-
- /*
- * Validate each sub-Package in the parent Package
- *
- * NOTE: assumes list of sub-packages contains no NULL elements.
- * Any NULL elements should have been removed by earlier call
- * to AcpiNsRemoveNullElements.
- */
- for (i = 0; i < Count; i++)
- {
- SubPackage = *Elements;
- SubElements = SubPackage->Package.Elements;
- Data->ParentPackage = SubPackage;
-
- /* Each sub-object must be of type Package */
-
- Status = AcpiNsCheckObjectType (Data, &SubPackage,
- ACPI_RTYPE_PACKAGE, i);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /* Examine the different types of expected sub-packages */
-
- Data->ParentPackage = SubPackage;
- switch (Package->RetInfo.Type)
- {
- case ACPI_PTYPE2:
- case ACPI_PTYPE2_PKG_COUNT:
- case ACPI_PTYPE2_REV_FIXED:
-
- /* Each subpackage has a fixed number of elements */
-
- ExpectedCount = Package->RetInfo.Count1 + Package->RetInfo.Count2;
- if (SubPackage->Package.Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
-
- Status = AcpiNsCheckPackageElements (Data, SubElements,
- Package->RetInfo.ObjectType1,
- Package->RetInfo.Count1,
- Package->RetInfo.ObjectType2,
- Package->RetInfo.Count2, 0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- break;
-
-
- case ACPI_PTYPE2_FIXED:
-
- /* Each sub-package has a fixed length */
-
- ExpectedCount = Package->RetInfo2.Count;
- if (SubPackage->Package.Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
-
- /* Check the type of each sub-package element */
-
- for (j = 0; j < ExpectedCount; j++)
- {
- Status = AcpiNsCheckObjectType (Data, &SubElements[j],
- Package->RetInfo2.ObjectType[j], j);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- break;
-
-
- case ACPI_PTYPE2_MIN:
-
- /* Each sub-package has a variable but minimum length */
-
- ExpectedCount = Package->RetInfo.Count1;
- if (SubPackage->Package.Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
-
- /* Check the type of each sub-package element */
-
- Status = AcpiNsCheckPackageElements (Data, SubElements,
- Package->RetInfo.ObjectType1,
- SubPackage->Package.Count, 0, 0, 0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- break;
-
-
- case ACPI_PTYPE2_COUNT:
-
- /*
- * First element is the (Integer) count of elements, including
- * the count field (the ACPI name is NumElements)
- */
- Status = AcpiNsCheckObjectType (Data, SubElements,
- ACPI_RTYPE_INTEGER, 0);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
-
- /*
- * Make sure package is large enough for the Count and is
- * is as large as the minimum size
- */
- ExpectedCount = (UINT32) (*SubElements)->Integer.Value;
- if (SubPackage->Package.Count < ExpectedCount)
- {
- goto PackageTooSmall;
- }
- if (SubPackage->Package.Count < Package->RetInfo.Count1)
- {
- ExpectedCount = Package->RetInfo.Count1;
- goto PackageTooSmall;
- }
- if (ExpectedCount == 0)
- {
- /*
- * Either the NumEntries element was originally zero or it was
- * a NULL element and repaired to an Integer of value zero.
- * In either case, repair it by setting NumEntries to be the
- * actual size of the subpackage.
- */
- ExpectedCount = SubPackage->Package.Count;
- (*SubElements)->Integer.Value = ExpectedCount;
- }
-
- /* Check the type of each sub-package element */
-
- Status = AcpiNsCheckPackageElements (Data, (SubElements + 1),
- Package->RetInfo.ObjectType1,
- (ExpectedCount - 1), 0, 0, 1);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- break;
-
-
- default: /* Should not get here, type was validated by caller */
-
- return (AE_AML_INTERNAL);
- }
-
- Elements++;
- }
-
- return (AE_OK);
-
-
-PackageTooSmall:
-
- /* The sub-package count was smaller than required */
-
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
- "Return Sub-Package[%u] is too small - found %u elements, expected %u",
- i, SubPackage->Package.Count, ExpectedCount));
-
- return (AE_AML_OPERAND_VALUE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsCheckPackageElements
- *
- * PARAMETERS: Data - Pointer to validation data structure
- * Elements - Pointer to the package elements array
- * Type1 - Object type for first group
- * Count1 - Count for first group
- * Type2 - Object type for second group
- * Count2 - Count for second group
- * StartIndex - Start of the first group of elements
- *
- * RETURN: Status
- *
- * DESCRIPTION: Check that all elements of a package are of the correct object
- * type. Supports up to two groups of different object types.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsCheckPackageElements (
- ACPI_PREDEFINED_DATA *Data,
- ACPI_OPERAND_OBJECT **Elements,
- UINT8 Type1,
- UINT32 Count1,
- UINT8 Type2,
- UINT32 Count2,
- UINT32 StartIndex)
-{
- ACPI_OPERAND_OBJECT **ThisElement = Elements;
- ACPI_STATUS Status;
- UINT32 i;
-
-
- /*
- * Up to two groups of package elements are supported by the data
- * structure. All elements in each group must be of the same type.
- * The second group can have a count of zero.
- */
- for (i = 0; i < Count1; i++)
- {
- Status = AcpiNsCheckObjectType (Data, ThisElement,
- Type1, i + StartIndex);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- ThisElement++;
- }
-
- for (i = 0; i < Count2; i++)
- {
- Status = AcpiNsCheckObjectType (Data, ThisElement,
- Type2, (i + Count1 + StartIndex));
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- ThisElement++;
- }
-
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiNsCheckObjectType
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
* ExpectedBtypes - Bitmap of expected return type(s)
@@ -1004,41 +240,24 @@ AcpiNsCheckPackageElements (
*
******************************************************************************/
-static ACPI_STATUS
+ACPI_STATUS
AcpiNsCheckObjectType (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr,
UINT32 ExpectedBtypes,
UINT32 PackageIndex)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
ACPI_STATUS Status = AE_OK;
- UINT32 ReturnBtype;
- char TypeBuffer[48]; /* Room for 5 types */
-
+ char TypeBuffer[96]; /* Room for 10 types */
- /*
- * If we get a NULL ReturnObject here, it is a NULL package element.
- * Since all extraneous NULL package elements were removed earlier by a
- * call to AcpiNsRemoveNullElements, this is an unexpected NULL element.
- * We will attempt to repair it.
- */
- if (!ReturnObject)
- {
- Status = AcpiNsRepairNullElement (Data, ExpectedBtypes,
- PackageIndex, ReturnObjectPtr);
- if (ACPI_SUCCESS (Status))
- {
- return (AE_OK); /* Repair was successful */
- }
- goto TypeErrorExit;
- }
/* A Namespace node should not get here, but make sure */
- if (ACPI_GET_DESCRIPTOR_TYPE (ReturnObject) == ACPI_DESC_TYPE_NAMED)
+ if (ReturnObject &&
+ ACPI_GET_DESCRIPTOR_TYPE (ReturnObject) == ACPI_DESC_TYPE_NAMED)
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
"Invalid return type - Found a Namespace node [%4.4s] type %s",
ReturnObject->Node.Name.Ascii,
AcpiUtGetTypeName (ReturnObject->Node.Type)));
@@ -1053,55 +272,28 @@ AcpiNsCheckObjectType (
* from all of the predefined names (including elements of returned
* packages)
*/
- switch (ReturnObject->Common.Type)
+ Info->ReturnBtype = AcpiNsGetBitmappedType (ReturnObject);
+ if (Info->ReturnBtype == ACPI_RTYPE_ANY)
{
- case ACPI_TYPE_INTEGER:
- ReturnBtype = ACPI_RTYPE_INTEGER;
- break;
-
- case ACPI_TYPE_BUFFER:
- ReturnBtype = ACPI_RTYPE_BUFFER;
- break;
-
- case ACPI_TYPE_STRING:
- ReturnBtype = ACPI_RTYPE_STRING;
- break;
-
- case ACPI_TYPE_PACKAGE:
- ReturnBtype = ACPI_RTYPE_PACKAGE;
- break;
-
- case ACPI_TYPE_LOCAL_REFERENCE:
- ReturnBtype = ACPI_RTYPE_REFERENCE;
- break;
-
- default:
/* Not one of the supported objects, must be incorrect */
-
goto TypeErrorExit;
}
- /* Is the object one of the expected types? */
+ /* For reference objects, check that the reference type is correct */
- if (ReturnBtype & ExpectedBtypes)
+ if ((Info->ReturnBtype & ExpectedBtypes) == ACPI_RTYPE_REFERENCE)
{
- /* For reference objects, check that the reference type is correct */
-
- if (ReturnObject->Common.Type == ACPI_TYPE_LOCAL_REFERENCE)
- {
- Status = AcpiNsCheckReference (Data, ReturnObject);
- }
-
+ Status = AcpiNsCheckReference (Info, ReturnObject);
return (Status);
}
- /* Type mismatch -- attempt repair of the returned object */
+ /* Attempt simple repair of the returned object if necessary */
- Status = AcpiNsRepairObject (Data, ExpectedBtypes,
- PackageIndex, ReturnObjectPtr);
+ Status = AcpiNsSimpleRepair (Info, ExpectedBtypes,
+ PackageIndex, ReturnObjectPtr);
if (ACPI_SUCCESS (Status))
{
- return (AE_OK); /* Repair was successful */
+ return (AE_OK); /* Successful repair */
}
@@ -1109,17 +301,23 @@ TypeErrorExit:
/* Create a string with all expected types for this predefined object */
- AcpiNsGetExpectedTypes (TypeBuffer, ExpectedBtypes);
+ AcpiUtGetExpectedReturnTypes (TypeBuffer, ExpectedBtypes);
- if (PackageIndex == ACPI_NOT_PACKAGE_ELEMENT)
+ if (!ReturnObject)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Expected return object of type %s",
+ TypeBuffer));
+ }
+ else if (PackageIndex == ACPI_NOT_PACKAGE_ELEMENT)
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
"Return type mismatch - found %s, expected %s",
AcpiUtGetObjectTypeName (ReturnObject), TypeBuffer));
}
else
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
"Return Package type mismatch at index %u - "
"found %s, expected %s", PackageIndex,
AcpiUtGetObjectTypeName (ReturnObject), TypeBuffer));
@@ -1133,7 +331,7 @@ TypeErrorExit:
*
* FUNCTION: AcpiNsCheckReference
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObject - Object returned from the evaluation of a
* method or object
*
@@ -1147,7 +345,7 @@ TypeErrorExit:
static ACPI_STATUS
AcpiNsCheckReference (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT *ReturnObject)
{
@@ -1161,7 +359,7 @@ AcpiNsCheckReference (
return (AE_OK);
}
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
"Return type mismatch - unexpected reference object type [%s] %2.2X",
AcpiUtGetReferenceName (ReturnObject),
ReturnObject->Reference.Class));
@@ -1172,41 +370,66 @@ AcpiNsCheckReference (
/*******************************************************************************
*
- * FUNCTION: AcpiNsGetExpectedTypes
+ * FUNCTION: AcpiNsGetBitmappedType
*
- * PARAMETERS: Buffer - Pointer to where the string is returned
- * ExpectedBtypes - Bitmap of expected return type(s)
+ * PARAMETERS: ReturnObject - Object returned from method/obj evaluation
*
- * RETURN: Buffer is populated with type names.
+ * RETURN: Object return type. ACPI_RTYPE_ANY indicates that the object
+ * type is not supported. ACPI_RTYPE_NONE indicates that no
+ * object was returned (ReturnObject is NULL).
*
- * DESCRIPTION: Translate the expected types bitmap into a string of ascii
- * names of expected types, for use in warning messages.
+ * DESCRIPTION: Convert object type into a bitmapped object return type.
*
******************************************************************************/
-static void
-AcpiNsGetExpectedTypes (
- char *Buffer,
- UINT32 ExpectedBtypes)
+static UINT32
+AcpiNsGetBitmappedType (
+ ACPI_OPERAND_OBJECT *ReturnObject)
{
- UINT32 ThisRtype;
- UINT32 i;
- UINT32 j;
+ UINT32 ReturnBtype;
- j = 1;
- Buffer[0] = 0;
- ThisRtype = ACPI_RTYPE_INTEGER;
+ if (!ReturnObject)
+ {
+ return (ACPI_RTYPE_NONE);
+ }
+
+ /* Map ACPI_OBJECT_TYPE to internal bitmapped type */
- for (i = 0; i < ACPI_NUM_RTYPES; i++)
+ switch (ReturnObject->Common.Type)
{
- /* If one of the expected types, concatenate the name of this type */
+ case ACPI_TYPE_INTEGER:
- if (ExpectedBtypes & ThisRtype)
- {
- ACPI_STRCAT (Buffer, &AcpiRtypeNames[i][j]);
- j = 0; /* Use name separator from now on */
- }
- ThisRtype <<= 1; /* Next Rtype */
+ ReturnBtype = ACPI_RTYPE_INTEGER;
+ break;
+
+ case ACPI_TYPE_BUFFER:
+
+ ReturnBtype = ACPI_RTYPE_BUFFER;
+ break;
+
+ case ACPI_TYPE_STRING:
+
+ ReturnBtype = ACPI_RTYPE_STRING;
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+
+ ReturnBtype = ACPI_RTYPE_PACKAGE;
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+
+ ReturnBtype = ACPI_RTYPE_REFERENCE;
+ break;
+
+ default:
+
+ /* Not one of the supported objects, must be incorrect */
+
+ ReturnBtype = ACPI_RTYPE_ANY;
+ break;
}
+
+ return (ReturnBtype);
}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsprepkg.c b/usr/src/uts/intel/io/acpica/namespace/nsprepkg.c
new file mode 100644
index 0000000000..ae75c1a018
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/namespace/nsprepkg.c
@@ -0,0 +1,788 @@
+/******************************************************************************
+ *
+ * Module Name: nsprepkg - Validation of package objects for predefined names
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acpredef.h"
+
+
+#define _COMPONENT ACPI_NAMESPACE
+ ACPI_MODULE_NAME ("nsprepkg")
+
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiNsCheckPackageList (
+ ACPI_EVALUATE_INFO *Info,
+ const ACPI_PREDEFINED_INFO *Package,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT32 Count);
+
+static ACPI_STATUS
+AcpiNsCheckPackageElements (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT8 Type1,
+ UINT32 Count1,
+ UINT8 Type2,
+ UINT32 Count2,
+ UINT32 StartIndex);
+
+static ACPI_STATUS
+AcpiNsCustomPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT32 Count);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckPackage
+ *
+ * PARAMETERS: Info - Method execution information block
+ * ReturnObjectPtr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check a returned package object for the correct count and
+ * correct type of all sub-objects.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiNsCheckPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr)
+{
+ ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
+ const ACPI_PREDEFINED_INFO *Package;
+ ACPI_OPERAND_OBJECT **Elements;
+ ACPI_STATUS Status = AE_OK;
+ UINT32 ExpectedCount;
+ UINT32 Count;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_NAME (NsCheckPackage);
+
+
+ /* The package info for this name is in the next table entry */
+
+ Package = Info->Predefined + 1;
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
+ "%s Validating return Package of Type %X, Count %X\n",
+ Info->FullPathname, Package->RetInfo.Type,
+ ReturnObject->Package.Count));
+
+ /*
+ * For variable-length Packages, we can safely remove all embedded
+ * and trailing NULL package elements
+ */
+ AcpiNsRemoveNullElements (Info, Package->RetInfo.Type, ReturnObject);
+
+ /* Extract package count and elements array */
+
+ Elements = ReturnObject->Package.Elements;
+ Count = ReturnObject->Package.Count;
+
+ /*
+ * Most packages must have at least one element. The only exception
+ * is the variable-length package (ACPI_PTYPE1_VAR).
+ */
+ if (!Count)
+ {
+ if (Package->RetInfo.Type == ACPI_PTYPE1_VAR)
+ {
+ return (AE_OK);
+ }
+
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Return Package has no elements (empty)"));
+
+ return (AE_AML_OPERAND_VALUE);
+ }
+
+ /*
+ * Decode the type of the expected package contents
+ *
+ * PTYPE1 packages contain no subpackages
+ * PTYPE2 packages contain subpackages
+ */
+ switch (Package->RetInfo.Type)
+ {
+ case ACPI_PTYPE_CUSTOM:
+
+ Status = AcpiNsCustomPackage (Info, Elements, Count);
+ break;
+
+ case ACPI_PTYPE1_FIXED:
+ /*
+ * The package count is fixed and there are no subpackages
+ *
+ * If package is too small, exit.
+ * If package is larger than expected, issue warning but continue
+ */
+ ExpectedCount = Package->RetInfo.Count1 + Package->RetInfo.Count2;
+ if (Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+ else if (Count > ExpectedCount)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
+ "%s: Return Package is larger than needed - "
+ "found %u, expected %u\n",
+ Info->FullPathname, Count, ExpectedCount));
+ }
+
+ /* Validate all elements of the returned package */
+
+ Status = AcpiNsCheckPackageElements (Info, Elements,
+ Package->RetInfo.ObjectType1, Package->RetInfo.Count1,
+ Package->RetInfo.ObjectType2, Package->RetInfo.Count2, 0);
+ break;
+
+ case ACPI_PTYPE1_VAR:
+ /*
+ * The package count is variable, there are no subpackages, and all
+ * elements must be of the same type
+ */
+ for (i = 0; i < Count; i++)
+ {
+ Status = AcpiNsCheckObjectType (Info, Elements,
+ Package->RetInfo.ObjectType1, i);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ Elements++;
+ }
+ break;
+
+ case ACPI_PTYPE1_OPTION:
+ /*
+ * The package count is variable, there are no subpackages. There are
+ * a fixed number of required elements, and a variable number of
+ * optional elements.
+ *
+ * Check if package is at least as large as the minimum required
+ */
+ ExpectedCount = Package->RetInfo3.Count;
+ if (Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ /* Variable number of sub-objects */
+
+ for (i = 0; i < Count; i++)
+ {
+ if (i < Package->RetInfo3.Count)
+ {
+ /* These are the required package elements (0, 1, or 2) */
+
+ Status = AcpiNsCheckObjectType (Info, Elements,
+ Package->RetInfo3.ObjectType[i], i);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+ else
+ {
+ /* These are the optional package elements */
+
+ Status = AcpiNsCheckObjectType (Info, Elements,
+ Package->RetInfo3.TailObjectType, i);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+
+ Elements++;
+ }
+ break;
+
+ case ACPI_PTYPE2_REV_FIXED:
+
+ /* First element is the (Integer) revision */
+
+ Status = AcpiNsCheckObjectType (
+ Info, Elements, ACPI_RTYPE_INTEGER, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ Elements++;
+ Count--;
+
+ /* Examine the subpackages */
+
+ Status = AcpiNsCheckPackageList (Info, Package, Elements, Count);
+ break;
+
+ case ACPI_PTYPE2_PKG_COUNT:
+
+ /* First element is the (Integer) count of subpackages to follow */
+
+ Status = AcpiNsCheckObjectType (
+ Info, Elements, ACPI_RTYPE_INTEGER, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /*
+ * Count cannot be larger than the parent package length, but allow it
+ * to be smaller. The >= accounts for the Integer above.
+ */
+ ExpectedCount = (UINT32) (*Elements)->Integer.Value;
+ if (ExpectedCount >= Count)
+ {
+ goto PackageTooSmall;
+ }
+
+ Count = ExpectedCount;
+ Elements++;
+
+ /* Examine the subpackages */
+
+ Status = AcpiNsCheckPackageList (Info, Package, Elements, Count);
+ break;
+
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_FIXED:
+ case ACPI_PTYPE2_MIN:
+ case ACPI_PTYPE2_COUNT:
+ case ACPI_PTYPE2_FIX_VAR:
+ /*
+ * These types all return a single Package that consists of a
+ * variable number of subpackages.
+ *
+ * First, ensure that the first element is a subpackage. If not,
+ * the BIOS may have incorrectly returned the object as a single
+ * package instead of a Package of Packages (a common error if
+ * there is only one entry). We may be able to repair this by
+ * wrapping the returned Package with a new outer Package.
+ */
+ if (*Elements && ((*Elements)->Common.Type != ACPI_TYPE_PACKAGE))
+ {
+ /* Create the new outer package and populate it */
+
+ Status = AcpiNsWrapWithPackage (
+ Info, ReturnObject, ReturnObjectPtr);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /* Update locals to point to the new package (of 1 element) */
+
+ ReturnObject = *ReturnObjectPtr;
+ Elements = ReturnObject->Package.Elements;
+ Count = 1;
+ }
+
+ /* Examine the subpackages */
+
+ Status = AcpiNsCheckPackageList (Info, Package, Elements, Count);
+ break;
+
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Returns a variable list of packages, each with a variable list
+ * of objects.
+ */
+ break;
+
+ case ACPI_PTYPE2_UUID_PAIR:
+
+ /* The package must contain pairs of (UUID + type) */
+
+ if (Count & 1)
+ {
+ ExpectedCount = Count + 1;
+ goto PackageTooSmall;
+ }
+
+ while (Count > 0)
+ {
+ Status = AcpiNsCheckObjectType(Info, Elements,
+ Package->RetInfo.ObjectType1, 0);
+ if (ACPI_FAILURE(Status))
+ {
+ return (Status);
+ }
+
+ /* Validate length of the UUID buffer */
+
+ if ((*Elements)->Buffer.Length != 16)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname,
+ Info->NodeFlags, "Invalid length for UUID Buffer"));
+ return (AE_AML_OPERAND_VALUE);
+ }
+
+ Status = AcpiNsCheckObjectType(Info, Elements + 1,
+ Package->RetInfo.ObjectType2, 0);
+ if (ACPI_FAILURE(Status))
+ {
+ return (Status);
+ }
+
+ Elements += 2;
+ Count -= 2;
+ }
+ break;
+
+ default:
+
+ /* Should not get here if predefined info table is correct */
+
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Invalid internal return type in table entry: %X",
+ Package->RetInfo.Type));
+
+ return (AE_AML_INTERNAL);
+ }
+
+ return (Status);
+
+
+PackageTooSmall:
+
+ /* Error exit for the case with an incorrect package count */
+
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Return Package is too small - found %u elements, expected %u",
+ Count, ExpectedCount));
+
+ return (AE_AML_OPERAND_VALUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckPackageList
+ *
+ * PARAMETERS: Info - Method execution information block
+ * Package - Pointer to package-specific info for method
+ * Elements - Element list of parent package. All elements
+ * of this list should be of type Package.
+ * Count - Count of subpackages
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Examine a list of subpackages
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiNsCheckPackageList (
+ ACPI_EVALUATE_INFO *Info,
+ const ACPI_PREDEFINED_INFO *Package,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT32 Count)
+{
+ ACPI_OPERAND_OBJECT *SubPackage;
+ ACPI_OPERAND_OBJECT **SubElements;
+ ACPI_STATUS Status;
+ UINT32 ExpectedCount;
+ UINT32 i;
+ UINT32 j;
+
+
+ /*
+ * Validate each subpackage in the parent Package
+ *
+ * NOTE: assumes list of subpackages contains no NULL elements.
+ * Any NULL elements should have been removed by earlier call
+ * to AcpiNsRemoveNullElements.
+ */
+ for (i = 0; i < Count; i++)
+ {
+ SubPackage = *Elements;
+ SubElements = SubPackage->Package.Elements;
+ Info->ParentPackage = SubPackage;
+
+ /* Each sub-object must be of type Package */
+
+ Status = AcpiNsCheckObjectType (Info, &SubPackage,
+ ACPI_RTYPE_PACKAGE, i);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /* Examine the different types of expected subpackages */
+
+ Info->ParentPackage = SubPackage;
+ switch (Package->RetInfo.Type)
+ {
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_PKG_COUNT:
+ case ACPI_PTYPE2_REV_FIXED:
+
+ /* Each subpackage has a fixed number of elements */
+
+ ExpectedCount = Package->RetInfo.Count1 + Package->RetInfo.Count2;
+ if (SubPackage->Package.Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ Status = AcpiNsCheckPackageElements (Info, SubElements,
+ Package->RetInfo.ObjectType1,
+ Package->RetInfo.Count1,
+ Package->RetInfo.ObjectType2,
+ Package->RetInfo.Count2, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ case ACPI_PTYPE2_FIX_VAR:
+ /*
+ * Each subpackage has a fixed number of elements and an
+ * optional element
+ */
+ ExpectedCount = Package->RetInfo.Count1 + Package->RetInfo.Count2;
+ if (SubPackage->Package.Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ Status = AcpiNsCheckPackageElements (Info, SubElements,
+ Package->RetInfo.ObjectType1,
+ Package->RetInfo.Count1,
+ Package->RetInfo.ObjectType2,
+ SubPackage->Package.Count - Package->RetInfo.Count1, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Each subpackage has a fixed or variable number of elements
+ */
+ break;
+
+ case ACPI_PTYPE2_FIXED:
+
+ /* Each subpackage has a fixed length */
+
+ ExpectedCount = Package->RetInfo2.Count;
+ if (SubPackage->Package.Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ /* Check the type of each subpackage element */
+
+ for (j = 0; j < ExpectedCount; j++)
+ {
+ Status = AcpiNsCheckObjectType (Info, &SubElements[j],
+ Package->RetInfo2.ObjectType[j], j);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+ break;
+
+ case ACPI_PTYPE2_MIN:
+
+ /* Each subpackage has a variable but minimum length */
+
+ ExpectedCount = Package->RetInfo.Count1;
+ if (SubPackage->Package.Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ /* Check the type of each subpackage element */
+
+ Status = AcpiNsCheckPackageElements (Info, SubElements,
+ Package->RetInfo.ObjectType1,
+ SubPackage->Package.Count, 0, 0, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ case ACPI_PTYPE2_COUNT:
+ /*
+ * First element is the (Integer) count of elements, including
+ * the count field (the ACPI name is NumElements)
+ */
+ Status = AcpiNsCheckObjectType (Info, SubElements,
+ ACPI_RTYPE_INTEGER, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /*
+ * Make sure package is large enough for the Count and is
+ * is as large as the minimum size
+ */
+ ExpectedCount = (UINT32) (*SubElements)->Integer.Value;
+ if (SubPackage->Package.Count < ExpectedCount)
+ {
+ goto PackageTooSmall;
+ }
+
+ if (SubPackage->Package.Count < Package->RetInfo.Count1)
+ {
+ ExpectedCount = Package->RetInfo.Count1;
+ goto PackageTooSmall;
+ }
+
+ if (ExpectedCount == 0)
+ {
+ /*
+ * Either the NumEntries element was originally zero or it was
+ * a NULL element and repaired to an Integer of value zero.
+ * In either case, repair it by setting NumEntries to be the
+ * actual size of the subpackage.
+ */
+ ExpectedCount = SubPackage->Package.Count;
+ (*SubElements)->Integer.Value = ExpectedCount;
+ }
+
+ /* Check the type of each subpackage element */
+
+ Status = AcpiNsCheckPackageElements (Info, (SubElements + 1),
+ Package->RetInfo.ObjectType1,
+ (ExpectedCount - 1), 0, 0, 1);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ break;
+
+ default: /* Should not get here, type was validated by caller */
+
+ return (AE_AML_INTERNAL);
+ }
+
+ Elements++;
+ }
+
+ return (AE_OK);
+
+
+PackageTooSmall:
+
+ /* The subpackage count was smaller than required */
+
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Return SubPackage[%u] is too small - found %u elements, expected %u",
+ i, SubPackage->Package.Count, ExpectedCount));
+
+ return (AE_AML_OPERAND_VALUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCustomPackage
+ *
+ * PARAMETERS: Info - Method execution information block
+ * Elements - Pointer to the package elements array
+ * Count - Element count for the package
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check a returned package object for the correct count and
+ * correct type of all sub-objects.
+ *
+ * NOTE: Currently used for the _BIX method only. When needed for two or more
+ * methods, probably a detect/dispatch mechanism will be required.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiNsCustomPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT32 Count)
+{
+ UINT32 ExpectedCount;
+ UINT32 Version;
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_NAME (NsCustomPackage);
+
+
+ /* Get version number, must be Integer */
+
+ if ((*Elements)->Common.Type != ACPI_TYPE_INTEGER)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Return Package has invalid object type for version number"));
+ return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
+ }
+
+ Version = (UINT32) (*Elements)->Integer.Value;
+ ExpectedCount = 21; /* Version 1 */
+
+ if (Version == 0)
+ {
+ ExpectedCount = 20; /* Version 0 */
+ }
+
+ if (Count < ExpectedCount)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname, Info->NodeFlags,
+ "Return Package is too small - found %u elements, expected %u",
+ Count, ExpectedCount));
+ return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
+ }
+ else if (Count > ExpectedCount)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
+ "%s: Return Package is larger than needed - "
+ "found %u, expected %u\n",
+ Info->FullPathname, Count, ExpectedCount));
+ }
+
+ /* Validate all elements of the returned package */
+
+ Status = AcpiNsCheckPackageElements (Info, Elements,
+ ACPI_RTYPE_INTEGER, 16,
+ ACPI_RTYPE_STRING, 4, 0);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Version 1 has a single trailing integer */
+
+ if (Version > 0)
+ {
+ Status = AcpiNsCheckPackageElements (Info, Elements + 20,
+ ACPI_RTYPE_INTEGER, 1, 0, 0, 20);
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiNsCheckPackageElements
+ *
+ * PARAMETERS: Info - Method execution information block
+ * Elements - Pointer to the package elements array
+ * Type1 - Object type for first group
+ * Count1 - Count for first group
+ * Type2 - Object type for second group
+ * Count2 - Count for second group
+ * StartIndex - Start of the first group of elements
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Check that all elements of a package are of the correct object
+ * type. Supports up to two groups of different object types.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiNsCheckPackageElements (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **Elements,
+ UINT8 Type1,
+ UINT32 Count1,
+ UINT8 Type2,
+ UINT32 Count2,
+ UINT32 StartIndex)
+{
+ ACPI_OPERAND_OBJECT **ThisElement = Elements;
+ ACPI_STATUS Status;
+ UINT32 i;
+
+
+ /*
+ * Up to two groups of package elements are supported by the data
+ * structure. All elements in each group must be of the same type.
+ * The second group can have a count of zero.
+ */
+ for (i = 0; i < Count1; i++)
+ {
+ Status = AcpiNsCheckObjectType (Info, ThisElement,
+ Type1, i + StartIndex);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ ThisElement++;
+ }
+
+ for (i = 0; i < Count2; i++)
+ {
+ Status = AcpiNsCheckObjectType (Info, ThisElement,
+ Type2, (i + Count1 + StartIndex));
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ ThisElement++;
+ }
+
+ return (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsrepair.c b/usr/src/uts/intel/io/acpica/namespace/nsrepair.c
index be73953ded..524fcf6671 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsrepair.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsrepair.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,13 +41,12 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSREPAIR_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
#include "acinterp.h"
#include "acpredef.h"
+#include "amlresrc.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME ("nsrepair")
@@ -75,42 +74,67 @@
* Buffer -> Package of Integers
* Package -> Package of one Package
*
- * Additional possible repairs:
+ * Additional conversions that are available:
+ * Convert a null return or zero return value to an EndTag descriptor
+ * Convert an ASCII string to a Unicode buffer
*
+ * An incorrect standalone object is wrapped with required outer package
+ *
+ * Additional possible repairs:
* Required package elements that are NULL replaced by Integer/String/Buffer
- * Incorrect standalone package wrapped with required outer package
*
******************************************************************************/
/* Local prototypes */
-static ACPI_STATUS
-AcpiNsConvertToInteger (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject);
+static const ACPI_SIMPLE_REPAIR_INFO *
+AcpiNsMatchSimpleRepair (
+ ACPI_NAMESPACE_NODE *Node,
+ UINT32 ReturnBtype,
+ UINT32 PackageIndex);
-static ACPI_STATUS
-AcpiNsConvertToString (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject);
-static ACPI_STATUS
-AcpiNsConvertToBuffer (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject);
+/*
+ * Special but simple repairs for some names.
+ *
+ * 2nd argument: Unexpected types that can be repaired
+ */
+static const ACPI_SIMPLE_REPAIR_INFO AcpiObjectRepairInfo[] =
+{
+ /* Resource descriptor conversions */
-static ACPI_STATUS
-AcpiNsConvertToPackage (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject);
+ { "_CRS", ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ AcpiNsConvertToResource },
+ { "_DMA", ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ AcpiNsConvertToResource },
+ { "_PRS", ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | ACPI_RTYPE_NONE,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ AcpiNsConvertToResource },
+
+ /* Object reference conversions */
+
+ { "_DEP", ACPI_RTYPE_STRING, ACPI_ALL_PACKAGE_ELEMENTS,
+ AcpiNsConvertToReference },
+
+ /* Unicode conversions */
+
+ { "_MLS", ACPI_RTYPE_STRING, 1,
+ AcpiNsConvertToUnicode },
+ { "_STR", ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER,
+ ACPI_NOT_PACKAGE_ELEMENT,
+ AcpiNsConvertToUnicode },
+ { {0,0,0,0}, 0, 0, NULL } /* Table terminator */
+};
/*******************************************************************************
*
- * FUNCTION: AcpiNsRepairObject
+ * FUNCTION: AcpiNsSimpleRepair
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ExpectedBtypes - Object types expected
* PackageIndex - Index of object within parent package (if
* applicable - ACPI_NOT_PACKAGE_ELEMENT
@@ -126,26 +150,100 @@ AcpiNsConvertToPackage (
******************************************************************************/
ACPI_STATUS
-AcpiNsRepairObject (
- ACPI_PREDEFINED_DATA *Data,
+AcpiNsSimpleRepair (
+ ACPI_EVALUATE_INFO *Info,
UINT32 ExpectedBtypes,
UINT32 PackageIndex,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
- ACPI_OPERAND_OBJECT *NewObject;
+ ACPI_OPERAND_OBJECT *NewObject = NULL;
ACPI_STATUS Status;
+ const ACPI_SIMPLE_REPAIR_INFO *Predefined;
- ACPI_FUNCTION_NAME (NsRepairObject);
+ ACPI_FUNCTION_NAME (NsSimpleRepair);
/*
+ * Special repairs for certain names that are in the repair table.
+ * Check if this name is in the list of repairable names.
+ */
+ Predefined = AcpiNsMatchSimpleRepair (Info->Node,
+ Info->ReturnBtype, PackageIndex);
+ if (Predefined)
+ {
+ if (!ReturnObject)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname,
+ ACPI_WARN_ALWAYS, "Missing expected return value"));
+ }
+
+ Status = Predefined->ObjectConverter (Info->Node, ReturnObject,
+ &NewObject);
+ if (ACPI_FAILURE (Status))
+ {
+ /* A fatal error occurred during a conversion */
+
+ ACPI_EXCEPTION ((AE_INFO, Status,
+ "During return object analysis"));
+ return (Status);
+ }
+ if (NewObject)
+ {
+ goto ObjectRepaired;
+ }
+ }
+
+ /*
+ * Do not perform simple object repair unless the return type is not
+ * expected.
+ */
+ if (Info->ReturnBtype & ExpectedBtypes)
+ {
+ return (AE_OK);
+ }
+
+ /*
* At this point, we know that the type of the returned object was not
* one of the expected types for this predefined name. Attempt to
* repair the object by converting it to one of the expected object
* types for this predefined name.
*/
+
+ /*
+ * If there is no return value, check if we require a return value for
+ * this predefined name. Either one return value is expected, or none,
+ * for both methods and other objects.
+ *
+ * Try to fix if there was no return object. Warning if failed to fix.
+ */
+ if (!ReturnObject)
+ {
+ if (ExpectedBtypes && (!(ExpectedBtypes & ACPI_RTYPE_NONE)))
+ {
+ if (PackageIndex != ACPI_NOT_PACKAGE_ELEMENT)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname,
+ ACPI_WARN_ALWAYS, "Found unexpected NULL package element"));
+
+ Status = AcpiNsRepairNullElement (Info, ExpectedBtypes,
+ PackageIndex, ReturnObjectPtr);
+ if (ACPI_SUCCESS (Status))
+ {
+ return (AE_OK); /* Repair was successful */
+ }
+ }
+ else
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Info->FullPathname,
+ ACPI_WARN_ALWAYS, "Missing expected return value"));
+ }
+
+ return (AE_AML_NO_RETURN_VALUE);
+ }
+ }
+
if (ExpectedBtypes & ACPI_RTYPE_INTEGER)
{
Status = AcpiNsConvertToInteger (ReturnObject, &NewObject);
@@ -172,10 +270,24 @@ AcpiNsRepairObject (
}
if (ExpectedBtypes & ACPI_RTYPE_PACKAGE)
{
- Status = AcpiNsConvertToPackage (ReturnObject, &NewObject);
+ /*
+ * A package is expected. We will wrap the existing object with a
+ * new package object. It is often the case that if a variable-length
+ * package is required, but there is only a single object needed, the
+ * BIOS will return that object instead of wrapping it with a Package
+ * object. Note: after the wrapping, the package will be validated
+ * for correct contents (expected object type or types).
+ */
+ Status = AcpiNsWrapWithPackage (Info, ReturnObject, &NewObject);
if (ACPI_SUCCESS (Status))
{
- goto ObjectRepaired;
+ /*
+ * The original object just had its reference count
+ * incremented for being inserted into the new package.
+ */
+ *ReturnObjectPtr = NewObject; /* New Package object */
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
}
}
@@ -188,32 +300,38 @@ ObjectRepaired:
/* Object was successfully repaired */
- /*
- * If the original object is a package element, we need to:
- * 1. Set the reference count of the new object to match the
- * reference count of the old object.
- * 2. Decrement the reference count of the original object.
- */
if (PackageIndex != ACPI_NOT_PACKAGE_ELEMENT)
{
- NewObject->Common.ReferenceCount =
- ReturnObject->Common.ReferenceCount;
-
- if (ReturnObject->Common.ReferenceCount > 1)
+ /*
+ * The original object is a package element. We need to
+ * decrement the reference count of the original object,
+ * for removing it from the package.
+ *
+ * However, if the original object was just wrapped with a
+ * package object as part of the repair, we don't need to
+ * change the reference count.
+ */
+ if (!(Info->ReturnFlags & ACPI_OBJECT_WRAPPED))
{
- ReturnObject->Common.ReferenceCount--;
+ NewObject->Common.ReferenceCount =
+ ReturnObject->Common.ReferenceCount;
+
+ if (ReturnObject->Common.ReferenceCount > 1)
+ {
+ ReturnObject->Common.ReferenceCount--;
+ }
}
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
- "%s: Converted %s to expected %s at index %u\n",
- Data->Pathname, AcpiUtGetObjectTypeName (ReturnObject),
+ "%s: Converted %s to expected %s at Package index %u\n",
+ Info->FullPathname, AcpiUtGetObjectTypeName (ReturnObject),
AcpiUtGetObjectTypeName (NewObject), PackageIndex));
}
else
{
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
"%s: Converted %s to expected %s\n",
- Data->Pathname, AcpiUtGetObjectTypeName (ReturnObject),
+ Info->FullPathname, AcpiUtGetObjectTypeName (ReturnObject),
AcpiUtGetObjectTypeName (NewObject)));
}
@@ -221,343 +339,59 @@ ObjectRepaired:
AcpiUtRemoveReference (ReturnObject);
*ReturnObjectPtr = NewObject;
- Data->Flags |= ACPI_OBJECT_REPAIRED;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
return (AE_OK);
}
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsConvertToInteger
- *
- * PARAMETERS: OriginalObject - Object to be converted
- * ReturnObject - Where the new converted object is returned
- *
- * RETURN: Status. AE_OK if conversion was successful.
- *
- * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsConvertToInteger (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject)
-{
- ACPI_OPERAND_OBJECT *NewObject;
- ACPI_STATUS Status;
- UINT64 Value = 0;
- UINT32 i;
-
-
- switch (OriginalObject->Common.Type)
- {
- case ACPI_TYPE_STRING:
-
- /* String-to-Integer conversion */
-
- Status = AcpiUtStrtoul64 (OriginalObject->String.Pointer,
- ACPI_ANY_BASE, &Value);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- break;
-
- case ACPI_TYPE_BUFFER:
-
- /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */
-
- if (OriginalObject->Buffer.Length > 8)
- {
- return (AE_AML_OPERAND_TYPE);
- }
-
- /* Extract each buffer byte to create the integer */
-
- for (i = 0; i < OriginalObject->Buffer.Length; i++)
- {
- Value |= ((UINT64) OriginalObject->Buffer.Pointer[i] << (i * 8));
- }
- break;
-
- default:
- return (AE_AML_OPERAND_TYPE);
- }
-
- NewObject = AcpiUtCreateIntegerObject (Value);
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
-
- *ReturnObject = NewObject;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
+/******************************************************************************
*
- * FUNCTION: AcpiNsConvertToString
+ * FUNCTION: AcpiNsMatchSimpleRepair
*
- * PARAMETERS: OriginalObject - Object to be converted
- * ReturnObject - Where the new converted object is returned
+ * PARAMETERS: Node - Namespace node for the method/object
+ * ReturnBtype - Object type that was returned
+ * PackageIndex - Index of object within parent package (if
+ * applicable - ACPI_NOT_PACKAGE_ELEMENT
+ * otherwise)
*
- * RETURN: Status. AE_OK if conversion was successful.
+ * RETURN: Pointer to entry in repair table. NULL indicates not found.
*
- * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String.
+ * DESCRIPTION: Check an object name against the repairable object list.
*
- ******************************************************************************/
+ *****************************************************************************/
-static ACPI_STATUS
-AcpiNsConvertToString (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject)
+static const ACPI_SIMPLE_REPAIR_INFO *
+AcpiNsMatchSimpleRepair (
+ ACPI_NAMESPACE_NODE *Node,
+ UINT32 ReturnBtype,
+ UINT32 PackageIndex)
{
- ACPI_OPERAND_OBJECT *NewObject;
- ACPI_SIZE Length;
- ACPI_STATUS Status;
-
-
- switch (OriginalObject->Common.Type)
- {
- case ACPI_TYPE_INTEGER:
- /*
- * Integer-to-String conversion. Commonly, convert
- * an integer of value 0 to a NULL string. The last element of
- * _BIF and _BIX packages occasionally need this fix.
- */
- if (OriginalObject->Integer.Value == 0)
- {
- /* Allocate a new NULL string object */
+ const ACPI_SIMPLE_REPAIR_INFO *ThisName;
- NewObject = AcpiUtCreateStringObject (0);
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
- }
- else
- {
- Status = AcpiExConvertToString (OriginalObject, &NewObject,
- ACPI_IMPLICIT_CONVERT_HEX);
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
- break;
- case ACPI_TYPE_BUFFER:
- /*
- * Buffer-to-String conversion. Use a ToString
- * conversion, no transform performed on the buffer data. The best
- * example of this is the _BIF method, where the string data from
- * the battery is often (incorrectly) returned as buffer object(s).
- */
- Length = 0;
- while ((Length < OriginalObject->Buffer.Length) &&
- (OriginalObject->Buffer.Pointer[Length]))
- {
- Length++;
- }
-
- /* Allocate a new string object */
-
- NewObject = AcpiUtCreateStringObject (Length);
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
+ /* Search info table for a repairable predefined method/object name */
- /*
- * Copy the raw buffer data with no transform. String is already NULL
- * terminated at Length+1.
- */
- ACPI_MEMCPY (NewObject->String.Pointer,
- OriginalObject->Buffer.Pointer, Length);
- break;
-
- default:
- return (AE_AML_OPERAND_TYPE);
- }
-
- *ReturnObject = NewObject;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsConvertToBuffer
- *
- * PARAMETERS: OriginalObject - Object to be converted
- * ReturnObject - Where the new converted object is returned
- *
- * RETURN: Status. AE_OK if conversion was successful.
- *
- * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsConvertToBuffer (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject)
-{
- ACPI_OPERAND_OBJECT *NewObject;
- ACPI_STATUS Status;
- ACPI_OPERAND_OBJECT **Elements;
- UINT32 *DwordBuffer;
- UINT32 Count;
- UINT32 i;
-
-
- switch (OriginalObject->Common.Type)
+ ThisName = AcpiObjectRepairInfo;
+ while (ThisName->ObjectConverter)
{
- case ACPI_TYPE_INTEGER:
- /*
- * Integer-to-Buffer conversion.
- * Convert the Integer to a packed-byte buffer. _MAT and other
- * objects need this sometimes, if a read has been performed on a
- * Field object that is less than or equal to the global integer
- * size (32 or 64 bits).
- */
- Status = AcpiExConvertToBuffer (OriginalObject, &NewObject);
- if (ACPI_FAILURE (Status))
+ if (ACPI_COMPARE_NAME (Node->Name.Ascii, ThisName->Name))
{
- return (Status);
- }
- break;
-
- case ACPI_TYPE_STRING:
-
- /* String-to-Buffer conversion. Simple data copy */
-
- NewObject = AcpiUtCreateBufferObject (OriginalObject->String.Length);
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
-
- ACPI_MEMCPY (NewObject->Buffer.Pointer,
- OriginalObject->String.Pointer, OriginalObject->String.Length);
- break;
-
- case ACPI_TYPE_PACKAGE:
- /*
- * This case is often seen for predefined names that must return a
- * Buffer object with multiple DWORD integers within. For example,
- * _FDE and _GTM. The Package can be converted to a Buffer.
- */
+ /* Check if we can actually repair this name/type combination */
- /* All elements of the Package must be integers */
-
- Elements = OriginalObject->Package.Elements;
- Count = OriginalObject->Package.Count;
-
- for (i = 0; i < Count; i++)
- {
- if ((!*Elements) ||
- ((*Elements)->Common.Type != ACPI_TYPE_INTEGER))
+ if ((ReturnBtype & ThisName->UnexpectedBtypes) &&
+ (ThisName->PackageIndex == ACPI_ALL_PACKAGE_ELEMENTS ||
+ PackageIndex == ThisName->PackageIndex))
{
- return (AE_AML_OPERAND_TYPE);
+ return (ThisName);
}
- Elements++;
- }
- /* Create the new buffer object to replace the Package */
-
- NewObject = AcpiUtCreateBufferObject (ACPI_MUL_4 (Count));
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
-
- /* Copy the package elements (integers) to the buffer as DWORDs */
-
- Elements = OriginalObject->Package.Elements;
- DwordBuffer = ACPI_CAST_PTR (UINT32, NewObject->Buffer.Pointer);
-
- for (i = 0; i < Count; i++)
- {
- *DwordBuffer = (UINT32) (*Elements)->Integer.Value;
- DwordBuffer++;
- Elements++;
+ return (NULL);
}
- break;
- default:
- return (AE_AML_OPERAND_TYPE);
+ ThisName++;
}
- *ReturnObject = NewObject;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsConvertToPackage
- *
- * PARAMETERS: OriginalObject - Object to be converted
- * ReturnObject - Where the new converted object is returned
- *
- * RETURN: Status. AE_OK if conversion was successful.
- *
- * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of
- * the buffer is converted to a single integer package element.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiNsConvertToPackage (
- ACPI_OPERAND_OBJECT *OriginalObject,
- ACPI_OPERAND_OBJECT **ReturnObject)
-{
- ACPI_OPERAND_OBJECT *NewObject;
- ACPI_OPERAND_OBJECT **Elements;
- UINT32 Length;
- UINT8 *Buffer;
-
-
- switch (OriginalObject->Common.Type)
- {
- case ACPI_TYPE_BUFFER:
-
- /* Buffer-to-Package conversion */
-
- Length = OriginalObject->Buffer.Length;
- NewObject = AcpiUtCreatePackageObject (Length);
- if (!NewObject)
- {
- return (AE_NO_MEMORY);
- }
-
- /* Convert each buffer byte to an integer package element */
-
- Elements = NewObject->Package.Elements;
- Buffer = OriginalObject->Buffer.Pointer;
-
- while (Length--)
- {
- *Elements = AcpiUtCreateIntegerObject ((UINT64) *Buffer);
- if (!*Elements)
- {
- AcpiUtRemoveReference (NewObject);
- return (AE_NO_MEMORY);
- }
- Elements++;
- Buffer++;
- }
- break;
-
- default:
- return (AE_AML_OPERAND_TYPE);
- }
-
- *ReturnObject = NewObject;
- return (AE_OK);
+ return (NULL); /* Name was not found in the repair table */
}
@@ -565,7 +399,7 @@ AcpiNsConvertToPackage (
*
* FUNCTION: AcpiNsRepairNullElement
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ExpectedBtypes - Object types expected
* PackageIndex - Index of object within parent package (if
* applicable - ACPI_NOT_PACKAGE_ELEMENT
@@ -581,7 +415,7 @@ AcpiNsConvertToPackage (
ACPI_STATUS
AcpiNsRepairNullElement (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
UINT32 ExpectedBtypes,
UINT32 PackageIndex,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
@@ -638,14 +472,16 @@ AcpiNsRepairNullElement (
/* Set the reference count according to the parent Package object */
- NewObject->Common.ReferenceCount = Data->ParentPackage->Common.ReferenceCount;
+ NewObject->Common.ReferenceCount =
+ Info->ParentPackage->Common.ReferenceCount;
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
"%s: Converted NULL package element to expected %s at index %u\n",
- Data->Pathname, AcpiUtGetObjectTypeName (NewObject), PackageIndex));
+ Info->FullPathname, AcpiUtGetObjectTypeName (NewObject),
+ PackageIndex));
*ReturnObjectPtr = NewObject;
- Data->Flags |= ACPI_OBJECT_REPAIRED;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
return (AE_OK);
}
@@ -654,21 +490,21 @@ AcpiNsRepairNullElement (
*
* FUNCTION: AcpiNsRemoveNullElements
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* PackageType - An AcpiReturnPackageTypes value
* ObjDesc - A Package object
*
* RETURN: None.
*
* DESCRIPTION: Remove all NULL package elements from packages that contain
- * a variable number of sub-packages. For these types of
+ * a variable number of subpackages. For these types of
* packages, NULL elements can be safely removed.
*
*****************************************************************************/
void
AcpiNsRemoveNullElements (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
UINT8 PackageType,
ACPI_OPERAND_OBJECT *ObjDesc)
{
@@ -685,7 +521,7 @@ AcpiNsRemoveNullElements (
/*
* We can safely remove all NULL elements from these package types:
* PTYPE1_VAR packages contain a variable number of simple data types.
- * PTYPE2 packages contain a variable number of sub-packages.
+ * PTYPE2 packages contain a variable number of subpackages.
*/
switch (PackageType)
{
@@ -696,9 +532,11 @@ AcpiNsRemoveNullElements (
case ACPI_PTYPE2_FIXED:
case ACPI_PTYPE2_MIN:
case ACPI_PTYPE2_REV_FIXED:
+ case ACPI_PTYPE2_FIX_VAR:
break;
default:
+ case ACPI_PTYPE2_VAR_VAR:
case ACPI_PTYPE1_FIXED:
case ACPI_PTYPE1_OPTION:
return;
@@ -723,6 +561,7 @@ AcpiNsRemoveNullElements (
*Dest = *Source;
Dest++;
}
+
Source++;
}
@@ -732,7 +571,7 @@ AcpiNsRemoveNullElements (
{
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
"%s: Found and removed %u NULL elements\n",
- Data->Pathname, (Count - NewCount)));
+ Info->FullPathname, (Count - NewCount)));
/* NULL terminate list and update the package count */
@@ -744,42 +583,43 @@ AcpiNsRemoveNullElements (
/*******************************************************************************
*
- * FUNCTION: AcpiNsRepairPackageList
+ * FUNCTION: AcpiNsWrapWithPackage
*
- * PARAMETERS: Data - Pointer to validation data structure
- * ObjDescPtr - Pointer to the object to repair. The new
- * package object is returned here,
- * overwriting the old object.
+ * PARAMETERS: Info - Method execution information block
+ * OriginalObject - Pointer to the object to repair.
+ * ObjDescPtr - The new package object is returned here
*
* RETURN: Status, new object in *ObjDescPtr
*
- * DESCRIPTION: Repair a common problem with objects that are defined to return
- * a variable-length Package of Packages. If the variable-length
- * is one, some BIOS code mistakenly simply declares a single
- * Package instead of a Package with one sub-Package. This
- * function attempts to repair this error by wrapping a Package
- * object around the original Package, creating the correct
- * Package with one sub-Package.
+ * DESCRIPTION: Repair a common problem with objects that are defined to
+ * return a variable-length Package of sub-objects. If there is
+ * only one sub-object, some BIOS code mistakenly simply declares
+ * the single object instead of a Package with one sub-object.
+ * This function attempts to repair this error by wrapping a
+ * Package object around the original object, creating the
+ * correct and expected Package with one sub-object.
*
* Names that can be repaired in this manner include:
- * _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, TSS
+ * _ALR, _CSD, _HPX, _MLS, _PLD, _PRT, _PSS, _TRT, _TSS,
+ * _BCL, _DOD, _FIX, _Sx
*
******************************************************************************/
ACPI_STATUS
-AcpiNsRepairPackageList (
- ACPI_PREDEFINED_DATA *Data,
+AcpiNsWrapWithPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT *OriginalObject,
ACPI_OPERAND_OBJECT **ObjDescPtr)
{
ACPI_OPERAND_OBJECT *PkgObjDesc;
- ACPI_FUNCTION_NAME (NsRepairPackageList);
+ ACPI_FUNCTION_NAME (NsWrapWithPackage);
/*
- * Create the new outer package and populate it. The new package will
- * have a single element, the lone subpackage.
+ * Create the new outer package and populate it. The new
+ * package will have a single element, the lone sub-object.
*/
PkgObjDesc = AcpiUtCreatePackageObject (1);
if (!PkgObjDesc)
@@ -787,15 +627,15 @@ AcpiNsRepairPackageList (
return (AE_NO_MEMORY);
}
- PkgObjDesc->Package.Elements[0] = *ObjDescPtr;
+ PkgObjDesc->Package.Elements[0] = OriginalObject;
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
+ "%s: Wrapped %s with expected Package object\n",
+ Info->FullPathname, AcpiUtGetObjectTypeName (OriginalObject)));
/* Return the new object in the object pointer */
*ObjDescPtr = PkgObjDesc;
- Data->Flags |= ACPI_OBJECT_REPAIRED;
-
- ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
- "%s: Repaired incorrectly formed Package\n", Data->Pathname));
-
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED;
return (AE_OK);
}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsrepair2.c b/usr/src/uts/intel/io/acpica/namespace/nsrepair2.c
index 4c9c6d7825..d8625899a6 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsrepair2.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsrepair2.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSREPAIR2_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -58,7 +56,7 @@
*/
typedef
ACPI_STATUS (*ACPI_REPAIR_FUNCTION) (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
typedef struct acpi_repair_info
@@ -72,48 +70,69 @@ typedef struct acpi_repair_info
/* Local prototypes */
static const ACPI_REPAIR_INFO *
-AcpiNsMatchRepairableName (
+AcpiNsMatchComplexRepair (
ACPI_NAMESPACE_NODE *Node);
static ACPI_STATUS
AcpiNsRepair_ALR (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsRepair_CID (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr);
+
+static ACPI_STATUS
+AcpiNsRepair_CST (
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsRepair_FDE (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsRepair_HID (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr);
+
+static ACPI_STATUS
+AcpiNsRepair_PRT (
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsRepair_PSS (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsRepair_TSS (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
static ACPI_STATUS
AcpiNsCheckSortedList (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT *ReturnObject,
+ UINT32 StartIndex,
UINT32 ExpectedCount,
UINT32 SortIndex,
UINT8 SortDirection,
char *SortKeyName);
+/* Values for SortDirection above */
+
+#define ACPI_SORT_ASCENDING 0
+#define ACPI_SORT_DESCENDING 1
+
+static void
+AcpiNsRemoveElement (
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ UINT32 Index);
+
static void
AcpiNsSortList (
ACPI_OPERAND_OBJECT **Elements,
@@ -121,11 +140,6 @@ AcpiNsSortList (
UINT32 Index,
UINT8 SortDirection);
-/* Values for SortDirection above */
-
-#define ACPI_SORT_ASCENDING 0
-#define ACPI_SORT_DESCENDING 1
-
/*
* This table contains the names of the predefined methods for which we can
@@ -135,9 +149,11 @@ AcpiNsSortList (
*
* _ALR: Sort the list ascending by AmbientIlluminance
* _CID: Strings: uppercase all, remove any leading asterisk
+ * _CST: Sort the list ascending by C state type
* _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
* _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
* _HID: Strings: uppercase all, remove any leading asterisk
+ * _PRT: Fix reversed SourceName and SourceIndex
* _PSS: Sort the list descending by Power
* _TSS: Sort the list descending by Power
*
@@ -152,9 +168,11 @@ static const ACPI_REPAIR_INFO AcpiNsRepairableNames[] =
{
{"_ALR", AcpiNsRepair_ALR},
{"_CID", AcpiNsRepair_CID},
+ {"_CST", AcpiNsRepair_CST},
{"_FDE", AcpiNsRepair_FDE},
{"_GTM", AcpiNsRepair_FDE}, /* _GTM has same repair as _FDE */
{"_HID", AcpiNsRepair_HID},
+ {"_PRT", AcpiNsRepair_PRT},
{"_PSS", AcpiNsRepair_PSS},
{"_TSS", AcpiNsRepair_TSS},
{{0,0,0,0}, NULL} /* Table terminator */
@@ -170,7 +188,7 @@ static const ACPI_REPAIR_INFO AcpiNsRepairableNames[] =
*
* FUNCTION: AcpiNsComplexRepairs
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* Node - Namespace node for the method/object
* ValidateStatus - Original status of earlier validation
* ReturnObjectPtr - Pointer to the object returned from the
@@ -186,7 +204,7 @@ static const ACPI_REPAIR_INFO AcpiNsRepairableNames[] =
ACPI_STATUS
AcpiNsComplexRepairs (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_NAMESPACE_NODE *Node,
ACPI_STATUS ValidateStatus,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
@@ -197,20 +215,20 @@ AcpiNsComplexRepairs (
/* Check if this name is in the list of repairable names */
- Predefined = AcpiNsMatchRepairableName (Node);
+ Predefined = AcpiNsMatchComplexRepair (Node);
if (!Predefined)
{
return (ValidateStatus);
}
- Status = Predefined->RepairFunction (Data, ReturnObjectPtr);
+ Status = Predefined->RepairFunction (Info, ReturnObjectPtr);
return (Status);
}
/******************************************************************************
*
- * FUNCTION: AcpiNsMatchRepairableName
+ * FUNCTION: AcpiNsMatchComplexRepair
*
* PARAMETERS: Node - Namespace node for the method/object
*
@@ -221,7 +239,7 @@ AcpiNsComplexRepairs (
*****************************************************************************/
static const ACPI_REPAIR_INFO *
-AcpiNsMatchRepairableName (
+AcpiNsMatchComplexRepair (
ACPI_NAMESPACE_NODE *Node)
{
const ACPI_REPAIR_INFO *ThisName;
@@ -236,6 +254,7 @@ AcpiNsMatchRepairableName (
{
return (ThisName);
}
+
ThisName++;
}
@@ -247,7 +266,7 @@ AcpiNsMatchRepairableName (
*
* FUNCTION: AcpiNsRepair_ALR
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
@@ -260,15 +279,15 @@ AcpiNsMatchRepairableName (
static ACPI_STATUS
AcpiNsRepair_ALR (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
ACPI_STATUS Status;
- Status = AcpiNsCheckSortedList (Data, ReturnObject, 2, 1,
- ACPI_SORT_ASCENDING, "AmbientIlluminance");
+ Status = AcpiNsCheckSortedList (Info, ReturnObject, 0, 2, 1,
+ ACPI_SORT_ASCENDING, "AmbientIlluminance");
return (Status);
}
@@ -278,7 +297,7 @@ AcpiNsRepair_ALR (
*
* FUNCTION: AcpiNsRepair_FDE
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
@@ -293,7 +312,7 @@ AcpiNsRepair_ALR (
static ACPI_STATUS
AcpiNsRepair_FDE (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
@@ -321,7 +340,8 @@ AcpiNsRepair_FDE (
if (ReturnObject->Buffer.Length != ACPI_FDE_BYTE_BUFFER_SIZE)
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
"Incorrect return buffer length %u, expected %u",
ReturnObject->Buffer.Length, ACPI_FDE_DWORD_BUFFER_SIZE));
@@ -330,7 +350,8 @@ AcpiNsRepair_FDE (
/* Create the new (larger) buffer object */
- BufferObject = AcpiUtCreateBufferObject (ACPI_FDE_DWORD_BUFFER_SIZE);
+ BufferObject = AcpiUtCreateBufferObject (
+ ACPI_FDE_DWORD_BUFFER_SIZE);
if (!BufferObject)
{
return (AE_NO_MEMORY);
@@ -339,7 +360,8 @@ AcpiNsRepair_FDE (
/* Expand each byte to a DWORD */
ByteBuffer = ReturnObject->Buffer.Pointer;
- DwordBuffer = ACPI_CAST_PTR (UINT32, BufferObject->Buffer.Pointer);
+ DwordBuffer = ACPI_CAST_PTR (UINT32,
+ BufferObject->Buffer.Pointer);
for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++)
{
@@ -350,10 +372,11 @@ AcpiNsRepair_FDE (
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
"%s Expanded Byte Buffer to expected DWord Buffer\n",
- Data->Pathname));
+ Info->FullPathname));
break;
default:
+
return (AE_AML_OPERAND_TYPE);
}
@@ -362,7 +385,7 @@ AcpiNsRepair_FDE (
AcpiUtRemoveReference (ReturnObject);
*ReturnObjectPtr = BufferObject;
- Data->Flags |= ACPI_OBJECT_REPAIRED;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
return (AE_OK);
}
@@ -371,7 +394,7 @@ AcpiNsRepair_FDE (
*
* FUNCTION: AcpiNsRepair_CID
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
@@ -385,7 +408,7 @@ AcpiNsRepair_FDE (
static ACPI_STATUS
AcpiNsRepair_CID (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_STATUS Status;
@@ -400,7 +423,7 @@ AcpiNsRepair_CID (
if (ReturnObject->Common.Type == ACPI_TYPE_STRING)
{
- Status = AcpiNsRepair_HID (Data, ReturnObjectPtr);
+ Status = AcpiNsRepair_HID (Info, ReturnObjectPtr);
return (Status);
}
@@ -419,7 +442,7 @@ AcpiNsRepair_CID (
OriginalElement = *ElementPtr;
OriginalRefCount = OriginalElement->Common.ReferenceCount;
- Status = AcpiNsRepair_HID (Data, ElementPtr);
+ Status = AcpiNsRepair_HID (Info, ElementPtr);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -446,9 +469,104 @@ AcpiNsRepair_CID (
/******************************************************************************
*
+ * FUNCTION: AcpiNsRepair_CST
+ *
+ * PARAMETERS: Info - Method execution information block
+ * ReturnObjectPtr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _CST object:
+ * 1. Sort the list ascending by C state type
+ * 2. Ensure type cannot be zero
+ * 3. A subpackage count of zero means _CST is meaningless
+ * 4. Count must match the number of C state subpackages
+ *
+ *****************************************************************************/
+
+static ACPI_STATUS
+AcpiNsRepair_CST (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr)
+{
+ ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
+ ACPI_OPERAND_OBJECT **OuterElements;
+ UINT32 OuterElementCount;
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_STATUS Status;
+ BOOLEAN Removing;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_NAME (NsRepair_CST);
+
+
+ /*
+ * Check if the C-state type values are proportional.
+ */
+ OuterElementCount = ReturnObject->Package.Count - 1;
+ i = 0;
+ while (i < OuterElementCount)
+ {
+ OuterElements = &ReturnObject->Package.Elements[i + 1];
+ Removing = FALSE;
+
+ if ((*OuterElements)->Package.Count == 0)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
+ "SubPackage[%u] - removing entry due to zero count", i));
+ Removing = TRUE;
+ goto RemoveElement;
+ }
+
+ ObjDesc = (*OuterElements)->Package.Elements[1]; /* Index1 = Type */
+ if ((UINT32) ObjDesc->Integer.Value == 0)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
+ "SubPackage[%u] - removing entry due to invalid Type(0)", i));
+ Removing = TRUE;
+ }
+
+RemoveElement:
+ if (Removing)
+ {
+ AcpiNsRemoveElement (ReturnObject, i + 1);
+ OuterElementCount--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ /* Update top-level package count, Type "Integer" checked elsewhere */
+
+ ObjDesc = ReturnObject->Package.Elements[0];
+ ObjDesc->Integer.Value = OuterElementCount;
+
+ /*
+ * Entries (subpackages) in the _CST Package must be sorted by the
+ * C-state type, in ascending order.
+ */
+ Status = AcpiNsCheckSortedList (Info, ReturnObject, 1, 4, 1,
+ ACPI_SORT_ASCENDING, "C-State Type");
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ return (AE_OK);
+}
+
+
+/******************************************************************************
+ *
* FUNCTION: AcpiNsRepair_HID
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
@@ -461,7 +579,7 @@ AcpiNsRepair_CID (
static ACPI_STATUS
AcpiNsRepair_HID (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
@@ -482,12 +600,13 @@ AcpiNsRepair_HID (
if (ReturnObject->String.Length == 0)
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
"Invalid zero-length _HID or _CID string"));
/* Return AE_OK anyway, let driver handle it */
- Data->Flags |= ACPI_OBJECT_REPAIRED;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
return (AE_OK);
}
@@ -512,19 +631,20 @@ AcpiNsRepair_HID (
NewString->String.Length--;
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
- "%s: Removed invalid leading asterisk\n", Data->Pathname));
+ "%s: Removed invalid leading asterisk\n", Info->FullPathname));
}
/*
- * Copy and uppercase the string. From the ACPI specification:
+ * Copy and uppercase the string. From the ACPI 5.0 specification:
*
* A valid PNP ID must be of the form "AAA####" where A is an uppercase
* letter and # is a hex digit. A valid ACPI ID must be of the form
- * "ACPI####" where # is a hex digit.
+ * "NNNN####" where N is an uppercase letter or decimal digit, and
+ * # is a hex digit.
*/
for (Dest = NewString->String.Pointer; *Source; Dest++, Source++)
{
- *Dest = (char) ACPI_TOUPPER (*Source);
+ *Dest = (char) toupper ((int) *Source);
}
AcpiUtRemoveReference (ReturnObject);
@@ -535,32 +655,73 @@ AcpiNsRepair_HID (
/******************************************************************************
*
- * FUNCTION: AcpiNsRepair_TSS
+ * FUNCTION: AcpiNsRepair_PRT
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
* RETURN: Status. AE_OK if object is OK or was repaired successfully
*
- * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list
- * descending by the power dissipation values.
+ * DESCRIPTION: Repair for the _PRT object. If necessary, fix reversed
+ * SourceName and SourceIndex field, a common BIOS bug.
*
*****************************************************************************/
static ACPI_STATUS
-AcpiNsRepair_TSS (
- ACPI_PREDEFINED_DATA *Data,
+AcpiNsRepair_PRT (
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
- ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
- ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *PackageObject = *ReturnObjectPtr;
+ ACPI_OPERAND_OBJECT **TopObjectList;
+ ACPI_OPERAND_OBJECT **SubObjectList;
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT *SubPackage;
+ UINT32 ElementCount;
+ UINT32 Index;
- Status = AcpiNsCheckSortedList (Data, ReturnObject, 5, 1,
- ACPI_SORT_DESCENDING, "PowerDissipation");
+ /* Each element in the _PRT package is a subpackage */
- return (Status);
+ TopObjectList = PackageObject->Package.Elements;
+ ElementCount = PackageObject->Package.Count;
+
+ /* Examine each subpackage */
+
+ for (Index = 0; Index < ElementCount; Index++, TopObjectList++)
+ {
+ SubPackage = *TopObjectList;
+ SubObjectList = SubPackage->Package.Elements;
+
+ /* Check for minimum required element count */
+
+ if (SubPackage->Package.Count < 4)
+ {
+ continue;
+ }
+
+ /*
+ * If the BIOS has erroneously reversed the _PRT SourceName (index 2)
+ * and the SourceIndex (index 3), fix it. _PRT is important enough to
+ * workaround this BIOS error. This also provides compatibility with
+ * other ACPI implementations.
+ */
+ ObjDesc = SubObjectList[3];
+ if (!ObjDesc || (ObjDesc->Common.Type != ACPI_TYPE_INTEGER))
+ {
+ SubObjectList[3] = SubObjectList[2];
+ SubObjectList[2] = ObjDesc;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
+
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
+ "PRT[%X]: Fixed reversed SourceName and SourceIndex",
+ Index));
+ }
+ }
+
+ return (AE_OK);
}
@@ -568,7 +729,7 @@ AcpiNsRepair_TSS (
*
* FUNCTION: AcpiNsRepair_PSS
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObjectPtr - Pointer to the object returned from the
* evaluation of a method or object
*
@@ -583,7 +744,7 @@ AcpiNsRepair_TSS (
static ACPI_STATUS
AcpiNsRepair_PSS (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT **ReturnObjectPtr)
{
ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
@@ -597,13 +758,13 @@ AcpiNsRepair_PSS (
/*
- * Entries (sub-packages) in the _PSS Package must be sorted by power
+ * Entries (subpackages) in the _PSS Package must be sorted by power
* dissipation, in descending order. If it appears that the list is
* incorrectly sorted, sort it. We sort by CpuFrequency, since this
* should be proportional to the power.
*/
- Status =AcpiNsCheckSortedList (Data, ReturnObject, 6, 0,
- ACPI_SORT_DESCENDING, "CpuFrequency");
+ Status = AcpiNsCheckSortedList (Info, ReturnObject, 0, 6, 0,
+ ACPI_SORT_DESCENDING, "CpuFrequency");
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -624,7 +785,8 @@ AcpiNsRepair_PSS (
if ((UINT32) ObjDesc->Integer.Value > PreviousValue)
{
- ACPI_WARN_PREDEFINED ((AE_INFO, Data->Pathname, Data->NodeFlags,
+ ACPI_WARN_PREDEFINED ((AE_INFO,
+ Info->FullPathname, Info->NodeFlags,
"SubPackage[%u,%u] - suspicious power dissipation values",
i-1, i));
}
@@ -639,12 +801,60 @@ AcpiNsRepair_PSS (
/******************************************************************************
*
+ * FUNCTION: AcpiNsRepair_TSS
+ *
+ * PARAMETERS: Info - Method execution information block
+ * ReturnObjectPtr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list
+ * descending by the power dissipation values.
+ *
+ *****************************************************************************/
+
+static ACPI_STATUS
+AcpiNsRepair_TSS (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr)
+{
+ ACPI_OPERAND_OBJECT *ReturnObject = *ReturnObjectPtr;
+ ACPI_STATUS Status;
+ ACPI_NAMESPACE_NODE *Node;
+
+
+ /*
+ * We can only sort the _TSS return package if there is no _PSS in the
+ * same scope. This is because if _PSS is present, the ACPI specification
+ * dictates that the _TSS Power Dissipation field is to be ignored, and
+ * therefore some BIOSs leave garbage values in the _TSS Power field(s).
+ * In this case, it is best to just return the _TSS package as-is.
+ * (May, 2011)
+ */
+ Status = AcpiNsGetNode (Info->Node, "^_PSS",
+ ACPI_NS_NO_UPSEARCH, &Node);
+ if (ACPI_SUCCESS (Status))
+ {
+ return (AE_OK);
+ }
+
+ Status = AcpiNsCheckSortedList (Info, ReturnObject, 0, 5, 1,
+ ACPI_SORT_DESCENDING, "PowerDissipation");
+
+ return (Status);
+}
+
+
+/******************************************************************************
+ *
* FUNCTION: AcpiNsCheckSortedList
*
- * PARAMETERS: Data - Pointer to validation data structure
+ * PARAMETERS: Info - Method execution information block
* ReturnObject - Pointer to the top-level returned object
- * ExpectedCount - Minimum length of each sub-package
- * SortIndex - Sub-package entry to sort on
+ * StartIndex - Index of the first subpackage
+ * ExpectedCount - Minimum length of each subpackage
+ * SortIndex - Subpackage entry to sort on
* SortDirection - Ascending or descending
* SortKeyName - Name of the SortIndex field
*
@@ -658,8 +868,9 @@ AcpiNsRepair_PSS (
static ACPI_STATUS
AcpiNsCheckSortedList (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_OPERAND_OBJECT *ReturnObject,
+ UINT32 StartIndex,
UINT32 ExpectedCount,
UINT32 SortIndex,
UINT8 SortDirection,
@@ -684,17 +895,19 @@ AcpiNsCheckSortedList (
}
/*
- * NOTE: assumes list of sub-packages contains no NULL elements.
+ * NOTE: assumes list of subpackages contains no NULL elements.
* Any NULL elements should have been removed by earlier call
* to AcpiNsRemoveNullElements.
*/
- OuterElements = ReturnObject->Package.Elements;
OuterElementCount = ReturnObject->Package.Count;
- if (!OuterElementCount)
+ if (!OuterElementCount || StartIndex >= OuterElementCount)
{
return (AE_AML_PACKAGE_LIMIT);
}
+ OuterElements = &ReturnObject->Package.Elements[StartIndex];
+ OuterElementCount -= StartIndex;
+
PreviousValue = 0;
if (SortDirection == ACPI_SORT_DESCENDING)
{
@@ -712,7 +925,7 @@ AcpiNsCheckSortedList (
return (AE_AML_OPERAND_TYPE);
}
- /* Each sub-package must have the minimum length */
+ /* Each subpackage must have the minimum length */
if ((*OuterElements)->Package.Count < ExpectedCount)
{
@@ -736,14 +949,14 @@ AcpiNsCheckSortedList (
((SortDirection == ACPI_SORT_DESCENDING) &&
(ObjDesc->Integer.Value > PreviousValue)))
{
- AcpiNsSortList (ReturnObject->Package.Elements,
+ AcpiNsSortList (&ReturnObject->Package.Elements[StartIndex],
OuterElementCount, SortIndex, SortDirection);
- Data->Flags |= ACPI_OBJECT_REPAIRED;
+ Info->ReturnFlags |= ACPI_OBJECT_REPAIRED;
ACPI_DEBUG_PRINT ((ACPI_DB_REPAIR,
"%s: Repaired unsorted list - now sorted by %s\n",
- Data->Pathname, SortKeyName));
+ Info->FullPathname, SortKeyName));
return (AE_OK);
}
@@ -809,3 +1022,62 @@ AcpiNsSortList (
}
}
}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiNsRemoveElement
+ *
+ * PARAMETERS: ObjDesc - Package object element list
+ * Index - Index of element to remove
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove the requested element of a package and delete it.
+ *
+ *****************************************************************************/
+
+static void
+AcpiNsRemoveElement (
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ UINT32 Index)
+{
+ ACPI_OPERAND_OBJECT **Source;
+ ACPI_OPERAND_OBJECT **Dest;
+ UINT32 Count;
+ UINT32 NewCount;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_NAME (NsRemoveElement);
+
+
+ Count = ObjDesc->Package.Count;
+ NewCount = Count - 1;
+
+ Source = ObjDesc->Package.Elements;
+ Dest = Source;
+
+ /* Examine all elements of the package object, remove matched index */
+
+ for (i = 0; i < Count; i++)
+ {
+ if (i == Index)
+ {
+ AcpiUtRemoveReference (*Source); /* Remove one ref for being in pkg */
+ AcpiUtRemoveReference (*Source);
+ }
+ else
+ {
+ *Dest = *Source;
+ Dest++;
+ }
+
+ Source++;
+ }
+
+ /* NULL terminate list and update the package count */
+
+ *Dest = NULL;
+ ObjDesc->Package.Count = NewCount;
+}
diff --git a/usr/src/uts/intel/io/acpica/namespace/nssearch.c b/usr/src/uts/intel/io/acpica/namespace/nssearch.c
index 1951c2548b..3a0fe90ea4 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nssearch.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nssearch.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSSEARCH_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -114,7 +112,7 @@ AcpiNsSearchOneScope (
{
char *ScopeName;
- ScopeName = AcpiNsGetExternalPathname (ParentNode);
+ ScopeName = AcpiNsGetNormalizedPathname (ParentNode, TRUE);
if (ScopeName)
{
ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
@@ -250,7 +248,7 @@ AcpiNsSearchParentTree (
* the actual name we are searching for. Typechecking comes later.
*/
Status = AcpiNsSearchOneScope (
- TargetName, ParentNode, ACPI_TYPE_ANY, ReturnNode);
+ TargetName, ParentNode, ACPI_TYPE_ANY, ReturnNode);
if (ACPI_SUCCESS (Status))
{
return_ACPI_STATUS (Status);
@@ -340,10 +338,42 @@ AcpiNsSearchAndEnter (
* If we found it AND the request specifies that a find is an error,
* return the error
*/
- if ((Status == AE_OK) &&
- (Flags & ACPI_NS_ERROR_IF_FOUND))
+ if (Status == AE_OK)
{
- Status = AE_ALREADY_EXISTS;
+ /* The node was found in the namespace */
+
+ /*
+ * If the namespace override feature is enabled for this node,
+ * delete any existing attached sub-object and make the node
+ * look like a new node that is owned by the override table.
+ */
+ if (Flags & ACPI_NS_OVERRIDE_IF_FOUND)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
+ "Namespace override: %4.4s pass %u type %X Owner %X\n",
+ ACPI_CAST_PTR(char, &TargetName), InterpreterMode,
+ (*ReturnNode)->Type, WalkState->OwnerId));
+
+ AcpiNsDeleteChildren (*ReturnNode);
+ if (AcpiGbl_RuntimeNamespaceOverride)
+ {
+ AcpiUtRemoveReference ((*ReturnNode)->Object);
+ (*ReturnNode)->Object = NULL;
+ (*ReturnNode)->OwnerId = WalkState->OwnerId;
+ }
+ else
+ {
+ AcpiNsRemoveNode (*ReturnNode);
+ *ReturnNode = ACPI_ENTRY_NOT_FOUND;
+ }
+ }
+
+ /* Return an error if we don't expect to find the object */
+
+ else if (Flags & ACPI_NS_ERROR_IF_FOUND)
+ {
+ Status = AE_ALREADY_EXISTS;
+ }
}
#ifdef ACPI_ASL_COMPILER
@@ -421,4 +451,3 @@ AcpiNsSearchAndEnter (
*ReturnNode = NewNode;
return_ACPI_STATUS (AE_OK);
}
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsutils.c b/usr/src/uts/intel/io/acpica/namespace/nsutils.c
index fad2a57c5e..72f9105ef5 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsutils.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsutils.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSUTILS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -54,10 +52,6 @@
/* Local prototypes */
-static BOOLEAN
-AcpiNsValidPathSeparator (
- char Sep);
-
#ifdef ACPI_OBSOLETE_FUNCTIONS
ACPI_NAME
AcpiNsFindParentName (
@@ -96,7 +90,7 @@ AcpiNsPrintNodePathname (
Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (Node, &Buffer);
+ Status = AcpiNsHandleToPathname (Node, &Buffer, TRUE);
if (ACPI_SUCCESS (Status))
{
if (Message)
@@ -112,48 +106,6 @@ AcpiNsPrintNodePathname (
/*******************************************************************************
*
- * FUNCTION: AcpiNsValidRootPrefix
- *
- * PARAMETERS: Prefix - Character to be checked
- *
- * RETURN: TRUE if a valid prefix
- *
- * DESCRIPTION: Check if a character is a valid ACPI Root prefix
- *
- ******************************************************************************/
-
-BOOLEAN
-AcpiNsValidRootPrefix (
- char Prefix)
-{
-
- return ((BOOLEAN) (Prefix == '\\'));
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiNsValidPathSeparator
- *
- * PARAMETERS: Sep - Character to be checked
- *
- * RETURN: TRUE if a valid path separator
- *
- * DESCRIPTION: Check if a character is a valid ACPI path separator
- *
- ******************************************************************************/
-
-static BOOLEAN
-AcpiNsValidPathSeparator (
- char Sep)
-{
-
- return ((BOOLEAN) (Sep == '.'));
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiNsGetType
*
* PARAMETERS: Node - Parent Node to be examined
@@ -174,10 +126,10 @@ AcpiNsGetType (
if (!Node)
{
ACPI_WARNING ((AE_INFO, "Null Node parameter"));
- return_UINT32 (ACPI_TYPE_ANY);
+ return_UINT8 (ACPI_TYPE_ANY);
}
- return_UINT32 ((ACPI_OBJECT_TYPE) Node->Type);
+ return_UINT8 (Node->Type);
}
@@ -209,7 +161,7 @@ AcpiNsLocal (
return_UINT32 (ACPI_NS_NORMAL);
}
- return_UINT32 ((UINT32) AcpiGbl_NsProperties[Type] & ACPI_NS_LOCAL);
+ return_UINT32 (AcpiGbl_NsProperties[Type] & ACPI_NS_LOCAL);
}
@@ -244,20 +196,21 @@ AcpiNsGetInternalNameLength (
Info->FullyQualified = FALSE;
/*
- * For the internal name, the required length is 4 bytes per segment, plus
- * 1 each for RootPrefix, MultiNamePrefixOp, segment count, trailing null
- * (which is not really needed, but no there's harm in putting it there)
+ * For the internal name, the required length is 4 bytes per segment,
+ * plus 1 each for RootPrefix, MultiNamePrefixOp, segment count,
+ * trailing null (which is not really needed, but no there's harm in
+ * putting it there)
*
* strlen() + 1 covers the first NameSeg, which has no path separator
*/
- if (AcpiNsValidRootPrefix (*NextExternalChar))
+ if (ACPI_IS_ROOT_PREFIX (*NextExternalChar))
{
Info->FullyQualified = TRUE;
NextExternalChar++;
/* Skip redundant RootPrefix, like \\_SB.PCI0.SBRG.EC0 */
- while (AcpiNsValidRootPrefix (*NextExternalChar))
+ while (ACPI_IS_ROOT_PREFIX (*NextExternalChar))
{
NextExternalChar++;
}
@@ -266,7 +219,7 @@ AcpiNsGetInternalNameLength (
{
/* Handle Carat prefixes */
- while (*NextExternalChar == '^')
+ while (ACPI_IS_PARENT_PREFIX (*NextExternalChar))
{
Info->NumCarats++;
NextExternalChar++;
@@ -283,7 +236,7 @@ AcpiNsGetInternalNameLength (
Info->NumSegments = 1;
for (i = 0; NextExternalChar[i]; i++)
{
- if (AcpiNsValidPathSeparator (NextExternalChar[i]))
+ if (ACPI_IS_PATH_SEPARATOR (NextExternalChar[i]))
{
Info->NumSegments++;
}
@@ -291,7 +244,7 @@ AcpiNsGetInternalNameLength (
}
Info->Length = (ACPI_NAME_SIZE * Info->NumSegments) +
- 4 + Info->NumCarats;
+ 4 + Info->NumCarats;
Info->NextExternalChar = NextExternalChar;
}
@@ -328,7 +281,7 @@ AcpiNsBuildInternalName (
if (Info->FullyQualified)
{
- InternalName[0] = '\\';
+ InternalName[0] = AML_ROOT_PREFIX;
if (NumSegments <= 1)
{
@@ -357,7 +310,7 @@ AcpiNsBuildInternalName (
{
for (i = 0; i < Info->NumCarats; i++)
{
- InternalName[i] = '^';
+ InternalName[i] = AML_PARENT_PREFIX;
}
}
@@ -384,7 +337,7 @@ AcpiNsBuildInternalName (
{
for (i = 0; i < ACPI_NAME_SIZE; i++)
{
- if (AcpiNsValidPathSeparator (*ExternalName) ||
+ if (ACPI_IS_PATH_SEPARATOR (*ExternalName) ||
(*ExternalName == 0))
{
/* Pad the segment with underscore(s) if segment is short */
@@ -395,17 +348,17 @@ AcpiNsBuildInternalName (
{
/* Convert the character to uppercase and save it */
- Result[i] = (char) ACPI_TOUPPER ((int) *ExternalName);
+ Result[i] = (char) toupper ((int) *ExternalName);
ExternalName++;
}
}
/* Now we must have a path separator, or the pathname is bad */
- if (!AcpiNsValidPathSeparator (*ExternalName) &&
+ if (!ACPI_IS_PATH_SEPARATOR (*ExternalName) &&
(*ExternalName != 0))
{
- return_ACPI_STATUS (AE_BAD_PARAMETER);
+ return_ACPI_STATUS (AE_BAD_PATHNAME);
}
/* Move on the next segment */
@@ -542,14 +495,16 @@ AcpiNsExternalizeName (
switch (InternalName[0])
{
- case '\\':
+ case AML_ROOT_PREFIX:
+
PrefixLength = 1;
break;
- case '^':
+ case AML_PARENT_PREFIX:
+
for (i = 0; i < InternalNameLength; i++)
{
- if (InternalName[i] == '^')
+ if (ACPI_IS_PARENT_PREFIX (InternalName[i]))
{
PrefixLength = i + 1;
}
@@ -567,6 +522,7 @@ AcpiNsExternalizeName (
break;
default:
+
break;
}
@@ -619,10 +575,10 @@ AcpiNsExternalizeName (
* punctuation ('.') between object names, plus the NULL terminator.
*/
RequiredLength = PrefixLength + (4 * NumSegments) +
- ((NumSegments > 0) ? (NumSegments - 1) : 0) + 1;
+ ((NumSegments > 0) ? (NumSegments - 1) : 0) + 1;
/*
- * Check to see if we're still in bounds. If not, there's a problem
+ * Check to see if we're still in bounds. If not, there's a problem
* with InternalName (invalid format).
*/
if (RequiredLength > InternalNameLength)
@@ -655,10 +611,14 @@ AcpiNsExternalizeName (
(*ConvertedName)[j++] = '.';
}
- (*ConvertedName)[j++] = InternalName[NamesIndex++];
- (*ConvertedName)[j++] = InternalName[NamesIndex++];
- (*ConvertedName)[j++] = InternalName[NamesIndex++];
- (*ConvertedName)[j++] = InternalName[NamesIndex++];
+ /* Copy and validate the 4-char name segment */
+
+ ACPI_MOVE_NAME (&(*ConvertedName)[j],
+ &InternalName[NamesIndex]);
+ AcpiUtRepairName (&(*ConvertedName)[j]);
+
+ j += ACPI_NAME_SIZE;
+ NamesIndex += ACPI_NAME_SIZE;
}
}
@@ -733,27 +693,47 @@ void
AcpiNsTerminate (
void)
{
- ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_STATUS Status;
ACPI_FUNCTION_TRACE (NsTerminate);
+#ifdef ACPI_EXEC_APP
+ {
+ ACPI_OPERAND_OBJECT *Prev;
+ ACPI_OPERAND_OBJECT *Next;
+
+ /* Delete any module-level code blocks */
+
+ Next = AcpiGbl_ModuleCodeList;
+ while (Next)
+ {
+ Prev = Next;
+ Next = Next->Method.Mutex;
+ Prev->Method.Mutex = NULL; /* Clear the Mutex (cheated) field */
+ AcpiUtRemoveReference (Prev);
+ }
+ }
+#endif
+
/*
- * 1) Free the entire namespace -- all nodes and objects
- *
- * Delete all object descriptors attached to namepsace nodes
+ * Free the entire namespace -- all nodes and all objects
+ * attached to the nodes
*/
AcpiNsDeleteNamespaceSubtree (AcpiGbl_RootNode);
- /* Detach any objects attached to the root */
+ /* Delete any objects attached to the root node */
- ObjDesc = AcpiNsGetAttachedObject (AcpiGbl_RootNode);
- if (ObjDesc)
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
{
- AcpiNsDetachObject (AcpiGbl_RootNode);
+ return_VOID;
}
+ AcpiNsDeleteNode (AcpiGbl_RootNode);
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Namespace freed\n"));
return_VOID;
}
@@ -774,18 +754,18 @@ UINT32
AcpiNsOpensScope (
ACPI_OBJECT_TYPE Type)
{
- ACPI_FUNCTION_TRACE_STR (NsOpensScope, AcpiUtGetTypeName (Type));
+ ACPI_FUNCTION_ENTRY ();
- if (!AcpiUtValidObjectType (Type))
+ if (Type > ACPI_TYPE_LOCAL_MAX)
{
/* type code out of range */
ACPI_WARNING ((AE_INFO, "Invalid Object Type 0x%X", Type));
- return_UINT32 (ACPI_NS_NORMAL);
+ return (ACPI_NS_NORMAL);
}
- return_UINT32 (((UINT32) AcpiGbl_NsProperties[Type]) & ACPI_NS_NEWSCOPE);
+ return (((UINT32) AcpiGbl_NsProperties[Type]) & ACPI_NS_NEWSCOPE);
}
@@ -797,7 +777,7 @@ AcpiNsOpensScope (
* \ (backslash) and ^ (carat) prefixes, and the
* . (period) to separate segments are supported.
* PrefixNode - Root of subtree to be searched, or NS_ALL for the
- * root of the name space. If Name is fully
+ * root of the name space. If Name is fully
* qualified (first INT8 is '\'), the passed value
* of Scope will not be accessed.
* Flags - Used to indicate whether to perform upsearch or
@@ -805,7 +785,7 @@ AcpiNsOpensScope (
* ReturnNode - Where the Node is returned
*
* DESCRIPTION: Look up a name relative to a given scope and return the
- * corresponding Node. NOTE: Scope can be null.
+ * corresponding Node. NOTE: Scope can be null.
*
* MUTEX: Locks namespace
*
@@ -826,6 +806,8 @@ AcpiNsGetNode (
ACPI_FUNCTION_TRACE_PTR (NsGetNode, ACPI_CAST_PTR (char, Pathname));
+ /* Simplest case is a null pathname */
+
if (!Pathname)
{
*ReturnNode = PrefixNode;
@@ -833,6 +815,15 @@ AcpiNsGetNode (
{
*ReturnNode = AcpiGbl_RootNode;
}
+
+ return_ACPI_STATUS (AE_OK);
+ }
+
+ /* Quick check for a reference to the root */
+
+ if (ACPI_IS_ROOT_PREFIX (Pathname[0]) && (!Pathname[1]))
+ {
+ *ReturnNode = AcpiGbl_RootNode;
return_ACPI_STATUS (AE_OK);
}
@@ -859,12 +850,12 @@ AcpiNsGetNode (
/* Lookup the name in the namespace */
Status = AcpiNsLookup (&ScopeInfo, InternalPath, ACPI_TYPE_ANY,
- ACPI_IMODE_EXECUTE, (Flags | ACPI_NS_DONT_OPEN_SCOPE),
- NULL, ReturnNode);
+ ACPI_IMODE_EXECUTE, (Flags | ACPI_NS_DONT_OPEN_SCOPE),
+ NULL, ReturnNode);
if (ACPI_FAILURE (Status))
{
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%s, %s\n",
- Pathname, AcpiFormatException (Status)));
+ Pathname, AcpiFormatException (Status)));
}
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
diff --git a/usr/src/uts/intel/io/acpica/namespace/nswalk.c b/usr/src/uts/intel/io/acpica/namespace/nswalk.c
index d44775212a..08637a6eee 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nswalk.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nswalk.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSWALK_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -65,8 +62,8 @@
* RETURN: ACPI_NAMESPACE_NODE - Pointer to the NEXT child or NULL if
* none is found.
*
- * DESCRIPTION: Return the next peer node within the namespace. If Handle
- * is valid, Scope is ignored. Otherwise, the first node
+ * DESCRIPTION: Return the next peer node within the namespace. If Handle
+ * is valid, Scope is ignored. Otherwise, the first node
* within Scope is returned.
*
******************************************************************************/
@@ -105,8 +102,8 @@ AcpiNsGetNextNode (
* RETURN: ACPI_NAMESPACE_NODE - Pointer to the NEXT child or NULL if
* none is found.
*
- * DESCRIPTION: Return the next peer node within the namespace. If Handle
- * is valid, Scope is ignored. Otherwise, the first node
+ * DESCRIPTION: Return the next peer node within the namespace. If Handle
+ * is valid, Scope is ignored. Otherwise, the first node
* within Scope is returned.
*
******************************************************************************/
@@ -165,9 +162,9 @@ AcpiNsGetNextNodeTyped (
* MaxDepth - Depth to which search is to reach
* Flags - Whether to unlock the NS before invoking
* the callback routine
- * PreOrderVisit - Called during tree pre-order visit
+ * DescendingCallback - Called during tree descent
* when an object of "Type" is found
- * PostOrderVisit - Called during tree post-order visit
+ * AscendingCallback - Called during tree ascent
* when an object of "Type" is found
* Context - Passed to user function(s) above
* ReturnValue - from the UserFunction if terminated
@@ -195,8 +192,8 @@ AcpiNsWalkNamespace (
ACPI_HANDLE StartNode,
UINT32 MaxDepth,
UINT32 Flags,
- ACPI_WALK_CALLBACK PreOrderVisit,
- ACPI_WALK_CALLBACK PostOrderVisit,
+ ACPI_WALK_CALLBACK DescendingCallback,
+ ACPI_WALK_CALLBACK AscendingCallback,
void *Context,
void **ReturnValue)
{
@@ -221,10 +218,10 @@ AcpiNsWalkNamespace (
/* Null child means "get first node" */
- ParentNode = StartNode;
- ChildNode = AcpiNsGetNextNode (ParentNode, NULL);
- ChildType = ACPI_TYPE_ANY;
- Level = 1;
+ ParentNode = StartNode;
+ ChildNode = AcpiNsGetNextNode (ParentNode, NULL);
+ ChildType = ACPI_TYPE_ANY;
+ Level = 1;
/*
* Traverse the tree of nodes until we bubble back up to where we
@@ -274,23 +271,23 @@ AcpiNsWalkNamespace (
}
/*
- * Invoke the user function, either pre-order or post-order
+ * Invoke the user function, either descending, ascending,
* or both.
*/
if (!NodePreviouslyVisited)
{
- if (PreOrderVisit)
+ if (DescendingCallback)
{
- Status = PreOrderVisit (ChildNode, Level,
- Context, ReturnValue);
+ Status = DescendingCallback (ChildNode, Level,
+ Context, ReturnValue);
}
}
else
{
- if (PostOrderVisit)
+ if (AscendingCallback)
{
- Status = PostOrderVisit (ChildNode, Level,
- Context, ReturnValue);
+ Status = AscendingCallback (ChildNode, Level,
+ Context, ReturnValue);
}
}
@@ -327,7 +324,7 @@ AcpiNsWalkNamespace (
/*
* Depth first search: Attempt to go down another level in the
- * namespace if we are allowed to. Don't go any further if we have
+ * namespace if we are allowed to. Don't go any further if we have
* reached the caller specified maximum depth or if the user
* function has specified that the maximum depth has been reached.
*/
@@ -382,5 +379,3 @@ AcpiNsWalkNamespace (
return_ACPI_STATUS (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsxfeval.c b/usr/src/uts/intel/io/acpica/namespace/nsxfeval.c
index 0854cac9e9..b88ecf507e 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsxfeval.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsxfeval.c
@@ -6,7 +6,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSXFEVAL_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -68,16 +67,16 @@ AcpiNsResolveReferences (
* PARAMETERS: Handle - Object handle (optional)
* Pathname - Object pathname (optional)
* ExternalParams - List of parameters to pass to method,
- * terminated by NULL. May be NULL
+ * terminated by NULL. May be NULL
* if no parameters are being passed.
* ReturnBuffer - Where to put method's return value (if
- * any). If NULL, no value is returned.
+ * any). If NULL, no value is returned.
* ReturnType - Expected type of return object
*
* RETURN: Status
*
* DESCRIPTION: Find and evaluate the given object, passing the given
- * parameters if necessary. One of "Handle" or "Pathname" must
+ * parameters if necessary. One of "Handle" or "Pathname" must
* be valid (non-null)
*
******************************************************************************/
@@ -91,7 +90,7 @@ AcpiEvaluateObjectTyped (
ACPI_OBJECT_TYPE ReturnType)
{
ACPI_STATUS Status;
- BOOLEAN MustFree = FALSE;
+ BOOLEAN FreeBufferOnError = FALSE;
ACPI_FUNCTION_TRACE (AcpiEvaluateObjectTyped);
@@ -106,12 +105,13 @@ AcpiEvaluateObjectTyped (
if (ReturnBuffer->Length == ACPI_ALLOCATE_BUFFER)
{
- MustFree = TRUE;
+ FreeBufferOnError = TRUE;
}
/* Evaluate the object */
- Status = AcpiEvaluateObject (Handle, Pathname, ExternalParams, ReturnBuffer);
+ Status = AcpiEvaluateObject (Handle, Pathname,
+ ExternalParams, ReturnBuffer);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -146,10 +146,15 @@ AcpiEvaluateObjectTyped (
AcpiUtGetTypeName (((ACPI_OBJECT *) ReturnBuffer->Pointer)->Type),
AcpiUtGetTypeName (ReturnType)));
- if (MustFree)
+ if (FreeBufferOnError)
{
- /* Caller used ACPI_ALLOCATE_BUFFER, free the return buffer */
-
+ /*
+ * Free a buffer created via ACPI_ALLOCATE_BUFFER.
+ * Note: We use AcpiOsFree here because AcpiOsAllocate was used
+ * to allocate the buffer. This purposefully bypasses the
+ * (optionally enabled) allocation tracking mechanism since we
+ * only want to track internal allocations.
+ */
AcpiOsFree (ReturnBuffer->Pointer);
ReturnBuffer->Pointer = NULL;
}
@@ -168,15 +173,15 @@ ACPI_EXPORT_SYMBOL (AcpiEvaluateObjectTyped)
* PARAMETERS: Handle - Object handle (optional)
* Pathname - Object pathname (optional)
* ExternalParams - List of parameters to pass to method,
- * terminated by NULL. May be NULL
+ * terminated by NULL. May be NULL
* if no parameters are being passed.
* ReturnBuffer - Where to put method's return value (if
- * any). If NULL, no value is returned.
+ * any). If NULL, no value is returned.
*
* RETURN: Status
*
* DESCRIPTION: Find and evaluate the given object, passing the given
- * parameters if necessary. One of "Handle" or "Pathname" must
+ * parameters if necessary. One of "Handle" or "Pathname" must
* be valid (non-null)
*
******************************************************************************/
@@ -205,8 +210,6 @@ AcpiEvaluateObject (
return_ACPI_STATUS (AE_NO_MEMORY);
}
- Info->Pathname = Pathname;
-
/* Convert and validate the device handle */
Info->PrefixNode = AcpiNsValidateHandle (Handle);
@@ -217,17 +220,69 @@ AcpiEvaluateObject (
}
/*
- * If there are parameters to be passed to a control method, the external
- * objects must all be converted to internal objects
+ * Get the actual namespace node for the target object.
+ * Handles these cases:
+ *
+ * 1) Null node, valid pathname from root (absolute path)
+ * 2) Node and valid pathname (path relative to Node)
+ * 3) Node, Null pathname
+ */
+ if ((Pathname) &&
+ (ACPI_IS_ROOT_PREFIX (Pathname[0])))
+ {
+ /* The path is fully qualified, just evaluate by name */
+
+ Info->PrefixNode = NULL;
+ }
+ else if (!Handle)
+ {
+ /*
+ * A handle is optional iff a fully qualified pathname is specified.
+ * Since we've already handled fully qualified names above, this is
+ * an error.
+ */
+ if (!Pathname)
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Both Handle and Pathname are NULL"));
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Null Handle with relative pathname [%s]", Pathname));
+ }
+
+ Status = AE_BAD_PARAMETER;
+ goto Cleanup;
+ }
+
+ Info->RelativePathname = Pathname;
+
+ /*
+ * Convert all external objects passed as arguments to the
+ * internal version(s).
*/
if (ExternalParams && ExternalParams->Count)
{
+ Info->ParamCount = (UINT16) ExternalParams->Count;
+
+ /* Warn on impossible argument count */
+
+ if (Info->ParamCount > ACPI_METHOD_NUM_ARGS)
+ {
+ ACPI_WARN_PREDEFINED ((AE_INFO, Pathname, ACPI_WARN_ALWAYS,
+ "Excess arguments (%u) - using only %u",
+ Info->ParamCount, ACPI_METHOD_NUM_ARGS));
+
+ Info->ParamCount = ACPI_METHOD_NUM_ARGS;
+ }
+
/*
* Allocate a new parameter block for the internal objects
* Add 1 to count to allow for null terminated internal list
*/
Info->Parameters = ACPI_ALLOCATE_ZEROED (
- ((ACPI_SIZE) ExternalParams->Count + 1) * sizeof (void *));
+ ((ACPI_SIZE) Info->ParamCount + 1) * sizeof (void *));
if (!Info->Parameters)
{
Status = AE_NO_MEMORY;
@@ -236,126 +291,181 @@ AcpiEvaluateObject (
/* Convert each external object in the list to an internal object */
- for (i = 0; i < ExternalParams->Count; i++)
+ for (i = 0; i < Info->ParamCount; i++)
{
Status = AcpiUtCopyEobjectToIobject (
- &ExternalParams->Pointer[i], &Info->Parameters[i]);
+ &ExternalParams->Pointer[i], &Info->Parameters[i]);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
}
}
- Info->Parameters[ExternalParams->Count] = NULL;
+
+ Info->Parameters[Info->ParamCount] = NULL;
}
+
+#ifdef _FUTURE_FEATURE
+
/*
- * Three major cases:
- * 1) Fully qualified pathname
- * 2) No handle, not fully qualified pathname (error)
- * 3) Valid handle
+ * Begin incoming argument count analysis. Check for too few args
+ * and too many args.
*/
- if ((Pathname) &&
- (AcpiNsValidRootPrefix (Pathname[0])))
+ switch (AcpiNsGetType (Info->Node))
{
- /* The path is fully qualified, just evaluate by name */
+ case ACPI_TYPE_METHOD:
+
+ /* Check incoming argument count against the method definition */
+
+ if (Info->ObjDesc->Method.ParamCount > Info->ParamCount)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Insufficient arguments (%u) - %u are required",
+ Info->ParamCount,
+ Info->ObjDesc->Method.ParamCount));
+
+ Status = AE_MISSING_ARGUMENTS;
+ goto Cleanup;
+ }
+
+ else if (Info->ObjDesc->Method.ParamCount < Info->ParamCount)
+ {
+ ACPI_WARNING ((AE_INFO,
+ "Excess arguments (%u) - only %u are required",
+ Info->ParamCount,
+ Info->ObjDesc->Method.ParamCount));
+
+ /* Just pass the required number of arguments */
+
+ Info->ParamCount = Info->ObjDesc->Method.ParamCount;
+ }
- Info->PrefixNode = NULL;
- Status = AcpiNsEvaluate (Info);
- }
- else if (!Handle)
- {
/*
- * A handle is optional iff a fully qualified pathname is specified.
- * Since we've already handled fully qualified names above, this is
- * an error
+ * Any incoming external objects to be passed as arguments to the
+ * method must be converted to internal objects
*/
- if (!Pathname)
+ if (Info->ParamCount)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Both Handle and Pathname are NULL"));
+ /*
+ * Allocate a new parameter block for the internal objects
+ * Add 1 to count to allow for null terminated internal list
+ */
+ Info->Parameters = ACPI_ALLOCATE_ZEROED (
+ ((ACPI_SIZE) Info->ParamCount + 1) * sizeof (void *));
+ if (!Info->Parameters)
+ {
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ /* Convert each external object in the list to an internal object */
+
+ for (i = 0; i < Info->ParamCount; i++)
+ {
+ Status = AcpiUtCopyEobjectToIobject (
+ &ExternalParams->Pointer[i], &Info->Parameters[i]);
+ if (ACPI_FAILURE (Status))
+ {
+ goto Cleanup;
+ }
+ }
+
+ Info->Parameters[Info->ParamCount] = NULL;
}
- else
+ break;
+
+ default:
+
+ /* Warn if arguments passed to an object that is not a method */
+
+ if (Info->ParamCount)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Null Handle with relative pathname [%s]", Pathname));
+ ACPI_WARNING ((AE_INFO,
+ "%u arguments were passed to a non-method ACPI object",
+ Info->ParamCount));
}
-
- Status = AE_BAD_PARAMETER;
+ break;
}
- else
- {
- /* We have a namespace a node and a possible relative path */
- Status = AcpiNsEvaluate (Info);
- }
+#endif
+
+
+ /* Now we can evaluate the object */
+
+ Status = AcpiNsEvaluate (Info);
/*
* If we are expecting a return value, and all went well above,
* copy the return value to an external object.
*/
- if (ReturnBuffer)
+ if (!ReturnBuffer)
{
- if (!Info->ReturnObject)
+ goto CleanupReturnObject;
+ }
+
+ if (!Info->ReturnObject)
+ {
+ ReturnBuffer->Length = 0;
+ goto Cleanup;
+ }
+
+ if (ACPI_GET_DESCRIPTOR_TYPE (Info->ReturnObject) ==
+ ACPI_DESC_TYPE_NAMED)
+ {
+ /*
+ * If we received a NS Node as a return object, this means that
+ * the object we are evaluating has nothing interesting to
+ * return (such as a mutex, etc.) We return an error because
+ * these types are essentially unsupported by this interface.
+ * We don't check up front because this makes it easier to add
+ * support for various types at a later date if necessary.
+ */
+ Status = AE_TYPE;
+ Info->ReturnObject = NULL; /* No need to delete a NS Node */
+ ReturnBuffer->Length = 0;
+ }
+
+ if (ACPI_FAILURE (Status))
+ {
+ goto CleanupReturnObject;
+ }
+
+ /* Dereference Index and RefOf references */
+
+ AcpiNsResolveReferences (Info);
+
+ /* Get the size of the returned object */
+
+ Status = AcpiUtGetObjectSize (Info->ReturnObject,
+ &BufferSpaceNeeded);
+ if (ACPI_SUCCESS (Status))
+ {
+ /* Validate/Allocate/Clear caller buffer */
+
+ Status = AcpiUtInitializeBuffer (ReturnBuffer,
+ BufferSpaceNeeded);
+ if (ACPI_FAILURE (Status))
{
- ReturnBuffer->Length = 0;
+ /*
+ * Caller's buffer is too small or a new one can't
+ * be allocated
+ */
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Needed buffer size %X, %s\n",
+ (UINT32) BufferSpaceNeeded,
+ AcpiFormatException (Status)));
}
else
{
- if (ACPI_GET_DESCRIPTOR_TYPE (Info->ReturnObject) ==
- ACPI_DESC_TYPE_NAMED)
- {
- /*
- * If we received a NS Node as a return object, this means that
- * the object we are evaluating has nothing interesting to
- * return (such as a mutex, etc.) We return an error because
- * these types are essentially unsupported by this interface.
- * We don't check up front because this makes it easier to add
- * support for various types at a later date if necessary.
- */
- Status = AE_TYPE;
- Info->ReturnObject = NULL; /* No need to delete a NS Node */
- ReturnBuffer->Length = 0;
- }
-
- if (ACPI_SUCCESS (Status))
- {
- /* Dereference Index and RefOf references */
-
- AcpiNsResolveReferences (Info);
-
- /* Get the size of the returned object */
+ /* We have enough space for the object, build it */
- Status = AcpiUtGetObjectSize (Info->ReturnObject,
- &BufferSpaceNeeded);
- if (ACPI_SUCCESS (Status))
- {
- /* Validate/Allocate/Clear caller buffer */
-
- Status = AcpiUtInitializeBuffer (ReturnBuffer,
- BufferSpaceNeeded);
- if (ACPI_FAILURE (Status))
- {
- /*
- * Caller's buffer is too small or a new one can't
- * be allocated
- */
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Needed buffer size %X, %s\n",
- (UINT32) BufferSpaceNeeded,
- AcpiFormatException (Status)));
- }
- else
- {
- /* We have enough space for the object, build it */
-
- Status = AcpiUtCopyIobjectToEobject (Info->ReturnObject,
- ReturnBuffer);
- }
- }
- }
+ Status = AcpiUtCopyIobjectToEobject (
+ Info->ReturnObject, ReturnBuffer);
}
}
+CleanupReturnObject:
+
if (Info->ReturnObject)
{
/*
@@ -449,6 +559,7 @@ AcpiNsResolveReferences (
break;
default:
+
return;
}
@@ -472,9 +583,9 @@ AcpiNsResolveReferences (
* PARAMETERS: Type - ACPI_OBJECT_TYPE to search for
* StartObject - Handle in namespace where search begins
* MaxDepth - Depth to which search is to reach
- * PreOrderVisit - Called during tree pre-order visit
+ * DescendingCallback - Called during tree descent
* when an object of "Type" is found
- * PostOrderVisit - Called during tree post-order visit
+ * AscendingCallback - Called during tree ascent
* when an object of "Type" is found
* Context - Passed to user function(s) above
* ReturnValue - Location where return value of
@@ -503,8 +614,8 @@ AcpiWalkNamespace (
ACPI_OBJECT_TYPE Type,
ACPI_HANDLE StartObject,
UINT32 MaxDepth,
- ACPI_WALK_CALLBACK PreOrderVisit,
- ACPI_WALK_CALLBACK PostOrderVisit,
+ ACPI_WALK_CALLBACK DescendingCallback,
+ ACPI_WALK_CALLBACK AscendingCallback,
void *Context,
void **ReturnValue)
{
@@ -518,7 +629,7 @@ AcpiWalkNamespace (
if ((Type > ACPI_TYPE_LOCAL_MAX) ||
(!MaxDepth) ||
- (!PreOrderVisit && !PostOrderVisit))
+ (!DescendingCallback && !AscendingCallback))
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
@@ -537,7 +648,7 @@ AcpiWalkNamespace (
Status = AcpiUtAcquireReadLock (&AcpiGbl_NamespaceRwLock);
if (ACPI_FAILURE (Status))
{
- return (Status);
+ return_ACPI_STATUS (Status);
}
/*
@@ -552,10 +663,19 @@ AcpiWalkNamespace (
goto UnlockAndExit;
}
+ /* Now we can validate the starting node */
+
+ if (!AcpiNsValidateHandle (StartObject))
+ {
+ Status = AE_BAD_PARAMETER;
+ goto UnlockAndExit2;
+ }
+
Status = AcpiNsWalkNamespace (Type, StartObject, MaxDepth,
- ACPI_NS_WALK_UNLOCK, PreOrderVisit,
- PostOrderVisit, Context, ReturnValue);
+ ACPI_NS_WALK_UNLOCK, DescendingCallback,
+ AscendingCallback, Context, ReturnValue);
+UnlockAndExit2:
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
UnlockAndExit:
@@ -591,8 +711,8 @@ AcpiNsGetDeviceCallback (
ACPI_STATUS Status;
ACPI_NAMESPACE_NODE *Node;
UINT32 Flags;
- ACPI_DEVICE_ID *Hid;
- ACPI_DEVICE_ID_LIST *Cid;
+ ACPI_PNP_DEVICE_ID *Hid;
+ ACPI_PNP_DEVICE_ID_LIST *Cid;
UINT32 i;
BOOLEAN Found;
int NoMatch;
@@ -642,7 +762,7 @@ AcpiNsGetDeviceCallback (
return (AE_CTRL_DEPTH);
}
- NoMatch = ACPI_STRCMP (Hid->String, Info->Hid);
+ NoMatch = strcmp (Hid->String, Info->Hid);
ACPI_FREE (Hid);
if (NoMatch)
@@ -666,7 +786,7 @@ AcpiNsGetDeviceCallback (
Found = FALSE;
for (i = 0; i < Cid->Count; i++)
{
- if (ACPI_STRCMP (Cid->Ids[i].String, Info->Hid) == 0)
+ if (strcmp (Cid->Ids[i].String, Info->Hid) == 0)
{
/* Found a matching CID */
@@ -704,8 +824,8 @@ AcpiNsGetDeviceCallback (
/* We have a valid device, invoke the user function */
- Status = Info->UserFunction (ObjHandle, NestingLevel, Info->Context,
- ReturnValue);
+ Status = Info->UserFunction (ObjHandle, NestingLevel,
+ Info->Context, ReturnValue);
return (Status);
}
@@ -726,7 +846,7 @@ AcpiNsGetDeviceCallback (
* DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
* starting (and ending) at the object specified by StartHandle.
* The UserFunction is called whenever an object of type
- * Device is found. If the user function returns
+ * Device is found. If the user function returns
* a non-zero value, the search is terminated immediately and this
* value is returned to the caller.
*
@@ -760,8 +880,8 @@ AcpiGetDevices (
* We're going to call their callback from OUR callback, so we need
* to know what it is, and their context parameter.
*/
- Info.Hid = HID;
- Info.Context = Context;
+ Info.Hid = HID;
+ Info.Context = Context;
Info.UserFunction = UserFunction;
/*
@@ -777,8 +897,8 @@ AcpiGetDevices (
}
Status = AcpiNsWalkNamespace (ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
- AcpiNsGetDeviceCallback, NULL, &Info, ReturnValue);
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
+ AcpiNsGetDeviceCallback, NULL, &Info, ReturnValue);
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return_ACPI_STATUS (Status);
@@ -956,5 +1076,3 @@ UnlockAndExit:
}
ACPI_EXPORT_SYMBOL (AcpiGetData)
-
-
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsxfname.c b/usr/src/uts/intel/io/acpica/namespace/nsxfname.c
index b0ccff5bdc..cfd6937f49 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsxfname.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsxfname.c
@@ -6,7 +6,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __NSXFNAME_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -58,8 +58,8 @@
static char *
AcpiNsCopyDeviceId (
- ACPI_DEVICE_ID *Dest,
- ACPI_DEVICE_ID *Source,
+ ACPI_PNP_DEVICE_ID *Dest,
+ ACPI_PNP_DEVICE_ID *Source,
char *StringArea);
@@ -75,8 +75,8 @@ AcpiNsCopyDeviceId (
* RETURN: Status
*
* DESCRIPTION: This routine will search for a caller specified name in the
- * name space. The caller can restrict the search region by
- * specifying a non NULL parent. The parent value is itself a
+ * name space. The caller can restrict the search region by
+ * specifying a non NULL parent. The parent value is itself a
* namespace handle.
*
******************************************************************************/
@@ -120,13 +120,13 @@ AcpiGetHandle (
*
* Error for <null Parent + relative path>
*/
- if (AcpiNsValidRootPrefix (Pathname[0]))
+ if (ACPI_IS_ROOT_PREFIX (Pathname[0]))
{
/* Pathname is fully qualified (starts with '\') */
/* Special case for root-only, since we can't search for it */
- if (!ACPI_STRCMP (Pathname, ACPI_NS_ROOT_PATH))
+ if (!strcmp (Pathname, ACPI_NS_ROOT_PATH))
{
*RetHandle = ACPI_CAST_PTR (ACPI_HANDLE, AcpiGbl_RootNode);
return (AE_OK);
@@ -164,7 +164,7 @@ ACPI_EXPORT_SYMBOL (AcpiGetHandle)
* RETURN: Pointer to a string containing the fully qualified Name.
*
* DESCRIPTION: This routine returns the fully qualified name associated with
- * the Handle parameter. This and the AcpiPathnameToHandle are
+ * the Handle parameter. This and the AcpiPathnameToHandle are
* complementary functions.
*
******************************************************************************/
@@ -177,6 +177,7 @@ AcpiGetName (
{
ACPI_STATUS Status;
ACPI_NAMESPACE_NODE *Node;
+ const char *NodeName;
/* Parameter validation */
@@ -192,11 +193,13 @@ AcpiGetName (
return (Status);
}
- if (NameType == ACPI_FULL_PATHNAME)
+ if (NameType == ACPI_FULL_PATHNAME ||
+ NameType == ACPI_FULL_PATHNAME_NO_TRAILING)
{
/* Get the full pathname (From the namespace root) */
- Status = AcpiNsHandleToPathname (Handle, Buffer);
+ Status = AcpiNsHandleToPathname (Handle, Buffer,
+ NameType == ACPI_FULL_PATHNAME ? FALSE : TRUE);
return (Status);
}
@@ -227,8 +230,8 @@ AcpiGetName (
/* Just copy the ACPI name from the Node and zero terminate it */
- ACPI_STRNCPY (Buffer->Pointer, AcpiUtGetNodeName (Node),
- ACPI_NAME_SIZE);
+ NodeName = AcpiUtGetNodeName (Node);
+ ACPI_MOVE_NAME (Buffer->Pointer, NodeName);
((char *) Buffer->Pointer) [ACPI_NAME_SIZE] = 0;
Status = AE_OK;
@@ -246,30 +249,30 @@ ACPI_EXPORT_SYMBOL (AcpiGetName)
*
* FUNCTION: AcpiNsCopyDeviceId
*
- * PARAMETERS: Dest - Pointer to the destination DEVICE_ID
- * Source - Pointer to the source DEVICE_ID
+ * PARAMETERS: Dest - Pointer to the destination PNP_DEVICE_ID
+ * Source - Pointer to the source PNP_DEVICE_ID
* StringArea - Pointer to where to copy the dest string
*
* RETURN: Pointer to the next string area
*
- * DESCRIPTION: Copy a single DEVICE_ID, including the string data.
+ * DESCRIPTION: Copy a single PNP_DEVICE_ID, including the string data.
*
******************************************************************************/
static char *
AcpiNsCopyDeviceId (
- ACPI_DEVICE_ID *Dest,
- ACPI_DEVICE_ID *Source,
+ ACPI_PNP_DEVICE_ID *Dest,
+ ACPI_PNP_DEVICE_ID *Source,
char *StringArea)
{
- /* Create the destination DEVICE_ID */
+ /* Create the destination PNP_DEVICE_ID */
Dest->String = StringArea;
Dest->Length = Source->Length;
/* Copy actual string and return a pointer to the next string area */
- ACPI_MEMCPY (StringArea, Source->String, Source->Length);
+ memcpy (StringArea, Source->String, Source->Length);
return (StringArea + Source->Length);
}
@@ -288,10 +291,17 @@ AcpiNsCopyDeviceId (
* control methods (Such as in the case of a device.)
*
* For Device and Processor objects, run the Device _HID, _UID, _CID, _STA,
- * _ADR, _SxW, and _SxD methods.
+ * _CLS, _ADR, _SxW, and _SxD methods.
*
* Note: Allocates the return buffer, must be freed by the caller.
*
+ * Note: This interface is intended to be used during the initial device
+ * discovery namespace traversal. Therefore, no complex methods can be
+ * executed, especially those that access operation regions. Therefore, do
+ * not add any additional methods that could cause problems in this area.
+ * this was the fate of the _SUB method which was found to cause such
+ * problems and was removed (11/2015).
+ *
******************************************************************************/
ACPI_STATUS
@@ -301,14 +311,15 @@ AcpiGetObjectInfo (
{
ACPI_NAMESPACE_NODE *Node;
ACPI_DEVICE_INFO *Info;
- ACPI_DEVICE_ID_LIST *CidList = NULL;
- ACPI_DEVICE_ID *Hid = NULL;
- ACPI_DEVICE_ID *Uid = NULL;
+ ACPI_PNP_DEVICE_ID_LIST *CidList = NULL;
+ ACPI_PNP_DEVICE_ID *Hid = NULL;
+ ACPI_PNP_DEVICE_ID *Uid = NULL;
+ ACPI_PNP_DEVICE_ID *Cls = NULL;
char *NextIdString;
ACPI_OBJECT_TYPE Type;
ACPI_NAME Name;
UINT8 ParamCount= 0;
- UINT8 Valid = 0;
+ UINT16 Valid = 0;
UINT32 InfoSize;
UINT32 i;
ACPI_STATUS Status;
@@ -324,7 +335,7 @@ AcpiGetObjectInfo (
Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE (Status))
{
- goto Cleanup;
+ return (Status);
}
Node = AcpiNsValidateHandle (Handle);
@@ -356,7 +367,7 @@ AcpiGetObjectInfo (
{
/*
* Get extra info for ACPI Device/Processor objects only:
- * Run the Device _HID, _UID, and _CID methods.
+ * Run the Device _HID, _UID, _CLS, and _CID methods.
*
* Note: none of these methods are required, so they may or may
* not be present for this device. The Info->Valid bitfield is used
@@ -388,9 +399,18 @@ AcpiGetObjectInfo (
{
/* Add size of CID strings and CID pointer array */
- InfoSize += (CidList->ListSize - sizeof (ACPI_DEVICE_ID_LIST));
+ InfoSize += (CidList->ListSize - sizeof (ACPI_PNP_DEVICE_ID_LIST));
Valid |= ACPI_VALID_CID;
}
+
+ /* Execute the Device._CLS method */
+
+ Status = AcpiUtExecute_CLS (Node, &Cls);
+ if (ACPI_SUCCESS (Status))
+ {
+ InfoSize += Cls->Length;
+ Valid |= ACPI_VALID_CLS;
+ }
}
/*
@@ -413,9 +433,14 @@ AcpiGetObjectInfo (
* Get extra info for ACPI Device/Processor objects only:
* Run the _STA, _ADR and, SxW, and _SxD methods.
*
- * Note: none of these methods are required, so they may or may
+ * Notes: none of these methods are required, so they may or may
* not be present for this device. The Info->Valid bitfield is used
* to indicate which methods were found and run successfully.
+ *
+ * For _STA, if the method does not exist, then (as per the ACPI
+ * specification), the returned CurrentStatus flags will indicate
+ * that the device is present/functional/enabled. Otherwise, the
+ * CurrentStatus flags reflect the value returned from _STA.
*/
/* Execute the Device._STA method */
@@ -429,7 +454,7 @@ AcpiGetObjectInfo (
/* Execute the Device._ADR method */
Status = AcpiUtEvaluateNumericObject (METHOD_NAME__ADR, Node,
- &Info->Address);
+ &Info->Address);
if (ACPI_SUCCESS (Status))
{
Valid |= ACPI_VALID_ADR;
@@ -438,8 +463,8 @@ AcpiGetObjectInfo (
/* Execute the Device._SxW methods */
Status = AcpiUtExecutePowerMethods (Node,
- AcpiGbl_LowestDstateNames, ACPI_NUM_SxW_METHODS,
- Info->LowestDstates);
+ AcpiGbl_LowestDstateNames, ACPI_NUM_SxW_METHODS,
+ Info->LowestDstates);
if (ACPI_SUCCESS (Status))
{
Valid |= ACPI_VALID_SXWS;
@@ -448,8 +473,8 @@ AcpiGetObjectInfo (
/* Execute the Device._SxD methods */
Status = AcpiUtExecutePowerMethods (Node,
- AcpiGbl_HighestDstateNames, ACPI_NUM_SxD_METHODS,
- Info->HighestDstates);
+ AcpiGbl_HighestDstateNames, ACPI_NUM_SxD_METHODS,
+ Info->HighestDstates);
if (ACPI_SUCCESS (Status))
{
Valid |= ACPI_VALID_SXDS;
@@ -463,9 +488,9 @@ AcpiGetObjectInfo (
NextIdString = ACPI_CAST_PTR (char, Info->CompatibleIdList.Ids);
if (CidList)
{
- /* Point past the CID DEVICE_ID array */
+ /* Point past the CID PNP_DEVICE_ID array */
- NextIdString += ((ACPI_SIZE) CidList->Count * sizeof (ACPI_DEVICE_ID));
+ NextIdString += ((ACPI_SIZE) CidList->Count * sizeof (ACPI_PNP_DEVICE_ID));
}
/*
@@ -510,6 +535,12 @@ AcpiGetObjectInfo (
}
}
+ if (Cls)
+ {
+ NextIdString = AcpiNsCopyDeviceId (&Info->ClassCode,
+ Cls, NextIdString);
+ }
+
/* Copy the fixed-length data */
Info->InfoSize = InfoSize;
@@ -535,6 +566,10 @@ Cleanup:
{
ACPI_FREE (CidList);
}
+ if (Cls)
+ {
+ ACPI_FREE (Cls);
+ }
return (Status);
}
@@ -602,6 +637,7 @@ AcpiInstallMethod (
ParserState.Aml += AcpiPsGetOpcodeSize (Opcode);
ParserState.PkgEnd = AcpiPsGetNextPackageEnd (&ParserState);
Path = AcpiPsGetNextNamestring (&ParserState);
+
MethodFlags = *ParserState.Aml++;
AmlStart = ParserState.Aml;
AmlLength = ACPI_PTR_DIFF (ParserState.PkgEnd, AmlStart);
@@ -634,7 +670,7 @@ AcpiInstallMethod (
/* The lookup either returns an existing node or creates a new one */
Status = AcpiNsLookup (NULL, Path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1,
- ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, NULL, &Node);
+ ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, NULL, &Node);
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
@@ -656,7 +692,7 @@ AcpiInstallMethod (
/* Copy the method AML to the local buffer */
- ACPI_MEMCPY (AmlBuffer, AmlStart, AmlLength);
+ memcpy (AmlBuffer, AmlStart, AmlLength);
/* Initialize the method object with the new method's information */
diff --git a/usr/src/uts/intel/io/acpica/namespace/nsxfobj.c b/usr/src/uts/intel/io/acpica/namespace/nsxfobj.c
index 9910248220..f006047431 100644
--- a/usr/src/uts/intel/io/acpica/namespace/nsxfobj.c
+++ b/usr/src/uts/intel/io/acpica/namespace/nsxfobj.c
@@ -6,7 +6,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +42,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __NSXFOBJ_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -82,10 +81,8 @@ AcpiGetType (
return (AE_BAD_PARAMETER);
}
- /*
- * Special case for the predefined Root Node
- * (return type ANY)
- */
+ /* Special case for the predefined Root Node (return type ANY) */
+
if (Handle == ACPI_ROOT_OBJECT)
{
*RetType = ACPI_TYPE_ANY;
@@ -109,7 +106,6 @@ AcpiGetType (
*RetType = Node->Type;
-
Status = AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
return (Status);
}
@@ -202,8 +198,8 @@ ACPI_EXPORT_SYMBOL (AcpiGetParent)
*
* RETURN: Status
*
- * DESCRIPTION: Return the next peer object within the namespace. If Handle is
- * valid, Scope is ignored. Otherwise, the first object within
+ * DESCRIPTION: Return the next peer object within the namespace. If Handle is
+ * valid, Scope is ignored. Otherwise, the first object within
* Scope is returned.
*
******************************************************************************/
@@ -282,4 +278,3 @@ UnlockAndExit:
}
ACPI_EXPORT_SYMBOL (AcpiGetNextObject)
-
diff --git a/usr/src/uts/intel/io/acpica/osl.c b/usr/src/uts/intel/io/acpica/osl.c
index d5bfab754f..3a2d124ef5 100644
--- a/usr/src/uts/intel/io/acpica/osl.c
+++ b/usr/src/uts/intel/io/acpica/osl.c
@@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
* Copyright (c) 2009-2010, Intel Corporation.
@@ -315,6 +315,12 @@ AcpiOsTableOverride(ACPI_TABLE_HEADER *ExistingTable,
return (AE_OK);
}
+ACPI_STATUS
+AcpiOsPhysicalTableOverride(ACPI_TABLE_HEADER *ExistingTable,
+ ACPI_PHYSICAL_ADDRESS *NewAddress, UINT32 *NewTableLength)
+{
+ return (AE_SUPPORT);
+}
/*
* ACPI semaphore implementation
@@ -743,6 +749,22 @@ AcpiOsExecute(ACPI_EXECUTE_TYPE Type, ACPI_OSD_EXEC_CALLBACK Function,
}
+
+void
+AcpiOsWaitEventsComplete(void)
+{
+ int i;
+
+ /*
+ * Wait for event queues to be empty.
+ */
+ for (i = OSL_GLOBAL_LOCK_HANDLER; i <= OSL_EC_BURST_HANDLER; i++) {
+ if (osl_eventq[i] != NULL) {
+ ddi_taskq_wait(osl_eventq[i]);
+ }
+ }
+}
+
void
AcpiOsSleep(ACPI_INTEGER Milliseconds)
{
@@ -883,7 +905,7 @@ AcpiOsWritePort(ACPI_IO_ADDRESS Address, UINT32 Value, UINT32 Width)
static void
-osl_rw_memory(ACPI_PHYSICAL_ADDRESS Address, UINT32 *Value,
+osl_rw_memory(ACPI_PHYSICAL_ADDRESS Address, UINT64 *Value,
UINT32 Width, int write)
{
size_t maplen = Width / 8;
@@ -902,6 +924,9 @@ osl_rw_memory(ACPI_PHYSICAL_ADDRESS Address, UINT32 *Value,
case 4:
OSL_RW(ptr, Value, uint32_t, write);
break;
+ case 8:
+ OSL_RW(ptr, Value, uint64_t, write);
+ break;
default:
cmn_err(CE_WARN, "!osl_rw_memory: invalid size %d",
Width);
@@ -913,7 +938,7 @@ osl_rw_memory(ACPI_PHYSICAL_ADDRESS Address, UINT32 *Value,
ACPI_STATUS
AcpiOsReadMemory(ACPI_PHYSICAL_ADDRESS Address,
- UINT32 *Value, UINT32 Width)
+ UINT64 *Value, UINT32 Width)
{
osl_rw_memory(Address, Value, Width, 0);
return (AE_OK);
@@ -921,7 +946,7 @@ AcpiOsReadMemory(ACPI_PHYSICAL_ADDRESS Address,
ACPI_STATUS
AcpiOsWriteMemory(ACPI_PHYSICAL_ADDRESS Address,
- UINT32 Value, UINT32 Width)
+ UINT64 Value, UINT32 Width)
{
osl_rw_memory(Address, &Value, Width, 1);
return (AE_OK);
@@ -2341,3 +2366,15 @@ acpica_write_cpupm_capabilities(boolean_t pstates, boolean_t cstates)
(void) AcpiHwRegisterWrite(ACPI_REGISTER_SMI_COMMAND_BLOCK,
AcpiGbl_FADT.CstControl);
}
+
+uint32_t
+acpi_strtoul(const char *str, char **ep, int base)
+{
+ ulong_t v;
+
+ if (ddi_strtoul(str, ep, base, &v) != 0 || v > ACPI_UINT32_MAX) {
+ return (ACPI_UINT32_MAX);
+ }
+
+ return ((uint32_t)v);
+}
diff --git a/usr/src/uts/intel/io/acpica/parser/psargs.c b/usr/src/uts/intel/io/acpica/parser/psargs.c
index e383def11e..a4745c3adc 100644
--- a/usr/src/uts/intel/io/acpica/parser/psargs.c
+++ b/usr/src/uts/intel/io/acpica/parser/psargs.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __PSARGS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -131,7 +129,7 @@ AcpiPsGetNextPackageLength (
* RETURN: Pointer to end-of-package +1
*
* DESCRIPTION: Get next package length and return a pointer past the end of
- * the package. Consumes the package length field
+ * the package. Consumes the package length field
*
******************************************************************************/
@@ -163,8 +161,8 @@ AcpiPsGetNextPackageEnd (
* RETURN: Pointer to the start of the name string (pointer points into
* the AML.
*
- * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name
- * prefix characters. Set parser state to point past the string.
+ * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name
+ * prefix characters. Set parser state to point past the string.
* (Name is consumed from the AML.)
*
******************************************************************************/
@@ -182,7 +180,8 @@ AcpiPsGetNextNamestring (
/* Point past any namestring prefix characters (backslash or carat) */
- while (AcpiPsIsPrefixChar (*End))
+ while (ACPI_IS_ROOT_PREFIX (*End) ||
+ ACPI_IS_PARENT_PREFIX (*End))
{
End++;
}
@@ -244,7 +243,7 @@ AcpiPsGetNextNamestring (
*
* DESCRIPTION: Get next name (if method call, return # of required args).
* Names are looked up in the internal namespace to determine
- * if the name represents a control method. If a method
+ * if the name represents a control method. If a method
* is found, the number of arguments to the method is returned.
* This information is critical for parsing to continue correctly.
*
@@ -288,8 +287,8 @@ AcpiPsGetNextNamepath (
* the upsearch)
*/
Status = AcpiNsLookup (WalkState->ScopeInfo, Path,
- ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, &Node);
+ ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, &Node);
/*
* If this name is a control method invocation, we must
@@ -317,7 +316,7 @@ AcpiPsGetNextNamepath (
ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
"Control Method - %p Desc %p Path=%p\n", Node, MethodDesc, Path));
- NameOp = AcpiPsAllocOp (AML_INT_NAMEPATH_OP);
+ NameOp = AcpiPsAllocOp (AML_INT_NAMEPATH_OP, Start);
if (!NameOp)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -360,7 +359,7 @@ AcpiPsGetNextNamepath (
/* 1) NotFound is ok during load pass 1/2 (allow forward references) */
if ((WalkState->ParseFlags & ACPI_PARSE_MODE_MASK) !=
- ACPI_PARSE_EXECUTE)
+ ACPI_PARSE_EXECUTE)
{
Status = AE_OK;
}
@@ -392,7 +391,7 @@ AcpiPsGetNextNamepath (
ACPI_ERROR_NAMESPACE (Path, Status);
if ((WalkState->ParseFlags & ACPI_PARSE_MODE_MASK) ==
- ACPI_PARSE_EXECUTE)
+ ACPI_PARSE_EXECUTE)
{
/* Report a control method execution error */
@@ -446,7 +445,6 @@ AcpiPsGetNextSimpleArg (
Length = 1;
break;
-
case ARGP_WORDDATA:
/* Get 2 bytes from the AML stream */
@@ -456,7 +454,6 @@ AcpiPsGetNextSimpleArg (
Length = 2;
break;
-
case ARGP_DWORDDATA:
/* Get 4 bytes from the AML stream */
@@ -466,7 +463,6 @@ AcpiPsGetNextSimpleArg (
Length = 4;
break;
-
case ARGP_QWORDDATA:
/* Get 8 bytes from the AML stream */
@@ -476,7 +472,6 @@ AcpiPsGetNextSimpleArg (
Length = 8;
break;
-
case ARGP_CHARLIST:
/* Get a pointer to the string, point past the string */
@@ -494,7 +489,6 @@ AcpiPsGetNextSimpleArg (
Length++;
break;
-
case ARGP_NAME:
case ARGP_NAMESTRING:
@@ -502,7 +496,6 @@ AcpiPsGetNextSimpleArg (
Arg->Common.Value.Name = AcpiPsGetNextNamestring (ParserState);
return_VOID;
-
default:
ACPI_ERROR ((AE_INFO, "Invalid ArgType 0x%X", ArgType));
@@ -531,49 +524,66 @@ static ACPI_PARSE_OBJECT *
AcpiPsGetNextField (
ACPI_PARSE_STATE *ParserState)
{
- UINT32 AmlOffset = (UINT32)
- ACPI_PTR_DIFF (ParserState->Aml,
- ParserState->AmlStart);
+ UINT8 *Aml;
ACPI_PARSE_OBJECT *Field;
+ ACPI_PARSE_OBJECT *Arg = NULL;
UINT16 Opcode;
UINT32 Name;
+ UINT8 AccessType;
+ UINT8 AccessAttribute;
+ UINT8 AccessLength;
+ UINT32 PkgLength;
+ UINT8 *PkgEnd;
+ UINT32 BufferLength;
ACPI_FUNCTION_TRACE (PsGetNextField);
+ Aml = ParserState->Aml;
+
/* Determine field type */
switch (ACPI_GET8 (ParserState->Aml))
{
- default:
+ case AML_FIELD_OFFSET_OP:
- Opcode = AML_INT_NAMEDFIELD_OP;
+ Opcode = AML_INT_RESERVEDFIELD_OP;
+ ParserState->Aml++;
break;
- case 0x00:
+ case AML_FIELD_ACCESS_OP:
- Opcode = AML_INT_RESERVEDFIELD_OP;
+ Opcode = AML_INT_ACCESSFIELD_OP;
ParserState->Aml++;
break;
- case 0x01:
+ case AML_FIELD_CONNECTION_OP:
- Opcode = AML_INT_ACCESSFIELD_OP;
+ Opcode = AML_INT_CONNECTION_OP;
ParserState->Aml++;
break;
+
+ case AML_FIELD_EXT_ACCESS_OP:
+
+ Opcode = AML_INT_EXTACCESSFIELD_OP;
+ ParserState->Aml++;
+ break;
+
+ default:
+
+ Opcode = AML_INT_NAMEDFIELD_OP;
+ break;
}
/* Allocate a new field op */
- Field = AcpiPsAllocOp (Opcode);
+ Field = AcpiPsAllocOp (Opcode, Aml);
if (!Field)
{
return_PTR (NULL);
}
- Field->Common.AmlOffset = AmlOffset;
-
/* Decode the field type */
switch (Opcode)
@@ -601,17 +611,123 @@ AcpiPsGetNextField (
case AML_INT_ACCESSFIELD_OP:
+ case AML_INT_EXTACCESSFIELD_OP:
/*
* Get AccessType and AccessAttrib and merge into the field Op
- * AccessType is first operand, AccessAttribute is second
+ * AccessType is first operand, AccessAttribute is second. stuff
+ * these bytes into the node integer value for convenience.
*/
- Field->Common.Value.Integer = (((UINT32) ACPI_GET8 (ParserState->Aml) << 8));
+
+ /* Get the two bytes (Type/Attribute) */
+
+ AccessType = ACPI_GET8 (ParserState->Aml);
ParserState->Aml++;
- Field->Common.Value.Integer |= ACPI_GET8 (ParserState->Aml);
+ AccessAttribute = ACPI_GET8 (ParserState->Aml);
ParserState->Aml++;
+
+ Field->Common.Value.Integer = (UINT8) AccessType;
+ Field->Common.Value.Integer |= (UINT16) (AccessAttribute << 8);
+
+ /* This opcode has a third byte, AccessLength */
+
+ if (Opcode == AML_INT_EXTACCESSFIELD_OP)
+ {
+ AccessLength = ACPI_GET8 (ParserState->Aml);
+ ParserState->Aml++;
+
+ Field->Common.Value.Integer |= (UINT32) (AccessLength << 16);
+ }
break;
+
+ case AML_INT_CONNECTION_OP:
+
+ /*
+ * Argument for Connection operator can be either a Buffer
+ * (resource descriptor), or a NameString.
+ */
+ Aml = ParserState->Aml;
+ if (ACPI_GET8 (ParserState->Aml) == AML_BUFFER_OP)
+ {
+ ParserState->Aml++;
+
+ PkgEnd = ParserState->Aml;
+ PkgLength = AcpiPsGetNextPackageLength (ParserState);
+ PkgEnd += PkgLength;
+
+ if (ParserState->Aml < PkgEnd)
+ {
+ /* Non-empty list */
+
+ Arg = AcpiPsAllocOp (AML_INT_BYTELIST_OP, Aml);
+ if (!Arg)
+ {
+ AcpiPsFreeOp (Field);
+ return_PTR (NULL);
+ }
+
+ /* Get the actual buffer length argument */
+
+ Opcode = ACPI_GET8 (ParserState->Aml);
+ ParserState->Aml++;
+
+ switch (Opcode)
+ {
+ case AML_BYTE_OP: /* AML_BYTEDATA_ARG */
+
+ BufferLength = ACPI_GET8 (ParserState->Aml);
+ ParserState->Aml += 1;
+ break;
+
+ case AML_WORD_OP: /* AML_WORDDATA_ARG */
+
+ BufferLength = ACPI_GET16 (ParserState->Aml);
+ ParserState->Aml += 2;
+ break;
+
+ case AML_DWORD_OP: /* AML_DWORDATA_ARG */
+
+ BufferLength = ACPI_GET32 (ParserState->Aml);
+ ParserState->Aml += 4;
+ break;
+
+ default:
+
+ BufferLength = 0;
+ break;
+ }
+
+ /* Fill in bytelist data */
+
+ Arg->Named.Value.Size = BufferLength;
+ Arg->Named.Data = ParserState->Aml;
+ }
+
+ /* Skip to End of byte data */
+
+ ParserState->Aml = PkgEnd;
+ }
+ else
+ {
+ Arg = AcpiPsAllocOp (AML_INT_NAMEPATH_OP, Aml);
+ if (!Arg)
+ {
+ AcpiPsFreeOp (Field);
+ return_PTR (NULL);
+ }
+
+ /* Get the Namestring argument */
+
+ Arg->Common.Value.Name = AcpiPsGetNextNamestring (ParserState);
+ }
+
+ /* Link the buffer/namestring to parent (CONNECTION_OP) */
+
+ AcpiPsAppendArg (Field, Arg);
+ break;
+
+
default:
/* Opcode was set in previous switch */
@@ -666,15 +782,15 @@ AcpiPsGetNextArg (
/* Constants, strings, and namestrings are all the same size */
- Arg = AcpiPsAllocOp (AML_BYTE_OP);
+ Arg = AcpiPsAllocOp (AML_BYTE_OP, ParserState->Aml);
if (!Arg)
{
return_ACPI_STATUS (AE_NO_MEMORY);
}
+
AcpiPsGetNextSimpleArg (ParserState, ArgType, Arg);
break;
-
case ARGP_PKGLENGTH:
/* Package length, nothing returned */
@@ -682,7 +798,6 @@ AcpiPsGetNextArg (
ParserState->PkgEnd = AcpiPsGetNextPackageEnd (ParserState);
break;
-
case ARGP_FIELDLIST:
if (ParserState->Aml < ParserState->PkgEnd)
@@ -714,14 +829,14 @@ AcpiPsGetNextArg (
}
break;
-
case ARGP_BYTELIST:
if (ParserState->Aml < ParserState->PkgEnd)
{
/* Non-empty list */
- Arg = AcpiPsAllocOp (AML_INT_BYTELIST_OP);
+ Arg = AcpiPsAllocOp (AML_INT_BYTELIST_OP,
+ ParserState->Aml);
if (!Arg)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -739,19 +854,20 @@ AcpiPsGetNextArg (
}
break;
-
case ARGP_TARGET:
case ARGP_SUPERNAME:
case ARGP_SIMPLENAME:
+ case ARGP_NAME_OR_REF:
Subop = AcpiPsPeekOpcode (ParserState);
if (Subop == 0 ||
AcpiPsIsLeadingChar (Subop) ||
- AcpiPsIsPrefixChar (Subop))
+ ACPI_IS_ROOT_PREFIX (Subop) ||
+ ACPI_IS_PARENT_PREFIX (Subop))
{
/* NullName or NameString */
- Arg = AcpiPsAllocOp (AML_INT_NAMEPATH_OP);
+ Arg = AcpiPsAllocOp (AML_INT_NAMEPATH_OP, ParserState->Aml);
if (!Arg)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -761,11 +877,12 @@ AcpiPsGetNextArg (
if (WalkState->Opcode == AML_UNLOAD_OP)
{
- Status = AcpiPsGetNextNamepath (WalkState, ParserState, Arg, 1);
+ Status = AcpiPsGetNextNamepath (WalkState, ParserState,
+ Arg, ACPI_POSSIBLE_METHOD_CALL);
/*
- * If the SuperName arg of Unload is a method call,
- * we have restored the AML pointer, just free this Arg
+ * If the SuperName argument is a method call, we have
+ * already restored the AML pointer, just free this Arg
*/
if (Arg->Common.AmlOpcode == AML_INT_METHODCALL_OP)
{
@@ -775,7 +892,8 @@ AcpiPsGetNextArg (
}
else
{
- Status = AcpiPsGetNextNamepath (WalkState, ParserState, Arg, 0);
+ Status = AcpiPsGetNextNamepath (WalkState, ParserState,
+ Arg, ACPI_NOT_METHOD_CALL);
}
}
else
@@ -786,7 +904,6 @@ AcpiPsGetNextArg (
}
break;
-
case ARGP_DATAOBJ:
case ARGP_TERMARG:
@@ -795,7 +912,6 @@ AcpiPsGetNextArg (
WalkState->ArgCount = 1;
break;
-
case ARGP_DATAOBJLIST:
case ARGP_TERMLIST:
case ARGP_OBJLIST:
@@ -808,7 +924,6 @@ AcpiPsGetNextArg (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Invalid ArgType: 0x%X", ArgType));
diff --git a/usr/src/uts/intel/io/acpica/parser/psloop.c b/usr/src/uts/intel/io/acpica/parser/psloop.c
index a98989e099..e0839fd7d8 100644
--- a/usr/src/uts/intel/io/acpica/parser/psloop.c
+++ b/usr/src/uts/intel/io/acpica/parser/psloop.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
/*
* Parse the AML and build an operation tree as most interpreters, (such as
* Perl) do. Parsing is done by hand rather than with a YACC generated parser
@@ -52,6 +51,7 @@
#include "acpi.h"
#include "accommon.h"
+#include "acinterp.h"
#include "acparser.h"
#include "acdispat.h"
#include "amlcode.h"
@@ -59,46 +59,15 @@
#define _COMPONENT ACPI_PARSER
ACPI_MODULE_NAME ("psloop")
-static UINT32 AcpiGbl_Depth = 0;
-
/* Local prototypes */
static ACPI_STATUS
-AcpiPsGetAmlOpcode (
- ACPI_WALK_STATE *WalkState);
-
-static ACPI_STATUS
-AcpiPsBuildNamedOp (
- ACPI_WALK_STATE *WalkState,
- UINT8 *AmlOpStart,
- ACPI_PARSE_OBJECT *UnnamedOp,
- ACPI_PARSE_OBJECT **Op);
-
-static ACPI_STATUS
-AcpiPsCreateOp (
- ACPI_WALK_STATE *WalkState,
- UINT8 *AmlOpStart,
- ACPI_PARSE_OBJECT **NewOp);
-
-static ACPI_STATUS
AcpiPsGetArguments (
ACPI_WALK_STATE *WalkState,
UINT8 *AmlOpStart,
ACPI_PARSE_OBJECT *Op);
-static ACPI_STATUS
-AcpiPsCompleteOp (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT **Op,
- ACPI_STATUS Status);
-
-static ACPI_STATUS
-AcpiPsCompleteFinalOp (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op,
- ACPI_STATUS Status);
-
static void
AcpiPsLinkModuleCode (
ACPI_PARSE_OBJECT *ParentOp,
@@ -109,314 +78,6 @@ AcpiPsLinkModuleCode (
/*******************************************************************************
*
- * FUNCTION: AcpiPsGetAmlOpcode
- *
- * PARAMETERS: WalkState - Current state
- *
- * RETURN: Status
- *
- * DESCRIPTION: Extract the next AML opcode from the input stream.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiPsGetAmlOpcode (
- ACPI_WALK_STATE *WalkState)
-{
-
- ACPI_FUNCTION_TRACE_PTR (PsGetAmlOpcode, WalkState);
-
-
- WalkState->AmlOffset = (UINT32) ACPI_PTR_DIFF (WalkState->ParserState.Aml,
- WalkState->ParserState.AmlStart);
- WalkState->Opcode = AcpiPsPeekOpcode (&(WalkState->ParserState));
-
- /*
- * First cut to determine what we have found:
- * 1) A valid AML opcode
- * 2) A name string
- * 3) An unknown/invalid opcode
- */
- WalkState->OpInfo = AcpiPsGetOpcodeInfo (WalkState->Opcode);
-
- switch (WalkState->OpInfo->Class)
- {
- case AML_CLASS_ASCII:
- case AML_CLASS_PREFIX:
- /*
- * Starts with a valid prefix or ASCII char, this is a name
- * string. Convert the bare name string to a namepath.
- */
- WalkState->Opcode = AML_INT_NAMEPATH_OP;
- WalkState->ArgTypes = ARGP_NAMESTRING;
- break;
-
- case AML_CLASS_UNKNOWN:
-
- /* The opcode is unrecognized. Just skip unknown opcodes */
-
- ACPI_ERROR ((AE_INFO,
- "Found unknown opcode 0x%X at AML address %p offset 0x%X, ignoring",
- WalkState->Opcode, WalkState->ParserState.Aml, WalkState->AmlOffset));
-
- ACPI_DUMP_BUFFER (WalkState->ParserState.Aml, 128);
-
- /* Assume one-byte bad opcode */
-
- WalkState->ParserState.Aml++;
- return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
-
- default:
-
- /* Found opcode info, this is a normal opcode */
-
- WalkState->ParserState.Aml += AcpiPsGetOpcodeSize (WalkState->Opcode);
- WalkState->ArgTypes = WalkState->OpInfo->ParseArgs;
- break;
- }
-
- return_ACPI_STATUS (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsBuildNamedOp
- *
- * PARAMETERS: WalkState - Current state
- * AmlOpStart - Begin of named Op in AML
- * UnnamedOp - Early Op (not a named Op)
- * Op - Returned Op
- *
- * RETURN: Status
- *
- * DESCRIPTION: Parse a named Op
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiPsBuildNamedOp (
- ACPI_WALK_STATE *WalkState,
- UINT8 *AmlOpStart,
- ACPI_PARSE_OBJECT *UnnamedOp,
- ACPI_PARSE_OBJECT **Op)
-{
- ACPI_STATUS Status = AE_OK;
- ACPI_PARSE_OBJECT *Arg = NULL;
-
-
- ACPI_FUNCTION_TRACE_PTR (PsBuildNamedOp, WalkState);
-
-
- UnnamedOp->Common.Value.Arg = NULL;
- UnnamedOp->Common.ArgListLength = 0;
- UnnamedOp->Common.AmlOpcode = WalkState->Opcode;
-
- /*
- * Get and append arguments until we find the node that contains
- * the name (the type ARGP_NAME).
- */
- while (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) &&
- (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) != ARGP_NAME))
- {
- Status = AcpiPsGetNextArg (WalkState, &(WalkState->ParserState),
- GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), &Arg);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- AcpiPsAppendArg (UnnamedOp, Arg);
- INCREMENT_ARG_LIST (WalkState->ArgTypes);
- }
-
- /*
- * Make sure that we found a NAME and didn't run out of arguments
- */
- if (!GET_CURRENT_ARG_TYPE (WalkState->ArgTypes))
- {
- return_ACPI_STATUS (AE_AML_NO_OPERAND);
- }
-
- /* We know that this arg is a name, move to next arg */
-
- INCREMENT_ARG_LIST (WalkState->ArgTypes);
-
- /*
- * Find the object. This will either insert the object into
- * the namespace or simply look it up
- */
- WalkState->Op = NULL;
-
- Status = WalkState->DescendingCallback (WalkState, Op);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During name lookup/catalog"));
- return_ACPI_STATUS (Status);
- }
-
- if (!*Op)
- {
- return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
- }
-
- Status = AcpiPsNextParseState (WalkState, *Op, Status);
- if (ACPI_FAILURE (Status))
- {
- if (Status == AE_CTRL_PENDING)
- {
- return_ACPI_STATUS (AE_CTRL_PARSE_PENDING);
- }
- return_ACPI_STATUS (Status);
- }
-
- AcpiPsAppendArg (*Op, UnnamedOp->Common.Value.Arg);
- AcpiGbl_Depth++;
-
- if ((*Op)->Common.AmlOpcode == AML_REGION_OP ||
- (*Op)->Common.AmlOpcode == AML_DATA_REGION_OP)
- {
- /*
- * Defer final parsing of an OperationRegion body, because we don't
- * have enough info in the first pass to parse it correctly (i.e.,
- * there may be method calls within the TermArg elements of the body.)
- *
- * However, we must continue parsing because the opregion is not a
- * standalone package -- we don't know where the end is at this point.
- *
- * (Length is unknown until parse of the body complete)
- */
- (*Op)->Named.Data = AmlOpStart;
- (*Op)->Named.Length = 0;
- }
-
- return_ACPI_STATUS (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsCreateOp
- *
- * PARAMETERS: WalkState - Current state
- * AmlOpStart - Op start in AML
- * NewOp - Returned Op
- *
- * RETURN: Status
- *
- * DESCRIPTION: Get Op from AML
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiPsCreateOp (
- ACPI_WALK_STATE *WalkState,
- UINT8 *AmlOpStart,
- ACPI_PARSE_OBJECT **NewOp)
-{
- ACPI_STATUS Status = AE_OK;
- ACPI_PARSE_OBJECT *Op;
- ACPI_PARSE_OBJECT *NamedOp = NULL;
- ACPI_PARSE_OBJECT *ParentScope;
- UINT8 ArgumentCount;
- const ACPI_OPCODE_INFO *OpInfo;
-
-
- ACPI_FUNCTION_TRACE_PTR (PsCreateOp, WalkState);
-
-
- Status = AcpiPsGetAmlOpcode (WalkState);
- if (Status == AE_CTRL_PARSE_CONTINUE)
- {
- return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
- }
-
- /* Create Op structure and append to parent's argument list */
-
- WalkState->OpInfo = AcpiPsGetOpcodeInfo (WalkState->Opcode);
- Op = AcpiPsAllocOp (WalkState->Opcode);
- if (!Op)
- {
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
-
- if (WalkState->OpInfo->Flags & AML_NAMED)
- {
- Status = AcpiPsBuildNamedOp (WalkState, AmlOpStart, Op, &NamedOp);
- AcpiPsFreeOp (Op);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- *NewOp = NamedOp;
- return_ACPI_STATUS (AE_OK);
- }
-
- /* Not a named opcode, just allocate Op and append to parent */
-
- if (WalkState->OpInfo->Flags & AML_CREATE)
- {
- /*
- * Backup to beginning of CreateXXXfield declaration
- * BodyLength is unknown until we parse the body
- */
- Op->Named.Data = AmlOpStart;
- Op->Named.Length = 0;
- }
-
- if (WalkState->Opcode == AML_BANK_FIELD_OP)
- {
- /*
- * Backup to beginning of BankField declaration
- * BodyLength is unknown until we parse the body
- */
- Op->Named.Data = AmlOpStart;
- Op->Named.Length = 0;
- }
-
- ParentScope = AcpiPsGetParentScope (&(WalkState->ParserState));
- AcpiPsAppendArg (ParentScope, Op);
-
- if (ParentScope)
- {
- OpInfo = AcpiPsGetOpcodeInfo (ParentScope->Common.AmlOpcode);
- if (OpInfo->Flags & AML_HAS_TARGET)
- {
- ArgumentCount = AcpiPsGetArgumentCount (OpInfo->Type);
- if (ParentScope->Common.ArgListLength > ArgumentCount)
- {
- Op->Common.Flags |= ACPI_PARSEOP_TARGET;
- }
- }
- else if (ParentScope->Common.AmlOpcode == AML_INCREMENT_OP)
- {
- Op->Common.Flags |= ACPI_PARSEOP_TARGET;
- }
- }
-
- if (WalkState->DescendingCallback != NULL)
- {
- /*
- * Find the object. This will either insert the object into
- * the namespace or simply look it up
- */
- WalkState->Op = *NewOp = Op;
-
- Status = WalkState->DescendingCallback (WalkState, &Op);
- Status = AcpiPsNextParseState (WalkState, Op, Status);
- if (Status == AE_CTRL_PENDING)
- {
- Status = AE_CTRL_PARSE_PENDING;
- }
- }
-
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiPsGetArguments
*
* PARAMETERS: WalkState - Current state
@@ -459,7 +120,8 @@ AcpiPsGetArguments (
case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */
- Status = AcpiPsGetNextNamepath (WalkState, &(WalkState->ParserState), Op, 1);
+ Status = AcpiPsGetNextNamepath (WalkState,
+ &(WalkState->ParserState), Op, ACPI_POSSIBLE_METHOD_CALL);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -472,13 +134,13 @@ AcpiPsGetArguments (
/*
* Op is not a constant or string, append each argument to the Op
*/
- while (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) && !WalkState->ArgCount)
+ while (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) &&
+ !WalkState->ArgCount)
{
- WalkState->AmlOffset = (UINT32) ACPI_PTR_DIFF (WalkState->ParserState.Aml,
- WalkState->ParserState.AmlStart);
+ WalkState->Aml = WalkState->ParserState.Aml;
Status = AcpiPsGetNextArg (WalkState, &(WalkState->ParserState),
- GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), &Arg);
+ GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), &Arg);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -486,7 +148,6 @@ AcpiPsGetArguments (
if (Arg)
{
- Arg->Common.AmlOffset = WalkState->AmlOffset;
AcpiPsAppendArg (Op, Arg);
}
@@ -513,7 +174,6 @@ AcpiPsGetArguments (
case AML_IF_OP:
case AML_ELSE_OP:
case AML_WHILE_OP:
-
/*
* Currently supported module-level opcodes are:
* IF/ELSE/WHILE. These appear to be the most common,
@@ -551,8 +211,8 @@ AcpiPsGetArguments (
(!Arg))
{
ACPI_WARNING ((AE_INFO,
- "Detected an unsupported executable opcode "
- "at module-level: [0x%.4X] at table offset 0x%.4X",
+ "Unsupported module-level executable opcode "
+ "0x%.2X at table offset 0x%.4X",
Op->Common.AmlOpcode,
(UINT32) (ACPI_PTR_DIFF (AmlOpStart,
WalkState->ParserState.AmlStart) +
@@ -619,6 +279,7 @@ AcpiPsGetArguments (
default:
/* No action for all other opcodes */
+
break;
}
@@ -659,6 +320,9 @@ AcpiPsLinkModuleCode (
ACPI_NAMESPACE_NODE *ParentNode;
+ ACPI_FUNCTION_TRACE (PsLinkModuleCode);
+
+
/* Get the tail of the list */
Prev = Next = AcpiGbl_ModuleCodeList;
@@ -680,9 +344,12 @@ AcpiPsLinkModuleCode (
MethodObj = AcpiUtCreateInternalObject (ACPI_TYPE_METHOD);
if (!MethodObj)
{
- return;
+ return_VOID;
}
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
+ "Create/Link new code block: %p\n", MethodObj));
+
if (ParentOp->Common.Node)
{
ParentNode = ParentOp->Common.Node;
@@ -715,302 +382,15 @@ AcpiPsLinkModuleCode (
}
else
{
- Prev->Method.AmlLength += AmlLength;
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsCompleteOp
- *
- * PARAMETERS: WalkState - Current state
- * Op - Returned Op
- * Status - Parse status before complete Op
- *
- * RETURN: Status
- *
- * DESCRIPTION: Complete Op
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiPsCompleteOp (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT **Op,
- ACPI_STATUS Status)
-{
- ACPI_STATUS Status2;
-
-
- ACPI_FUNCTION_TRACE_PTR (PsCompleteOp, WalkState);
-
-
- /*
- * Finished one argument of the containing scope
- */
- WalkState->ParserState.Scope->ParseScope.ArgCount--;
-
- /* Close this Op (will result in parse subtree deletion) */
-
- Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
-
- *Op = NULL;
-
- switch (Status)
- {
- case AE_OK:
- break;
-
-
- case AE_CTRL_TRANSFER:
-
- /* We are about to transfer to a called method */
-
- WalkState->PrevOp = NULL;
- WalkState->PrevArgTypes = WalkState->ArgTypes;
- return_ACPI_STATUS (Status);
-
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
+ "Appending to existing code block: %p\n", Prev));
- case AE_CTRL_END:
-
- AcpiPsPopScope (&(WalkState->ParserState), Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
-
- if (*Op)
- {
- WalkState->Op = *Op;
- WalkState->OpInfo = AcpiPsGetOpcodeInfo ((*Op)->Common.AmlOpcode);
- WalkState->Opcode = (*Op)->Common.AmlOpcode;
-
- Status = WalkState->AscendingCallback (WalkState);
- Status = AcpiPsNextParseState (WalkState, *Op, Status);
-
- Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
- }
-
- Status = AE_OK;
- break;
-
-
- case AE_CTRL_BREAK:
- case AE_CTRL_CONTINUE:
-
- /* Pop off scopes until we find the While */
-
- while (!(*Op) || ((*Op)->Common.AmlOpcode != AML_WHILE_OP))
- {
- AcpiPsPopScope (&(WalkState->ParserState), Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
- }
-
- /* Close this iteration of the While loop */
-
- WalkState->Op = *Op;
- WalkState->OpInfo = AcpiPsGetOpcodeInfo ((*Op)->Common.AmlOpcode);
- WalkState->Opcode = (*Op)->Common.AmlOpcode;
-
- Status = WalkState->AscendingCallback (WalkState);
- Status = AcpiPsNextParseState (WalkState, *Op, Status);
-
- Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
-
- Status = AE_OK;
- break;
-
-
- case AE_CTRL_TERMINATE:
-
- /* Clean up */
- do
- {
- if (*Op)
- {
- Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
-
- AcpiUtDeleteGenericState (
- AcpiUtPopGenericState (&WalkState->ControlState));
- }
-
- AcpiPsPopScope (&(WalkState->ParserState), Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
-
- } while (*Op);
-
- return_ACPI_STATUS (AE_OK);
-
-
- default: /* All other non-AE_OK status */
-
- do
- {
- if (*Op)
- {
- Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
- }
-
- AcpiPsPopScope (&(WalkState->ParserState), Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
-
- } while (*Op);
-
-
-#if 0
- /*
- * TBD: Cleanup parse ops on error
- */
- if (*Op == NULL)
- {
- AcpiPsPopScope (ParserState, Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
- }
-#endif
- WalkState->PrevOp = NULL;
- WalkState->PrevArgTypes = WalkState->ArgTypes;
- return_ACPI_STATUS (Status);
- }
-
- /* This scope complete? */
-
- if (AcpiPsHasCompletedScope (&(WalkState->ParserState)))
- {
- AcpiPsPopScope (&(WalkState->ParserState), Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
- ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped scope, Op=%p\n", *Op));
- }
- else
- {
- *Op = NULL;
+ Prev->Method.AmlLength += AmlLength;
}
- return_ACPI_STATUS (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsCompleteFinalOp
- *
- * PARAMETERS: WalkState - Current state
- * Op - Current Op
- * Status - Current parse status before complete last
- * Op
- *
- * RETURN: Status
- *
- * DESCRIPTION: Complete last Op.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiPsCompleteFinalOp (
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op,
- ACPI_STATUS Status)
-{
- ACPI_STATUS Status2;
-
-
- ACPI_FUNCTION_TRACE_PTR (PsCompleteFinalOp, WalkState);
-
-
- /*
- * Complete the last Op (if not completed), and clear the scope stack.
- * It is easily possible to end an AML "package" with an unbounded number
- * of open scopes (such as when several ASL blocks are closed with
- * sequential closing braces). We want to terminate each one cleanly.
- */
- ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "AML package complete at Op %p\n", Op));
- do
- {
- if (Op)
- {
- if (WalkState->AscendingCallback != NULL)
- {
- WalkState->Op = Op;
- WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
- WalkState->Opcode = Op->Common.AmlOpcode;
-
- Status = WalkState->AscendingCallback (WalkState);
- Status = AcpiPsNextParseState (WalkState, Op, Status);
- if (Status == AE_CTRL_PENDING)
- {
- Status = AcpiPsCompleteOp (WalkState, &Op, AE_OK);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- if (Status == AE_CTRL_TERMINATE)
- {
- Status = AE_OK;
-
- /* Clean up */
- do
- {
- if (Op)
- {
- Status2 = AcpiPsCompleteThisOp (WalkState, Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
- }
-
- AcpiPsPopScope (&(WalkState->ParserState), &Op,
- &WalkState->ArgTypes, &WalkState->ArgCount);
-
- } while (Op);
-
- return_ACPI_STATUS (Status);
- }
-
- else if (ACPI_FAILURE (Status))
- {
- /* First error is most important */
-
- (void) AcpiPsCompleteThisOp (WalkState, Op);
- return_ACPI_STATUS (Status);
- }
- }
-
- Status2 = AcpiPsCompleteThisOp (WalkState, Op);
- if (ACPI_FAILURE (Status2))
- {
- return_ACPI_STATUS (Status2);
- }
- }
-
- AcpiPsPopScope (&(WalkState->ParserState), &Op, &WalkState->ArgTypes,
- &WalkState->ArgCount);
-
- } while (Op);
-
- return_ACPI_STATUS (Status);
+ return_VOID;
}
-
/*******************************************************************************
*
* FUNCTION: AcpiPsParseLoop
@@ -1120,6 +500,11 @@ AcpiPsParseLoop (
Status = AE_OK;
}
+ if (Status == AE_CTRL_TERMINATE)
+ {
+ return_ACPI_STATUS (Status);
+ }
+
Status = AcpiPsCompleteOp (WalkState, &Op, Status);
if (ACPI_FAILURE (Status))
{
@@ -1129,15 +514,7 @@ AcpiPsParseLoop (
continue;
}
- Op->Common.AmlOffset = WalkState->AmlOffset;
-
- if (WalkState->OpInfo)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
- "Opcode %4.4X [%s] Op %p Aml %p AmlOffset %5.5X\n",
- (UINT32) Op->Common.AmlOpcode, WalkState->OpInfo->Name,
- Op, ParserState->Aml, Op->Common.AmlOffset));
- }
+ AcpiExStartTraceOpcode (Op, WalkState);
}
@@ -1175,7 +552,7 @@ AcpiPsParseLoop (
* prepare for argument
*/
Status = AcpiPsPushScope (ParserState, Op,
- WalkState->ArgTypes, WalkState->ArgCount);
+ WalkState->ArgTypes, WalkState->ArgCount);
if (ACPI_FAILURE (Status))
{
Status = AcpiPsCompleteOp (WalkState, &Op, Status);
@@ -1198,11 +575,6 @@ AcpiPsParseLoop (
WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
if (WalkState->OpInfo->Flags & AML_NAMED)
{
- if (AcpiGbl_Depth)
- {
- AcpiGbl_Depth--;
- }
-
if (Op->Common.AmlOpcode == AML_REGION_OP ||
Op->Common.AmlOpcode == AML_DATA_REGION_OP)
{
@@ -1265,4 +637,3 @@ AcpiPsParseLoop (
Status = AcpiPsCompleteFinalOp (WalkState, Op, Status);
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/parser/psobject.c b/usr/src/uts/intel/io/acpica/parser/psobject.c
new file mode 100644
index 0000000000..7edc39e2b7
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/parser/psobject.c
@@ -0,0 +1,686 @@
+/******************************************************************************
+ *
+ * Module Name: psobject - Support for parse objects
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acparser.h"
+#include "amlcode.h"
+
+#define _COMPONENT ACPI_PARSER
+ ACPI_MODULE_NAME ("psobject")
+
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiPsGetAmlOpcode (
+ ACPI_WALK_STATE *WalkState);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsGetAmlOpcode
+ *
+ * PARAMETERS: WalkState - Current state
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Extract the next AML opcode from the input stream.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiPsGetAmlOpcode (
+ ACPI_WALK_STATE *WalkState)
+{
+ UINT32 AmlOffset;
+
+
+ ACPI_FUNCTION_TRACE_PTR (PsGetAmlOpcode, WalkState);
+
+
+ WalkState->Aml = WalkState->ParserState.Aml;
+ WalkState->Opcode = AcpiPsPeekOpcode (&(WalkState->ParserState));
+
+ /*
+ * First cut to determine what we have found:
+ * 1) A valid AML opcode
+ * 2) A name string
+ * 3) An unknown/invalid opcode
+ */
+ WalkState->OpInfo = AcpiPsGetOpcodeInfo (WalkState->Opcode);
+
+ switch (WalkState->OpInfo->Class)
+ {
+ case AML_CLASS_ASCII:
+ case AML_CLASS_PREFIX:
+ /*
+ * Starts with a valid prefix or ASCII char, this is a name
+ * string. Convert the bare name string to a namepath.
+ */
+ WalkState->Opcode = AML_INT_NAMEPATH_OP;
+ WalkState->ArgTypes = ARGP_NAMESTRING;
+ break;
+
+ case AML_CLASS_UNKNOWN:
+
+ /* The opcode is unrecognized. Complain and skip unknown opcodes */
+
+ if (WalkState->PassNumber == 2)
+ {
+ AmlOffset = (UINT32) ACPI_PTR_DIFF (WalkState->Aml,
+ WalkState->ParserState.AmlStart);
+
+ ACPI_ERROR ((AE_INFO,
+ "Unknown opcode 0x%.2X at table offset 0x%.4X, ignoring",
+ WalkState->Opcode,
+ (UINT32) (AmlOffset + sizeof (ACPI_TABLE_HEADER))));
+
+ ACPI_DUMP_BUFFER ((WalkState->ParserState.Aml - 16), 48);
+
+#ifdef ACPI_ASL_COMPILER
+ /*
+ * This is executed for the disassembler only. Output goes
+ * to the disassembled ASL output file.
+ */
+ AcpiOsPrintf (
+ "/*\nError: Unknown opcode 0x%.2X at table offset 0x%.4X, context:\n",
+ WalkState->Opcode,
+ (UINT32) (AmlOffset + sizeof (ACPI_TABLE_HEADER)));
+
+ /* Dump the context surrounding the invalid opcode */
+
+ AcpiUtDumpBuffer (((UINT8 *) WalkState->ParserState.Aml - 16),
+ 48, DB_BYTE_DISPLAY,
+ (AmlOffset + sizeof (ACPI_TABLE_HEADER) - 16));
+ AcpiOsPrintf (" */\n");
+#endif
+ }
+
+ /* Increment past one-byte or two-byte opcode */
+
+ WalkState->ParserState.Aml++;
+ if (WalkState->Opcode > 0xFF) /* Can only happen if first byte is 0x5B */
+ {
+ WalkState->ParserState.Aml++;
+ }
+
+ return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
+
+ default:
+
+ /* Found opcode info, this is a normal opcode */
+
+ WalkState->ParserState.Aml +=
+ AcpiPsGetOpcodeSize (WalkState->Opcode);
+ WalkState->ArgTypes = WalkState->OpInfo->ParseArgs;
+ break;
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsBuildNamedOp
+ *
+ * PARAMETERS: WalkState - Current state
+ * AmlOpStart - Begin of named Op in AML
+ * UnnamedOp - Early Op (not a named Op)
+ * Op - Returned Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Parse a named Op
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiPsBuildNamedOp (
+ ACPI_WALK_STATE *WalkState,
+ UINT8 *AmlOpStart,
+ ACPI_PARSE_OBJECT *UnnamedOp,
+ ACPI_PARSE_OBJECT **Op)
+{
+ ACPI_STATUS Status = AE_OK;
+ ACPI_PARSE_OBJECT *Arg = NULL;
+
+
+ ACPI_FUNCTION_TRACE_PTR (PsBuildNamedOp, WalkState);
+
+
+ UnnamedOp->Common.Value.Arg = NULL;
+ UnnamedOp->Common.ArgListLength = 0;
+ UnnamedOp->Common.AmlOpcode = WalkState->Opcode;
+
+ /*
+ * Get and append arguments until we find the node that contains
+ * the name (the type ARGP_NAME).
+ */
+ while (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) &&
+ (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) != ARGP_NAME))
+ {
+ Status = AcpiPsGetNextArg (WalkState, &(WalkState->ParserState),
+ GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), &Arg);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ AcpiPsAppendArg (UnnamedOp, Arg);
+ INCREMENT_ARG_LIST (WalkState->ArgTypes);
+ }
+
+ /*
+ * Make sure that we found a NAME and didn't run out of arguments
+ */
+ if (!GET_CURRENT_ARG_TYPE (WalkState->ArgTypes))
+ {
+ return_ACPI_STATUS (AE_AML_NO_OPERAND);
+ }
+
+ /* We know that this arg is a name, move to next arg */
+
+ INCREMENT_ARG_LIST (WalkState->ArgTypes);
+
+ /*
+ * Find the object. This will either insert the object into
+ * the namespace or simply look it up
+ */
+ WalkState->Op = NULL;
+
+ Status = WalkState->DescendingCallback (WalkState, Op);
+ if (ACPI_FAILURE (Status))
+ {
+ if (Status != AE_CTRL_TERMINATE)
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During name lookup/catalog"));
+ }
+ return_ACPI_STATUS (Status);
+ }
+
+ if (!*Op)
+ {
+ return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
+ }
+
+ Status = AcpiPsNextParseState (WalkState, *Op, Status);
+ if (ACPI_FAILURE (Status))
+ {
+ if (Status == AE_CTRL_PENDING)
+ {
+ Status = AE_CTRL_PARSE_PENDING;
+ }
+ return_ACPI_STATUS (Status);
+ }
+
+ AcpiPsAppendArg (*Op, UnnamedOp->Common.Value.Arg);
+
+ if ((*Op)->Common.AmlOpcode == AML_REGION_OP ||
+ (*Op)->Common.AmlOpcode == AML_DATA_REGION_OP)
+ {
+ /*
+ * Defer final parsing of an OperationRegion body, because we don't
+ * have enough info in the first pass to parse it correctly (i.e.,
+ * there may be method calls within the TermArg elements of the body.)
+ *
+ * However, we must continue parsing because the opregion is not a
+ * standalone package -- we don't know where the end is at this point.
+ *
+ * (Length is unknown until parse of the body complete)
+ */
+ (*Op)->Named.Data = AmlOpStart;
+ (*Op)->Named.Length = 0;
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsCreateOp
+ *
+ * PARAMETERS: WalkState - Current state
+ * AmlOpStart - Op start in AML
+ * NewOp - Returned Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get Op from AML
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiPsCreateOp (
+ ACPI_WALK_STATE *WalkState,
+ UINT8 *AmlOpStart,
+ ACPI_PARSE_OBJECT **NewOp)
+{
+ ACPI_STATUS Status = AE_OK;
+ ACPI_PARSE_OBJECT *Op;
+ ACPI_PARSE_OBJECT *NamedOp = NULL;
+ ACPI_PARSE_OBJECT *ParentScope;
+ UINT8 ArgumentCount;
+ const ACPI_OPCODE_INFO *OpInfo;
+
+
+ ACPI_FUNCTION_TRACE_PTR (PsCreateOp, WalkState);
+
+
+ Status = AcpiPsGetAmlOpcode (WalkState);
+ if (Status == AE_CTRL_PARSE_CONTINUE)
+ {
+ return_ACPI_STATUS (AE_CTRL_PARSE_CONTINUE);
+ }
+
+ /* Create Op structure and append to parent's argument list */
+
+ WalkState->OpInfo = AcpiPsGetOpcodeInfo (WalkState->Opcode);
+ Op = AcpiPsAllocOp (WalkState->Opcode, AmlOpStart);
+ if (!Op)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ if (WalkState->OpInfo->Flags & AML_NAMED)
+ {
+ Status = AcpiPsBuildNamedOp (WalkState, AmlOpStart, Op, &NamedOp);
+ AcpiPsFreeOp (Op);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ *NewOp = NamedOp;
+ return_ACPI_STATUS (AE_OK);
+ }
+
+ /* Not a named opcode, just allocate Op and append to parent */
+
+ if (WalkState->OpInfo->Flags & AML_CREATE)
+ {
+ /*
+ * Backup to beginning of CreateXXXfield declaration
+ * BodyLength is unknown until we parse the body
+ */
+ Op->Named.Data = AmlOpStart;
+ Op->Named.Length = 0;
+ }
+
+ if (WalkState->Opcode == AML_BANK_FIELD_OP)
+ {
+ /*
+ * Backup to beginning of BankField declaration
+ * BodyLength is unknown until we parse the body
+ */
+ Op->Named.Data = AmlOpStart;
+ Op->Named.Length = 0;
+ }
+
+ ParentScope = AcpiPsGetParentScope (&(WalkState->ParserState));
+ AcpiPsAppendArg (ParentScope, Op);
+
+ if (ParentScope)
+ {
+ OpInfo = AcpiPsGetOpcodeInfo (ParentScope->Common.AmlOpcode);
+ if (OpInfo->Flags & AML_HAS_TARGET)
+ {
+ ArgumentCount = AcpiPsGetArgumentCount (OpInfo->Type);
+ if (ParentScope->Common.ArgListLength > ArgumentCount)
+ {
+ Op->Common.Flags |= ACPI_PARSEOP_TARGET;
+ }
+ }
+ else if (ParentScope->Common.AmlOpcode == AML_INCREMENT_OP)
+ {
+ Op->Common.Flags |= ACPI_PARSEOP_TARGET;
+ }
+ }
+
+ if (WalkState->DescendingCallback != NULL)
+ {
+ /*
+ * Find the object. This will either insert the object into
+ * the namespace or simply look it up
+ */
+ WalkState->Op = *NewOp = Op;
+
+ Status = WalkState->DescendingCallback (WalkState, &Op);
+ Status = AcpiPsNextParseState (WalkState, Op, Status);
+ if (Status == AE_CTRL_PENDING)
+ {
+ Status = AE_CTRL_PARSE_PENDING;
+ }
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsCompleteOp
+ *
+ * PARAMETERS: WalkState - Current state
+ * Op - Returned Op
+ * Status - Parse status before complete Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Complete Op
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiPsCompleteOp (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT **Op,
+ ACPI_STATUS Status)
+{
+ ACPI_STATUS Status2;
+
+
+ ACPI_FUNCTION_TRACE_PTR (PsCompleteOp, WalkState);
+
+
+ /*
+ * Finished one argument of the containing scope
+ */
+ WalkState->ParserState.Scope->ParseScope.ArgCount--;
+
+ /* Close this Op (will result in parse subtree deletion) */
+
+ Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+
+ *Op = NULL;
+
+ switch (Status)
+ {
+ case AE_OK:
+
+ break;
+
+ case AE_CTRL_TRANSFER:
+
+ /* We are about to transfer to a called method */
+
+ WalkState->PrevOp = NULL;
+ WalkState->PrevArgTypes = WalkState->ArgTypes;
+ return_ACPI_STATUS (Status);
+
+ case AE_CTRL_END:
+
+ AcpiPsPopScope (&(WalkState->ParserState), Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+
+ if (*Op)
+ {
+ WalkState->Op = *Op;
+ WalkState->OpInfo = AcpiPsGetOpcodeInfo ((*Op)->Common.AmlOpcode);
+ WalkState->Opcode = (*Op)->Common.AmlOpcode;
+
+ Status = WalkState->AscendingCallback (WalkState);
+ Status = AcpiPsNextParseState (WalkState, *Op, Status);
+
+ Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+ }
+
+ Status = AE_OK;
+ break;
+
+ case AE_CTRL_BREAK:
+ case AE_CTRL_CONTINUE:
+
+ /* Pop off scopes until we find the While */
+
+ while (!(*Op) || ((*Op)->Common.AmlOpcode != AML_WHILE_OP))
+ {
+ AcpiPsPopScope (&(WalkState->ParserState), Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+ }
+
+ /* Close this iteration of the While loop */
+
+ WalkState->Op = *Op;
+ WalkState->OpInfo = AcpiPsGetOpcodeInfo ((*Op)->Common.AmlOpcode);
+ WalkState->Opcode = (*Op)->Common.AmlOpcode;
+
+ Status = WalkState->AscendingCallback (WalkState);
+ Status = AcpiPsNextParseState (WalkState, *Op, Status);
+
+ Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+
+ Status = AE_OK;
+ break;
+
+ case AE_CTRL_TERMINATE:
+
+ /* Clean up */
+ do
+ {
+ if (*Op)
+ {
+ Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+
+ AcpiUtDeleteGenericState (
+ AcpiUtPopGenericState (&WalkState->ControlState));
+ }
+
+ AcpiPsPopScope (&(WalkState->ParserState), Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+
+ } while (*Op);
+
+ return_ACPI_STATUS (AE_OK);
+
+ default: /* All other non-AE_OK status */
+
+ do
+ {
+ if (*Op)
+ {
+ Status2 = AcpiPsCompleteThisOp (WalkState, *Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+ }
+
+ AcpiPsPopScope (&(WalkState->ParserState), Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+
+ } while (*Op);
+
+
+#if 0
+ /*
+ * TBD: Cleanup parse ops on error
+ */
+ if (*Op == NULL)
+ {
+ AcpiPsPopScope (ParserState, Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+ }
+#endif
+ WalkState->PrevOp = NULL;
+ WalkState->PrevArgTypes = WalkState->ArgTypes;
+ return_ACPI_STATUS (Status);
+ }
+
+ /* This scope complete? */
+
+ if (AcpiPsHasCompletedScope (&(WalkState->ParserState)))
+ {
+ AcpiPsPopScope (&(WalkState->ParserState), Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped scope, Op=%p\n", *Op));
+ }
+ else
+ {
+ *Op = NULL;
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsCompleteFinalOp
+ *
+ * PARAMETERS: WalkState - Current state
+ * Op - Current Op
+ * Status - Current parse status before complete last
+ * Op
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Complete last Op.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiPsCompleteFinalOp (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_STATUS Status)
+{
+ ACPI_STATUS Status2;
+
+
+ ACPI_FUNCTION_TRACE_PTR (PsCompleteFinalOp, WalkState);
+
+
+ /*
+ * Complete the last Op (if not completed), and clear the scope stack.
+ * It is easily possible to end an AML "package" with an unbounded number
+ * of open scopes (such as when several ASL blocks are closed with
+ * sequential closing braces). We want to terminate each one cleanly.
+ */
+ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "AML package complete at Op %p\n", Op));
+ do
+ {
+ if (Op)
+ {
+ if (WalkState->AscendingCallback != NULL)
+ {
+ WalkState->Op = Op;
+ WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
+ WalkState->Opcode = Op->Common.AmlOpcode;
+
+ Status = WalkState->AscendingCallback (WalkState);
+ Status = AcpiPsNextParseState (WalkState, Op, Status);
+ if (Status == AE_CTRL_PENDING)
+ {
+ Status = AcpiPsCompleteOp (WalkState, &Op, AE_OK);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ if (Status == AE_CTRL_TERMINATE)
+ {
+ Status = AE_OK;
+
+ /* Clean up */
+ do
+ {
+ if (Op)
+ {
+ Status2 = AcpiPsCompleteThisOp (WalkState, Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+ }
+
+ AcpiPsPopScope (&(WalkState->ParserState), &Op,
+ &WalkState->ArgTypes, &WalkState->ArgCount);
+
+ } while (Op);
+
+ return_ACPI_STATUS (Status);
+ }
+
+ else if (ACPI_FAILURE (Status))
+ {
+ /* First error is most important */
+
+ (void) AcpiPsCompleteThisOp (WalkState, Op);
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ Status2 = AcpiPsCompleteThisOp (WalkState, Op);
+ if (ACPI_FAILURE (Status2))
+ {
+ return_ACPI_STATUS (Status2);
+ }
+ }
+
+ AcpiPsPopScope (&(WalkState->ParserState), &Op, &WalkState->ArgTypes,
+ &WalkState->ArgCount);
+
+ } while (Op);
+
+ return_ACPI_STATUS (Status);
+}
diff --git a/usr/src/uts/intel/io/acpica/parser/psopcode.c b/usr/src/uts/intel/io/acpica/parser/psopcode.c
index 0d82976d7f..ec44339b76 100644
--- a/usr/src/uts/intel/io/acpica/parser/psopcode.c
+++ b/usr/src/uts/intel/io/acpica/parser/psopcode.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,10 +41,8 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
-#include "acparser.h"
#include "acopcode.h"
#include "amlcode.h"
@@ -53,16 +51,13 @@
ACPI_MODULE_NAME ("psopcode")
-static const UINT8 AcpiGbl_ArgumentCount[] = {0,1,1,1,1,2,2,2,2,3,3,6};
-
-
/*******************************************************************************
*
* NAME: AcpiGbl_AmlOpInfo
*
* DESCRIPTION: Opcode table. Each entry contains <opcode, type, name, operands>
* The name is a simple ascii string, the operand specifier is an
- * ascii string with one letter per operand. The letter specifies
+ * ascii string with one letter per operand. The letter specifies
* the operand type.
*
******************************************************************************/
@@ -187,7 +182,7 @@ static const UINT8 AcpiGbl_ArgumentCount[] = {0,1,1,1,1,2,2,2,2,3,3,6};
/*
- * Master Opcode information table. A summary of everything we know about each
+ * Master Opcode information table. A summary of everything we know about each
* opcode, all in one place.
*/
const ACPI_OPCODE_INFO AcpiGbl_AmlOpInfo[AML_NUM_OPCODES] =
@@ -250,7 +245,7 @@ const ACPI_OPCODE_INFO AcpiGbl_AmlOpInfo[AML_NUM_OPCODES] =
/* 34 */ ACPI_OP ("CreateWordField", ARGP_CREATE_WORD_FIELD_OP, ARGI_CREATE_WORD_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE),
/* 35 */ ACPI_OP ("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP, ARGI_CREATE_BYTE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE),
/* 36 */ ACPI_OP ("CreateBitField", ARGP_CREATE_BIT_FIELD_OP, ARGI_CREATE_BIT_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE),
-/* 37 */ ACPI_OP ("ObjectType", ARGP_TYPE_OP, ARGI_TYPE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
+/* 37 */ ACPI_OP ("ObjectType", ARGP_OBJECT_TYPE_OP, ARGI_OBJECT_TYPE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
/* 38 */ ACPI_OP ("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT),
/* 39 */ ACPI_OP ("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT),
/* 3A */ ACPI_OP ("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
@@ -328,190 +323,21 @@ const ACPI_OPCODE_INFO AcpiGbl_AmlOpInfo[AML_NUM_OPCODES] =
/* 79 */ ACPI_OP ("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R, AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT),
/* 7A */ ACPI_OP ("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
/* 7B */ ACPI_OP ("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R),
-/* 7C */ ACPI_OP ("DataTableRegion", ARGP_DATA_REGION_OP, ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED | AML_DEFER),
+/* 7C */ ACPI_OP ("DataTableRegion", ARGP_DATA_REGION_OP, ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED | AML_DEFER),
/* 7D */ ACPI_OP ("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE),
/* ACPI 3.0 opcodes */
-/* 7E */ ACPI_OP ("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, AML_FLAGS_EXEC_0A_0T_1R)
-
-/*! [End] no source code translation !*/
-};
-
-/*
- * This table is directly indexed by the opcodes, and returns an
- * index into the table above
- */
-static const UINT8 AcpiGbl_ShortOpIndex[256] =
-{
-/* 0 1 2 3 4 5 6 7 */
-/* 8 9 A B C D E F */
-/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK,
-/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK,
-/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, _UNK, _UNK, _UNK,
-/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX,
-/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D,
-/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
-/* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
-/* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
-/* 0x58 */ _ASC, _ASC, _ASC, _UNK, _PFX, _UNK, _PFX, _ASC,
-/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
-/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK,
-/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
-/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
-/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, 0x70, 0x71, 0x2f, 0x30,
-/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x72,
-/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x73, 0x74,
-/* 0x98 */ 0x75, 0x76, _UNK, _UNK, 0x77, 0x78, 0x79, 0x7A,
-/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61,
-/* 0xA8 */ 0x62, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK,
-/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45,
-};
-
-/*
- * This table is indexed by the second opcode of the extended opcode
- * pair. It returns an index into the opcode table (AcpiGbl_AmlOpInfo)
- */
-static const UINT8 AcpiGbl_LongOpIndex[NUM_EXTENDED_OPCODE] =
-{
-/* 0 1 2 3 4 5 6 7 */
-/* 8 9 A B C D E F */
-/* 0x00 */ _UNK, 0x46, 0x47, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x08 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x10 */ _UNK, _UNK, 0x48, 0x49, _UNK, _UNK, _UNK, _UNK,
-/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x7B,
-/* 0x20 */ 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
-/* 0x28 */ 0x52, 0x53, 0x54, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x30 */ 0x55, 0x56, 0x57, 0x7e, _UNK, _UNK, _UNK, _UNK,
-/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x40 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x48 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x60 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x68 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x70 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x78 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
-/* 0x80 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
-/* 0x88 */ 0x7C,
-};
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsGetOpcodeInfo
- *
- * PARAMETERS: Opcode - The AML opcode
- *
- * RETURN: A pointer to the info about the opcode.
- *
- * DESCRIPTION: Find AML opcode description based on the opcode.
- * NOTE: This procedure must ALWAYS return a valid pointer!
- *
- ******************************************************************************/
-
-const ACPI_OPCODE_INFO *
-AcpiPsGetOpcodeInfo (
- UINT16 Opcode)
-{
- ACPI_FUNCTION_NAME (PsGetOpcodeInfo);
-
-
- /*
- * Detect normal 8-bit opcode or extended 16-bit opcode
- */
- if (!(Opcode & 0xFF00))
- {
- /* Simple (8-bit) opcode: 0-255, can't index beyond table */
-
- return (&AcpiGbl_AmlOpInfo [AcpiGbl_ShortOpIndex [(UINT8) Opcode]]);
- }
-
- if (((Opcode & 0xFF00) == AML_EXTENDED_OPCODE) &&
- (((UINT8) Opcode) <= MAX_EXTENDED_OPCODE))
- {
- /* Valid extended (16-bit) opcode */
-
- return (&AcpiGbl_AmlOpInfo [AcpiGbl_LongOpIndex [(UINT8) Opcode]]);
- }
+/* 7E */ ACPI_OP ("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, AML_FLAGS_EXEC_0A_0T_1R),
- /* Unknown AML opcode */
+/* ACPI 5.0 opcodes */
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "Unknown AML opcode [%4.4X]\n", Opcode));
+/* 7F */ ACPI_OP ("-ConnectField-", ARGP_CONNECTFIELD_OP, ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS),
+/* 80 */ ACPI_OP ("-ExtAccessField-", ARGP_CONNECTFIELD_OP, ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
- return (&AcpiGbl_AmlOpInfo [_UNK]);
-}
+/* ACPI 6.0 opcodes */
+/* 81 */ ACPI_OP ("External", ARGP_EXTERNAL_OP, ARGI_EXTERNAL_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE,/* ? */ AML_TYPE_EXEC_3A_0T_0R, AML_FLAGS_EXEC_3A_0T_0R)
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsGetOpcodeName
- *
- * PARAMETERS: Opcode - The AML opcode
- *
- * RETURN: A pointer to the name of the opcode (ASCII String)
- * Note: Never returns NULL.
- *
- * DESCRIPTION: Translate an opcode into a human-readable string
- *
- ******************************************************************************/
-
-char *
-AcpiPsGetOpcodeName (
- UINT16 Opcode)
-{
-#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT)
-
- const ACPI_OPCODE_INFO *Op;
-
-
- Op = AcpiPsGetOpcodeInfo (Opcode);
-
- /* Always guaranteed to return a valid pointer */
-
- return (Op->Name);
-
-#else
- return ("OpcodeName unavailable");
-
-#endif
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsGetArgumentCount
- *
- * PARAMETERS: OpType - Type associated with the AML opcode
- *
- * RETURN: Argument count
- *
- * DESCRIPTION: Obtain the number of expected arguments for an AML opcode
- *
- ******************************************************************************/
-
-UINT8
-AcpiPsGetArgumentCount (
- UINT32 OpType)
-{
-
- if (OpType <= AML_TYPE_EXEC_6A_0T_1R)
- {
- return (AcpiGbl_ArgumentCount[OpType]);
- }
-
- return (0);
-}
+/*! [End] no source code translation !*/
+};
diff --git a/usr/src/uts/intel/io/acpica/parser/psopinfo.c b/usr/src/uts/intel/io/acpica/parser/psopinfo.c
new file mode 100644
index 0000000000..324d87a93f
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/parser/psopinfo.c
@@ -0,0 +1,284 @@
+/******************************************************************************
+ *
+ * Module Name: psopinfo - AML opcode information functions and dispatch tables
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acparser.h"
+#include "acopcode.h"
+#include "amlcode.h"
+
+
+#define _COMPONENT ACPI_PARSER
+ ACPI_MODULE_NAME ("psopinfo")
+
+
+static const UINT8 AcpiGbl_ArgumentCount[] = {0,1,1,1,1,2,2,2,2,3,3,6};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsGetOpcodeInfo
+ *
+ * PARAMETERS: Opcode - The AML opcode
+ *
+ * RETURN: A pointer to the info about the opcode.
+ *
+ * DESCRIPTION: Find AML opcode description based on the opcode.
+ * NOTE: This procedure must ALWAYS return a valid pointer!
+ *
+ ******************************************************************************/
+
+const ACPI_OPCODE_INFO *
+AcpiPsGetOpcodeInfo (
+ UINT16 Opcode)
+{
+#ifdef ACPI_DEBUG_OUTPUT
+ const char *OpcodeName = "Unknown AML opcode";
+#endif
+
+ ACPI_FUNCTION_NAME (PsGetOpcodeInfo);
+
+
+ /*
+ * Detect normal 8-bit opcode or extended 16-bit opcode
+ */
+ if (!(Opcode & 0xFF00))
+ {
+ /* Simple (8-bit) opcode: 0-255, can't index beyond table */
+
+ return (&AcpiGbl_AmlOpInfo [AcpiGbl_ShortOpIndex [(UINT8) Opcode]]);
+ }
+
+ if (((Opcode & 0xFF00) == AML_EXTENDED_OPCODE) &&
+ (((UINT8) Opcode) <= MAX_EXTENDED_OPCODE))
+ {
+ /* Valid extended (16-bit) opcode */
+
+ return (&AcpiGbl_AmlOpInfo [AcpiGbl_LongOpIndex [(UINT8) Opcode]]);
+ }
+
+#if defined ACPI_ASL_COMPILER && defined ACPI_DEBUG_OUTPUT
+#include "asldefine.h"
+
+ switch (Opcode)
+ {
+ case AML_RAW_DATA_BYTE:
+ OpcodeName = "-Raw Data Byte-";
+ break;
+
+ case AML_RAW_DATA_WORD:
+ OpcodeName = "-Raw Data Word-";
+ break;
+
+ case AML_RAW_DATA_DWORD:
+ OpcodeName = "-Raw Data Dword-";
+ break;
+
+ case AML_RAW_DATA_QWORD:
+ OpcodeName = "-Raw Data Qword-";
+ break;
+
+ case AML_RAW_DATA_BUFFER:
+ OpcodeName = "-Raw Data Buffer-";
+ break;
+
+ case AML_RAW_DATA_CHAIN:
+ OpcodeName = "-Raw Data Buffer Chain-";
+ break;
+
+ case AML_PACKAGE_LENGTH:
+ OpcodeName = "-Package Length-";
+ break;
+
+ case AML_UNASSIGNED_OPCODE:
+ OpcodeName = "-Unassigned Opcode-";
+ break;
+
+ case AML_DEFAULT_ARG_OP:
+ OpcodeName = "-Default Arg-";
+ break;
+
+ default:
+ break;
+ }
+#endif
+
+ /* Unknown AML opcode */
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "%s [%4.4X]\n", OpcodeName, Opcode));
+
+ return (&AcpiGbl_AmlOpInfo [_UNK]);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsGetOpcodeName
+ *
+ * PARAMETERS: Opcode - The AML opcode
+ *
+ * RETURN: A pointer to the name of the opcode (ASCII String)
+ * Note: Never returns NULL.
+ *
+ * DESCRIPTION: Translate an opcode into a human-readable string
+ *
+ ******************************************************************************/
+
+const char *
+AcpiPsGetOpcodeName (
+ UINT16 Opcode)
+{
+#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT)
+
+ const ACPI_OPCODE_INFO *Op;
+
+
+ Op = AcpiPsGetOpcodeInfo (Opcode);
+
+ /* Always guaranteed to return a valid pointer */
+
+ return (Op->Name);
+
+#else
+ return ("OpcodeName unavailable");
+
+#endif
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiPsGetArgumentCount
+ *
+ * PARAMETERS: OpType - Type associated with the AML opcode
+ *
+ * RETURN: Argument count
+ *
+ * DESCRIPTION: Obtain the number of expected arguments for an AML opcode
+ *
+ ******************************************************************************/
+
+UINT8
+AcpiPsGetArgumentCount (
+ UINT32 OpType)
+{
+
+ if (OpType <= AML_TYPE_EXEC_6A_0T_1R)
+ {
+ return (AcpiGbl_ArgumentCount[OpType]);
+ }
+
+ return (0);
+}
+
+
+/*
+ * This table is directly indexed by the opcodes It returns
+ * an index into the opcode table (AcpiGbl_AmlOpInfo)
+ */
+const UINT8 AcpiGbl_ShortOpIndex[256] =
+{
+/* 0 1 2 3 4 5 6 7 */
+/* 8 9 A B C D E F */
+/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK,
+/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK,
+/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, 0x81, _UNK, _UNK,
+/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX,
+/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D,
+/* 0x38 */ 0x7F, 0x80, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC,
+/* 0x58 */ _ASC, _ASC, _ASC, _UNK, _PFX, _UNK, _PFX, _ASC,
+/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK,
+/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, 0x70, 0x71, 0x2f, 0x30,
+/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x72,
+/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x73, 0x74,
+/* 0x98 */ 0x75, 0x76, _UNK, _UNK, 0x77, 0x78, 0x79, 0x7A,
+/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61,
+/* 0xA8 */ 0x62, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK,
+/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45,
+};
+
+/*
+ * This table is indexed by the second opcode of the extended opcode
+ * pair. It returns an index into the opcode table (AcpiGbl_AmlOpInfo)
+ */
+const UINT8 AcpiGbl_LongOpIndex[NUM_EXTENDED_OPCODE] =
+{
+/* 0 1 2 3 4 5 6 7 */
+/* 8 9 A B C D E F */
+/* 0x00 */ _UNK, 0x46, 0x47, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x08 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x10 */ _UNK, _UNK, 0x48, 0x49, _UNK, _UNK, _UNK, _UNK,
+/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x7B,
+/* 0x20 */ 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
+/* 0x28 */ 0x52, 0x53, 0x54, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x30 */ 0x55, 0x56, 0x57, 0x7e, _UNK, _UNK, _UNK, _UNK,
+/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x40 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x48 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x60 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x68 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x70 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x78 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK,
+/* 0x80 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+/* 0x88 */ 0x7C,
+};
diff --git a/usr/src/uts/intel/io/acpica/parser/psparse.c b/usr/src/uts/intel/io/acpica/parser/psparse.c
index f817749b75..810d97e0e0 100644
--- a/usr/src/uts/intel/io/acpica/parser/psparse.c
+++ b/usr/src/uts/intel/io/acpica/parser/psparse.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,12 +41,11 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
/*
* Parse the AML and build an operation tree as most interpreters,
- * like Perl, do. Parsing is done by hand rather than with a YACC
+ * like Perl, do. Parsing is done by hand rather than with a YACC
* generated parser to tightly constrain stack and dynamic memory
- * usage. At the same time, parsing is kept flexible and the code
+ * usage. At the same time, parsing is kept flexible and the code
* fairly compact by parsing based on a list of AML opcode
* templates in AmlOpInfo[]
*/
@@ -162,6 +161,8 @@ AcpiPsCompleteThisOp (
return_ACPI_STATUS (AE_OK); /* OK for now */
}
+ AcpiExStopTraceOpcode (Op, WalkState);
+
/* Delete this op and the subtree below it if asked to */
if (((WalkState->ParseFlags & ACPI_PARSE_TREE_MASK) != ACPI_PARSE_DELETE_TREE) ||
@@ -191,15 +192,16 @@ AcpiPsCompleteThisOp (
switch (ParentInfo->Class)
{
case AML_CLASS_CONTROL:
+
break;
case AML_CLASS_CREATE:
-
/*
- * These opcodes contain TermArg operands. The current
+ * These opcodes contain TermArg operands. The current
* op must be replaced by a placeholder return op
*/
- ReplacementOp = AcpiPsAllocOp (AML_INT_RETURN_VALUE_OP);
+ ReplacementOp = AcpiPsAllocOp (
+ AML_INT_RETURN_VALUE_OP, Op->Common.Aml);
if (!ReplacementOp)
{
Status = AE_NO_MEMORY;
@@ -207,9 +209,8 @@ AcpiPsCompleteThisOp (
break;
case AML_CLASS_NAMED_OBJECT:
-
/*
- * These opcodes contain TermArg operands. The current
+ * These opcodes contain TermArg operands. The current
* op must be replaced by a placeholder return op
*/
if ((Op->Common.Parent->Common.AmlOpcode == AML_REGION_OP) ||
@@ -219,7 +220,8 @@ AcpiPsCompleteThisOp (
(Op->Common.Parent->Common.AmlOpcode == AML_BANK_FIELD_OP) ||
(Op->Common.Parent->Common.AmlOpcode == AML_VAR_PACKAGE_OP))
{
- ReplacementOp = AcpiPsAllocOp (AML_INT_RETURN_VALUE_OP);
+ ReplacementOp = AcpiPsAllocOp (
+ AML_INT_RETURN_VALUE_OP, Op->Common.Aml);
if (!ReplacementOp)
{
Status = AE_NO_MEMORY;
@@ -232,7 +234,8 @@ AcpiPsCompleteThisOp (
(Op->Common.AmlOpcode == AML_PACKAGE_OP) ||
(Op->Common.AmlOpcode == AML_VAR_PACKAGE_OP))
{
- ReplacementOp = AcpiPsAllocOp (Op->Common.AmlOpcode);
+ ReplacementOp = AcpiPsAllocOp (Op->Common.AmlOpcode,
+ Op->Common.Aml);
if (!ReplacementOp)
{
Status = AE_NO_MEMORY;
@@ -248,7 +251,8 @@ AcpiPsCompleteThisOp (
default:
- ReplacementOp = AcpiPsAllocOp (AML_INT_RETURN_VALUE_OP);
+ ReplacementOp = AcpiPsAllocOp (
+ AML_INT_RETURN_VALUE_OP, Op->Common.Aml);
if (!ReplacementOp)
{
Status = AE_NO_MEMORY;
@@ -263,11 +267,11 @@ AcpiPsCompleteThisOp (
if (ReplacementOp)
{
- ReplacementOp->Common.Parent = Op->Common.Parent;
- ReplacementOp->Common.Value.Arg = NULL;
- ReplacementOp->Common.Node = Op->Common.Node;
+ ReplacementOp->Common.Parent = Op->Common.Parent;
+ ReplacementOp->Common.Value.Arg = NULL;
+ ReplacementOp->Common.Node = Op->Common.Node;
Op->Common.Parent->Common.Value.Arg = ReplacementOp;
- ReplacementOp->Common.Next = Op->Common.Next;
+ ReplacementOp->Common.Next = Op->Common.Next;
}
else
{
@@ -286,11 +290,11 @@ AcpiPsCompleteThisOp (
{
if (ReplacementOp)
{
- ReplacementOp->Common.Parent = Op->Common.Parent;
+ ReplacementOp->Common.Parent = Op->Common.Parent;
ReplacementOp->Common.Value.Arg = NULL;
- ReplacementOp->Common.Node = Op->Common.Node;
- Prev->Common.Next = ReplacementOp;
- ReplacementOp->Common.Next = Op->Common.Next;
+ ReplacementOp->Common.Node = Op->Common.Node;
+ Prev->Common.Next = ReplacementOp;
+ ReplacementOp->Common.Next = Op->Common.Next;
Next = NULL;
}
else
@@ -352,7 +356,6 @@ AcpiPsNextParseState (
Status = AE_CTRL_TERMINATE;
break;
-
case AE_CTRL_BREAK:
ParserState->Aml = WalkState->AmlLastWhile;
@@ -360,14 +363,12 @@ AcpiPsNextParseState (
Status = AE_CTRL_BREAK;
break;
-
case AE_CTRL_CONTINUE:
ParserState->Aml = WalkState->AmlLastWhile;
Status = AE_CTRL_CONTINUE;
break;
-
case AE_CTRL_PENDING:
ParserState->Aml = WalkState->AmlLastWhile;
@@ -390,11 +391,10 @@ AcpiPsNextParseState (
Status = AE_CTRL_PENDING;
break;
-
case AE_CTRL_FALSE:
/*
* Either an IF/WHILE Predicate was false or we encountered a BREAK
- * opcode. In both cases, we do not execute the rest of the
+ * opcode. In both cases, we do not execute the rest of the
* package; We simply close out the parent (finishing the walk of
* this branch of the tree) and continue execution at the parent
* level.
@@ -407,7 +407,6 @@ AcpiPsNextParseState (
Status = AE_CTRL_END;
break;
-
case AE_CTRL_TRANSFER:
/* A method call (invocation) -- transfer control */
@@ -422,7 +421,6 @@ AcpiPsNextParseState (
WalkState->ReturnUsed = AcpiDsIsResultUsed (Op, WalkState);
break;
-
default:
Status = CallbackStatus;
@@ -496,7 +494,8 @@ AcpiPsParseAml (
*/
if (WalkState->MethodDesc)
{
- WalkState->Thread->CurrentSyncLevel = WalkState->MethodDesc->Method.SyncLevel;
+ WalkState->Thread->CurrentSyncLevel =
+ WalkState->MethodDesc->Method.SyncLevel;
}
AcpiDsPushWalkState (WalkState, Thread);
@@ -508,7 +507,7 @@ AcpiPsParseAml (
AcpiGbl_CurrentWalkList = Thread;
/*
- * Execute the walk loop as long as there is a valid Walk State. This
+ * Execute the walk loop as long as there is a valid Walk State. This
* handles nested control method invocations without recursion.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "State=%p\n", WalkState));
@@ -542,8 +541,8 @@ AcpiPsParseAml (
}
/*
- * If the transfer to the new method method call worked, a new walk
- * state was created -- get it
+ * If the transfer to the new method method call worked
+ *, a new walk state was created -- get it
*/
WalkState = AcpiDsGetCurrentWalkState (Thread);
continue;
@@ -562,7 +561,8 @@ AcpiPsParseAml (
/* Check for possible multi-thread reentrancy problem */
if ((Status == AE_ALREADY_EXISTS) &&
- (!(WalkState->MethodDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED)))
+ (!(WalkState->MethodDesc->Method.InfoFlags &
+ ACPI_METHOD_SERIALIZED)))
{
/*
* Method is not serialized and tried to create an object
@@ -588,7 +588,8 @@ AcpiPsParseAml (
* encountered an error during the method parse phase, there's lots of
* cleanup to do
*/
- if (((WalkState->ParseFlags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) ||
+ if (((WalkState->ParseFlags & ACPI_PARSE_MODE_MASK) ==
+ ACPI_PARSE_EXECUTE) ||
(ACPI_FAILURE (Status)))
{
AcpiDsTerminateControlMethod (WalkState->MethodDesc, WalkState);
@@ -635,7 +636,7 @@ AcpiPsParseAml (
/* Restart the calling control method */
Status = AcpiDsRestartControlMethod (WalkState,
- PreviousWalkState->ImplicitReturnObj);
+ PreviousWalkState->ImplicitReturnObj);
}
else
{
@@ -646,7 +647,7 @@ AcpiPsParseAml (
AcpiDsClearImplicitReturn (PreviousWalkState);
Status = AcpiDsRestartControlMethod (WalkState,
- PreviousWalkState->ReturnDesc);
+ PreviousWalkState->ReturnDesc);
}
if (ACPI_SUCCESS (Status))
{
@@ -707,5 +708,3 @@ AcpiPsParseAml (
AcpiGbl_CurrentWalkList = PrevWalkList;
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/parser/psscope.c b/usr/src/uts/intel/io/acpica/parser/psscope.c
index 827531d460..f5b89d0f5d 100644
--- a/usr/src/uts/intel/io/acpica/parser/psscope.c
+++ b/usr/src/uts/intel/io/acpica/parser/psscope.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -237,9 +236,9 @@ AcpiPsPopScope (
/* Return to parsing previous op */
- *Op = Scope->ParseScope.Op;
- *ArgList = Scope->ParseScope.ArgList;
- *ArgCount = Scope->ParseScope.ArgCount;
+ *Op = Scope->ParseScope.Op;
+ *ArgList = Scope->ParseScope.ArgList;
+ *ArgCount = Scope->ParseScope.ArgCount;
ParserState->PkgEnd = Scope->ParseScope.PkgEnd;
/* All done with this scope state structure */
@@ -250,8 +249,8 @@ AcpiPsPopScope (
{
/* Empty parse stack, prepare to fetch next opcode */
- *Op = NULL;
- *ArgList = 0;
+ *Op = NULL;
+ *ArgList = 0;
*ArgCount = 0;
}
@@ -299,4 +298,3 @@ AcpiPsCleanupScope (
return_VOID;
}
-
diff --git a/usr/src/uts/intel/io/acpica/parser/pstree.c b/usr/src/uts/intel/io/acpica/parser/pstree.c
index d62bc7b9db..729856982b 100644
--- a/usr/src/uts/intel/io/acpica/parser/pstree.c
+++ b/usr/src/uts/intel/io/acpica/parser/pstree.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __PSTREE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -85,7 +82,12 @@ AcpiPsGetArg (
ACPI_FUNCTION_ENTRY ();
-
+/*
+ if (Op->Common.AmlOpcode == AML_INT_CONNECTION_OP)
+ {
+ return (Op->Common.Value.Arg);
+ }
+*/
/* Get the info structure for this opcode */
OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
@@ -317,7 +319,6 @@ AcpiPsGetChild (
Child = AcpiPsGetArg (Op, 0);
break;
-
case AML_BUFFER_OP:
case AML_PACKAGE_OP:
case AML_METHOD_OP:
@@ -328,28 +329,25 @@ AcpiPsGetChild (
Child = AcpiPsGetArg (Op, 1);
break;
-
case AML_POWER_RES_OP:
case AML_INDEX_FIELD_OP:
Child = AcpiPsGetArg (Op, 2);
break;
-
case AML_PROCESSOR_OP:
case AML_BANK_FIELD_OP:
Child = AcpiPsGetArg (Op, 3);
break;
-
default:
+
/* All others have no children */
+
break;
}
return (Child);
}
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/parser/psutils.c b/usr/src/uts/intel/io/acpica/parser/psutils.c
index 426d2ccdd7..9b1019c711 100644
--- a/usr/src/uts/intel/io/acpica/parser/psutils.c
+++ b/usr/src/uts/intel/io/acpica/parser/psutils.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -65,12 +64,12 @@
ACPI_PARSE_OBJECT *
AcpiPsCreateScopeOp (
- void)
+ UINT8 *Aml)
{
ACPI_PARSE_OBJECT *ScopeOp;
- ScopeOp = AcpiPsAllocOp (AML_SCOPE_OP);
+ ScopeOp = AcpiPsAllocOp (AML_SCOPE_OP, Aml);
if (!ScopeOp)
{
return (NULL);
@@ -105,9 +104,9 @@ AcpiPsInitOp (
Op->Common.DescriptorType = ACPI_DESC_TYPE_PARSER;
Op->Common.AmlOpcode = Opcode;
- ACPI_DISASM_ONLY_MEMBERS (ACPI_STRNCPY (Op->Common.AmlOpName,
- (AcpiPsGetOpcodeInfo (Opcode))->Name,
- sizeof (Op->Common.AmlOpName)));
+ ACPI_DISASM_ONLY_MEMBERS (strncpy (Op->Common.AmlOpName,
+ (AcpiPsGetOpcodeInfo (Opcode))->Name,
+ sizeof (Op->Common.AmlOpName)));
}
@@ -116,18 +115,20 @@ AcpiPsInitOp (
* FUNCTION: AcpiPsAllocOp
*
* PARAMETERS: Opcode - Opcode that will be stored in the new Op
+ * Aml - Address of the opcode
*
* RETURN: Pointer to the new Op, null on failure
*
* DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on
- * opcode. A cache of opcodes is available for the pure
+ * opcode. A cache of opcodes is available for the pure
* GENERIC_OP, since this is by far the most commonly used.
*
******************************************************************************/
ACPI_PARSE_OBJECT*
AcpiPsAllocOp (
- UINT16 Opcode)
+ UINT16 Opcode,
+ UINT8 *Aml)
{
ACPI_PARSE_OBJECT *Op;
const ACPI_OPCODE_INFO *OpInfo;
@@ -147,7 +148,7 @@ AcpiPsAllocOp (
}
else if (OpInfo->Flags & AML_NAMED)
{
- Flags = ACPI_PARSEOP_NAMED;
+ Flags = ACPI_PARSEOP_NAMED_OBJECT;
}
else if (Opcode == AML_INT_BYTELIST_OP)
{
@@ -174,6 +175,7 @@ AcpiPsAllocOp (
if (Op)
{
AcpiPsInitOp (Op, Opcode);
+ Op->Common.Aml = Aml;
Op->Common.Flags = Flags;
}
@@ -189,7 +191,7 @@ AcpiPsAllocOp (
*
* RETURN: None.
*
- * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list
+ * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list
* or actually free it.
*
******************************************************************************/
@@ -203,7 +205,8 @@ AcpiPsFreeOp (
if (Op->Common.AmlOpcode == AML_INT_RETURN_VALUE_OP)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Free retval op: %p\n", Op));
+ ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
+ "Free retval op: %p\n", Op));
}
if (Op->Common.Flags & ACPI_PARSEOP_GENERIC)
@@ -238,17 +241,6 @@ AcpiPsIsLeadingChar (
/*
- * Is "c" a namestring prefix character?
- */
-BOOLEAN
-AcpiPsIsPrefixChar (
- UINT32 c)
-{
- return ((BOOLEAN) (c == '\\' || c == '^'));
-}
-
-
-/*
* Get op's name (4-byte name segment) or 0 if unnamed
*/
UINT32
@@ -287,4 +279,3 @@ AcpiPsSetName (
Op->Named.Name = name;
}
-
diff --git a/usr/src/uts/intel/io/acpica/parser/pswalk.c b/usr/src/uts/intel/io/acpica/parser/pswalk.c
index dbb4a17fe9..fb6071adef 100644
--- a/usr/src/uts/intel/io/acpica/parser/pswalk.c
+++ b/usr/src/uts/intel/io/acpica/parser/pswalk.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
@@ -107,6 +106,7 @@ AcpiPsDeleteParseTree (
{
return_VOID;
}
+
if (Next)
{
Op = Next;
diff --git a/usr/src/uts/intel/io/acpica/parser/psxface.c b/usr/src/uts/intel/io/acpica/parser/psxface.c
index e7a5d9fe9a..a6fe31648e 100644
--- a/usr/src/uts/intel/io/acpica/parser/psxface.c
+++ b/usr/src/uts/intel/io/acpica/parser/psxface.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,14 +41,13 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __PSXFACE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acparser.h"
#include "acdispat.h"
#include "acinterp.h"
#include "actables.h"
+#include "acnamesp.h"
#define _COMPONENT ACPI_PARSER
@@ -57,14 +56,6 @@
/* Local Prototypes */
static void
-AcpiPsStartTrace (
- ACPI_EVALUATE_INFO *Info);
-
-static void
-AcpiPsStopTrace (
- ACPI_EVALUATE_INFO *Info);
-
-static void
AcpiPsUpdateParameterList (
ACPI_EVALUATE_INFO *Info,
UINT16 Action);
@@ -88,7 +79,7 @@ AcpiPsUpdateParameterList (
ACPI_STATUS
AcpiDebugTrace (
- char *Name,
+ const char *Name,
UINT32 DebugLevel,
UINT32 DebugLayer,
UINT32 Flags)
@@ -102,128 +93,14 @@ AcpiDebugTrace (
return (Status);
}
- /* TBDs: Validate name, allow full path or just nameseg */
-
- AcpiGbl_TraceMethodName = *ACPI_CAST_PTR (UINT32, Name);
+ AcpiGbl_TraceMethodName = Name;
AcpiGbl_TraceFlags = Flags;
+ AcpiGbl_TraceDbgLevel = DebugLevel;
+ AcpiGbl_TraceDbgLayer = DebugLayer;
+ Status = AE_OK;
- if (DebugLevel)
- {
- AcpiGbl_TraceDbgLevel = DebugLevel;
- }
- if (DebugLayer)
- {
- AcpiGbl_TraceDbgLayer = DebugLayer;
- }
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsStartTrace
- *
- * PARAMETERS: Info - Method info struct
- *
- * RETURN: None
- *
- * DESCRIPTION: Start control method execution trace
- *
- ******************************************************************************/
-
-static void
-AcpiPsStartTrace (
- ACPI_EVALUATE_INFO *Info)
-{
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE (Status))
- {
- return;
- }
-
- if ((!AcpiGbl_TraceMethodName) ||
- (AcpiGbl_TraceMethodName != Info->ResolvedNode->Name.Integer))
- {
- goto Exit;
- }
-
- AcpiGbl_OriginalDbgLevel = AcpiDbgLevel;
- AcpiGbl_OriginalDbgLayer = AcpiDbgLayer;
-
- AcpiDbgLevel = 0x00FFFFFF;
- AcpiDbgLayer = ACPI_UINT32_MAX;
-
- if (AcpiGbl_TraceDbgLevel)
- {
- AcpiDbgLevel = AcpiGbl_TraceDbgLevel;
- }
- if (AcpiGbl_TraceDbgLayer)
- {
- AcpiDbgLayer = AcpiGbl_TraceDbgLayer;
- }
-
-
-Exit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiPsStopTrace
- *
- * PARAMETERS: Info - Method info struct
- *
- * RETURN: None
- *
- * DESCRIPTION: Stop control method execution trace
- *
- ******************************************************************************/
-
-static void
-AcpiPsStopTrace (
- ACPI_EVALUATE_INFO *Info)
-{
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE (Status))
- {
- return;
- }
-
- if ((!AcpiGbl_TraceMethodName) ||
- (AcpiGbl_TraceMethodName != Info->ResolvedNode->Name.Integer))
- {
- goto Exit;
- }
-
- /* Disable further tracing if type is one-shot */
-
- if (AcpiGbl_TraceFlags & 1)
- {
- AcpiGbl_TraceMethodName = 0;
- AcpiGbl_TraceDbgLevel = 0;
- AcpiGbl_TraceDbgLayer = 0;
- }
-
- AcpiDbgLevel = AcpiGbl_OriginalDbgLevel;
- AcpiDbgLayer = AcpiGbl_OriginalDbgLayer;
-
-Exit:
(void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ return (Status);
}
@@ -268,14 +145,14 @@ AcpiPsExecuteMethod (
/* Validate the Info and method Node */
- if (!Info || !Info->ResolvedNode)
+ if (!Info || !Info->Node)
{
return_ACPI_STATUS (AE_NULL_ENTRY);
}
/* Init for new method, wait on concurrency semaphore */
- Status = AcpiDsBeginMethodExecution (Info->ResolvedNode, Info->ObjDesc, NULL);
+ Status = AcpiDsBeginMethodExecution (Info->Node, Info->ObjDesc, NULL);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -286,20 +163,16 @@ AcpiPsExecuteMethod (
*/
AcpiPsUpdateParameterList (Info, REF_INCREMENT);
- /* Begin tracing if requested */
-
- AcpiPsStartTrace (Info);
-
/*
* Execute the method. Performs parse simultaneously
*/
ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
"**** Begin Method Parse/Execute [%4.4s] **** Node=%p Obj=%p\n",
- Info->ResolvedNode->Name.Ascii, Info->ResolvedNode, Info->ObjDesc));
+ Info->Node->Name.Ascii, Info->Node, Info->ObjDesc));
/* Create and init a Root Node */
- Op = AcpiPsCreateScopeOp ();
+ Op = AcpiPsCreateScopeOp (Info->ObjDesc->Method.AmlStart);
if (!Op)
{
Status = AE_NO_MEMORY;
@@ -310,16 +183,16 @@ AcpiPsExecuteMethod (
Info->PassNumber = ACPI_IMODE_EXECUTE;
WalkState = AcpiDsCreateWalkState (
- Info->ObjDesc->Method.OwnerId, NULL, NULL, NULL);
+ Info->ObjDesc->Method.OwnerId, NULL, NULL, NULL);
if (!WalkState)
{
Status = AE_NO_MEMORY;
goto Cleanup;
}
- Status = AcpiDsInitAmlWalk (WalkState, Op, Info->ResolvedNode,
- Info->ObjDesc->Method.AmlStart,
- Info->ObjDesc->Method.AmlLength, Info, Info->PassNumber);
+ Status = AcpiDsInitAmlWalk (WalkState, Op, Info->Node,
+ Info->ObjDesc->Method.AmlStart,
+ Info->ObjDesc->Method.AmlLength, Info, Info->PassNumber);
if (ACPI_FAILURE (Status))
{
AcpiDsDeleteWalkState (WalkState);
@@ -348,8 +221,8 @@ AcpiPsExecuteMethod (
}
/*
- * Start method evaluation with an implicit return of zero. This is done
- * for Windows compatibility.
+ * Start method evaluation with an implicit return of zero.
+ * This is done for Windows compatibility.
*/
if (AcpiGbl_EnableInterpreterSlack)
{
@@ -372,10 +245,6 @@ AcpiPsExecuteMethod (
Cleanup:
AcpiPsDeleteParseTree (Op);
- /* End optional tracing */
-
- AcpiPsStopTrace (Info);
-
/* Take away the extra reference that we gave the parameters above */
AcpiPsUpdateParameterList (Info, REF_DECREMENT);
@@ -434,9 +303,8 @@ AcpiPsUpdateParameterList (
{
/* Ignore errors, just do them all */
- (void) AcpiUtUpdateObjectReference (Info->Parameters[i], Action);
+ (void) AcpiUtUpdateObjectReference (
+ Info->Parameters[i], Action);
}
}
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsaddr.c b/usr/src/uts/intel/io/acpica/resources/rsaddr.c
index c11e8ca128..4a3823c82c 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsaddr.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsaddr.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSADDR_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -79,7 +77,7 @@ ACPI_RSCONVERT_INFO AcpiRsConvertAddress16[5] =
* Address Translation Offset
* Address Length
*/
- {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.Address16.Granularity),
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.Address16.Address.Granularity),
AML_OFFSET (Address16.Granularity),
5},
@@ -119,7 +117,7 @@ ACPI_RSCONVERT_INFO AcpiRsConvertAddress32[5] =
* Address Translation Offset
* Address Length
*/
- {ACPI_RSC_MOVE32, ACPI_RS_OFFSET (Data.Address32.Granularity),
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET (Data.Address32.Address.Granularity),
AML_OFFSET (Address32.Granularity),
5},
@@ -159,7 +157,7 @@ ACPI_RSCONVERT_INFO AcpiRsConvertAddress64[5] =
* Address Translation Offset
* Address Length
*/
- {ACPI_RSC_MOVE64, ACPI_RS_OFFSET (Data.Address64.Granularity),
+ {ACPI_RSC_MOVE64, ACPI_RS_OFFSET (Data.Address64.Address.Granularity),
AML_OFFSET (Address64.Granularity),
5},
@@ -205,7 +203,7 @@ ACPI_RSCONVERT_INFO AcpiRsConvertExtAddress64[5] =
* Address Length
* Type-Specific Attribute
*/
- {ACPI_RSC_MOVE64, ACPI_RS_OFFSET (Data.ExtAddress64.Granularity),
+ {ACPI_RSC_MOVE64, ACPI_RS_OFFSET (Data.ExtAddress64.Address.Granularity),
AML_OFFSET (ExtAddress64.Granularity),
6}
};
@@ -330,30 +328,35 @@ AcpiRsGetAddressCommon (
/* Validate the Resource Type */
- if ((Aml->Address.ResourceType > 2) && (Aml->Address.ResourceType < 0xC0))
+ if ((Aml->Address.ResourceType > 2) &&
+ (Aml->Address.ResourceType < 0xC0))
{
return (FALSE);
}
/* Get the Resource Type and General Flags */
- (void) AcpiRsConvertAmlToResource (Resource, Aml, AcpiRsConvertGeneralFlags);
+ (void) AcpiRsConvertAmlToResource (
+ Resource, Aml, AcpiRsConvertGeneralFlags);
/* Get the Type-Specific Flags (Memory and I/O descriptors only) */
if (Resource->Data.Address.ResourceType == ACPI_MEMORY_RANGE)
{
- (void) AcpiRsConvertAmlToResource (Resource, Aml, AcpiRsConvertMemFlags);
+ (void) AcpiRsConvertAmlToResource (
+ Resource, Aml, AcpiRsConvertMemFlags);
}
else if (Resource->Data.Address.ResourceType == ACPI_IO_RANGE)
{
- (void) AcpiRsConvertAmlToResource (Resource, Aml, AcpiRsConvertIoFlags);
+ (void) AcpiRsConvertAmlToResource (
+ Resource, Aml, AcpiRsConvertIoFlags);
}
else
{
/* Generic resource type, just grab the TypeSpecific byte */
- Resource->Data.Address.Info.TypeSpecific = Aml->Address.SpecificFlags;
+ Resource->Data.Address.Info.TypeSpecific =
+ Aml->Address.SpecificFlags;
}
return (TRUE);
@@ -384,24 +387,26 @@ AcpiRsSetAddressCommon (
/* Set the Resource Type and General Flags */
- (void) AcpiRsConvertResourceToAml (Resource, Aml, AcpiRsConvertGeneralFlags);
+ (void) AcpiRsConvertResourceToAml (
+ Resource, Aml, AcpiRsConvertGeneralFlags);
/* Set the Type-Specific Flags (Memory and I/O descriptors only) */
if (Resource->Data.Address.ResourceType == ACPI_MEMORY_RANGE)
{
- (void) AcpiRsConvertResourceToAml (Resource, Aml, AcpiRsConvertMemFlags);
+ (void) AcpiRsConvertResourceToAml (
+ Resource, Aml, AcpiRsConvertMemFlags);
}
else if (Resource->Data.Address.ResourceType == ACPI_IO_RANGE)
{
- (void) AcpiRsConvertResourceToAml (Resource, Aml, AcpiRsConvertIoFlags);
+ (void) AcpiRsConvertResourceToAml (
+ Resource, Aml, AcpiRsConvertIoFlags);
}
else
{
/* Generic resource type, just copy the TypeSpecific byte */
- Aml->Address.SpecificFlags = Resource->Data.Address.Info.TypeSpecific;
+ Aml->Address.SpecificFlags =
+ Resource->Data.Address.Info.TypeSpecific;
}
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rscalc.c b/usr/src/uts/intel/io/acpica/resources/rscalc.c
index 15cf685857..92a710c1eb 100644
--- a/usr/src/uts/intel/io/acpica/resources/rscalc.c
+++ b/usr/src/uts/intel/io/acpica/resources/rscalc.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSCALC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -166,16 +164,17 @@ AcpiRsStreamOptionLength (
/*
- * The ResourceSourceIndex and ResourceSource are optional elements of some
- * Large-type resource descriptors.
+ * The ResourceSourceIndex and ResourceSource are optional elements of
+ * some Large-type resource descriptors.
*/
/*
- * If the length of the actual resource descriptor is greater than the ACPI
- * spec-defined minimum length, it means that a ResourceSourceIndex exists
- * and is followed by a (required) null terminated string. The string length
- * (including the null terminator) is the resource length minus the minimum
- * length, minus one byte for the ResourceSourceIndex itself.
+ * If the length of the actual resource descriptor is greater than the
+ * ACPI spec-defined minimum length, it means that a ResourceSourceIndex
+ * exists and is followed by a (required) null terminated string. The
+ * string length (including the null terminator) is the resource length
+ * minus the minimum length, minus one byte for the ResourceSourceIndex
+ * itself.
*/
if (ResourceLength > MinimumAmlResourceLength)
{
@@ -197,6 +196,7 @@ AcpiRsStreamOptionLength (
* FUNCTION: AcpiRsGetAmlLength
*
* PARAMETERS: Resource - Pointer to the resource linked list
+ * ResourceListSize - Size of the resource linked list
* SizeNeeded - Where the required size is returned
*
* RETURN: Status
@@ -210,9 +210,11 @@ AcpiRsStreamOptionLength (
ACPI_STATUS
AcpiRsGetAmlLength (
ACPI_RESOURCE *Resource,
+ ACPI_SIZE ResourceListSize,
ACPI_SIZE *SizeNeeded)
{
ACPI_SIZE AmlSizeNeeded = 0;
+ ACPI_RESOURCE *ResourceEnd;
ACPI_RS_LENGTH TotalSize;
@@ -221,7 +223,8 @@ AcpiRsGetAmlLength (
/* Traverse entire list of internal resource descriptors */
- while (Resource)
+ ResourceEnd = ACPI_ADD_PTR (ACPI_RESOURCE, Resource, ResourceListSize);
+ while (Resource < ResourceEnd)
{
/* Validate the descriptor type */
@@ -230,6 +233,13 @@ AcpiRsGetAmlLength (
return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE);
}
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!Resource->Length)
+ {
+ return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
/* Get the base size of the (external stream) resource descriptor */
TotalSize = AcpiGbl_AmlResourceSizes [Resource->Type];
@@ -300,9 +310,9 @@ AcpiRsGetAmlLength (
* 16-Bit Address Resource:
* Add the size of the optional ResourceSource info
*/
- TotalSize = (ACPI_RS_LENGTH)
- (TotalSize + AcpiRsStructOptionLength (
- &Resource->Data.Address16.ResourceSource));
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
+ AcpiRsStructOptionLength (
+ &Resource->Data.Address16.ResourceSource));
break;
@@ -311,9 +321,9 @@ AcpiRsGetAmlLength (
* 32-Bit Address Resource:
* Add the size of the optional ResourceSource info
*/
- TotalSize = (ACPI_RS_LENGTH)
- (TotalSize + AcpiRsStructOptionLength (
- &Resource->Data.Address32.ResourceSource));
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
+ AcpiRsStructOptionLength (
+ &Resource->Data.Address32.ResourceSource));
break;
@@ -322,9 +332,9 @@ AcpiRsGetAmlLength (
* 64-Bit Address Resource:
* Add the size of the optional ResourceSource info
*/
- TotalSize = (ACPI_RS_LENGTH)
- (TotalSize + AcpiRsStructOptionLength (
- &Resource->Data.Address64.ResourceSource));
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
+ AcpiRsStructOptionLength (
+ &Resource->Data.Address64.ResourceSource));
break;
@@ -334,8 +344,7 @@ AcpiRsGetAmlLength (
* Add the size of each additional optional interrupt beyond the
* required 1 (4 bytes for each UINT32 interrupt number)
*/
- TotalSize = (ACPI_RS_LENGTH)
- (TotalSize +
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
((Resource->Data.ExtendedIrq.InterruptCount - 1) * 4) +
/* Add the size of the optional ResourceSource info */
@@ -345,7 +354,29 @@ AcpiRsGetAmlLength (
break;
+ case ACPI_RESOURCE_TYPE_GPIO:
+
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
+ (Resource->Data.Gpio.PinTableLength * 2) +
+ Resource->Data.Gpio.ResourceSource.StringLength +
+ Resource->Data.Gpio.VendorLength);
+
+ break;
+
+
+ case ACPI_RESOURCE_TYPE_SERIAL_BUS:
+
+ TotalSize = AcpiGbl_AmlResourceSerialBusSizes [
+ Resource->Data.CommonSerialBus.Type];
+
+ TotalSize = (ACPI_RS_LENGTH) (TotalSize +
+ Resource->Data.I2cSerialBus.ResourceSource.StringLength +
+ Resource->Data.I2cSerialBus.VendorLength);
+
+ break;
+
default:
+
break;
}
@@ -395,12 +426,13 @@ AcpiRsGetListLength (
UINT32 ExtraStructBytes;
UINT8 ResourceIndex;
UINT8 MinimumAmlResourceLength;
+ AML_RESOURCE *AmlResource;
ACPI_FUNCTION_TRACE (RsGetListLength);
- *SizeNeeded = 0;
+ *SizeNeeded = ACPI_RS_SIZE_MIN; /* Minimum size is one EndTag */
EndAml = AmlBuffer + AmlBufferLength;
/* Walk the list of AML resource descriptors */
@@ -409,12 +441,18 @@ AcpiRsGetListLength (
{
/* Validate the Resource Type and Resource Length */
- Status = AcpiUtValidateResource (AmlBuffer, &ResourceIndex);
+ Status = AcpiUtValidateResource (NULL, AmlBuffer, &ResourceIndex);
if (ACPI_FAILURE (Status))
{
+ /*
+ * Exit on failure. Cannot continue because the descriptor length
+ * may be bogus also.
+ */
return_ACPI_STATUS (Status);
}
+ AmlResource = (void *) AmlBuffer;
+
/* Get the resource length and base (minimum) AML size */
ResourceLength = AcpiUtGetResourceLength (AmlBuffer);
@@ -455,15 +493,23 @@ AcpiRsGetListLength (
* Get the number of vendor data bytes
*/
ExtraStructBytes = ResourceLength;
+
+ /*
+ * There is already one byte included in the minimum
+ * descriptor size. If there are extra struct bytes,
+ * subtract one from the count.
+ */
+ if (ExtraStructBytes)
+ {
+ ExtraStructBytes--;
+ }
break;
case ACPI_RESOURCE_NAME_END_TAG:
/*
- * End Tag:
- * This is the normal exit, add size of EndTag
+ * End Tag: This is the normal exit
*/
- *SizeNeeded += ACPI_RS_SIZE_MIN;
return_ACPI_STATUS (AE_OK);
@@ -494,8 +540,37 @@ AcpiRsGetListLength (
ResourceLength - ExtraStructBytes, MinimumAmlResourceLength);
break;
+ case ACPI_RESOURCE_NAME_GPIO:
+
+ /* Vendor data is optional */
+
+ if (AmlResource->Gpio.VendorLength)
+ {
+ ExtraStructBytes +=
+ AmlResource->Gpio.VendorOffset -
+ AmlResource->Gpio.PinTableOffset +
+ AmlResource->Gpio.VendorLength;
+ }
+ else
+ {
+ ExtraStructBytes +=
+ AmlResource->LargeHeader.ResourceLength +
+ sizeof (AML_RESOURCE_LARGE_HEADER) -
+ AmlResource->Gpio.PinTableOffset;
+ }
+ break;
+
+ case ACPI_RESOURCE_NAME_SERIAL_BUS:
+
+ MinimumAmlResourceLength = AcpiGbl_ResourceAmlSerialBusSizes[
+ AmlResource->CommonSerialBus.Type];
+ ExtraStructBytes +=
+ AmlResource->CommonSerialBus.ResourceLength -
+ MinimumAmlResourceLength;
+ break;
default:
+
break;
}
@@ -505,10 +580,19 @@ AcpiRsGetListLength (
* Important: Round the size up for the appropriate alignment. This
* is a requirement on IA64.
*/
- BufferSize = AcpiGbl_ResourceStructSizes[ResourceIndex] +
- ExtraStructBytes;
- BufferSize = (UINT32) ACPI_ROUND_UP_TO_NATIVE_WORD (BufferSize);
+ if (AcpiUtGetResourceType (AmlBuffer) ==
+ ACPI_RESOURCE_NAME_SERIAL_BUS)
+ {
+ BufferSize = AcpiGbl_ResourceStructSerialBusSizes[
+ AmlResource->CommonSerialBus.Type] + ExtraStructBytes;
+ }
+ else
+ {
+ BufferSize = AcpiGbl_ResourceStructSizes[ResourceIndex] +
+ ExtraStructBytes;
+ }
+ BufferSize = (UINT32) ACPI_ROUND_UP_TO_NATIVE_WORD (BufferSize);
*SizeNeeded += BufferSize;
ACPI_DEBUG_PRINT ((ACPI_DB_RESOURCES,
@@ -569,7 +653,7 @@ AcpiRsGetPciRoutingTableLength (
/*
* Calculate the size of the return buffer.
* The base size is the number of elements * the sizes of the
- * structures. Additional space for the strings is added below.
+ * structures. Additional space for the strings is added below.
* The minus one is to subtract the size of the UINT8 Source[1]
* member because it is added below.
*
@@ -580,7 +664,7 @@ AcpiRsGetPciRoutingTableLength (
for (Index = 0; Index < NumberOfElements; Index++)
{
- /* Dereference the sub-package */
+ /* Dereference the subpackage */
PackageElement = *TopObjectList;
@@ -602,7 +686,9 @@ AcpiRsGetPciRoutingTableLength (
NameFound = FALSE;
- for (TableIndex = 0; TableIndex < 4 && !NameFound; TableIndex++)
+ for (TableIndex = 0;
+ TableIndex < PackageElement->Package.Count && !NameFound;
+ TableIndex++)
{
if (*SubObjectList && /* Null object allowed */
@@ -643,7 +729,7 @@ AcpiRsGetPciRoutingTableLength (
else
{
TempSizeNeeded += AcpiNsGetPathnameLength (
- (*SubObjectList)->Reference.Node);
+ (*SubObjectList)->Reference.Node);
}
}
else
diff --git a/usr/src/uts/intel/io/acpica/resources/rscreate.c b/usr/src/uts/intel/io/acpica/resources/rscreate.c
index 40cf447c29..64bbdbe995 100644
--- a/usr/src/uts/intel/io/acpica/resources/rscreate.c
+++ b/usr/src/uts/intel/io/acpica/resources/rscreate.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSCREATE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -54,6 +52,85 @@
/*******************************************************************************
*
+ * FUNCTION: AcpiBufferToResource
+ *
+ * PARAMETERS: AmlBuffer - Pointer to the resource byte stream
+ * AmlBufferLength - Length of the AmlBuffer
+ * ResourcePtr - Where the converted resource is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Convert a raw AML buffer to a resource list
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiBufferToResource (
+ UINT8 *AmlBuffer,
+ UINT16 AmlBufferLength,
+ ACPI_RESOURCE **ResourcePtr)
+{
+ ACPI_STATUS Status;
+ ACPI_SIZE ListSizeNeeded;
+ void *Resource;
+ void *CurrentResourcePtr;
+
+
+ ACPI_FUNCTION_TRACE (AcpiBufferToResource);
+
+
+ /*
+ * Note: we allow AE_AML_NO_RESOURCE_END_TAG, since an end tag
+ * is not required here.
+ */
+
+ /* Get the required length for the converted resource */
+
+ Status = AcpiRsGetListLength (
+ AmlBuffer, AmlBufferLength, &ListSizeNeeded);
+ if (Status == AE_AML_NO_RESOURCE_END_TAG)
+ {
+ Status = AE_OK;
+ }
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Allocate a buffer for the converted resource */
+
+ Resource = ACPI_ALLOCATE_ZEROED (ListSizeNeeded);
+ CurrentResourcePtr = Resource;
+ if (!Resource)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ /* Perform the AML-to-Resource conversion */
+
+ Status = AcpiUtWalkAmlResources (NULL, AmlBuffer, AmlBufferLength,
+ AcpiRsConvertAmlToResources, &CurrentResourcePtr);
+ if (Status == AE_AML_NO_RESOURCE_END_TAG)
+ {
+ Status = AE_OK;
+ }
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_FREE (Resource);
+ }
+ else
+ {
+ *ResourcePtr = Resource;
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiBufferToResource)
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiRsCreateResourceList
*
* PARAMETERS: AmlBuffer - Pointer to the resource byte stream
@@ -119,15 +196,15 @@ AcpiRsCreateResourceList (
/* Do the conversion */
Resource = OutputBuffer->Pointer;
- Status = AcpiUtWalkAmlResources (AmlStart, AmlBufferLength,
- AcpiRsConvertAmlToResources, &Resource);
+ Status = AcpiUtWalkAmlResources (NULL, AmlStart, AmlBufferLength,
+ AcpiRsConvertAmlToResources, &Resource);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
- OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
+ OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
return_ACPI_STATUS (AE_OK);
}
@@ -136,8 +213,8 @@ AcpiRsCreateResourceList (
*
* FUNCTION: AcpiRsCreatePciRoutingTable
*
- * PARAMETERS: PackageObject - Pointer to an ACPI_OPERAND_OBJECT
- * package
+ * PARAMETERS: PackageObject - Pointer to a package containing one
+ * of more ACPI_OPERAND_OBJECTs
* OutputBuffer - Pointer to the user's buffer
*
* RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code.
@@ -145,7 +222,7 @@ AcpiRsCreateResourceList (
* AE_BUFFER_OVERFLOW and OutputBuffer->Length will point
* to the size buffer needed.
*
- * DESCRIPTION: Takes the ACPI_OPERAND_OBJECT package and creates a
+ * DESCRIPTION: Takes the ACPI_OPERAND_OBJECT package and creates a
* linked list of PCI interrupt descriptions
*
* NOTE: It is the caller's responsibility to ensure that the start of the
@@ -178,8 +255,8 @@ AcpiRsCreatePciRoutingTable (
/* Get the required buffer length */
- Status = AcpiRsGetPciRoutingTableLength (PackageObject,
- &BufferSizeNeeded);
+ Status = AcpiRsGetPciRoutingTableLength (
+ PackageObject,&BufferSizeNeeded);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -201,10 +278,10 @@ AcpiRsCreatePciRoutingTable (
* package that in turn contains an UINT64 Address, a UINT8 Pin,
* a Name, and a UINT8 SourceIndex.
*/
- TopObjectList = PackageObject->Package.Elements;
+ TopObjectList = PackageObject->Package.Elements;
NumberOfElements = PackageObject->Package.Count;
- Buffer = OutputBuffer->Pointer;
- UserPrt = ACPI_CAST_PTR (ACPI_PCI_ROUTING_TABLE, Buffer);
+ Buffer = OutputBuffer->Pointer;
+ UserPrt = ACPI_CAST_PTR (ACPI_PCI_ROUTING_TABLE, Buffer);
for (Index = 0; Index < NumberOfElements; Index++)
{
@@ -218,23 +295,13 @@ AcpiRsCreatePciRoutingTable (
UserPrt = ACPI_CAST_PTR (ACPI_PCI_ROUTING_TABLE, Buffer);
/*
- * Fill in the Length field with the information we have at this point.
- * The minus four is to subtract the size of the UINT8 Source[4] member
- * because it is added below.
+ * Fill in the Length field with the information we have at this
+ * point. The minus four is to subtract the size of the UINT8
+ * Source[4] member because it is added below.
*/
UserPrt->Length = (sizeof (ACPI_PCI_ROUTING_TABLE) - 4);
- /* Each element of the top-level package must also be a package */
-
- if ((*TopObjectList)->Common.Type != ACPI_TYPE_PACKAGE)
- {
- ACPI_ERROR ((AE_INFO,
- "(PRT[%u]) Need sub-package, found %s",
- Index, AcpiUtGetObjectTypeName (*TopObjectList)));
- return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
- }
-
- /* Each sub-package must be of length 4 */
+ /* Each subpackage must be of length 4 */
if ((*TopObjectList)->Package.Count != 4)
{
@@ -245,7 +312,7 @@ AcpiRsCreatePciRoutingTable (
}
/*
- * Dereference the sub-package.
+ * Dereference the subpackage.
* The SubObjectList will now point to an array of the four IRQ
* elements: [Address, Pin, Source, SourceIndex]
*/
@@ -254,9 +321,10 @@ AcpiRsCreatePciRoutingTable (
/* 1) First subobject: Dereference the PRT.Address */
ObjDesc = SubObjectList[0];
- if (ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
+ if (!ObjDesc || ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
{
- ACPI_ERROR ((AE_INFO, "(PRT[%u].Address) Need Integer, found %s",
+ ACPI_ERROR ((AE_INFO,
+ "(PRT[%u].Address) Need Integer, found %s",
Index, AcpiUtGetObjectTypeName (ObjDesc)));
return_ACPI_STATUS (AE_BAD_DATA);
}
@@ -266,7 +334,7 @@ AcpiRsCreatePciRoutingTable (
/* 2) Second subobject: Dereference the PRT.Pin */
ObjDesc = SubObjectList[1];
- if (ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
+ if (!ObjDesc || ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
{
ACPI_ERROR ((AE_INFO, "(PRT[%u].Pin) Need Integer, found %s",
Index, AcpiUtGetObjectTypeName (ObjDesc)));
@@ -276,23 +344,6 @@ AcpiRsCreatePciRoutingTable (
UserPrt->Pin = (UINT32) ObjDesc->Integer.Value;
/*
- * If the BIOS has erroneously reversed the _PRT SourceName (index 2)
- * and the SourceIndex (index 3), fix it. _PRT is important enough to
- * workaround this BIOS error. This also provides compatibility with
- * other ACPI implementations.
- */
- ObjDesc = SubObjectList[3];
- if (!ObjDesc || (ObjDesc->Common.Type != ACPI_TYPE_INTEGER))
- {
- SubObjectList[3] = SubObjectList[2];
- SubObjectList[2] = ObjDesc;
-
- ACPI_WARNING ((AE_INFO,
- "(PRT[%X].Source) SourceName and SourceIndex are reversed, fixed",
- Index));
- }
-
- /*
* 3) Third subobject: Dereference the PRT.SourceName
* The name may be unresolved (slack mode), so allow a null object
*/
@@ -316,21 +367,21 @@ AcpiRsCreatePciRoutingTable (
/* Use *remaining* length of the buffer as max for pathname */
PathBuffer.Length = OutputBuffer->Length -
- (UINT32) ((UINT8 *) UserPrt->Source -
- (UINT8 *) OutputBuffer->Pointer);
+ (UINT32) ((UINT8 *) UserPrt->Source -
+ (UINT8 *) OutputBuffer->Pointer);
PathBuffer.Pointer = UserPrt->Source;
- Status = AcpiNsHandleToPathname ((ACPI_HANDLE) Node, &PathBuffer);
+ Status = AcpiNsHandleToPathname (
+ (ACPI_HANDLE) Node, &PathBuffer, FALSE);
/* +1 to include null terminator */
- UserPrt->Length += (UINT32) ACPI_STRLEN (UserPrt->Source) + 1;
+ UserPrt->Length += (UINT32) strlen (UserPrt->Source) + 1;
break;
-
case ACPI_TYPE_STRING:
- ACPI_STRCPY (UserPrt->Source, ObjDesc->String.Pointer);
+ strcpy (UserPrt->Source, ObjDesc->String.Pointer);
/*
* Add to the Length field the length of the string
@@ -339,18 +390,16 @@ AcpiRsCreatePciRoutingTable (
UserPrt->Length += ObjDesc->String.Length + 1;
break;
-
case ACPI_TYPE_INTEGER:
/*
- * If this is a number, then the Source Name is NULL, since the
- * entire buffer was zeroed out, we can leave this alone.
+ * If this is a number, then the Source Name is NULL, since
+ * the entire buffer was zeroed out, we can leave this alone.
*
* Add to the Length field the length of the UINT32 NULL
*/
UserPrt->Length += sizeof (UINT32);
break;
-
default:
ACPI_ERROR ((AE_INFO,
@@ -367,7 +416,7 @@ AcpiRsCreatePciRoutingTable (
/* 4) Fourth subobject: Dereference the PRT.SourceIndex */
ObjDesc = SubObjectList[3];
- if (ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
+ if (!ObjDesc || ObjDesc->Common.Type != ACPI_TYPE_INTEGER)
{
ACPI_ERROR ((AE_INFO,
"(PRT[%u].SourceIndex) Need Integer, found %s",
@@ -383,7 +432,7 @@ AcpiRsCreatePciRoutingTable (
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
- OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
+ OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
return_ACPI_STATUS (AE_OK);
}
@@ -392,23 +441,22 @@ AcpiRsCreatePciRoutingTable (
*
* FUNCTION: AcpiRsCreateAmlResources
*
- * PARAMETERS: LinkedListBuffer - Pointer to the resource linked list
- * OutputBuffer - Pointer to the user's buffer
+ * PARAMETERS: ResourceList - Pointer to the resource list buffer
+ * OutputBuffer - Where the AML buffer is returned
*
* RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code.
* If the OutputBuffer is too small, the error will be
* AE_BUFFER_OVERFLOW and OutputBuffer->Length will point
* to the size buffer needed.
*
- * DESCRIPTION: Takes the linked list of device resources and
- * creates a bytestream to be used as input for the
- * _SRS control method.
+ * DESCRIPTION: Converts a list of device resources to an AML bytestream
+ * to be used as input for the _SRS control method.
*
******************************************************************************/
ACPI_STATUS
AcpiRsCreateAmlResources (
- ACPI_RESOURCE *LinkedListBuffer,
+ ACPI_BUFFER *ResourceList,
ACPI_BUFFER *OutputBuffer)
{
ACPI_STATUS Status;
@@ -418,17 +466,15 @@ AcpiRsCreateAmlResources (
ACPI_FUNCTION_TRACE (RsCreateAmlResources);
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "LinkedListBuffer = %p\n",
- LinkedListBuffer));
+ /* Params already validated, no need to re-validate here */
- /*
- * Params already validated, so we don't re-validate here
- *
- * Pass the LinkedListBuffer into a module that calculates
- * the buffer size needed for the byte stream.
- */
- Status = AcpiRsGetAmlLength (LinkedListBuffer,
- &AmlSizeNeeded);
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ResourceList Buffer = %p\n",
+ ResourceList->Pointer));
+
+ /* Get the buffer size needed for the AML byte stream */
+
+ Status = AcpiRsGetAmlLength (
+ ResourceList->Pointer, ResourceList->Length, &AmlSizeNeeded);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "AmlSizeNeeded=%X, %s\n",
(UINT32) AmlSizeNeeded, AcpiFormatException (Status)));
@@ -447,15 +493,14 @@ AcpiRsCreateAmlResources (
/* Do the conversion */
- Status = AcpiRsConvertResourcesToAml (LinkedListBuffer, AmlSizeNeeded,
- OutputBuffer->Pointer);
+ Status = AcpiRsConvertResourcesToAml (ResourceList->Pointer,
+ AmlSizeNeeded, OutputBuffer->Pointer);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "OutputBuffer %p Length %X\n",
- OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
+ OutputBuffer->Pointer, (UINT32) OutputBuffer->Length));
return_ACPI_STATUS (AE_OK);
}
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsdump.c b/usr/src/uts/intel/io/acpica/resources/rsdump.c
index 5470677447..e7948850bc 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsdump.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsdump.c
@@ -1,11 +1,11 @@
/*******************************************************************************
*
- * Module Name: rsdump - Functions to display the resource structures.
+ * Module Name: rsdump - AML debugger support for resource structures.
*
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __RSDUMP_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -51,39 +48,40 @@
#define _COMPONENT ACPI_RESOURCES
ACPI_MODULE_NAME ("rsdump")
-
-#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+/*
+ * All functions in this module are used by the AML Debugger only
+ */
/* Local prototypes */
static void
AcpiRsOutString (
- char *Title,
- char *Value);
+ const char *Title,
+ const char *Value);
static void
AcpiRsOutInteger8 (
- char *Title,
+ const char *Title,
UINT8 Value);
static void
AcpiRsOutInteger16 (
- char *Title,
+ const char *Title,
UINT16 Value);
static void
AcpiRsOutInteger32 (
- char *Title,
+ const char *Title,
UINT32 Value);
static void
AcpiRsOutInteger64 (
- char *Title,
+ const char *Title,
UINT64 Value);
static void
AcpiRsOutTitle (
- char *Title);
+ const char *Title);
static void
AcpiRsDumpByteList (
@@ -91,14 +89,19 @@ AcpiRsDumpByteList (
UINT8 *Data);
static void
+AcpiRsDumpWordList (
+ UINT16 Length,
+ UINT16 *Data);
+
+static void
AcpiRsDumpDwordList (
UINT8 Length,
UINT32 *Data);
static void
AcpiRsDumpShortByteList (
- UINT8 Length,
- UINT8 *Data);
+ UINT8 Length,
+ UINT8 *Data);
static void
AcpiRsDumpResourceSource (
@@ -111,237 +114,145 @@ AcpiRsDumpAddressCommon (
static void
AcpiRsDumpDescriptor (
void *Resource,
- ACPI_RSDUMP_INFO *Table);
-
-
-#define ACPI_RSD_OFFSET(f) (UINT8) ACPI_OFFSET (ACPI_RESOURCE_DATA,f)
-#define ACPI_PRT_OFFSET(f) (UINT8) ACPI_OFFSET (ACPI_PCI_ROUTING_TABLE,f)
-#define ACPI_RSD_TABLE_SIZE(name) (sizeof(name) / sizeof (ACPI_RSDUMP_INFO))
+ ACPI_RSDUMP_INFO *Table);
/*******************************************************************************
*
- * Resource Descriptor info tables
+ * FUNCTION: AcpiRsDumpResourceList
*
- * Note: The first table entry must be a Title or Literal and must contain
- * the table length (number of table entries)
+ * PARAMETERS: ResourceList - Pointer to a resource descriptor list
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dispatches the structure to the correct dump routine.
*
******************************************************************************/
-ACPI_RSDUMP_INFO AcpiRsDumpIrq[7] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIrq), "IRQ", NULL},
- {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (Irq.DescriptorLength), "Descriptor Length", NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Irq.Triggering), "Triggering", AcpiGbl_HeDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Irq.Polarity), "Polarity", AcpiGbl_LlDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Irq.Sharable), "Sharing", AcpiGbl_ShrDecode},
- {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (Irq.InterruptCount), "Interrupt Count", NULL},
- {ACPI_RSD_SHORTLIST,ACPI_RSD_OFFSET (Irq.Interrupts[0]), "Interrupt List", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpDma[6] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpDma), "DMA", NULL},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Dma.Type), "Speed", AcpiGbl_TypDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Dma.BusMaster), "Mastering", AcpiGbl_BmDecode},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Dma.Transfer), "Transfer Type", AcpiGbl_SizDecode},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Dma.ChannelCount), "Channel Count", NULL},
- {ACPI_RSD_SHORTLIST,ACPI_RSD_OFFSET (Dma.Channels[0]), "Channel List", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpStartDpf[4] =
+void
+AcpiRsDumpResourceList (
+ ACPI_RESOURCE *ResourceList)
{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpStartDpf), "Start-Dependent-Functions",NULL},
- {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (StartDpf.DescriptorLength), "Descriptor Length", NULL},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (StartDpf.CompatibilityPriority), "Compatibility Priority", AcpiGbl_ConfigDecode},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (StartDpf.PerformanceRobustness), "Performance/Robustness", AcpiGbl_ConfigDecode}
-};
+ UINT32 Count = 0;
+ UINT32 Type;
-ACPI_RSDUMP_INFO AcpiRsDumpEndDpf[1] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpEndDpf), "End-Dependent-Functions", NULL}
-};
-ACPI_RSDUMP_INFO AcpiRsDumpIo[6] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIo), "I/O", NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Io.IoDecode), "Address Decoding", AcpiGbl_IoDecode},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Io.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Io.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Io.Alignment), "Alignment", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Io.AddressLength), "Address Length", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpFixedIo[3] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpFixedIo), "Fixed I/O", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (FixedIo.Address), "Address", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (FixedIo.AddressLength), "Address Length", NULL}
-};
+ ACPI_FUNCTION_ENTRY ();
-ACPI_RSDUMP_INFO AcpiRsDumpVendor[3] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpVendor), "Vendor Specific", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Vendor.ByteLength), "Length", NULL},
- {ACPI_RSD_LONGLIST, ACPI_RSD_OFFSET (Vendor.ByteData[0]), "Vendor Data", NULL}
-};
-ACPI_RSDUMP_INFO AcpiRsDumpEndTag[1] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpEndTag), "EndTag", NULL}
-};
+ /* Check if debug output enabled */
-ACPI_RSDUMP_INFO AcpiRsDumpMemory24[6] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemory24), "24-Bit Memory Range", NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Memory24.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Alignment), "Alignment", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.AddressLength), "Address Length", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpMemory32[6] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemory32), "32-Bit Memory Range", NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Memory32.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Alignment), "Alignment", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.AddressLength), "Address Length", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpFixedMemory32[4] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpFixedMemory32), "32-Bit Fixed Memory Range",NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (FixedMemory32.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (FixedMemory32.Address), "Address", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (FixedMemory32.AddressLength), "Address Length", NULL}
-};
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_RESOURCES, _COMPONENT))
+ {
+ return;
+ }
-ACPI_RSDUMP_INFO AcpiRsDumpAddress16[8] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress16), "16-Bit WORD Address Space",NULL},
- {ACPI_RSD_ADDRESS, 0, NULL, NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Granularity), "Granularity", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.TranslationOffset), "Translation Offset", NULL},
- {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.AddressLength), "Address Length", NULL},
- {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address16.ResourceSource), NULL, NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpAddress32[8] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress32), "32-Bit DWORD Address Space", NULL},
- {ACPI_RSD_ADDRESS, 0, NULL, NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Granularity), "Granularity", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.TranslationOffset), "Translation Offset", NULL},
- {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.AddressLength), "Address Length", NULL},
- {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address32.ResourceSource), NULL, NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpAddress64[8] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress64), "64-Bit QWORD Address Space", NULL},
- {ACPI_RSD_ADDRESS, 0, NULL, NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Granularity), "Granularity", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.TranslationOffset), "Translation Offset", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.AddressLength), "Address Length", NULL},
- {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address64.ResourceSource), NULL, NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpExtAddress64[8] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpExtAddress64), "64-Bit Extended Address Space", NULL},
- {ACPI_RSD_ADDRESS, 0, NULL, NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Granularity), "Granularity", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Minimum), "Address Minimum", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Maximum), "Address Maximum", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.TranslationOffset), "Translation Offset", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.AddressLength), "Address Length", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.TypeSpecific), "Type-Specific Attribute", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpExtIrq[8] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpExtIrq), "Extended IRQ", NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.ProducerConsumer), "Type", AcpiGbl_ConsumeDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Triggering), "Triggering", AcpiGbl_HeDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Polarity), "Polarity", AcpiGbl_LlDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Sharable), "Sharing", AcpiGbl_ShrDecode},
- {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (ExtendedIrq.ResourceSource), NULL, NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (ExtendedIrq.InterruptCount), "Interrupt Count", NULL},
- {ACPI_RSD_DWORDLIST,ACPI_RSD_OFFSET (ExtendedIrq.Interrupts[0]), "Interrupt List", NULL}
-};
-
-ACPI_RSDUMP_INFO AcpiRsDumpGenericReg[6] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpGenericReg), "Generic Register", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.SpaceId), "Space ID", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.BitWidth), "Bit Width", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.BitOffset), "Bit Offset", NULL},
- {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.AccessSize), "Access Size", NULL},
- {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (GenericReg.Address), "Address", NULL}
-};
+ /* Walk list and dump all resource descriptors (END_TAG terminates) */
+ do
+ {
+ AcpiOsPrintf ("\n[%02X] ", Count);
+ Count++;
-/*
- * Tables used for common address descriptor flag fields
- */
-static ACPI_RSDUMP_INFO AcpiRsDumpGeneralFlags[5] =
-{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpGeneralFlags), NULL, NULL},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.ProducerConsumer), "Consumer/Producer", AcpiGbl_ConsumeDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Decode), "Address Decode", AcpiGbl_DecDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.MinAddressFixed), "Min Relocatability", AcpiGbl_MinDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.MaxAddressFixed), "Max Relocatability", AcpiGbl_MaxDecode}
-};
-
-static ACPI_RSDUMP_INFO AcpiRsDumpMemoryFlags[5] =
-{
- {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemoryFlags), "Resource Type", (void *) "Memory Range"},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.Caching), "Caching", AcpiGbl_MemDecode},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.RangeType), "Range Type", AcpiGbl_MtpDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.Translation), "Translation", AcpiGbl_TtpDecode}
-};
-
-static ACPI_RSDUMP_INFO AcpiRsDumpIoFlags[4] =
-{
- {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIoFlags), "Resource Type", (void *) "I/O Range"},
- {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.RangeType), "Range Type", AcpiGbl_RngDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.Translation), "Translation", AcpiGbl_TtpDecode},
- {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.TranslationType), "Translation Type", AcpiGbl_TrsDecode}
-};
+ /* Validate Type before dispatch */
+ Type = ResourceList->Type;
+ if (Type > ACPI_RESOURCE_TYPE_MAX)
+ {
+ AcpiOsPrintf (
+ "Invalid descriptor type (%X) in resource list\n",
+ ResourceList->Type);
+ return;
+ }
-/*
- * Table used to dump _PRT contents
- */
-static ACPI_RSDUMP_INFO AcpiRsDumpPrt[5] =
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!ResourceList->Length)
+ {
+ AcpiOsPrintf (
+ "Invalid zero length descriptor in resource list\n");
+ return;
+ }
+
+ /* Dump the resource descriptor */
+
+ if (Type == ACPI_RESOURCE_TYPE_SERIAL_BUS)
+ {
+ AcpiRsDumpDescriptor (&ResourceList->Data,
+ AcpiGbl_DumpSerialBusDispatch[
+ ResourceList->Data.CommonSerialBus.Type]);
+ }
+ else
+ {
+ AcpiRsDumpDescriptor (&ResourceList->Data,
+ AcpiGbl_DumpResourceDispatch[Type]);
+ }
+
+ /* Point to the next resource structure */
+
+ ResourceList = ACPI_NEXT_RESOURCE (ResourceList);
+
+ /* Exit when END_TAG descriptor is reached */
+
+ } while (Type != ACPI_RESOURCE_TYPE_END_TAG);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiRsDumpIrqList
+ *
+ * PARAMETERS: RouteTable - Pointer to the routing table to dump.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print IRQ routing table
+ *
+ ******************************************************************************/
+
+void
+AcpiRsDumpIrqList (
+ UINT8 *RouteTable)
{
- {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpPrt), NULL, NULL},
- {ACPI_RSD_UINT64, ACPI_PRT_OFFSET (Address), "Address", NULL},
- {ACPI_RSD_UINT32, ACPI_PRT_OFFSET (Pin), "Pin", NULL},
- {ACPI_RSD_STRING, ACPI_PRT_OFFSET (Source[0]), "Source", NULL},
- {ACPI_RSD_UINT32, ACPI_PRT_OFFSET (SourceIndex), "Source Index", NULL}
-};
+ ACPI_PCI_ROUTING_TABLE *PrtElement;
+ UINT8 Count;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (ACPI_LV_RESOURCES, _COMPONENT))
+ {
+ return;
+ }
+
+ PrtElement = ACPI_CAST_PTR (ACPI_PCI_ROUTING_TABLE, RouteTable);
+
+ /* Dump all table elements, Exit on zero length element */
+
+ for (Count = 0; PrtElement->Length; Count++)
+ {
+ AcpiOsPrintf ("\n[%02X] PCI IRQ Routing Table Package\n", Count);
+ AcpiRsDumpDescriptor (PrtElement, AcpiRsDumpPrt);
+
+ PrtElement = ACPI_ADD_PTR (ACPI_PCI_ROUTING_TABLE,
+ PrtElement, PrtElement->Length);
+ }
+}
/*******************************************************************************
*
* FUNCTION: AcpiRsDumpDescriptor
*
- * PARAMETERS: Resource
+ * PARAMETERS: Resource - Buffer containing the resource
+ * Table - Table entry to decode the resource
*
* RETURN: None
*
- * DESCRIPTION:
+ * DESCRIPTION: Dump a resource descriptor based on a dump table entry.
*
******************************************************************************/
@@ -352,8 +263,8 @@ AcpiRsDumpDescriptor (
{
UINT8 *Target = NULL;
UINT8 *PreviousTarget;
- char *Name;
- UINT8 Count;
+ const char *Name;
+ UINT8 Count;
/* First table entry must contain the table length (# of table entries) */
@@ -381,41 +292,59 @@ AcpiRsDumpDescriptor (
/* Strings */
case ACPI_RSD_LITERAL:
+
AcpiRsOutString (Name, ACPI_CAST_PTR (char, Table->Pointer));
break;
case ACPI_RSD_STRING:
+
AcpiRsOutString (Name, ACPI_CAST_PTR (char, Target));
break;
/* Data items, 8/16/32/64 bit */
case ACPI_RSD_UINT8:
- AcpiRsOutInteger8 (Name, ACPI_GET8 (Target));
+
+ if (Table->Pointer)
+ {
+ AcpiRsOutString (Name, Table->Pointer [*Target]);
+ }
+ else
+ {
+ AcpiRsOutInteger8 (Name, ACPI_GET8 (Target));
+ }
break;
case ACPI_RSD_UINT16:
+
AcpiRsOutInteger16 (Name, ACPI_GET16 (Target));
break;
case ACPI_RSD_UINT32:
+
AcpiRsOutInteger32 (Name, ACPI_GET32 (Target));
break;
case ACPI_RSD_UINT64:
+
AcpiRsOutInteger64 (Name, ACPI_GET64 (Target));
break;
/* Flags: 1-bit and 2-bit flags supported */
case ACPI_RSD_1BITFLAG:
- AcpiRsOutString (Name, ACPI_CAST_PTR (char,
- Table->Pointer [*Target & 0x01]));
+
+ AcpiRsOutString (Name, Table->Pointer [*Target & 0x01]);
break;
case ACPI_RSD_2BITFLAG:
- AcpiRsOutString (Name, ACPI_CAST_PTR (char,
- Table->Pointer [*Target & 0x03]));
+
+ AcpiRsOutString (Name, Table->Pointer [*Target & 0x03]);
+ break;
+
+ case ACPI_RSD_3BITFLAG:
+
+ AcpiRsOutString (Name, Table->Pointer [*Target & 0x07]);
break;
case ACPI_RSD_SHORTLIST:
@@ -430,6 +359,19 @@ AcpiRsDumpDescriptor (
}
break;
+ case ACPI_RSD_SHORTLISTX:
+ /*
+ * Short byte list (single line output) for GPIO vendor data
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (PreviousTarget)
+ {
+ AcpiRsOutTitle (Name);
+ AcpiRsDumpShortByteList (*PreviousTarget,
+ *(ACPI_CAST_INDIRECT_PTR (UINT8, Target)));
+ }
+ break;
+
case ACPI_RSD_LONGLIST:
/*
* Long byte list for Vendor resource data
@@ -453,21 +395,36 @@ AcpiRsDumpDescriptor (
}
break;
+ case ACPI_RSD_WORDLIST:
+ /*
+ * Word list for GPIO Pin Table
+ * Note: The list length is obtained from the previous table entry
+ */
+ if (PreviousTarget)
+ {
+ AcpiRsDumpWordList (*PreviousTarget,
+ *(ACPI_CAST_INDIRECT_PTR (UINT16, Target)));
+ }
+ break;
+
case ACPI_RSD_ADDRESS:
/*
* Common flags for all Address resources
*/
- AcpiRsDumpAddressCommon (ACPI_CAST_PTR (ACPI_RESOURCE_DATA, Target));
+ AcpiRsDumpAddressCommon (ACPI_CAST_PTR (
+ ACPI_RESOURCE_DATA, Target));
break;
case ACPI_RSD_SOURCE:
/*
* Optional ResourceSource for Address resources
*/
- AcpiRsDumpResourceSource (ACPI_CAST_PTR (ACPI_RESOURCE_SOURCE, Target));
+ AcpiRsDumpResourceSource (ACPI_CAST_PTR (
+ ACPI_RESOURCE_SOURCE, Target));
break;
default:
+
AcpiOsPrintf ("**** Invalid table opcode [%X] ****\n",
Table->Opcode);
return;
@@ -567,111 +524,6 @@ AcpiRsDumpAddressCommon (
/*******************************************************************************
*
- * FUNCTION: AcpiRsDumpResourceList
- *
- * PARAMETERS: ResourceList - Pointer to a resource descriptor list
- *
- * RETURN: None
- *
- * DESCRIPTION: Dispatches the structure to the correct dump routine.
- *
- ******************************************************************************/
-
-void
-AcpiRsDumpResourceList (
- ACPI_RESOURCE *ResourceList)
-{
- UINT32 Count = 0;
- UINT32 Type;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- if (!(AcpiDbgLevel & ACPI_LV_RESOURCES) || !( _COMPONENT & AcpiDbgLayer))
- {
- return;
- }
-
- /* Walk list and dump all resource descriptors (END_TAG terminates) */
-
- do
- {
- AcpiOsPrintf ("\n[%02X] ", Count);
- Count++;
-
- /* Validate Type before dispatch */
-
- Type = ResourceList->Type;
- if (Type > ACPI_RESOURCE_TYPE_MAX)
- {
- AcpiOsPrintf (
- "Invalid descriptor type (%X) in resource list\n",
- ResourceList->Type);
- return;
- }
-
- /* Dump the resource descriptor */
-
- AcpiRsDumpDescriptor (&ResourceList->Data,
- AcpiGbl_DumpResourceDispatch[Type]);
-
- /* Point to the next resource structure */
-
- ResourceList = ACPI_ADD_PTR (ACPI_RESOURCE, ResourceList,
- ResourceList->Length);
-
- /* Exit when END_TAG descriptor is reached */
-
- } while (Type != ACPI_RESOURCE_TYPE_END_TAG);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiRsDumpIrqList
- *
- * PARAMETERS: RouteTable - Pointer to the routing table to dump.
- *
- * RETURN: None
- *
- * DESCRIPTION: Print IRQ routing table
- *
- ******************************************************************************/
-
-void
-AcpiRsDumpIrqList (
- UINT8 *RouteTable)
-{
- ACPI_PCI_ROUTING_TABLE *PrtElement;
- UINT8 Count;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- if (!(AcpiDbgLevel & ACPI_LV_RESOURCES) || !( _COMPONENT & AcpiDbgLayer))
- {
- return;
- }
-
- PrtElement = ACPI_CAST_PTR (ACPI_PCI_ROUTING_TABLE, RouteTable);
-
- /* Dump all table elements, Exit on zero length element */
-
- for (Count = 0; PrtElement->Length; Count++)
- {
- AcpiOsPrintf ("\n[%02X] PCI IRQ Routing Table Package\n", Count);
- AcpiRsDumpDescriptor (PrtElement, AcpiRsDumpPrt);
-
- PrtElement = ACPI_ADD_PTR (ACPI_PCI_ROUTING_TABLE,
- PrtElement, PrtElement->Length);
- }
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiRsOut*
*
* PARAMETERS: Title - Name of the resource field
@@ -686,9 +538,10 @@ AcpiRsDumpIrqList (
static void
AcpiRsOutString (
- char *Title,
- char *Value)
+ const char *Title,
+ const char *Value)
{
+
AcpiOsPrintf ("%27s : %s", Title, Value);
if (!*Value)
{
@@ -699,7 +552,7 @@ AcpiRsOutString (
static void
AcpiRsOutInteger8 (
- char *Title,
+ const char *Title,
UINT8 Value)
{
AcpiOsPrintf ("%27s : %2.2X\n", Title, Value);
@@ -707,33 +560,37 @@ AcpiRsOutInteger8 (
static void
AcpiRsOutInteger16 (
- char *Title,
+ const char *Title,
UINT16 Value)
{
+
AcpiOsPrintf ("%27s : %4.4X\n", Title, Value);
}
static void
AcpiRsOutInteger32 (
- char *Title,
+ const char *Title,
UINT32 Value)
{
+
AcpiOsPrintf ("%27s : %8.8X\n", Title, Value);
}
static void
AcpiRsOutInteger64 (
- char *Title,
+ const char *Title,
UINT64 Value)
{
+
AcpiOsPrintf ("%27s : %8.8X%8.8X\n", Title,
ACPI_FORMAT_UINT64 (Value));
}
static void
AcpiRsOutTitle (
- char *Title)
+ const char *Title)
{
+
AcpiOsPrintf ("%27s : ", Title);
}
@@ -761,15 +618,14 @@ AcpiRsDumpByteList (
for (i = 0; i < Length; i++)
{
- AcpiOsPrintf ("%25s%2.2X : %2.2X\n",
- "Byte", i, Data[i]);
+ AcpiOsPrintf ("%25s%2.2X : %2.2X\n", "Byte", i, Data[i]);
}
}
static void
AcpiRsDumpShortByteList (
- UINT8 Length,
- UINT8 *Data)
+ UINT8 Length,
+ UINT8 *Data)
{
UINT8 i;
@@ -778,6 +634,7 @@ AcpiRsDumpShortByteList (
{
AcpiOsPrintf ("%X ", Data[i]);
}
+
AcpiOsPrintf ("\n");
}
@@ -791,10 +648,20 @@ AcpiRsDumpDwordList (
for (i = 0; i < Length; i++)
{
- AcpiOsPrintf ("%25s%2.2X : %8.8X\n",
- "Dword", i, Data[i]);
+ AcpiOsPrintf ("%25s%2.2X : %8.8X\n", "Dword", i, Data[i]);
}
}
-#endif
+static void
+AcpiRsDumpWordList (
+ UINT16 Length,
+ UINT16 *Data)
+{
+ UINT16 i;
+
+ for (i = 0; i < Length; i++)
+ {
+ AcpiOsPrintf ("%25s%2.2X : %4.4X\n", "Word", i, Data[i]);
+ }
+}
diff --git a/usr/src/uts/intel/io/acpica/resources/rsdumpinfo.c b/usr/src/uts/intel/io/acpica/resources/rsdumpinfo.c
new file mode 100644
index 0000000000..bc779ce58c
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/resources/rsdumpinfo.c
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ *
+ * Module Name: rsdumpinfo - Tables used to display resource descriptors.
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ ACPI_MODULE_NAME ("rsdumpinfo")
+
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER)
+
+
+#define ACPI_RSD_OFFSET(f) (UINT8) ACPI_OFFSET (ACPI_RESOURCE_DATA,f)
+#define ACPI_PRT_OFFSET(f) (UINT8) ACPI_OFFSET (ACPI_PCI_ROUTING_TABLE,f)
+#define ACPI_RSD_TABLE_SIZE(name) (sizeof(name) / sizeof (ACPI_RSDUMP_INFO))
+
+
+/*******************************************************************************
+ *
+ * Resource Descriptor info tables
+ *
+ * Note: The first table entry must be a Title or Literal and must contain
+ * the table length (number of table entries)
+ *
+ ******************************************************************************/
+
+ACPI_RSDUMP_INFO AcpiRsDumpIrq[7] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIrq), "IRQ", NULL},
+ {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (Irq.DescriptorLength), "Descriptor Length", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Irq.Triggering), "Triggering", AcpiGbl_HeDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Irq.Polarity), "Polarity", AcpiGbl_LlDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Irq.Sharable), "Sharing", AcpiGbl_ShrDecode},
+ {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (Irq.InterruptCount), "Interrupt Count", NULL},
+ {ACPI_RSD_SHORTLIST,ACPI_RSD_OFFSET (Irq.Interrupts[0]), "Interrupt List", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpDma[6] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpDma), "DMA", NULL},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Dma.Type), "Speed", AcpiGbl_TypDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Dma.BusMaster), "Mastering", AcpiGbl_BmDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Dma.Transfer), "Transfer Type", AcpiGbl_SizDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Dma.ChannelCount), "Channel Count", NULL},
+ {ACPI_RSD_SHORTLIST,ACPI_RSD_OFFSET (Dma.Channels[0]), "Channel List", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpStartDpf[4] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpStartDpf), "Start-Dependent-Functions",NULL},
+ {ACPI_RSD_UINT8 , ACPI_RSD_OFFSET (StartDpf.DescriptorLength), "Descriptor Length", NULL},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (StartDpf.CompatibilityPriority), "Compatibility Priority", AcpiGbl_ConfigDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (StartDpf.PerformanceRobustness), "Performance/Robustness", AcpiGbl_ConfigDecode}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpEndDpf[1] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpEndDpf), "End-Dependent-Functions", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpIo[6] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIo), "I/O", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Io.IoDecode), "Address Decoding", AcpiGbl_IoDecode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Io.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Io.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Io.Alignment), "Alignment", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Io.AddressLength), "Address Length", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpFixedIo[3] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpFixedIo), "Fixed I/O", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (FixedIo.Address), "Address", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (FixedIo.AddressLength), "Address Length", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpVendor[3] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpVendor), "Vendor Specific", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Vendor.ByteLength), "Length", NULL},
+ {ACPI_RSD_LONGLIST, ACPI_RSD_OFFSET (Vendor.ByteData[0]), "Vendor Data", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpEndTag[1] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpEndTag), "EndTag", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpMemory24[6] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemory24), "24-Bit Memory Range", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Memory24.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.Alignment), "Alignment", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Memory24.AddressLength), "Address Length", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpMemory32[6] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemory32), "32-Bit Memory Range", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Memory32.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.Alignment), "Alignment", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Memory32.AddressLength), "Address Length", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpFixedMemory32[4] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpFixedMemory32), "32-Bit Fixed Memory Range",NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (FixedMemory32.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (FixedMemory32.Address), "Address", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (FixedMemory32.AddressLength), "Address Length", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpAddress16[8] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress16), "16-Bit WORD Address Space",NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Address.Granularity), "Granularity", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Address.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Address.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Address.TranslationOffset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Address16.Address.AddressLength), "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address16.ResourceSource), NULL, NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpAddress32[8] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress32), "32-Bit DWORD Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Address.Granularity), "Granularity", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Address.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Address.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Address.TranslationOffset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (Address32.Address.AddressLength), "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address32.ResourceSource), NULL, NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpAddress64[8] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpAddress64), "64-Bit QWORD Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Address.Granularity), "Granularity", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Address.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Address.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Address.TranslationOffset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (Address64.Address.AddressLength), "Address Length", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Address64.ResourceSource), NULL, NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpExtAddress64[8] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpExtAddress64), "64-Bit Extended Address Space", NULL},
+ {ACPI_RSD_ADDRESS, 0, NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Address.Granularity), "Granularity", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Address.Minimum), "Address Minimum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Address.Maximum), "Address Maximum", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Address.TranslationOffset),
+ "Translation Offset", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.Address.AddressLength),
+ "Address Length", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (ExtAddress64.TypeSpecific), "Type-Specific Attribute", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpExtIrq[8] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpExtIrq), "Extended IRQ", NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.ProducerConsumer), "Type", AcpiGbl_ConsumeDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Triggering), "Triggering", AcpiGbl_HeDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Polarity), "Polarity", AcpiGbl_LlDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (ExtendedIrq.Sharable), "Sharing", AcpiGbl_ShrDecode},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (ExtendedIrq.ResourceSource), NULL, NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (ExtendedIrq.InterruptCount), "Interrupt Count", NULL},
+ {ACPI_RSD_DWORDLIST,ACPI_RSD_OFFSET (ExtendedIrq.Interrupts[0]), "Interrupt List", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpGenericReg[6] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpGenericReg), "Generic Register", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.SpaceId), "Space ID", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.BitWidth), "Bit Width", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.BitOffset), "Bit Offset", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (GenericReg.AccessSize), "Access Size", NULL},
+ {ACPI_RSD_UINT64, ACPI_RSD_OFFSET (GenericReg.Address), "Address", NULL}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpGpio[16] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpGpio), "GPIO", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Gpio.RevisionId), "RevisionId", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Gpio.ConnectionType), "ConnectionType", AcpiGbl_CtDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Gpio.ProducerConsumer), "ProducerConsumer", AcpiGbl_ConsumeDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (Gpio.PinConfig), "PinConfig", AcpiGbl_PpcDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Gpio.Sharable), "Sharing", AcpiGbl_ShrDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Gpio.IoRestriction), "IoRestriction", AcpiGbl_IorDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Gpio.Triggering), "Triggering", AcpiGbl_HeDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Gpio.Polarity), "Polarity", AcpiGbl_LlDecode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Gpio.DriveStrength), "DriveStrength", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Gpio.DebounceTimeout), "DebounceTimeout", NULL},
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (Gpio.ResourceSource), "ResourceSource", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Gpio.PinTableLength), "PinTableLength", NULL},
+ {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET (Gpio.PinTable), "PinTable", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (Gpio.VendorLength), "VendorLength", NULL},
+ {ACPI_RSD_SHORTLISTX,ACPI_RSD_OFFSET (Gpio.VendorData), "VendorData", NULL},
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpFixedDma[4] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpFixedDma), "FixedDma", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (FixedDma.RequestLines), "RequestLines", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (FixedDma.Channels), "Channels", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (FixedDma.Width), "TransferWidth", AcpiGbl_DtsDecode},
+};
+
+#define ACPI_RS_DUMP_COMMON_SERIAL_BUS \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (CommonSerialBus.RevisionId), "RevisionId", NULL}, \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (CommonSerialBus.Type), "Type", AcpiGbl_SbtDecode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (CommonSerialBus.ProducerConsumer), "ProducerConsumer", AcpiGbl_ConsumeDecode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (CommonSerialBus.SlaveMode), "SlaveMode", AcpiGbl_SmDecode}, \
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (CommonSerialBus.ConnectionSharing),"ConnectionSharing", AcpiGbl_ShrDecode}, \
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (CommonSerialBus.TypeRevisionId), "TypeRevisionId", NULL}, \
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (CommonSerialBus.TypeDataLength), "TypeDataLength", NULL}, \
+ {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (CommonSerialBus.ResourceSource), "ResourceSource", NULL}, \
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (CommonSerialBus.VendorLength), "VendorLength", NULL}, \
+ {ACPI_RSD_SHORTLISTX,ACPI_RSD_OFFSET (CommonSerialBus.VendorData), "VendorData", NULL},
+
+ACPI_RSDUMP_INFO AcpiRsDumpCommonSerialBus[11] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpCommonSerialBus), "Common Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpI2cSerialBus[14] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpI2cSerialBus), "I2C Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (I2cSerialBus.AccessMode), "AccessMode", AcpiGbl_AmDecode},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (I2cSerialBus.ConnectionSpeed), "ConnectionSpeed", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (I2cSerialBus.SlaveAddress), "SlaveAddress", NULL},
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpSpiSerialBus[18] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpSpiSerialBus), "Spi Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (SpiSerialBus.WireMode), "WireMode", AcpiGbl_WmDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (SpiSerialBus.DevicePolarity), "DevicePolarity", AcpiGbl_DpDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (SpiSerialBus.DataBitLength), "DataBitLength", NULL},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (SpiSerialBus.ClockPhase), "ClockPhase", AcpiGbl_CphDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (SpiSerialBus.ClockPolarity), "ClockPolarity", AcpiGbl_CpoDecode},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (SpiSerialBus.DeviceSelection), "DeviceSelection", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (SpiSerialBus.ConnectionSpeed), "ConnectionSpeed", NULL},
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpUartSerialBus[20] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpUartSerialBus), "Uart Serial Bus", NULL},
+ ACPI_RS_DUMP_COMMON_SERIAL_BUS
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (UartSerialBus.FlowControl), "FlowControl", AcpiGbl_FcDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (UartSerialBus.StopBits), "StopBits", AcpiGbl_SbDecode},
+ {ACPI_RSD_3BITFLAG, ACPI_RSD_OFFSET (UartSerialBus.DataBits), "DataBits", AcpiGbl_BpbDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (UartSerialBus.Endian), "Endian", AcpiGbl_EdDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (UartSerialBus.Parity), "Parity", AcpiGbl_PtDecode},
+ {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (UartSerialBus.LinesEnabled), "LinesEnabled", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (UartSerialBus.RxFifoSize), "RxFifoSize", NULL},
+ {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (UartSerialBus.TxFifoSize), "TxFifoSize", NULL},
+ {ACPI_RSD_UINT32, ACPI_RSD_OFFSET (UartSerialBus.DefaultBaudRate), "ConnectionSpeed", NULL},
+};
+
+/*
+ * Tables used for common address descriptor flag fields
+ */
+ACPI_RSDUMP_INFO AcpiRsDumpGeneralFlags[5] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpGeneralFlags), NULL, NULL},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.ProducerConsumer), "Consumer/Producer", AcpiGbl_ConsumeDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Decode), "Address Decode", AcpiGbl_DecDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.MinAddressFixed), "Min Relocatability", AcpiGbl_MinDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.MaxAddressFixed), "Max Relocatability", AcpiGbl_MaxDecode}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpMemoryFlags[5] =
+{
+ {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE (AcpiRsDumpMemoryFlags), "Resource Type", (void *) "Memory Range"},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.WriteProtect), "Write Protect", AcpiGbl_RwDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.Caching), "Caching", AcpiGbl_MemDecode},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.RangeType), "Range Type", AcpiGbl_MtpDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Mem.Translation), "Translation", AcpiGbl_TtpDecode}
+};
+
+ACPI_RSDUMP_INFO AcpiRsDumpIoFlags[4] =
+{
+ {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE (AcpiRsDumpIoFlags), "Resource Type", (void *) "I/O Range"},
+ {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.RangeType), "Range Type", AcpiGbl_RngDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.Translation), "Translation", AcpiGbl_TtpDecode},
+ {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (Address.Info.Io.TranslationType), "Translation Type", AcpiGbl_TrsDecode}
+};
+
+
+/*
+ * Table used to dump _PRT contents
+ */
+ACPI_RSDUMP_INFO AcpiRsDumpPrt[5] =
+{
+ {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE (AcpiRsDumpPrt), NULL, NULL},
+ {ACPI_RSD_UINT64, ACPI_PRT_OFFSET (Address), "Address", NULL},
+ {ACPI_RSD_UINT32, ACPI_PRT_OFFSET (Pin), "Pin", NULL},
+ {ACPI_RSD_STRING, ACPI_PRT_OFFSET (Source[0]), "Source", NULL},
+ {ACPI_RSD_UINT32, ACPI_PRT_OFFSET (SourceIndex), "Source Index", NULL}
+};
+
+#endif
diff --git a/usr/src/uts/intel/io/acpica/resources/rsinfo.c b/usr/src/uts/intel/io/acpica/resources/rsinfo.c
index 5753667806..15f6105c80 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsinfo.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsinfo.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSINFO_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -82,7 +80,10 @@ ACPI_RSCONVERT_INFO *AcpiGbl_SetResourceDispatch[] =
AcpiRsConvertAddress64, /* 0x0D, ACPI_RESOURCE_TYPE_ADDRESS64 */
AcpiRsConvertExtAddress64, /* 0x0E, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
AcpiRsConvertExtIrq, /* 0x0F, ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
- AcpiRsConvertGenericReg /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ AcpiRsConvertGenericReg, /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ AcpiRsConvertGpio, /* 0x11, ACPI_RESOURCE_TYPE_GPIO */
+ AcpiRsConvertFixedDma, /* 0x12, ACPI_RESOURCE_TYPE_FIXED_DMA */
+ NULL, /* 0x13, ACPI_RESOURCE_TYPE_SERIAL_BUS - Use subtype table below */
};
/* Dispatch tables for AML-to-resource (Get Resource) conversion functions */
@@ -101,7 +102,7 @@ ACPI_RSCONVERT_INFO *AcpiGbl_GetResourceDispatch[] =
AcpiRsConvertEndDpf, /* 0x07, ACPI_RESOURCE_NAME_END_DEPENDENT */
AcpiRsConvertIo, /* 0x08, ACPI_RESOURCE_NAME_IO */
AcpiRsConvertFixedIo, /* 0x09, ACPI_RESOURCE_NAME_FIXED_IO */
- NULL, /* 0x0A, Reserved */
+ AcpiRsConvertFixedDma, /* 0x0A, ACPI_RESOURCE_NAME_FIXED_DMA */
NULL, /* 0x0B, Reserved */
NULL, /* 0x0C, Reserved */
NULL, /* 0x0D, Reserved */
@@ -121,11 +122,24 @@ ACPI_RSCONVERT_INFO *AcpiGbl_GetResourceDispatch[] =
AcpiRsConvertAddress16, /* 0x08, ACPI_RESOURCE_NAME_ADDRESS16 */
AcpiRsConvertExtIrq, /* 0x09, ACPI_RESOURCE_NAME_EXTENDED_IRQ */
AcpiRsConvertAddress64, /* 0x0A, ACPI_RESOURCE_NAME_ADDRESS64 */
- AcpiRsConvertExtAddress64 /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */
+ AcpiRsConvertExtAddress64, /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */
+ AcpiRsConvertGpio, /* 0x0C, ACPI_RESOURCE_NAME_GPIO */
+ NULL, /* 0x0D, Reserved */
+ NULL, /* 0x0E, ACPI_RESOURCE_NAME_SERIAL_BUS - Use subtype table below */
};
+/* Subtype table for SerialBus -- I2C, SPI, and UART */
+
+ACPI_RSCONVERT_INFO *AcpiGbl_ConvertResourceSerialBusDispatch[] =
+{
+ NULL,
+ AcpiRsConvertI2cSerialBus,
+ AcpiRsConvertSpiSerialBus,
+ AcpiRsConvertUartSerialBus,
+};
-#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
+
+#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER)
/* Dispatch table for resource dump functions */
@@ -148,6 +162,17 @@ ACPI_RSDUMP_INFO *AcpiGbl_DumpResourceDispatch[] =
AcpiRsDumpExtAddress64, /* ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
AcpiRsDumpExtIrq, /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
AcpiRsDumpGenericReg, /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ AcpiRsDumpGpio, /* ACPI_RESOURCE_TYPE_GPIO */
+ AcpiRsDumpFixedDma, /* ACPI_RESOURCE_TYPE_FIXED_DMA */
+ NULL, /* ACPI_RESOURCE_TYPE_SERIAL_BUS */
+};
+
+ACPI_RSDUMP_INFO *AcpiGbl_DumpSerialBusDispatch[] =
+{
+ NULL,
+ AcpiRsDumpI2cSerialBus, /* AML_RESOURCE_I2C_BUS_TYPE */
+ AcpiRsDumpSpiSerialBus, /* AML_RESOURCE_SPI_BUS_TYPE */
+ AcpiRsDumpUartSerialBus, /* AML_RESOURCE_UART_BUS_TYPE */
};
#endif
@@ -175,7 +200,10 @@ const UINT8 AcpiGbl_AmlResourceSizes[] =
sizeof (AML_RESOURCE_ADDRESS64), /* ACPI_RESOURCE_TYPE_ADDRESS64 */
sizeof (AML_RESOURCE_EXTENDED_ADDRESS64),/*ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */
sizeof (AML_RESOURCE_EXTENDED_IRQ), /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */
- sizeof (AML_RESOURCE_GENERIC_REGISTER) /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ sizeof (AML_RESOURCE_GENERIC_REGISTER), /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */
+ sizeof (AML_RESOURCE_GPIO), /* ACPI_RESOURCE_TYPE_GPIO */
+ sizeof (AML_RESOURCE_FIXED_DMA), /* ACPI_RESOURCE_TYPE_FIXED_DMA */
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS), /* ACPI_RESOURCE_TYPE_SERIAL_BUS */
};
@@ -193,7 +221,7 @@ const UINT8 AcpiGbl_ResourceStructSizes[] =
ACPI_RS_SIZE_MIN,
ACPI_RS_SIZE (ACPI_RESOURCE_IO),
ACPI_RS_SIZE (ACPI_RESOURCE_FIXED_IO),
- 0,
+ ACPI_RS_SIZE (ACPI_RESOURCE_FIXED_DMA),
0,
0,
0,
@@ -213,6 +241,23 @@ const UINT8 AcpiGbl_ResourceStructSizes[] =
ACPI_RS_SIZE (ACPI_RESOURCE_ADDRESS16),
ACPI_RS_SIZE (ACPI_RESOURCE_EXTENDED_IRQ),
ACPI_RS_SIZE (ACPI_RESOURCE_ADDRESS64),
- ACPI_RS_SIZE (ACPI_RESOURCE_EXTENDED_ADDRESS64)
+ ACPI_RS_SIZE (ACPI_RESOURCE_EXTENDED_ADDRESS64),
+ ACPI_RS_SIZE (ACPI_RESOURCE_GPIO),
+ ACPI_RS_SIZE (ACPI_RESOURCE_COMMON_SERIALBUS)
+};
+
+const UINT8 AcpiGbl_AmlResourceSerialBusSizes[] =
+{
+ 0,
+ sizeof (AML_RESOURCE_I2C_SERIALBUS),
+ sizeof (AML_RESOURCE_SPI_SERIALBUS),
+ sizeof (AML_RESOURCE_UART_SERIALBUS),
};
+const UINT8 AcpiGbl_ResourceStructSerialBusSizes[] =
+{
+ 0,
+ ACPI_RS_SIZE (ACPI_RESOURCE_I2C_SERIALBUS),
+ ACPI_RS_SIZE (ACPI_RESOURCE_SPI_SERIALBUS),
+ ACPI_RS_SIZE (ACPI_RESOURCE_UART_SERIALBUS),
+};
diff --git a/usr/src/uts/intel/io/acpica/resources/rsio.c b/usr/src/uts/intel/io/acpica/resources/rsio.c
index 2d0f9f7baa..442a7b8b3a 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsio.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsio.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSIO_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -300,5 +298,3 @@ ACPI_RSCONVERT_INFO AcpiRsSetStartDpf[10] =
{ACPI_RSC_LENGTH, 0, 0, sizeof (AML_RESOURCE_START_DEPENDENT_NOPRIO)}
};
-
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsirq.c b/usr/src/uts/intel/io/acpica/resources/rsirq.c
index ff952f96ff..d094a9bfd9 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsirq.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsirq.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSIRQ_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -57,7 +55,7 @@
*
******************************************************************************/
-ACPI_RSCONVERT_INFO AcpiRsGetIrq[8] =
+ACPI_RSCONVERT_INFO AcpiRsGetIrq[9] =
{
{ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_IRQ,
ACPI_RS_SIZE (ACPI_RESOURCE_IRQ),
@@ -85,7 +83,7 @@ ACPI_RSCONVERT_INFO AcpiRsGetIrq[8] =
{ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_AML_LENGTH, 0, 3},
- /* Get flags: Triggering[0], Polarity[3], Sharing[4] */
+ /* Get flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */
{ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Irq.Triggering),
AML_OFFSET (Irq.Flags),
@@ -97,7 +95,11 @@ ACPI_RSCONVERT_INFO AcpiRsGetIrq[8] =
{ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Irq.Sharable),
AML_OFFSET (Irq.Flags),
- 4}
+ 4},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Irq.WakeCapable),
+ AML_OFFSET (Irq.Flags),
+ 5}
};
@@ -107,7 +109,7 @@ ACPI_RSCONVERT_INFO AcpiRsGetIrq[8] =
*
******************************************************************************/
-ACPI_RSCONVERT_INFO AcpiRsSetIrq[13] =
+ACPI_RSCONVERT_INFO AcpiRsSetIrq[14] =
{
/* Start with a default descriptor of length 3 */
@@ -121,7 +123,7 @@ ACPI_RSCONVERT_INFO AcpiRsSetIrq[13] =
AML_OFFSET (Irq.IrqMask),
ACPI_RS_OFFSET (Data.Irq.InterruptCount)},
- /* Set the flags byte */
+ /* Set flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */
{ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Irq.Triggering),
AML_OFFSET (Irq.Flags),
@@ -135,6 +137,10 @@ ACPI_RSCONVERT_INFO AcpiRsSetIrq[13] =
AML_OFFSET (Irq.Flags),
4},
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Irq.WakeCapable),
+ AML_OFFSET (Irq.Flags),
+ 5},
+
/*
* All done if the output descriptor length is required to be 3
* (i.e., optimization to 2 bytes cannot be attempted)
@@ -189,7 +195,7 @@ ACPI_RSCONVERT_INFO AcpiRsSetIrq[13] =
*
******************************************************************************/
-ACPI_RSCONVERT_INFO AcpiRsConvertExtIrq[9] =
+ACPI_RSCONVERT_INFO AcpiRsConvertExtIrq[10] =
{
{ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_EXTENDED_IRQ,
ACPI_RS_SIZE (ACPI_RESOURCE_EXTENDED_IRQ),
@@ -199,8 +205,10 @@ ACPI_RSCONVERT_INFO AcpiRsConvertExtIrq[9] =
sizeof (AML_RESOURCE_EXTENDED_IRQ),
0},
- /* Flag bits */
-
+ /*
+ * Flags: Producer/Consumer[0], Triggering[1], Polarity[2],
+ * Sharing[3], Wake[4]
+ */
{ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.ExtendedIrq.ProducerConsumer),
AML_OFFSET (ExtendedIrq.Flags),
0},
@@ -217,6 +225,10 @@ ACPI_RSCONVERT_INFO AcpiRsConvertExtIrq[9] =
AML_OFFSET (ExtendedIrq.Flags),
3},
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.ExtendedIrq.WakeCapable),
+ AML_OFFSET (ExtendedIrq.Flags),
+ 4},
+
/* IRQ Table length (Byte4) */
{ACPI_RSC_COUNT, ACPI_RS_OFFSET (Data.ExtendedIrq.InterruptCount),
@@ -274,3 +286,33 @@ ACPI_RSCONVERT_INFO AcpiRsConvertDma[6] =
ACPI_RS_OFFSET (Data.Dma.ChannelCount)}
};
+
+/*******************************************************************************
+ *
+ * AcpiRsConvertFixedDma
+ *
+ ******************************************************************************/
+
+ACPI_RSCONVERT_INFO AcpiRsConvertFixedDma[4] =
+{
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_DMA,
+ ACPI_RS_SIZE (ACPI_RESOURCE_FIXED_DMA),
+ ACPI_RSC_TABLE_SIZE (AcpiRsConvertFixedDma)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_DMA,
+ sizeof (AML_RESOURCE_FIXED_DMA),
+ 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * RequestLines
+ * Channels
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.FixedDma.RequestLines),
+ AML_OFFSET (FixedDma.RequestLines),
+ 2},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.FixedDma.Width),
+ AML_OFFSET (FixedDma.Width),
+ 1},
+};
diff --git a/usr/src/uts/intel/io/acpica/resources/rslist.c b/usr/src/uts/intel/io/acpica/resources/rslist.c
index c64065f2ca..0f7a496031 100644
--- a/usr/src/uts/intel/io/acpica/resources/rslist.c
+++ b/usr/src/uts/intel/io/acpica/resources/rslist.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSLIST_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -72,11 +70,13 @@ AcpiRsConvertAmlToResources (
UINT32 Length,
UINT32 Offset,
UINT8 ResourceIndex,
- void *Context)
+ void **Context)
{
ACPI_RESOURCE **ResourcePtr = ACPI_CAST_INDIRECT_PTR (
ACPI_RESOURCE, Context);
ACPI_RESOURCE *Resource;
+ AML_RESOURCE *AmlResource;
+ ACPI_RSCONVERT_INFO *ConversionTable;
ACPI_STATUS Status;
@@ -94,11 +94,43 @@ AcpiRsConvertAmlToResources (
"Misaligned resource pointer %p", Resource));
}
- /* Convert the AML byte stream resource to a local resource struct */
+ /* Get the appropriate conversion info table */
+
+ AmlResource = ACPI_CAST_PTR (AML_RESOURCE, Aml);
+
+ if (AcpiUtGetResourceType (Aml) ==
+ ACPI_RESOURCE_NAME_SERIAL_BUS)
+ {
+ if (AmlResource->CommonSerialBus.Type >
+ AML_RESOURCE_MAX_SERIALBUSTYPE)
+ {
+ ConversionTable = NULL;
+ }
+ else
+ {
+ /* This is an I2C, SPI, or UART SerialBus descriptor */
+
+ ConversionTable = AcpiGbl_ConvertResourceSerialBusDispatch [
+ AmlResource->CommonSerialBus.Type];
+ }
+ }
+ else
+ {
+ ConversionTable = AcpiGbl_GetResourceDispatch[ResourceIndex];
+ }
+
+ if (!ConversionTable)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ ResourceIndex));
+ return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE);
+ }
+
+ /* Convert the AML byte stream resource to a local resource struct */
Status = AcpiRsConvertAmlToResource (
- Resource, ACPI_CAST_PTR (AML_RESOURCE, Aml),
- AcpiGbl_GetResourceDispatch[ResourceIndex]);
+ Resource, AmlResource, ConversionTable);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
@@ -113,7 +145,7 @@ AcpiRsConvertAmlToResources (
/* Point to the next structure in the output buffer */
- *ResourcePtr = ACPI_ADD_PTR (void, Resource, Resource->Length);
+ *ResourcePtr = ACPI_NEXT_RESOURCE (Resource);
return_ACPI_STATUS (AE_OK);
}
@@ -145,6 +177,7 @@ AcpiRsConvertResourcesToAml (
{
UINT8 *Aml = OutputBuffer;
UINT8 *EndAml = OutputBuffer + AmlSizeNeeded;
+ ACPI_RSCONVERT_INFO *ConversionTable;
ACPI_STATUS Status;
@@ -165,11 +198,47 @@ AcpiRsConvertResourcesToAml (
return_ACPI_STATUS (AE_BAD_DATA);
}
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!Resource->Length)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid zero length descriptor in resource list\n"));
+ return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
/* Perform the conversion */
+ if (Resource->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS)
+ {
+ if (Resource->Data.CommonSerialBus.Type >
+ AML_RESOURCE_MAX_SERIALBUSTYPE)
+ {
+ ConversionTable = NULL;
+ }
+ else
+ {
+ /* This is an I2C, SPI, or UART SerialBus descriptor */
+
+ ConversionTable = AcpiGbl_ConvertResourceSerialBusDispatch[
+ Resource->Data.CommonSerialBus.Type];
+ }
+ }
+ else
+ {
+ ConversionTable = AcpiGbl_SetResourceDispatch[Resource->Type];
+ }
+
+ if (!ConversionTable)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ Resource->Type));
+ return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE);
+ }
+
Status = AcpiRsConvertResourceToAml (Resource,
- ACPI_CAST_PTR (AML_RESOURCE, Aml),
- AcpiGbl_SetResourceDispatch[Resource->Type]);
+ ACPI_CAST_PTR (AML_RESOURCE, Aml), ConversionTable);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
@@ -181,7 +250,7 @@ AcpiRsConvertResourcesToAml (
/* Perform final sanity check on the new AML resource descriptor */
Status = AcpiUtValidateResource (
- ACPI_CAST_PTR (AML_RESOURCE, Aml), NULL);
+ NULL, ACPI_CAST_PTR (AML_RESOURCE, Aml), NULL);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -204,11 +273,10 @@ AcpiRsConvertResourcesToAml (
/* Point to the next input resource descriptor */
- Resource = ACPI_ADD_PTR (ACPI_RESOURCE, Resource, Resource->Length);
+ Resource = ACPI_NEXT_RESOURCE (Resource);
}
/* Completed buffer, but did not find an EndTag resource descriptor */
return_ACPI_STATUS (AE_AML_NO_RESOURCE_END_TAG);
}
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsmemory.c b/usr/src/uts/intel/io/acpica/resources/rsmemory.c
index 06262fa64b..90e58fc20e 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsmemory.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsmemory.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSMEMORY_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -247,5 +245,3 @@ ACPI_RSCONVERT_INFO AcpiRsSetVendor[7] =
sizeof (AML_RESOURCE_LARGE_HEADER),
0}
};
-
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsmisc.c b/usr/src/uts/intel/io/acpica/resources/rsmisc.c
index 45415faacc..73019a9b6d 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsmisc.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsmisc.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __RSMISC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acresrc.h"
@@ -94,6 +92,11 @@ AcpiRsConvertAmlToResource (
ACPI_FUNCTION_TRACE (RsConvertAmlToResource);
+ if (!Info)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
if (((ACPI_SIZE) Resource) & 0x3)
{
/* Each internal resource struct is expected to be 32-bit aligned */
@@ -112,14 +115,13 @@ AcpiRsConvertAmlToResource (
* table length (# of table entries)
*/
Count = INIT_TABLE_LENGTH (Info);
-
while (Count)
{
/*
* Source is the external AML byte stream buffer,
* destination is the internal resource descriptor
*/
- Source = ACPI_ADD_PTR (void, Aml, Info->AmlOffset);
+ Source = ACPI_ADD_PTR (void, Aml, Info->AmlOffset);
Destination = ACPI_ADD_PTR (void, Resource, Info->ResourceOffset);
switch (Info->Opcode)
@@ -128,66 +130,128 @@ AcpiRsConvertAmlToResource (
/*
* Get the resource type and the initial (minimum) length
*/
- ACPI_MEMSET (Resource, 0, INIT_RESOURCE_LENGTH (Info));
+ memset (Resource, 0, INIT_RESOURCE_LENGTH (Info));
Resource->Type = INIT_RESOURCE_TYPE (Info);
Resource->Length = INIT_RESOURCE_LENGTH (Info);
break;
-
case ACPI_RSC_INITSET:
break;
-
case ACPI_RSC_FLAGINIT:
FlagsMode = TRUE;
break;
-
case ACPI_RSC_1BITFLAG:
/*
* Mask and shift the flag bit
*/
- ACPI_SET8 (Destination) = (UINT8)
- ((ACPI_GET8 (Source) >> Info->Value) & 0x01);
+ ACPI_SET8 (Destination,
+ ((ACPI_GET8 (Source) >> Info->Value) & 0x01));
break;
-
case ACPI_RSC_2BITFLAG:
/*
* Mask and shift the flag bits
*/
- ACPI_SET8 (Destination) = (UINT8)
- ((ACPI_GET8 (Source) >> Info->Value) & 0x03);
+ ACPI_SET8 (Destination,
+ ((ACPI_GET8 (Source) >> Info->Value) & 0x03));
break;
+ case ACPI_RSC_3BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET8 (Destination,
+ ((ACPI_GET8 (Source) >> Info->Value) & 0x07));
+ break;
case ACPI_RSC_COUNT:
ItemCount = ACPI_GET8 (Source);
- ACPI_SET8 (Destination) = (UINT8) ItemCount;
+ ACPI_SET8 (Destination, ItemCount);
Resource->Length = Resource->Length +
(Info->Value * (ItemCount - 1));
break;
-
case ACPI_RSC_COUNT16:
ItemCount = AmlResourceLength;
- ACPI_SET16 (Destination) = ItemCount;
+ ACPI_SET16 (Destination, ItemCount);
Resource->Length = Resource->Length +
(Info->Value * (ItemCount - 1));
break;
+ case ACPI_RSC_COUNT_GPIO_PIN:
+
+ Target = ACPI_ADD_PTR (void, Aml, Info->Value);
+ ItemCount = ACPI_GET16 (Target) - ACPI_GET16 (Source);
+
+ Resource->Length = Resource->Length + ItemCount;
+ ItemCount = ItemCount / 2;
+ ACPI_SET16 (Destination, ItemCount);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_VEN:
+
+ ItemCount = ACPI_GET8 (Source);
+ ACPI_SET8 (Destination, ItemCount);
+
+ Resource->Length = Resource->Length + (Info->Value * ItemCount);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_RES:
+ /*
+ * Vendor data is optional (length/offset may both be zero)
+ * Examine vendor data length field first
+ */
+ Target = ACPI_ADD_PTR (void, Aml, (Info->Value + 2));
+ if (ACPI_GET16 (Target))
+ {
+ /* Use vendor offset to get resource source length */
+
+ Target = ACPI_ADD_PTR (void, Aml, Info->Value);
+ ItemCount = ACPI_GET16 (Target) - ACPI_GET16 (Source);
+ }
+ else
+ {
+ /* No vendor data to worry about */
+
+ ItemCount = Aml->LargeHeader.ResourceLength +
+ sizeof (AML_RESOURCE_LARGE_HEADER) -
+ ACPI_GET16 (Source);
+ }
+
+ Resource->Length = Resource->Length + ItemCount;
+ ACPI_SET16 (Destination, ItemCount);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_VEN:
+
+ ItemCount = ACPI_GET16 (Source) - Info->Value;
+
+ Resource->Length = Resource->Length + ItemCount;
+ ACPI_SET16 (Destination, ItemCount);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_RES:
+
+ ItemCount = (AmlResourceLength +
+ sizeof (AML_RESOURCE_LARGE_HEADER)) -
+ ACPI_GET16 (Source) - Info->Value;
+
+ Resource->Length = Resource->Length + ItemCount;
+ ACPI_SET16 (Destination, ItemCount);
+ break;
case ACPI_RSC_LENGTH:
Resource->Length = Resource->Length + Info->Value;
break;
-
case ACPI_RSC_MOVE8:
case ACPI_RSC_MOVE16:
case ACPI_RSC_MOVE32:
@@ -203,20 +267,74 @@ AcpiRsConvertAmlToResource (
AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
break;
+ case ACPI_RSC_MOVE_GPIO_PIN:
- case ACPI_RSC_SET8:
+ /* Generate and set the PIN data pointer */
+
+ Target = (char *) ACPI_ADD_PTR (void, Resource,
+ (Resource->Length - ItemCount * 2));
+ *(UINT16 **) Destination = ACPI_CAST_PTR (UINT16, Target);
+
+ /* Copy the PIN data */
+
+ Source = ACPI_ADD_PTR (void, Aml, ACPI_GET16 (Source));
+ AcpiRsMoveData (Target, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_RES:
+
+ /* Generate and set the ResourceSource string pointer */
+
+ Target = (char *) ACPI_ADD_PTR (void, Resource,
+ (Resource->Length - ItemCount));
+ *(UINT8 **) Destination = ACPI_CAST_PTR (UINT8, Target);
+
+ /* Copy the ResourceSource string */
+
+ Source = ACPI_ADD_PTR (void, Aml, ACPI_GET16 (Source));
+ AcpiRsMoveData (Target, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+
+ /* Generate and set the Vendor Data pointer */
+
+ Target = (char *) ACPI_ADD_PTR (void, Resource,
+ (Resource->Length - ItemCount));
+ *(UINT8 **) Destination = ACPI_CAST_PTR (UINT8, Target);
+
+ /* Copy the Vendor Data */
- ACPI_MEMSET (Destination, Info->AmlOffset, Info->Value);
+ Source = ACPI_ADD_PTR (void, Aml, Info->Value);
+ AcpiRsMoveData (Target, Source, ItemCount, Info->Opcode);
break;
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ /* Generate and set the ResourceSource string pointer */
+
+ Target = (char *) ACPI_ADD_PTR (void, Resource,
+ (Resource->Length - ItemCount));
+ *(UINT8 **) Destination = ACPI_CAST_PTR (UINT8, Target);
+
+ /* Copy the ResourceSource string */
+
+ Source = ACPI_ADD_PTR (
+ void, Aml, (ACPI_GET16 (Source) + Info->Value));
+ AcpiRsMoveData (Target, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_SET8:
+
+ memset (Destination, Info->AmlOffset, Info->Value);
+ break;
case ACPI_RSC_DATA8:
Target = ACPI_ADD_PTR (char, Resource, Info->Value);
- ACPI_MEMCPY (Destination, Source, ACPI_GET16 (Target));
+ memcpy (Destination, Source, ACPI_GET16 (Target));
break;
-
case ACPI_RSC_ADDRESS:
/*
* Common handler for address descriptor flags
@@ -227,7 +345,6 @@ AcpiRsConvertAmlToResource (
}
break;
-
case ACPI_RSC_SOURCE:
/*
* Optional ResourceSource (Index and String)
@@ -237,21 +354,20 @@ AcpiRsConvertAmlToResource (
Destination, Aml, NULL);
break;
-
case ACPI_RSC_SOURCEX:
/*
* Optional ResourceSource (Index and String). This is the more
* complicated case used by the Interrupt() macro
*/
- Target = ACPI_ADD_PTR (char, Resource, Info->AmlOffset + (ItemCount * 4));
+ Target = ACPI_ADD_PTR (char, Resource,
+ Info->AmlOffset + (ItemCount * 4));
Resource->Length +=
- AcpiRsGetResourceSource (AmlResourceLength,
- (ACPI_RS_LENGTH) (((ItemCount - 1) * sizeof (UINT32)) + Info->Value),
+ AcpiRsGetResourceSource (AmlResourceLength, (ACPI_RS_LENGTH)
+ (((ItemCount - 1) * sizeof (UINT32)) + Info->Value),
Destination, Aml, Target);
break;
-
case ACPI_RSC_BITMASK:
/*
* 8-bit encoded bitmask (DMA macro)
@@ -263,10 +379,9 @@ AcpiRsConvertAmlToResource (
}
Target = ACPI_ADD_PTR (char, Resource, Info->Value);
- ACPI_SET8 (Target) = (UINT8) ItemCount;
+ ACPI_SET8 (Target, ItemCount);
break;
-
case ACPI_RSC_BITMASK16:
/*
* 16-bit encoded bitmask (IRQ macro)
@@ -280,10 +395,9 @@ AcpiRsConvertAmlToResource (
}
Target = ACPI_ADD_PTR (char, Resource, Info->Value);
- ACPI_SET8 (Target) = (UINT8) ItemCount;
+ ACPI_SET8 (Target, ItemCount);
break;
-
case ACPI_RSC_EXIT_NE:
/*
* Control - Exit conversion if not equal
@@ -291,6 +405,7 @@ AcpiRsConvertAmlToResource (
switch (Info->ResourceOffset)
{
case ACPI_RSC_COMPARE_AML_LENGTH:
+
if (AmlResourceLength != Info->Value)
{
goto Exit;
@@ -298,6 +413,7 @@ AcpiRsConvertAmlToResource (
break;
case ACPI_RSC_COMPARE_VALUE:
+
if (ACPI_GET8 (Source) != Info->Value)
{
goto Exit;
@@ -311,7 +427,6 @@ AcpiRsConvertAmlToResource (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Invalid conversion opcode"));
@@ -327,7 +442,8 @@ Exit:
{
/* Round the resource struct length up to the next boundary (32 or 64) */
- Resource->Length = (UINT32) ACPI_ROUND_UP_TO_NATIVE_WORD (Resource->Length);
+ Resource->Length = (UINT32)
+ ACPI_ROUND_UP_TO_NATIVE_WORD (Resource->Length);
}
return_ACPI_STATUS (AE_OK);
}
@@ -356,6 +472,7 @@ AcpiRsConvertResourceToAml (
{
void *Source = NULL;
void *Destination;
+ char *Target;
ACPI_RSDESC_SIZE AmlLength = 0;
UINT8 Count;
UINT16 Temp16 = 0;
@@ -365,6 +482,11 @@ AcpiRsConvertResourceToAml (
ACPI_FUNCTION_TRACE (RsConvertResourceToAml);
+ if (!Info)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
/*
* First table entry must be ACPI_RSC_INITxxx and must contain the
* table length (# of table entries)
@@ -377,58 +499,62 @@ AcpiRsConvertResourceToAml (
* Source is the internal resource descriptor,
* destination is the external AML byte stream buffer
*/
- Source = ACPI_ADD_PTR (void, Resource, Info->ResourceOffset);
+ Source = ACPI_ADD_PTR (void, Resource, Info->ResourceOffset);
Destination = ACPI_ADD_PTR (void, Aml, Info->AmlOffset);
switch (Info->Opcode)
{
case ACPI_RSC_INITSET:
- ACPI_MEMSET (Aml, 0, INIT_RESOURCE_LENGTH (Info));
+ memset (Aml, 0, INIT_RESOURCE_LENGTH (Info));
AmlLength = INIT_RESOURCE_LENGTH (Info);
- AcpiRsSetResourceHeader (INIT_RESOURCE_TYPE (Info), AmlLength, Aml);
+ AcpiRsSetResourceHeader (
+ INIT_RESOURCE_TYPE (Info), AmlLength, Aml);
break;
-
case ACPI_RSC_INITGET:
break;
-
case ACPI_RSC_FLAGINIT:
/*
* Clear the flag byte
*/
- ACPI_SET8 (Destination) = 0;
+ ACPI_SET8 (Destination, 0);
break;
-
case ACPI_RSC_1BITFLAG:
/*
* Mask and shift the flag bit
*/
- ACPI_SET8 (Destination) |= (UINT8)
- ((ACPI_GET8 (Source) & 0x01) << Info->Value);
+ ACPI_SET_BIT (*ACPI_CAST8 (Destination), (UINT8)
+ ((ACPI_GET8 (Source) & 0x01) << Info->Value));
break;
-
case ACPI_RSC_2BITFLAG:
/*
* Mask and shift the flag bits
*/
- ACPI_SET8 (Destination) |= (UINT8)
- ((ACPI_GET8 (Source) & 0x03) << Info->Value);
+ ACPI_SET_BIT (*ACPI_CAST8 (Destination), (UINT8)
+ ((ACPI_GET8 (Source) & 0x03) << Info->Value));
break;
+ case ACPI_RSC_3BITFLAG:
+ /*
+ * Mask and shift the flag bits
+ */
+ ACPI_SET_BIT (*ACPI_CAST8 (Destination), (UINT8)
+ ((ACPI_GET8 (Source) & 0x07) << Info->Value));
+ break;
case ACPI_RSC_COUNT:
ItemCount = ACPI_GET8 (Source);
- ACPI_SET8 (Destination) = (UINT8) ItemCount;
+ ACPI_SET8 (Destination, ItemCount);
- AmlLength = (UINT16) (AmlLength + (Info->Value * (ItemCount - 1)));
+ AmlLength = (UINT16)
+ (AmlLength + (Info->Value * (ItemCount - 1)));
break;
-
case ACPI_RSC_COUNT16:
ItemCount = ACPI_GET16 (Source);
@@ -436,13 +562,69 @@ AcpiRsConvertResourceToAml (
AcpiRsSetResourceLength (AmlLength, Aml);
break;
+ case ACPI_RSC_COUNT_GPIO_PIN:
+
+ ItemCount = ACPI_GET16 (Source);
+ ACPI_SET16 (Destination, AmlLength);
+
+ AmlLength = (UINT16) (AmlLength + ItemCount * 2);
+ Target = ACPI_ADD_PTR (void, Aml, Info->Value);
+ ACPI_SET16 (Target, AmlLength);
+ AcpiRsSetResourceLength (AmlLength, Aml);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_VEN:
+
+ ItemCount = ACPI_GET16 (Source);
+ ACPI_SET16 (Destination, ItemCount);
+
+ AmlLength = (UINT16) (
+ AmlLength + (Info->Value * ItemCount));
+ AcpiRsSetResourceLength (AmlLength, Aml);
+ break;
+
+ case ACPI_RSC_COUNT_GPIO_RES:
+
+ /* Set resource source string length */
+
+ ItemCount = ACPI_GET16 (Source);
+ ACPI_SET16 (Destination, AmlLength);
+
+ /* Compute offset for the Vendor Data */
+
+ AmlLength = (UINT16) (AmlLength + ItemCount);
+ Target = ACPI_ADD_PTR (void, Aml, Info->Value);
+
+ /* Set vendor offset only if there is vendor data */
+
+ if (Resource->Data.Gpio.VendorLength)
+ {
+ ACPI_SET16 (Target, AmlLength);
+ }
+
+ AcpiRsSetResourceLength (AmlLength, Aml);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_VEN:
+
+ ItemCount = ACPI_GET16 (Source);
+ ACPI_SET16 (Destination, ItemCount + Info->Value);
+ AmlLength = (UINT16) (AmlLength + ItemCount);
+ AcpiRsSetResourceLength (AmlLength, Aml);
+ break;
+
+ case ACPI_RSC_COUNT_SERIAL_RES:
+
+ ItemCount = ACPI_GET16 (Source);
+ AmlLength = (UINT16) (AmlLength + ItemCount);
+ AcpiRsSetResourceLength (AmlLength, Aml);
+ break;
case ACPI_RSC_LENGTH:
AcpiRsSetResourceLength (Info->Value, Aml);
break;
-
case ACPI_RSC_MOVE8:
case ACPI_RSC_MOVE16:
case ACPI_RSC_MOVE32:
@@ -455,6 +637,39 @@ AcpiRsConvertResourceToAml (
AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
break;
+ case ACPI_RSC_MOVE_GPIO_PIN:
+
+ Destination = (char *) ACPI_ADD_PTR (void, Aml,
+ ACPI_GET16 (Destination));
+ Source = * (UINT16 **) Source;
+ AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_MOVE_GPIO_RES:
+
+ /* Used for both ResourceSource string and VendorData */
+
+ Destination = (char *) ACPI_ADD_PTR (void, Aml,
+ ACPI_GET16 (Destination));
+ Source = * (UINT8 **) Source;
+ AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+
+ Destination = (char *) ACPI_ADD_PTR (void, Aml,
+ (AmlLength - ItemCount));
+ Source = * (UINT8 **) Source;
+ AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
+ break;
+
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ Destination = (char *) ACPI_ADD_PTR (void, Aml,
+ (AmlLength - ItemCount));
+ Source = * (UINT8 **) Source;
+ AcpiRsMoveData (Destination, Source, ItemCount, Info->Opcode);
+ break;
case ACPI_RSC_ADDRESS:
@@ -463,17 +678,15 @@ AcpiRsConvertResourceToAml (
AcpiRsSetAddressCommon (Aml, Resource);
break;
-
case ACPI_RSC_SOURCEX:
/*
* Optional ResourceSource (Index and String)
*/
AmlLength = AcpiRsSetResourceSource (
- Aml, (ACPI_RS_LENGTH) AmlLength, Source);
+ Aml, (ACPI_RS_LENGTH) AmlLength, Source);
AcpiRsSetResourceLength (AmlLength, Aml);
break;
-
case ACPI_RSC_SOURCE:
/*
* Optional ResourceSource (Index and String). This is the more
@@ -483,27 +696,24 @@ AcpiRsConvertResourceToAml (
AcpiRsSetResourceLength (AmlLength, Aml);
break;
-
case ACPI_RSC_BITMASK:
/*
* 8-bit encoded bitmask (DMA macro)
*/
- ACPI_SET8 (Destination) = (UINT8)
+ ACPI_SET8 (Destination,
AcpiRsEncodeBitmask (Source,
- *ACPI_ADD_PTR (UINT8, Resource, Info->Value));
+ *ACPI_ADD_PTR (UINT8, Resource, Info->Value)));
break;
-
case ACPI_RSC_BITMASK16:
/*
* 16-bit encoded bitmask (IRQ macro)
*/
- Temp16 = AcpiRsEncodeBitmask (Source,
- *ACPI_ADD_PTR (UINT8, Resource, Info->Value));
+ Temp16 = AcpiRsEncodeBitmask (
+ Source, *ACPI_ADD_PTR (UINT8, Resource, Info->Value));
ACPI_MOVE_16_TO_16 (Destination, &Temp16);
break;
-
case ACPI_RSC_EXIT_LE:
/*
* Control - Exit conversion if less than or equal
@@ -514,7 +724,6 @@ AcpiRsConvertResourceToAml (
}
break;
-
case ACPI_RSC_EXIT_NE:
/*
* Control - Exit conversion if not equal
@@ -524,7 +733,7 @@ AcpiRsConvertResourceToAml (
case ACPI_RSC_COMPARE_VALUE:
if (*ACPI_ADD_PTR (UINT8, Resource,
- COMPARE_TARGET (Info)) != COMPARE_VALUE (Info))
+ COMPARE_TARGET (Info)) != COMPARE_VALUE (Info))
{
goto Exit;
}
@@ -537,19 +746,17 @@ AcpiRsConvertResourceToAml (
}
break;
-
case ACPI_RSC_EXIT_EQ:
/*
* Control - Exit conversion if equal
*/
if (*ACPI_ADD_PTR (UINT8, Resource,
- COMPARE_TARGET (Info)) == COMPARE_VALUE (Info))
+ COMPARE_TARGET (Info)) == COMPARE_VALUE (Info))
{
goto Exit;
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Invalid conversion opcode"));
@@ -568,7 +775,8 @@ Exit:
#if 0
/* Previous resource validations */
- if (Aml->ExtAddress64.RevisionID != AML_RESOURCE_EXTENDED_ADDRESS_REVISION)
+ if (Aml->ExtAddress64.RevisionID !=
+ AML_RESOURCE_EXTENDED_ADDRESS_REVISION)
{
return_ACPI_STATUS (AE_SUPPORT);
}
@@ -607,5 +815,3 @@ Exit:
return_ACPI_STATUS (AE_BAD_DATA);
}
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsserial.c b/usr/src/uts/intel/io/acpica/resources/rsserial.c
new file mode 100644
index 0000000000..1eae4502f7
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/resources/rsserial.c
@@ -0,0 +1,439 @@
+/*******************************************************************************
+ *
+ * Module Name: rsserial - GPIO/SerialBus resource descriptors
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acresrc.h"
+
+#define _COMPONENT ACPI_RESOURCES
+ ACPI_MODULE_NAME ("rsserial")
+
+
+/*******************************************************************************
+ *
+ * AcpiRsConvertGpio
+ *
+ ******************************************************************************/
+
+ACPI_RSCONVERT_INFO AcpiRsConvertGpio[18] =
+{
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GPIO,
+ ACPI_RS_SIZE (ACPI_RESOURCE_GPIO),
+ ACPI_RSC_TABLE_SIZE (AcpiRsConvertGpio)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GPIO,
+ sizeof (AML_RESOURCE_GPIO),
+ 0},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * RevisionId
+ * ConnectionType
+ */
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.Gpio.RevisionId),
+ AML_OFFSET (Gpio.RevisionId),
+ 2},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Gpio.ProducerConsumer),
+ AML_OFFSET (Gpio.Flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Gpio.Sharable),
+ AML_OFFSET (Gpio.IntFlags),
+ 3},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Gpio.WakeCapable),
+ AML_OFFSET (Gpio.IntFlags),
+ 4},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET (Data.Gpio.IoRestriction),
+ AML_OFFSET (Gpio.IntFlags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.Gpio.Triggering),
+ AML_OFFSET (Gpio.IntFlags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET (Data.Gpio.Polarity),
+ AML_OFFSET (Gpio.IntFlags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.Gpio.PinConfig),
+ AML_OFFSET (Gpio.PinConfig),
+ 1},
+
+ /*
+ * These fields are contiguous in both the source and destination:
+ * DriveStrength
+ * DebounceTimeout
+ */
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.Gpio.DriveStrength),
+ AML_OFFSET (Gpio.DriveStrength),
+ 2},
+
+ /* Pin Table */
+
+ {ACPI_RSC_COUNT_GPIO_PIN, ACPI_RS_OFFSET (Data.Gpio.PinTableLength),
+ AML_OFFSET (Gpio.PinTableOffset),
+ AML_OFFSET (Gpio.ResSourceOffset)},
+
+ {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET (Data.Gpio.PinTable),
+ AML_OFFSET (Gpio.PinTableOffset),
+ 0},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.Gpio.ResourceSource.Index),
+ AML_OFFSET (Gpio.ResSourceIndex),
+ 1},
+
+ {ACPI_RSC_COUNT_GPIO_RES, ACPI_RS_OFFSET (Data.Gpio.ResourceSource.StringLength),
+ AML_OFFSET (Gpio.ResSourceOffset),
+ AML_OFFSET (Gpio.VendorOffset)},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET (Data.Gpio.ResourceSource.StringPtr),
+ AML_OFFSET (Gpio.ResSourceOffset),
+ 0},
+
+ /* Vendor Data */
+
+ {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET (Data.Gpio.VendorLength),
+ AML_OFFSET (Gpio.VendorLength),
+ 1},
+
+ {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET (Data.Gpio.VendorData),
+ AML_OFFSET (Gpio.VendorOffset),
+ 0},
+};
+
+
+/*******************************************************************************
+ *
+ * AcpiRsConvertI2cSerialBus
+ *
+ ******************************************************************************/
+
+ACPI_RSCONVERT_INFO AcpiRsConvertI2cSerialBus[17] =
+{
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE (ACPI_RESOURCE_I2C_SERIALBUS),
+ ACPI_RSC_TABLE_SIZE (AcpiRsConvertI2cSerialBus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof (AML_RESOURCE_I2C_SERIALBUS),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.RevisionId),
+ AML_OFFSET (CommonSerialBus.RevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.Type),
+ AML_OFFSET (CommonSerialBus.Type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.SlaveMode),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ProducerConsumer),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ConnectionSharing),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 2},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeRevisionId),
+ AML_OFFSET (CommonSerialBus.TypeRevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeDataLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ AML_RESOURCE_I2C_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorData),
+ 0,
+ sizeof (AML_RESOURCE_I2C_SERIALBUS)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.Index),
+ AML_OFFSET (CommonSerialBus.ResSourceIndex),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringPtr),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ /* I2C bus type specific */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.I2cSerialBus.AccessMode),
+ AML_OFFSET (I2cSerialBus.TypeSpecificFlags),
+ 0},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET (Data.I2cSerialBus.ConnectionSpeed),
+ AML_OFFSET (I2cSerialBus.ConnectionSpeed),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.I2cSerialBus.SlaveAddress),
+ AML_OFFSET (I2cSerialBus.SlaveAddress),
+ 1},
+};
+
+
+/*******************************************************************************
+ *
+ * AcpiRsConvertSpiSerialBus
+ *
+ ******************************************************************************/
+
+ACPI_RSCONVERT_INFO AcpiRsConvertSpiSerialBus[21] =
+{
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE (ACPI_RESOURCE_SPI_SERIALBUS),
+ ACPI_RSC_TABLE_SIZE (AcpiRsConvertSpiSerialBus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof (AML_RESOURCE_SPI_SERIALBUS),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.RevisionId),
+ AML_OFFSET (CommonSerialBus.RevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.Type),
+ AML_OFFSET (CommonSerialBus.Type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.SlaveMode),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ProducerConsumer),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ConnectionSharing),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 2},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeRevisionId),
+ AML_OFFSET (CommonSerialBus.TypeRevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeDataLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ AML_RESOURCE_SPI_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorData),
+ 0,
+ sizeof (AML_RESOURCE_SPI_SERIALBUS)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.Index),
+ AML_OFFSET (CommonSerialBus.ResSourceIndex),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringPtr),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ /* Spi bus type specific */
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.SpiSerialBus.WireMode),
+ AML_OFFSET (SpiSerialBus.TypeSpecificFlags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.SpiSerialBus.DevicePolarity),
+ AML_OFFSET (SpiSerialBus.TypeSpecificFlags),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.SpiSerialBus.DataBitLength),
+ AML_OFFSET (SpiSerialBus.DataBitLength),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.SpiSerialBus.ClockPhase),
+ AML_OFFSET (SpiSerialBus.ClockPhase),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.SpiSerialBus.ClockPolarity),
+ AML_OFFSET (SpiSerialBus.ClockPolarity),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.SpiSerialBus.DeviceSelection),
+ AML_OFFSET (SpiSerialBus.DeviceSelection),
+ 1},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET (Data.SpiSerialBus.ConnectionSpeed),
+ AML_OFFSET (SpiSerialBus.ConnectionSpeed),
+ 1},
+};
+
+
+/*******************************************************************************
+ *
+ * AcpiRsConvertUartSerialBus
+ *
+ ******************************************************************************/
+
+ACPI_RSCONVERT_INFO AcpiRsConvertUartSerialBus[23] =
+{
+ {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS,
+ ACPI_RS_SIZE (ACPI_RESOURCE_UART_SERIALBUS),
+ ACPI_RSC_TABLE_SIZE (AcpiRsConvertUartSerialBus)},
+
+ {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS,
+ sizeof (AML_RESOURCE_UART_SERIALBUS),
+ 0},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.RevisionId),
+ AML_OFFSET (CommonSerialBus.RevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.Type),
+ AML_OFFSET (CommonSerialBus.Type),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.SlaveMode),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 0},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ProducerConsumer),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 1},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.CommonSerialBus.ConnectionSharing),
+ AML_OFFSET (CommonSerialBus.Flags),
+ 2},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeRevisionId),
+ AML_OFFSET (CommonSerialBus.TypeRevisionId),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.CommonSerialBus.TypeDataLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ 1},
+
+ /* Vendor data */
+
+ {ACPI_RSC_COUNT_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ AML_RESOURCE_UART_MIN_DATA_LEN},
+
+ {ACPI_RSC_MOVE_SERIAL_VEN, ACPI_RS_OFFSET (Data.CommonSerialBus.VendorData),
+ 0,
+ sizeof (AML_RESOURCE_UART_SERIALBUS)},
+
+ /* Resource Source */
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.Index),
+ AML_OFFSET (CommonSerialBus.ResSourceIndex),
+ 1},
+
+ {ACPI_RSC_COUNT_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringLength),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ {ACPI_RSC_MOVE_SERIAL_RES, ACPI_RS_OFFSET (Data.CommonSerialBus.ResourceSource.StringPtr),
+ AML_OFFSET (CommonSerialBus.TypeDataLength),
+ sizeof (AML_RESOURCE_COMMON_SERIALBUS)},
+
+ /* Uart bus type specific */
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET (Data.UartSerialBus.FlowControl),
+ AML_OFFSET (UartSerialBus.TypeSpecificFlags),
+ 0},
+
+ {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET (Data.UartSerialBus.StopBits),
+ AML_OFFSET (UartSerialBus.TypeSpecificFlags),
+ 2},
+
+ {ACPI_RSC_3BITFLAG, ACPI_RS_OFFSET (Data.UartSerialBus.DataBits),
+ AML_OFFSET (UartSerialBus.TypeSpecificFlags),
+ 4},
+
+ {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET (Data.UartSerialBus.Endian),
+ AML_OFFSET (UartSerialBus.TypeSpecificFlags),
+ 7},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.UartSerialBus.Parity),
+ AML_OFFSET (UartSerialBus.Parity),
+ 1},
+
+ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET (Data.UartSerialBus.LinesEnabled),
+ AML_OFFSET (UartSerialBus.LinesEnabled),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.UartSerialBus.RxFifoSize),
+ AML_OFFSET (UartSerialBus.RxFifoSize),
+ 1},
+
+ {ACPI_RSC_MOVE16, ACPI_RS_OFFSET (Data.UartSerialBus.TxFifoSize),
+ AML_OFFSET (UartSerialBus.TxFifoSize),
+ 1},
+
+ {ACPI_RSC_MOVE32, ACPI_RS_OFFSET (Data.UartSerialBus.DefaultBaudRate),
+ AML_OFFSET (UartSerialBus.DefaultBaudRate),
+ 1},
+};
diff --git a/usr/src/uts/intel/io/acpica/resources/rsutils.c b/usr/src/uts/intel/io/acpica/resources/rsutils.c
index 400bbcd91f..cec4180bfd 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsutils.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsutils.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __RSUTILS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -173,30 +170,42 @@ AcpiRsMoveData (
* since there are no alignment or endian issues
*/
case ACPI_RSC_MOVE8:
- ACPI_MEMCPY (Destination, Source, ItemCount);
+ case ACPI_RSC_MOVE_GPIO_RES:
+ case ACPI_RSC_MOVE_SERIAL_VEN:
+ case ACPI_RSC_MOVE_SERIAL_RES:
+
+ memcpy (Destination, Source, ItemCount);
return;
/*
* 16-, 32-, and 64-bit cases must use the move macros that perform
- * endian conversion and/or accomodate hardware that cannot perform
+ * endian conversion and/or accommodate hardware that cannot perform
* misaligned memory transfers
*/
case ACPI_RSC_MOVE16:
- ACPI_MOVE_16_TO_16 (&ACPI_CAST_PTR (UINT16, Destination)[i],
- &ACPI_CAST_PTR (UINT16, Source)[i]);
+ case ACPI_RSC_MOVE_GPIO_PIN:
+
+ ACPI_MOVE_16_TO_16 (
+ &ACPI_CAST_PTR (UINT16, Destination)[i],
+ &ACPI_CAST_PTR (UINT16, Source)[i]);
break;
case ACPI_RSC_MOVE32:
- ACPI_MOVE_32_TO_32 (&ACPI_CAST_PTR (UINT32, Destination)[i],
- &ACPI_CAST_PTR (UINT32, Source)[i]);
+
+ ACPI_MOVE_32_TO_32 (
+ &ACPI_CAST_PTR (UINT32, Destination)[i],
+ &ACPI_CAST_PTR (UINT32, Source)[i]);
break;
case ACPI_RSC_MOVE64:
- ACPI_MOVE_64_TO_64 (&ACPI_CAST_PTR (UINT64, Destination)[i],
- &ACPI_CAST_PTR (UINT64, Source)[i]);
+
+ ACPI_MOVE_64_TO_64 (
+ &ACPI_CAST_PTR (UINT64, Destination)[i],
+ &ACPI_CAST_PTR (UINT64, Source)[i]);
break;
default:
+
return;
}
}
@@ -242,18 +251,18 @@ AcpiRsSetResourceLength (
{
/* Large descriptor -- bytes 1-2 contain the 16-bit length */
- ACPI_MOVE_16_TO_16 (&Aml->LargeHeader.ResourceLength, &ResourceLength);
+ ACPI_MOVE_16_TO_16 (
+ &Aml->LargeHeader.ResourceLength, &ResourceLength);
}
else
{
- /* Small descriptor -- bits 2:0 of byte 0 contain the length */
-
+ /*
+ * Small descriptor -- bits 2:0 of byte 0 contain the length
+ * Clear any existing length, preserving descriptor type bits
+ */
Aml->SmallHeader.DescriptorType = (UINT8)
-
- /* Clear any existing length, preserving descriptor type bits */
-
- ((Aml->SmallHeader.DescriptorType & ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK)
-
+ ((Aml->SmallHeader.DescriptorType &
+ ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK)
| ResourceLength);
}
}
@@ -372,8 +381,8 @@ AcpiRsGetResourceSource (
AmlResourceSource = ACPI_ADD_PTR (UINT8, Aml, MinimumLength);
/*
- * ResourceSource is present if the length of the descriptor is longer than
- * the minimum length.
+ * ResourceSource is present if the length of the descriptor is longer
+ * than the minimum length.
*
* Note: Some resource descriptors will have an additional null, so
* we add 1 to the minimum length.
@@ -391,8 +400,8 @@ AcpiRsGetResourceSource (
* String destination pointer is not specified; Set the String
* pointer to the end of the current ResourceSource structure.
*/
- ResourceSource->StringPtr = ACPI_ADD_PTR (char, ResourceSource,
- sizeof (ACPI_RESOURCE_SOURCE));
+ ResourceSource->StringPtr = ACPI_ADD_PTR (
+ char, ResourceSource, sizeof (ACPI_RESOURCE_SOURCE));
}
/*
@@ -402,15 +411,17 @@ AcpiRsGetResourceSource (
*
* Zero the entire area of the buffer.
*/
- TotalLength = (UINT32) ACPI_STRLEN (
+ TotalLength = (UINT32) strlen (
ACPI_CAST_PTR (char, &AmlResourceSource[1])) + 1;
+
TotalLength = (UINT32) ACPI_ROUND_UP_TO_NATIVE_WORD (TotalLength);
- ACPI_MEMSET (ResourceSource->StringPtr, 0, TotalLength);
+ memset (ResourceSource->StringPtr, 0, TotalLength);
/* Copy the ResourceSource string to the destination */
- ResourceSource->StringLength = AcpiRsStrcpy (ResourceSource->StringPtr,
+ ResourceSource->StringLength = AcpiRsStrcpy (
+ ResourceSource->StringPtr,
ACPI_CAST_PTR (char, &AmlResourceSource[1]));
return ((ACPI_RS_LENGTH) TotalLength);
@@ -471,14 +482,15 @@ AcpiRsSetResourceSource (
/* Copy the ResourceSource string */
- ACPI_STRCPY (ACPI_CAST_PTR (char, &AmlResourceSource[1]),
+ strcpy (ACPI_CAST_PTR (char, &AmlResourceSource[1]),
ResourceSource->StringPtr);
/*
* Add the length of the string (+ 1 for null terminator) to the
* final descriptor length
*/
- DescriptorLength += ((ACPI_RSDESC_SIZE) ResourceSource->StringLength + 1);
+ DescriptorLength += ((ACPI_RSDESC_SIZE)
+ ResourceSource->StringLength + 1);
}
/* Return the new total length of the AML descriptor */
@@ -521,8 +533,8 @@ AcpiRsGetPrtMethodData (
/* Execute the method, no parameters */
- Status = AcpiUtEvaluateObject (Node, METHOD_NAME__PRT,
- ACPI_BTYPE_PACKAGE, &ObjDesc);
+ Status = AcpiUtEvaluateObject (
+ Node, METHOD_NAME__PRT, ACPI_BTYPE_PACKAGE, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -575,8 +587,8 @@ AcpiRsGetCrsMethodData (
/* Execute the method, no parameters */
- Status = AcpiUtEvaluateObject (Node, METHOD_NAME__CRS,
- ACPI_BTYPE_BUFFER, &ObjDesc);
+ Status = AcpiUtEvaluateObject (
+ Node, METHOD_NAME__CRS, ACPI_BTYPE_BUFFER, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -630,8 +642,63 @@ AcpiRsGetPrsMethodData (
/* Execute the method, no parameters */
- Status = AcpiUtEvaluateObject (Node, METHOD_NAME__PRS,
- ACPI_BTYPE_BUFFER, &ObjDesc);
+ Status = AcpiUtEvaluateObject (
+ Node, METHOD_NAME__PRS, ACPI_BTYPE_BUFFER, &ObjDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /*
+ * Make the call to create a resource linked list from the
+ * byte stream buffer that comes back from the _CRS method
+ * execution.
+ */
+ Status = AcpiRsCreateResourceList (ObjDesc, RetBuffer);
+
+ /* On exit, we must delete the object returned by evaluateObject */
+
+ AcpiUtRemoveReference (ObjDesc);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiRsGetAeiMethodData
+ *
+ * PARAMETERS: Node - Device node
+ * RetBuffer - Pointer to a buffer structure for the
+ * results
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the _AEI value of an object
+ * contained in an object specified by the handle passed in
+ *
+ * If the function fails an appropriate status will be returned
+ * and the contents of the callers buffer is undefined.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiRsGetAeiMethodData (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_BUFFER *RetBuffer)
+{
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (RsGetAeiMethodData);
+
+
+ /* Parameters guaranteed valid by caller */
+
+ /* Execute the method, no parameters */
+
+ Status = AcpiUtEvaluateObject (
+ Node, METHOD_NAME__AEI, ACPI_BTYPE_BUFFER, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -673,7 +740,7 @@ AcpiRsGetPrsMethodData (
ACPI_STATUS
AcpiRsGetMethodData (
ACPI_HANDLE Handle,
- char *Path,
+ const char *Path,
ACPI_BUFFER *RetBuffer)
{
ACPI_OPERAND_OBJECT *ObjDesc;
@@ -687,7 +754,9 @@ AcpiRsGetMethodData (
/* Execute the method, no parameters */
- Status = AcpiUtEvaluateObject (Handle, Path, ACPI_BTYPE_BUFFER, &ObjDesc);
+ Status = AcpiUtEvaluateObject (
+ ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Handle),
+ Path, ACPI_BTYPE_BUFFER, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -750,7 +819,7 @@ AcpiRsSetSrsMethodData (
}
Info->PrefixNode = Node;
- Info->Pathname = METHOD_NAME__SRS;
+ Info->RelativePathname = METHOD_NAME__SRS;
Info->Parameters = Args;
Info->Flags = ACPI_IGNORE_RETURN_VALUE;
@@ -762,7 +831,7 @@ AcpiRsSetSrsMethodData (
* Convert the linked list into a byte stream
*/
Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiRsCreateAmlResources (InBuffer->Pointer, &Buffer);
+ Status = AcpiRsCreateAmlResources (InBuffer, &Buffer);
if (ACPI_FAILURE (Status))
{
goto Cleanup;
@@ -799,4 +868,3 @@ Cleanup:
ACPI_FREE (Info);
return_ACPI_STATUS (Status);
}
-
diff --git a/usr/src/uts/intel/io/acpica/resources/rsxface.c b/usr/src/uts/intel/io/acpica/resources/rsxface.c
index 0aea206b73..81c9ac8b4c 100644
--- a/usr/src/uts/intel/io/acpica/resources/rsxface.c
+++ b/usr/src/uts/intel/io/acpica/resources/rsxface.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __RSXFACE_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
@@ -55,18 +54,18 @@
/* Local macros for 16,32-bit to 64-bit conversion */
#define ACPI_COPY_FIELD(Out, In, Field) ((Out)->Field = (In)->Field)
-#define ACPI_COPY_ADDRESS(Out, In) \
+#define ACPI_COPY_ADDRESS(Out, In) \
ACPI_COPY_FIELD(Out, In, ResourceType); \
ACPI_COPY_FIELD(Out, In, ProducerConsumer); \
ACPI_COPY_FIELD(Out, In, Decode); \
ACPI_COPY_FIELD(Out, In, MinAddressFixed); \
ACPI_COPY_FIELD(Out, In, MaxAddressFixed); \
ACPI_COPY_FIELD(Out, In, Info); \
- ACPI_COPY_FIELD(Out, In, Granularity); \
- ACPI_COPY_FIELD(Out, In, Minimum); \
- ACPI_COPY_FIELD(Out, In, Maximum); \
- ACPI_COPY_FIELD(Out, In, TranslationOffset); \
- ACPI_COPY_FIELD(Out, In, AddressLength); \
+ ACPI_COPY_FIELD(Out, In, Address.Granularity); \
+ ACPI_COPY_FIELD(Out, In, Address.Minimum); \
+ ACPI_COPY_FIELD(Out, In, Address.Maximum); \
+ ACPI_COPY_FIELD(Out, In, Address.TranslationOffset); \
+ ACPI_COPY_FIELD(Out, In, Address.AddressLength); \
ACPI_COPY_FIELD(Out, In, ResourceSource);
@@ -351,6 +350,52 @@ AcpiSetCurrentResources (
ACPI_EXPORT_SYMBOL (AcpiSetCurrentResources)
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiGetEventResources
+ *
+ * PARAMETERS: DeviceHandle - Handle to the device object for the
+ * device we are getting resources
+ * InBuffer - Pointer to a buffer containing the
+ * resources to be set for the device
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get the event resources for a
+ * specific device. The caller must first acquire a handle for
+ * the desired device. The resource data is passed to the routine
+ * the buffer pointed to by the InBuffer variable. Uses the
+ * _AEI method.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiGetEventResources (
+ ACPI_HANDLE DeviceHandle,
+ ACPI_BUFFER *RetBuffer)
+{
+ ACPI_STATUS Status;
+ ACPI_NAMESPACE_NODE *Node;
+
+
+ ACPI_FUNCTION_TRACE (AcpiGetEventResources);
+
+
+ /* Validate parameters then dispatch to internal routine */
+
+ Status = AcpiRsValidateParameters (DeviceHandle, RetBuffer, &Node);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Status = AcpiRsGetAeiMethodData (Node, RetBuffer);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiGetEventResources)
+
+
/******************************************************************************
*
* FUNCTION: AcpiResourceToAddress64
@@ -388,13 +433,15 @@ AcpiResourceToAddress64 (
{
case ACPI_RESOURCE_TYPE_ADDRESS16:
- Address16 = ACPI_CAST_PTR (ACPI_RESOURCE_ADDRESS16, &Resource->Data);
+ Address16 = ACPI_CAST_PTR (
+ ACPI_RESOURCE_ADDRESS16, &Resource->Data);
ACPI_COPY_ADDRESS (Out, Address16);
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
- Address32 = ACPI_CAST_PTR (ACPI_RESOURCE_ADDRESS32, &Resource->Data);
+ Address32 = ACPI_CAST_PTR (
+ ACPI_RESOURCE_ADDRESS32, &Resource->Data);
ACPI_COPY_ADDRESS (Out, Address32);
break;
@@ -402,10 +449,11 @@ AcpiResourceToAddress64 (
/* Simple copy for 64 bit source */
- ACPI_MEMCPY (Out, &Resource->Data, sizeof (ACPI_RESOURCE_ADDRESS64));
+ memcpy (Out, &Resource->Data, sizeof (ACPI_RESOURCE_ADDRESS64));
break;
default:
+
return (AE_BAD_PARAMETER);
}
@@ -428,7 +476,7 @@ ACPI_EXPORT_SYMBOL (AcpiResourceToAddress64)
*
* RETURN: Status
*
- * DESCRIPTION: Walk a resource template for the specified evice to find a
+ * DESCRIPTION: Walk a resource template for the specified device to find a
* vendor-defined resource that matches the supplied UUID and
* UUID subtype. Returns a ACPI_RESOURCE of type Vendor.
*
@@ -458,8 +506,8 @@ AcpiGetVendorResource (
/* Walk the _CRS or _PRS resource list for this device */
- Status = AcpiWalkResources (DeviceHandle, Name, AcpiRsMatchVendorResource,
- &Info);
+ Status = AcpiWalkResources (
+ DeviceHandle, Name, AcpiRsMatchVendorResource, &Info);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -512,7 +560,7 @@ AcpiRsMatchVendorResource (
*/
if ((Vendor->ByteLength < (ACPI_UUID_LENGTH + 1)) ||
(Vendor->UuidSubtype != Info->Uuid->Subtype) ||
- (ACPI_MEMCMP (Vendor->Uuid, Info->Uuid->Data, ACPI_UUID_LENGTH)))
+ (memcmp (Vendor->Uuid, Info->Uuid->Data, ACPI_UUID_LENGTH)))
{
return (AE_OK);
}
@@ -528,7 +576,7 @@ AcpiRsMatchVendorResource (
/* Found the correct resource, copy and return it */
- ACPI_MEMCPY (Buffer->Pointer, Resource, Resource->Length);
+ memcpy (Buffer->Pointer, Resource, Resource->Length);
Buffer->Length = Resource->Length;
/* Found the desired descriptor, terminate resource walk */
@@ -540,67 +588,52 @@ AcpiRsMatchVendorResource (
/*******************************************************************************
*
- * FUNCTION: AcpiWalkResources
+ * FUNCTION: AcpiWalkResourceBuffer
*
- * PARAMETERS: DeviceHandle - Handle to the device object for the
- * device we are querying
- * Name - Method name of the resources we want
- * (METHOD_NAME__CRS or METHOD_NAME__PRS)
+ * PARAMETERS: Buffer - Formatted buffer returned by one of the
+ * various Get*Resource functions
* UserFunction - Called for each resource
* Context - Passed to UserFunction
*
* RETURN: Status
*
- * DESCRIPTION: Retrieves the current or possible resource list for the
- * specified device. The UserFunction is called once for
- * each resource in the list.
+ * DESCRIPTION: Walks the input resource template. The UserFunction is called
+ * once for each resource in the list.
*
******************************************************************************/
ACPI_STATUS
-AcpiWalkResources (
- ACPI_HANDLE DeviceHandle,
- char *Name,
+AcpiWalkResourceBuffer (
+ ACPI_BUFFER *Buffer,
ACPI_WALK_RESOURCE_CALLBACK UserFunction,
void *Context)
{
- ACPI_STATUS Status;
- ACPI_BUFFER Buffer;
+ ACPI_STATUS Status = AE_OK;
ACPI_RESOURCE *Resource;
ACPI_RESOURCE *ResourceEnd;
- ACPI_FUNCTION_TRACE (AcpiWalkResources);
+ ACPI_FUNCTION_TRACE (AcpiWalkResourceBuffer);
/* Parameter validation */
- if (!DeviceHandle || !UserFunction || !Name ||
- (!ACPI_COMPARE_NAME (Name, METHOD_NAME__CRS) &&
- !ACPI_COMPARE_NAME (Name, METHOD_NAME__PRS)))
+ if (!Buffer || !Buffer->Pointer || !UserFunction)
{
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- /* Get the _CRS or _PRS resource list */
+ /* Buffer contains the resource list and length */
- Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiRsGetMethodData (DeviceHandle, Name, &Buffer);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Buffer now contains the resource list */
-
- Resource = ACPI_CAST_PTR (ACPI_RESOURCE, Buffer.Pointer);
- ResourceEnd = ACPI_ADD_PTR (ACPI_RESOURCE, Buffer.Pointer, Buffer.Length);
+ Resource = ACPI_CAST_PTR (ACPI_RESOURCE, Buffer->Pointer);
+ ResourceEnd = ACPI_ADD_PTR (
+ ACPI_RESOURCE, Buffer->Pointer, Buffer->Length);
/* Walk the resource list until the EndTag is found (or buffer end) */
while (Resource < ResourceEnd)
{
- /* Sanity check the resource */
+ /* Sanity check the resource type */
if (Resource->Type > ACPI_RESOURCE_TYPE_MAX)
{
@@ -608,6 +641,13 @@ AcpiWalkResources (
break;
}
+ /* Sanity check the length. It must not be zero, or we loop forever */
+
+ if (!Resource->Length)
+ {
+ return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH);
+ }
+
/* Invoke the user function, abort on any error returned */
Status = UserFunction (Resource, Context);
@@ -631,9 +671,71 @@ AcpiWalkResources (
/* Get the next resource descriptor */
- Resource = ACPI_ADD_PTR (ACPI_RESOURCE, Resource, Resource->Length);
+ Resource = ACPI_NEXT_RESOURCE (Resource);
}
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiWalkResourceBuffer)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiWalkResources
+ *
+ * PARAMETERS: DeviceHandle - Handle to the device object for the
+ * device we are querying
+ * Name - Method name of the resources we want.
+ * (METHOD_NAME__CRS, METHOD_NAME__PRS, or
+ * METHOD_NAME__AEI)
+ * UserFunction - Called for each resource
+ * Context - Passed to UserFunction
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Retrieves the current or possible resource list for the
+ * specified device. The UserFunction is called once for
+ * each resource in the list.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiWalkResources (
+ ACPI_HANDLE DeviceHandle,
+ char *Name,
+ ACPI_WALK_RESOURCE_CALLBACK UserFunction,
+ void *Context)
+{
+ ACPI_STATUS Status;
+ ACPI_BUFFER Buffer;
+
+
+ ACPI_FUNCTION_TRACE (AcpiWalkResources);
+
+
+ /* Parameter validation */
+
+ if (!DeviceHandle || !UserFunction || !Name ||
+ (!ACPI_COMPARE_NAME (Name, METHOD_NAME__CRS) &&
+ !ACPI_COMPARE_NAME (Name, METHOD_NAME__PRS) &&
+ !ACPI_COMPARE_NAME (Name, METHOD_NAME__AEI)))
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ /* Get the _CRS/_PRS/_AEI resource list */
+
+ Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ Status = AcpiRsGetMethodData (DeviceHandle, Name, &Buffer);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Walk the resource list and cleanup */
+
+ Status = AcpiWalkResourceBuffer (&Buffer, UserFunction, Context);
ACPI_FREE (Buffer.Pointer);
return_ACPI_STATUS (Status);
}
diff --git a/usr/src/uts/intel/io/acpica/tables/tbdata.c b/usr/src/uts/intel/io/acpica/tables/tbdata.c
new file mode 100644
index 0000000000..31fa25b92e
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/tables/tbdata.c
@@ -0,0 +1,869 @@
+/******************************************************************************
+ *
+ * Module Name: tbdata - Table manager data structure functions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ ACPI_MODULE_NAME ("tbdata")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbInitTableDescriptor
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ * Address - Physical address of the table
+ * Flags - Allocation flags of the table
+ * Table - Pointer to the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Initialize a new table descriptor
+ *
+ ******************************************************************************/
+
+void
+AcpiTbInitTableDescriptor (
+ ACPI_TABLE_DESC *TableDesc,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT8 Flags,
+ ACPI_TABLE_HEADER *Table)
+{
+
+ /*
+ * Initialize the table descriptor. Set the pointer to NULL, since the
+ * table is not fully mapped at this time.
+ */
+ memset (TableDesc, 0, sizeof (ACPI_TABLE_DESC));
+ TableDesc->Address = Address;
+ TableDesc->Length = Table->Length;
+ TableDesc->Flags = Flags;
+ ACPI_MOVE_32_TO_32 (TableDesc->Signature.Ascii, Table->Signature);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbAcquireTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ * TablePtr - Where table is returned
+ * TableLength - Where table length is returned
+ * TableFlags - Where table allocation flags are returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an ACPI table. It can be used for tables not
+ * maintained in the AcpiGbl_RootTableList.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbAcquireTable (
+ ACPI_TABLE_DESC *TableDesc,
+ ACPI_TABLE_HEADER **TablePtr,
+ UINT32 *TableLength,
+ UINT8 *TableFlags)
+{
+ ACPI_TABLE_HEADER *Table = NULL;
+
+
+ switch (TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK)
+ {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ Table = AcpiOsMapMemory (TableDesc->Address, TableDesc->Length);
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ Table = ACPI_CAST_PTR (ACPI_TABLE_HEADER,
+ ACPI_PHYSADDR_TO_PTR (TableDesc->Address));
+ break;
+
+ default:
+
+ break;
+ }
+
+ /* Table is not valid yet */
+
+ if (!Table)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ /* Fill the return values */
+
+ *TablePtr = Table;
+ *TableLength = TableDesc->Length;
+ *TableFlags = TableDesc->Flags;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbReleaseTable
+ *
+ * PARAMETERS: Table - Pointer for the table
+ * TableLength - Length for the table
+ * TableFlags - Allocation flags for the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Release a table. The inverse of AcpiTbAcquireTable().
+ *
+ ******************************************************************************/
+
+void
+AcpiTbReleaseTable (
+ ACPI_TABLE_HEADER *Table,
+ UINT32 TableLength,
+ UINT8 TableFlags)
+{
+
+ switch (TableFlags & ACPI_TABLE_ORIGIN_MASK)
+ {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ AcpiOsUnmapMemory (Table, TableLength);
+ break;
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+ default:
+
+ break;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbAcquireTempTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor to be acquired
+ * Address - Address of the table
+ * Flags - Allocation flags of the table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function validates the table header to obtain the length
+ * of a table and fills the table descriptor to make its state as
+ * "INSTALLED". Such a table descriptor is only used for verified
+ * installation.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbAcquireTempTable (
+ ACPI_TABLE_DESC *TableDesc,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT8 Flags)
+{
+ ACPI_TABLE_HEADER *TableHeader;
+
+
+ switch (Flags & ACPI_TABLE_ORIGIN_MASK)
+ {
+ case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+ /* Get the length of the full table from the header */
+
+ TableHeader = AcpiOsMapMemory (Address, sizeof (ACPI_TABLE_HEADER));
+ if (!TableHeader)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ AcpiTbInitTableDescriptor (TableDesc, Address, Flags, TableHeader);
+ AcpiOsUnmapMemory (TableHeader, sizeof (ACPI_TABLE_HEADER));
+ return (AE_OK);
+
+ case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+ case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+ TableHeader = ACPI_CAST_PTR (ACPI_TABLE_HEADER,
+ ACPI_PHYSADDR_TO_PTR (Address));
+ if (!TableHeader)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ AcpiTbInitTableDescriptor (TableDesc, Address, Flags, TableHeader);
+ return (AE_OK);
+
+ default:
+
+ break;
+ }
+
+ /* Table is not valid yet */
+
+ return (AE_NO_MEMORY);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbReleaseTempTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor to be released
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: The inverse of AcpiTbAcquireTempTable().
+ *
+ *****************************************************************************/
+
+void
+AcpiTbReleaseTempTable (
+ ACPI_TABLE_DESC *TableDesc)
+{
+
+ /*
+ * Note that the .Address is maintained by the callers of
+ * AcpiTbAcquireTempTable(), thus do not invoke AcpiTbUninstallTable()
+ * where .Address will be freed.
+ */
+ AcpiTbInvalidateTable (TableDesc);
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiTbValidateTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ * table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+ACPI_STATUS
+AcpiTbValidateTable (
+ ACPI_TABLE_DESC *TableDesc)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (TbValidateTable);
+
+
+ /* Validate the table if necessary */
+
+ if (!TableDesc->Pointer)
+ {
+ Status = AcpiTbAcquireTable (TableDesc, &TableDesc->Pointer,
+ &TableDesc->Length, &TableDesc->Flags);
+ if (!TableDesc->Pointer)
+ {
+ Status = AE_NO_MEMORY;
+ }
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbInvalidateTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Invalidate one internal ACPI table, this is the inverse of
+ * AcpiTbValidateTable().
+ *
+ ******************************************************************************/
+
+void
+AcpiTbInvalidateTable (
+ ACPI_TABLE_DESC *TableDesc)
+{
+
+ ACPI_FUNCTION_TRACE (TbInvalidateTable);
+
+
+ /* Table must be validated */
+
+ if (!TableDesc->Pointer)
+ {
+ return_VOID;
+ }
+
+ AcpiTbReleaseTable (TableDesc->Pointer, TableDesc->Length,
+ TableDesc->Flags);
+ TableDesc->Pointer = NULL;
+
+ return_VOID;
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiTbValidateTempTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ * table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+ACPI_STATUS
+AcpiTbValidateTempTable (
+ ACPI_TABLE_DESC *TableDesc)
+{
+
+ if (!TableDesc->Pointer && !AcpiGbl_VerifyTableChecksum)
+ {
+ /*
+ * Only validates the header of the table.
+ * Note that Length contains the size of the mapping after invoking
+ * this work around, this value is required by
+ * AcpiTbReleaseTempTable().
+ * We can do this because in AcpiInitTableDescriptor(), the Length
+ * field of the installed descriptor is filled with the actual
+ * table length obtaining from the table header.
+ */
+ TableDesc->Length = sizeof (ACPI_TABLE_HEADER);
+ }
+
+ return (AcpiTbValidateTable (TableDesc));
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiTbVerifyTempTable
+ *
+ * PARAMETERS: TableDesc - Table descriptor
+ * Signature - Table signature to verify
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to validate and verify the table, the
+ * returned table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+ACPI_STATUS
+AcpiTbVerifyTempTable (
+ ACPI_TABLE_DESC *TableDesc,
+ char *Signature)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (TbVerifyTempTable);
+
+
+ /* Validate the table */
+
+ Status = AcpiTbValidateTempTable (TableDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ /* If a particular signature is expected (DSDT/FACS), it must match */
+
+ if (Signature &&
+ !ACPI_COMPARE_NAME (&TableDesc->Signature, Signature))
+ {
+ ACPI_BIOS_ERROR ((AE_INFO,
+ "Invalid signature 0x%X for ACPI table, expected [%s]",
+ TableDesc->Signature.Integer, Signature));
+ Status = AE_BAD_SIGNATURE;
+ goto InvalidateAndExit;
+ }
+
+ /* Verify the checksum */
+
+ if (AcpiGbl_VerifyTableChecksum)
+ {
+ Status = AcpiTbVerifyChecksum (TableDesc->Pointer, TableDesc->Length);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, AE_NO_MEMORY,
+ "%4.4s 0x%8.8X%8.8X"
+ " Attempted table install failed",
+ AcpiUtValidNameseg (TableDesc->Signature.Ascii) ?
+ TableDesc->Signature.Ascii : "????",
+ ACPI_FORMAT_UINT64 (TableDesc->Address)));
+
+ goto InvalidateAndExit;
+ }
+ }
+
+ return_ACPI_STATUS (AE_OK);
+
+InvalidateAndExit:
+ AcpiTbInvalidateTable (TableDesc);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbResizeRootTableList
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Expand the size of global table array
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbResizeRootTableList (
+ void)
+{
+ ACPI_TABLE_DESC *Tables;
+ UINT32 TableCount;
+
+
+ ACPI_FUNCTION_TRACE (TbResizeRootTableList);
+
+
+ /* AllowResize flag is a parameter to AcpiInitializeTables */
+
+ if (!(AcpiGbl_RootTableList.Flags & ACPI_ROOT_ALLOW_RESIZE))
+ {
+ ACPI_ERROR ((AE_INFO, "Resize of Root Table Array is not allowed"));
+ return_ACPI_STATUS (AE_SUPPORT);
+ }
+
+ /* Increase the Table Array size */
+
+ if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
+ {
+ TableCount = AcpiGbl_RootTableList.MaxTableCount;
+ }
+ else
+ {
+ TableCount = AcpiGbl_RootTableList.CurrentTableCount;
+ }
+
+ Tables = ACPI_ALLOCATE_ZEROED (
+ ((ACPI_SIZE) TableCount + ACPI_ROOT_TABLE_SIZE_INCREMENT) *
+ sizeof (ACPI_TABLE_DESC));
+ if (!Tables)
+ {
+ ACPI_ERROR ((AE_INFO, "Could not allocate new root table array"));
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ /* Copy and free the previous table array */
+
+ if (AcpiGbl_RootTableList.Tables)
+ {
+ memcpy (Tables, AcpiGbl_RootTableList.Tables,
+ (ACPI_SIZE) TableCount * sizeof (ACPI_TABLE_DESC));
+
+ if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
+ {
+ ACPI_FREE (AcpiGbl_RootTableList.Tables);
+ }
+ }
+
+ AcpiGbl_RootTableList.Tables = Tables;
+ AcpiGbl_RootTableList.MaxTableCount =
+ TableCount + ACPI_ROOT_TABLE_SIZE_INCREMENT;
+ AcpiGbl_RootTableList.Flags |= ACPI_ROOT_ORIGIN_ALLOCATED;
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbGetNextTableDescriptor
+ *
+ * PARAMETERS: TableIndex - Where table index is returned
+ * TableDesc - Where table descriptor is returned
+ *
+ * RETURN: Status and table index/descriptor.
+ *
+ * DESCRIPTION: Allocate a new ACPI table entry to the global table list
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbGetNextTableDescriptor (
+ UINT32 *TableIndex,
+ ACPI_TABLE_DESC **TableDesc)
+{
+ ACPI_STATUS Status;
+ UINT32 i;
+
+
+ /* Ensure that there is room for the table in the Root Table List */
+
+ if (AcpiGbl_RootTableList.CurrentTableCount >=
+ AcpiGbl_RootTableList.MaxTableCount)
+ {
+ Status = AcpiTbResizeRootTableList();
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+
+ i = AcpiGbl_RootTableList.CurrentTableCount;
+ AcpiGbl_RootTableList.CurrentTableCount++;
+
+ if (TableIndex)
+ {
+ *TableIndex = i;
+ }
+ if (TableDesc)
+ {
+ *TableDesc = &AcpiGbl_RootTableList.Tables[i];
+ }
+
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbTerminate
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete all internal ACPI tables
+ *
+ ******************************************************************************/
+
+void
+AcpiTbTerminate (
+ void)
+{
+ UINT32 i;
+
+
+ ACPI_FUNCTION_TRACE (TbTerminate);
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+
+ /* Delete the individual tables */
+
+ for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
+ {
+ AcpiTbUninstallTable (&AcpiGbl_RootTableList.Tables[i]);
+ }
+
+ /*
+ * Delete the root table array if allocated locally. Array cannot be
+ * mapped, so we don't need to check for that flag.
+ */
+ if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
+ {
+ ACPI_FREE (AcpiGbl_RootTableList.Tables);
+ }
+
+ AcpiGbl_RootTableList.Tables = NULL;
+ AcpiGbl_RootTableList.Flags = 0;
+ AcpiGbl_RootTableList.CurrentTableCount = 0;
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Tables freed\n"));
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_VOID;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbDeleteNamespaceByOwner
+ *
+ * PARAMETERS: TableIndex - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Delete all namespace objects created when this table was loaded.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbDeleteNamespaceByOwner (
+ UINT32 TableIndex)
+{
+ ACPI_OWNER_ID OwnerId;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (TbDeleteNamespaceByOwner);
+
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ if (TableIndex >= AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ /* The table index does not exist */
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_ACPI_STATUS (AE_NOT_EXIST);
+ }
+
+ /* Get the owner ID for this table, used to delete namespace nodes */
+
+ OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+
+ /*
+ * Need to acquire the namespace writer lock to prevent interference
+ * with any concurrent namespace walks. The interpreter must be
+ * released during the deletion since the acquisition of the deletion
+ * lock may block, and also since the execution of a namespace walk
+ * must be allowed to use the interpreter.
+ */
+ (void) AcpiUtReleaseMutex (ACPI_MTX_INTERPRETER);
+ Status = AcpiUtAcquireWriteLock (&AcpiGbl_NamespaceRwLock);
+
+ AcpiNsDeleteNamespaceByOwner (OwnerId);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ AcpiUtReleaseWriteLock (&AcpiGbl_NamespaceRwLock);
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_INTERPRETER);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbAllocateOwnerId
+ *
+ * PARAMETERS: TableIndex - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocates OwnerId in TableDesc
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbAllocateOwnerId (
+ UINT32 TableIndex)
+{
+ ACPI_STATUS Status = AE_BAD_PARAMETER;
+
+
+ ACPI_FUNCTION_TRACE (TbAllocateOwnerId);
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ Status = AcpiUtAllocateOwnerId (
+ &(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbReleaseOwnerId
+ *
+ * PARAMETERS: TableIndex - Table index
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Releases OwnerId in TableDesc
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbReleaseOwnerId (
+ UINT32 TableIndex)
+{
+ ACPI_STATUS Status = AE_BAD_PARAMETER;
+
+
+ ACPI_FUNCTION_TRACE (TbReleaseOwnerId);
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ AcpiUtReleaseOwnerId (
+ &(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
+ Status = AE_OK;
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbGetOwnerId
+ *
+ * PARAMETERS: TableIndex - Table index
+ * OwnerId - Where the table OwnerId is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: returns OwnerId for the ACPI table
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbGetOwnerId (
+ UINT32 TableIndex,
+ ACPI_OWNER_ID *OwnerId)
+{
+ ACPI_STATUS Status = AE_BAD_PARAMETER;
+
+
+ ACPI_FUNCTION_TRACE (TbGetOwnerId);
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ *OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
+ Status = AE_OK;
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbIsTableLoaded
+ *
+ * PARAMETERS: TableIndex - Index into the root table
+ *
+ * RETURN: Table Loaded Flag
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiTbIsTableLoaded (
+ UINT32 TableIndex)
+{
+ BOOLEAN IsLoaded = FALSE;
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ IsLoaded = (BOOLEAN)
+ (AcpiGbl_RootTableList.Tables[TableIndex].Flags &
+ ACPI_TABLE_IS_LOADED);
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return (IsLoaded);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbSetTableLoadedFlag
+ *
+ * PARAMETERS: TableIndex - Table index
+ * IsLoaded - TRUE if table is loaded, FALSE otherwise
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
+ *
+ ******************************************************************************/
+
+void
+AcpiTbSetTableLoadedFlag (
+ UINT32 TableIndex,
+ BOOLEAN IsLoaded)
+{
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ {
+ if (IsLoaded)
+ {
+ AcpiGbl_RootTableList.Tables[TableIndex].Flags |=
+ ACPI_TABLE_IS_LOADED;
+ }
+ else
+ {
+ AcpiGbl_RootTableList.Tables[TableIndex].Flags &=
+ ~ACPI_TABLE_IS_LOADED;
+ }
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+}
diff --git a/usr/src/uts/intel/io/acpica/tables/tbfadt.c b/usr/src/uts/intel/io/acpica/tables/tbfadt.c
index f8c63408b9..51eb49b42a 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbfadt.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbfadt.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __TBFADT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "actables.h"
@@ -52,41 +50,47 @@
/* Local prototypes */
-static ACPI_INLINE void
+static void
AcpiTbInitGenericAddress (
ACPI_GENERIC_ADDRESS *GenericAddress,
UINT8 SpaceId,
UINT8 ByteWidth,
- UINT64 Address);
+ UINT64 Address,
+ const char *RegisterName,
+ UINT8 Flags);
static void
AcpiTbConvertFadt (
void);
static void
-AcpiTbValidateFadt (
- void);
-
-static void
AcpiTbSetupFadtRegisters (
void);
+static UINT64
+AcpiTbSelectAddress (
+ char *RegisterName,
+ UINT32 Address32,
+ UINT64 Address64);
+
/* Table for conversion of FADT to common internal format and FADT validation */
typedef struct acpi_fadt_info
{
- char *Name;
- UINT8 Address64;
- UINT8 Address32;
- UINT8 Length;
+ const char *Name;
+ UINT16 Address64;
+ UINT16 Address32;
+ UINT16 Length;
UINT8 DefaultLength;
- UINT8 Type;
+ UINT8 Flags;
} ACPI_FADT_INFO;
+#define ACPI_FADT_OPTIONAL 0
#define ACPI_FADT_REQUIRED 1
#define ACPI_FADT_SEPARATE_LENGTH 2
+#define ACPI_FADT_GPE_REGISTER 4
static ACPI_FADT_INFO FadtInfoTable[] =
{
@@ -102,7 +106,7 @@ static ACPI_FADT_INFO FadtInfoTable[] =
ACPI_FADT_OFFSET (Pm1bEventBlock),
ACPI_FADT_OFFSET (Pm1EventLength),
ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */
- 0},
+ ACPI_FADT_OPTIONAL},
{"Pm1aControlBlock",
ACPI_FADT_OFFSET (XPm1aControlBlock),
@@ -116,7 +120,7 @@ static ACPI_FADT_INFO FadtInfoTable[] =
ACPI_FADT_OFFSET (Pm1bControlBlock),
ACPI_FADT_OFFSET (Pm1ControlLength),
ACPI_PM1_REGISTER_WIDTH,
- 0},
+ ACPI_FADT_OPTIONAL},
{"Pm2ControlBlock",
ACPI_FADT_OFFSET (XPm2ControlBlock),
@@ -130,21 +134,21 @@ static ACPI_FADT_INFO FadtInfoTable[] =
ACPI_FADT_OFFSET (PmTimerBlock),
ACPI_FADT_OFFSET (PmTimerLength),
ACPI_PM_TIMER_WIDTH,
- ACPI_FADT_REQUIRED},
+ ACPI_FADT_SEPARATE_LENGTH}, /* ACPI 5.0A: Timer is optional */
{"Gpe0Block",
ACPI_FADT_OFFSET (XGpe0Block),
ACPI_FADT_OFFSET (Gpe0Block),
ACPI_FADT_OFFSET (Gpe0BlockLength),
0,
- ACPI_FADT_SEPARATE_LENGTH},
+ ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER},
{"Gpe1Block",
ACPI_FADT_OFFSET (XGpe1Block),
ACPI_FADT_OFFSET (Gpe1Block),
ACPI_FADT_OFFSET (Gpe1BlockLength),
0,
- ACPI_FADT_SEPARATE_LENGTH}
+ ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER}
};
#define ACPI_FADT_INFO_ENTRIES \
@@ -156,7 +160,7 @@ static ACPI_FADT_INFO FadtInfoTable[] =
typedef struct acpi_fadt_pm_info
{
ACPI_GENERIC_ADDRESS *Target;
- UINT8 Source;
+ UINT16 Source;
UINT8 RegisterNum;
} ACPI_FADT_PM_INFO;
@@ -190,8 +194,9 @@ static ACPI_FADT_PM_INFO FadtPmInfoTable[] =
*
* PARAMETERS: GenericAddress - GAS struct to be initialized
* SpaceId - ACPI Space ID for this register
- * ByteWidth - Width of this register, in bytes
+ * ByteWidth - Width of this register
* Address - Address of the register
+ * RegisterName - ASCII name of the ACPI register
*
* RETURN: None
*
@@ -201,13 +206,40 @@ static ACPI_FADT_PM_INFO FadtPmInfoTable[] =
*
******************************************************************************/
-static ACPI_INLINE void
+static void
AcpiTbInitGenericAddress (
ACPI_GENERIC_ADDRESS *GenericAddress,
UINT8 SpaceId,
UINT8 ByteWidth,
- UINT64 Address)
+ UINT64 Address,
+ const char *RegisterName,
+ UINT8 Flags)
{
+ UINT8 BitWidth;
+
+
+ /*
+ * Bit width field in the GAS is only one byte long, 255 max.
+ * Check for BitWidth overflow in GAS.
+ */
+ BitWidth = (UINT8) (ByteWidth * 8);
+ if (ByteWidth > 31) /* (31*8)=248, (32*8)=256 */
+ {
+ /*
+ * No error for GPE blocks, because we do not use the BitWidth
+ * for GPEs, the legacy length (ByteWidth) is used instead to
+ * allow for a large number of GPEs.
+ */
+ if (!(Flags & ACPI_FADT_GPE_REGISTER))
+ {
+ ACPI_ERROR ((AE_INFO,
+ "%s - 32-bit FADT register is too long (%u bytes, %u bits) "
+ "to convert to GAS struct - 255 bits max, truncating",
+ RegisterName, ByteWidth, (ByteWidth * 8)));
+ }
+
+ BitWidth = 255;
+ }
/*
* The 64-bit Address field is non-aligned in the byte packed
@@ -218,7 +250,7 @@ AcpiTbInitGenericAddress (
/* All other fields are byte-wide */
GenericAddress->SpaceId = SpaceId;
- GenericAddress->BitWidth = (UINT8) ACPI_MUL_8 (ByteWidth);
+ GenericAddress->BitWidth = BitWidth;
GenericAddress->BitOffset = 0;
GenericAddress->AccessWidth = 0; /* Access width ANY */
}
@@ -226,9 +258,75 @@ AcpiTbInitGenericAddress (
/*******************************************************************************
*
+ * FUNCTION: AcpiTbSelectAddress
+ *
+ * PARAMETERS: RegisterName - ASCII name of the ACPI register
+ * Address32 - 32-bit address of the register
+ * Address64 - 64-bit address of the register
+ *
+ * RETURN: The resolved 64-bit address
+ *
+ * DESCRIPTION: Select between 32-bit and 64-bit versions of addresses within
+ * the FADT. Used for the FACS and DSDT addresses.
+ *
+ * NOTES:
+ *
+ * Check for FACS and DSDT address mismatches. An address mismatch between
+ * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and
+ * DSDT/X_DSDT) could be a corrupted address field or it might indicate
+ * the presence of two FACS or two DSDT tables.
+ *
+ * November 2013:
+ * By default, as per the ACPICA specification, a valid 64-bit address is
+ * used regardless of the value of the 32-bit address. However, this
+ * behavior can be overridden via the AcpiGbl_Use32BitFadtAddresses flag.
+ *
+ ******************************************************************************/
+
+static UINT64
+AcpiTbSelectAddress (
+ char *RegisterName,
+ UINT32 Address32,
+ UINT64 Address64)
+{
+
+ if (!Address64)
+ {
+ /* 64-bit address is zero, use 32-bit address */
+
+ return ((UINT64) Address32);
+ }
+
+ if (Address32 &&
+ (Address64 != (UINT64) Address32))
+ {
+ /* Address mismatch between 32-bit and 64-bit versions */
+
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "32/64X %s address mismatch in FADT: "
+ "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
+ RegisterName, Address32, ACPI_FORMAT_UINT64 (Address64),
+ AcpiGbl_Use32BitFadtAddresses ? 32 : 64));
+
+ /* 32-bit address override */
+
+ if (AcpiGbl_Use32BitFadtAddresses)
+ {
+ return ((UINT64) Address32);
+ }
+ }
+
+ /* Default is to use the 64-bit address */
+
+ return (Address64);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiTbParseFadt
*
- * PARAMETERS: TableIndex - Index for the FADT
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -239,7 +337,7 @@ AcpiTbInitGenericAddress (
void
AcpiTbParseFadt (
- UINT32 TableIndex)
+ void)
{
UINT32 Length;
ACPI_TABLE_HEADER *Table;
@@ -252,10 +350,10 @@ AcpiTbParseFadt (
* Get a local copy of the FADT and convert it to a common format
* Map entire FADT, assumed to be smaller than one page.
*/
- Length = AcpiGbl_RootTableList.Tables[TableIndex].Length;
+ Length = AcpiGbl_RootTableList.Tables[AcpiGbl_FadtIndex].Length;
Table = AcpiOsMapMemory (
- AcpiGbl_RootTableList.Tables[TableIndex].Address, Length);
+ AcpiGbl_RootTableList.Tables[AcpiGbl_FadtIndex].Address, Length);
if (!Table)
{
return;
@@ -277,11 +375,24 @@ AcpiTbParseFadt (
/* Obtain the DSDT and FACS tables via their addresses within the FADT */
- AcpiTbInstallTable ((ACPI_PHYSICAL_ADDRESS) AcpiGbl_FADT.XDsdt,
- ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT);
+ AcpiTbInstallFixedTable ((ACPI_PHYSICAL_ADDRESS) AcpiGbl_FADT.XDsdt,
+ ACPI_SIG_DSDT, &AcpiGbl_DsdtIndex);
+
+ /* If Hardware Reduced flag is set, there is no FACS */
- AcpiTbInstallTable ((ACPI_PHYSICAL_ADDRESS) AcpiGbl_FADT.XFacs,
- ACPI_SIG_FACS, ACPI_TABLE_INDEX_FACS);
+ if (!AcpiGbl_ReducedHardware)
+ {
+ if (AcpiGbl_FADT.Facs)
+ {
+ AcpiTbInstallFixedTable ((ACPI_PHYSICAL_ADDRESS) AcpiGbl_FADT.Facs,
+ ACPI_SIG_FACS, &AcpiGbl_FacsIndex);
+ }
+ if (AcpiGbl_FADT.XFacs)
+ {
+ AcpiTbInstallFixedTable ((ACPI_PHYSICAL_ADDRESS) AcpiGbl_FADT.XFacs,
+ ACPI_SIG_FACS, &AcpiGbl_XFacsIndex);
+ }
+ }
}
@@ -309,33 +420,38 @@ AcpiTbCreateLocalFadt (
/*
* Check if the FADT is larger than the largest table that we expect
- * (the ACPI 2.0/3.0 version). If so, truncate the table, and issue
- * a warning.
+ * (typically the current ACPI specification version). If so, truncate
+ * the table, and issue a warning.
*/
if (Length > sizeof (ACPI_TABLE_FADT))
{
- ACPI_WARNING ((AE_INFO,
- "FADT (revision %u) is longer than ACPI 2.0 version, "
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "FADT (revision %u) is longer than %s length, "
"truncating length %u to %u",
- Table->Revision, Length, (UINT32) sizeof (ACPI_TABLE_FADT)));
+ Table->Revision, ACPI_FADT_CONFORMANCE, Length,
+ (UINT32) sizeof (ACPI_TABLE_FADT)));
}
/* Clear the entire local FADT */
- ACPI_MEMSET (&AcpiGbl_FADT, 0, sizeof (ACPI_TABLE_FADT));
+ memset (&AcpiGbl_FADT, 0, sizeof (ACPI_TABLE_FADT));
/* Copy the original FADT, up to sizeof (ACPI_TABLE_FADT) */
- ACPI_MEMCPY (&AcpiGbl_FADT, Table,
+ memcpy (&AcpiGbl_FADT, Table,
ACPI_MIN (Length, sizeof (ACPI_TABLE_FADT)));
- /* Convert the local copy of the FADT to the common internal format */
+ /* Take a copy of the Hardware Reduced flag */
- AcpiTbConvertFadt ();
+ AcpiGbl_ReducedHardware = FALSE;
+ if (AcpiGbl_FADT.Flags & ACPI_FADT_HW_REDUCED)
+ {
+ AcpiGbl_ReducedHardware = TRUE;
+ }
- /* Validate FADT values now, before we make any changes */
+ /* Convert the local copy of the FADT to the common internal format */
- AcpiTbValidateFadt ();
+ AcpiTbConvertFadt ();
/* Initialize the global ACPI register structures */
@@ -347,33 +463,43 @@ AcpiTbCreateLocalFadt (
*
* FUNCTION: AcpiTbConvertFadt
*
- * PARAMETERS: None, uses AcpiGbl_FADT
+ * PARAMETERS: None - AcpiGbl_FADT is used.
*
* RETURN: None
*
* DESCRIPTION: Converts all versions of the FADT to a common internal format.
- * Expand 32-bit addresses to 64-bit as necessary.
+ * Expand 32-bit addresses to 64-bit as necessary. Also validate
+ * important fields within the FADT.
*
- * NOTE: AcpiGbl_FADT must be of size (ACPI_TABLE_FADT),
- * and must contain a copy of the actual FADT.
+ * NOTE: AcpiGbl_FADT must be of size (ACPI_TABLE_FADT), and must
+ * contain a copy of the actual BIOS-provided FADT.
*
* Notes on 64-bit register addresses:
*
* After this FADT conversion, later ACPICA code will only use the 64-bit "X"
* fields of the FADT for all ACPI register addresses.
*
- * The 64-bit "X" fields are optional extensions to the original 32-bit FADT
+ * The 64-bit X fields are optional extensions to the original 32-bit FADT
* V1.0 fields. Even if they are present in the FADT, they are optional and
* are unused if the BIOS sets them to zero. Therefore, we must copy/expand
- * 32-bit V1.0 fields if the corresponding X field is zero.
+ * 32-bit V1.0 fields to the 64-bit X fields if the the 64-bit X field is
+ * originally zero.
*
- * For ACPI 1.0 FADTs, all 32-bit address fields are expanded to the
- * corresponding "X" fields in the internal FADT.
+ * For ACPI 1.0 FADTs (that contain no 64-bit addresses), all 32-bit address
+ * fields are expanded to the corresponding 64-bit X fields in the internal
+ * common FADT.
*
* For ACPI 2.0+ FADTs, all valid (non-zero) 32-bit address fields are expanded
- * to the corresponding 64-bit X fields. For compatibility with other ACPI
- * implementations, we ignore the 64-bit field if the 32-bit field is valid,
- * regardless of whether the host OS is 32-bit or 64-bit.
+ * to the corresponding 64-bit X fields, if the 64-bit field is originally
+ * zero. Adhering to the ACPI specification, we completely ignore the 32-bit
+ * field if the 64-bit field is valid, regardless of whether the host OS is
+ * 32-bit or 64-bit.
+ *
+ * Possible additional checks:
+ * (AcpiGbl_FADT.Pm1EventLength >= 4)
+ * (AcpiGbl_FADT.Pm1ControlLength >= 2)
+ * (AcpiGbl_FADT.PmTimerLength >= 4)
+ * Gpe block lengths must be multiple of 2
*
******************************************************************************/
@@ -381,28 +507,14 @@ static void
AcpiTbConvertFadt (
void)
{
+ const char *Name;
ACPI_GENERIC_ADDRESS *Address64;
UINT32 Address32;
+ UINT8 Length;
+ UINT8 Flags;
UINT32 i;
- /* Update the local FADT table header length */
-
- AcpiGbl_FADT.Header.Length = sizeof (ACPI_TABLE_FADT);
-
- /*
- * Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary.
- * Later code will always use the X 64-bit field.
- */
- if (!AcpiGbl_FADT.XFacs)
- {
- AcpiGbl_FADT.XFacs = (UINT64) AcpiGbl_FADT.Facs;
- }
- if (!AcpiGbl_FADT.XDsdt)
- {
- AcpiGbl_FADT.XDsdt = (UINT64) AcpiGbl_FADT.Dsdt;
- }
-
/*
* For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which
* should be zero are indeed zero. This will workaround BIOSs that
@@ -423,111 +535,24 @@ AcpiTbConvertFadt (
}
/*
- * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
- * generic address structures as necessary. Later code will always use
- * the 64-bit address structures.
- *
- * March 2009:
- * We now always use the 32-bit address if it is valid (non-null). This
- * is not in accordance with the ACPI specification which states that
- * the 64-bit address supersedes the 32-bit version, but we do this for
- * compatibility with other ACPI implementations. Most notably, in the
- * case where both the 32 and 64 versions are non-null, we use the 32-bit
- * version. This is the only address that is guaranteed to have been
- * tested by the BIOS manufacturer.
+ * Now we can update the local FADT length to the length of the
+ * current FADT version as defined by the ACPI specification.
+ * Thus, we will have a common FADT internally.
*/
- for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++)
- {
- Address32 = *ACPI_ADD_PTR (UINT32,
- &AcpiGbl_FADT, FadtInfoTable[i].Address32);
-
- Address64 = ACPI_ADD_PTR (ACPI_GENERIC_ADDRESS,
- &AcpiGbl_FADT, FadtInfoTable[i].Address64);
-
- /*
- * If both 32- and 64-bit addresses are valid (non-zero),
- * they must match.
- */
- if (Address64->Address && Address32 &&
- (Address64->Address != (UINT64) Address32))
- {
- ACPI_ERROR ((AE_INFO,
- "32/64X address mismatch in %s: 0x%8.8X/0x%8.8X%8.8X, using 32",
- FadtInfoTable[i].Name, Address32,
- ACPI_FORMAT_UINT64 (Address64->Address)));
- }
-
- /* Always use 32-bit address if it is valid (non-null) */
-
- if (Address32)
- {
- /*
- * Copy the 32-bit address to the 64-bit GAS structure. The
- * Space ID is always I/O for 32-bit legacy address fields
- */
- AcpiTbInitGenericAddress (Address64, ACPI_ADR_SPACE_SYSTEM_IO,
- *ACPI_ADD_PTR (UINT8, &AcpiGbl_FADT, FadtInfoTable[i].Length),
- (UINT64) Address32);
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbValidateFadt
- *
- * PARAMETERS: Table - Pointer to the FADT to be validated
- *
- * RETURN: None
- *
- * DESCRIPTION: Validate various important fields within the FADT. If a problem
- * is found, issue a message, but no status is returned.
- * Used by both the table manager and the disassembler.
- *
- * Possible additional checks:
- * (AcpiGbl_FADT.Pm1EventLength >= 4)
- * (AcpiGbl_FADT.Pm1ControlLength >= 2)
- * (AcpiGbl_FADT.PmTimerLength >= 4)
- * Gpe block lengths must be multiple of 2
- *
- ******************************************************************************/
-
-static void
-AcpiTbValidateFadt (
- void)
-{
- char *Name;
- ACPI_GENERIC_ADDRESS *Address64;
- UINT8 Length;
- UINT32 i;
-
+ AcpiGbl_FADT.Header.Length = sizeof (ACPI_TABLE_FADT);
/*
- * Check for FACS and DSDT address mismatches. An address mismatch between
- * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and
- * DSDT/X_DSDT) would indicate the presence of two FACS or two DSDT tables.
+ * Expand the 32-bit DSDT addresses to 64-bit as necessary.
+ * Later ACPICA code will always use the X 64-bit field.
*/
- if (AcpiGbl_FADT.Facs &&
- (AcpiGbl_FADT.XFacs != (UINT64) AcpiGbl_FADT.Facs))
- {
- ACPI_WARNING ((AE_INFO,
- "32/64X FACS address mismatch in FADT - "
- "0x%8.8X/0x%8.8X%8.8X, using 32",
- AcpiGbl_FADT.Facs, ACPI_FORMAT_UINT64 (AcpiGbl_FADT.XFacs)));
+ AcpiGbl_FADT.XDsdt = AcpiTbSelectAddress ("DSDT",
+ AcpiGbl_FADT.Dsdt, AcpiGbl_FADT.XDsdt);
- AcpiGbl_FADT.XFacs = (UINT64) AcpiGbl_FADT.Facs;
- }
+ /* If Hardware Reduced flag is set, we are all done */
- if (AcpiGbl_FADT.Dsdt &&
- (AcpiGbl_FADT.XDsdt != (UINT64) AcpiGbl_FADT.Dsdt))
+ if (AcpiGbl_ReducedHardware)
{
- ACPI_WARNING ((AE_INFO,
- "32/64X DSDT address mismatch in FADT - "
- "0x%8.8X/0x%8.8X%8.8X, using 32",
- AcpiGbl_FADT.Dsdt, ACPI_FORMAT_UINT64 (AcpiGbl_FADT.XDsdt)));
-
- AcpiGbl_FADT.XDsdt = (UINT64) AcpiGbl_FADT.Dsdt;
+ return;
}
/* Examine all of the 64-bit extended address fields (X fields) */
@@ -535,42 +560,113 @@ AcpiTbValidateFadt (
for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++)
{
/*
- * Generate pointer to the 64-bit address, get the register
- * length (width) and the register name
+ * Get the 32-bit and 64-bit addresses, as well as the register
+ * length and register name.
*/
+ Address32 = *ACPI_ADD_PTR (UINT32,
+ &AcpiGbl_FADT, FadtInfoTable[i].Address32);
+
Address64 = ACPI_ADD_PTR (ACPI_GENERIC_ADDRESS,
- &AcpiGbl_FADT, FadtInfoTable[i].Address64);
+ &AcpiGbl_FADT, FadtInfoTable[i].Address64);
+
Length = *ACPI_ADD_PTR (UINT8,
- &AcpiGbl_FADT, FadtInfoTable[i].Length);
+ &AcpiGbl_FADT, FadtInfoTable[i].Length);
+
Name = FadtInfoTable[i].Name;
+ Flags = FadtInfoTable[i].Flags;
+
+ /*
+ * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
+ * generic address structures as necessary. Later code will always use
+ * the 64-bit address structures.
+ *
+ * November 2013:
+ * Now always use the 64-bit address if it is valid (non-zero), in
+ * accordance with the ACPI specification which states that a 64-bit
+ * address supersedes the 32-bit version. This behavior can be
+ * overridden by the AcpiGbl_Use32BitFadtAddresses flag.
+ *
+ * During 64-bit address construction and verification,
+ * these cases are handled:
+ *
+ * Address32 zero, Address64 [don't care] - Use Address64
+ *
+ * Address32 non-zero, Address64 zero - Copy/use Address32
+ * Address32 non-zero == Address64 non-zero - Use Address64
+ * Address32 non-zero != Address64 non-zero - Warning, use Address64
+ *
+ * Override: if AcpiGbl_Use32BitFadtAddresses is TRUE, and:
+ * Address32 non-zero != Address64 non-zero - Warning, copy/use Address32
+ *
+ * Note: SpaceId is always I/O for 32-bit legacy address fields
+ */
+ if (Address32)
+ {
+ if (!Address64->Address)
+ {
+ /* 64-bit address is zero, use 32-bit address */
+
+ AcpiTbInitGenericAddress (Address64,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ *ACPI_ADD_PTR (UINT8, &AcpiGbl_FADT,
+ FadtInfoTable[i].Length),
+ (UINT64) Address32, Name, Flags);
+ }
+ else if (Address64->Address != (UINT64) Address32)
+ {
+ /* Address mismatch */
+
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "32/64X address mismatch in FADT/%s: "
+ "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
+ Name, Address32,
+ ACPI_FORMAT_UINT64 (Address64->Address),
+ AcpiGbl_Use32BitFadtAddresses ? 32 : 64));
+
+ if (AcpiGbl_Use32BitFadtAddresses)
+ {
+ /* 32-bit address override */
+
+ AcpiTbInitGenericAddress (Address64,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ *ACPI_ADD_PTR (UINT8, &AcpiGbl_FADT,
+ FadtInfoTable[i].Length),
+ (UINT64) Address32, Name, Flags);
+ }
+ }
+ }
/*
* For each extended field, check for length mismatch between the
* legacy length field and the corresponding 64-bit X length field.
+ * Note: If the legacy length field is > 0xFF bits, ignore this
+ * check. (GPE registers can be larger than the 64-bit GAS structure
+ * can accomodate, 0xFF bits).
*/
if (Address64->Address &&
+ (ACPI_MUL_8 (Length) <= ACPI_UINT8_MAX) &&
(Address64->BitWidth != ACPI_MUL_8 (Length)))
{
- ACPI_WARNING ((AE_INFO,
- "32/64X length mismatch in %s: %u/%u",
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "32/64X length mismatch in FADT/%s: %u/%u",
Name, ACPI_MUL_8 (Length), Address64->BitWidth));
}
- if (FadtInfoTable[i].Type & ACPI_FADT_REQUIRED)
+ if (FadtInfoTable[i].Flags & ACPI_FADT_REQUIRED)
{
/*
- * Field is required (PM1aEvent, PM1aControl, PmTimer).
+ * Field is required (PM1aEvent, PM1aControl).
* Both the address and length must be non-zero.
*/
if (!Address64->Address || !Length)
{
- ACPI_ERROR ((AE_INFO,
- "Required field %s has zero address and/or length:"
- " 0x%8.8X%8.8X/0x%X",
+ ACPI_BIOS_ERROR ((AE_INFO,
+ "Required FADT field %s has zero address and/or length: "
+ "0x%8.8X%8.8X/0x%X",
Name, ACPI_FORMAT_UINT64 (Address64->Address), Length));
}
}
- else if (FadtInfoTable[i].Type & ACPI_FADT_SEPARATE_LENGTH)
+ else if (FadtInfoTable[i].Flags & ACPI_FADT_SEPARATE_LENGTH)
{
/*
* Field is optional (PM2Control, GPE0, GPE1) AND has its own
@@ -580,10 +676,12 @@ AcpiTbValidateFadt (
if ((Address64->Address && !Length) ||
(!Address64->Address && Length))
{
- ACPI_WARNING ((AE_INFO,
- "Optional field %s has zero address or length: "
- "0x%8.8X%8.8X/0x%X",
- Name, ACPI_FORMAT_UINT64 (Address64->Address), Length));
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "Optional FADT field %s has valid %s but zero %s: "
+ "0x%8.8X%8.8X/0x%X", Name,
+ (Length ? "Length" : "Address"),
+ (Length ? "Address": "Length"),
+ ACPI_FORMAT_UINT64 (Address64->Address), Length));
}
}
}
@@ -632,8 +730,8 @@ AcpiTbSetupFadtRegisters (
(FadtInfoTable[i].DefaultLength > 0) &&
(FadtInfoTable[i].DefaultLength != Target64->BitWidth))
{
- ACPI_WARNING ((AE_INFO,
- "Invalid length for %s: %u, using default %u",
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "Invalid length for FADT/%s: %u, using default %u",
FadtInfoTable[i].Name, Target64->BitWidth,
FadtInfoTable[i].DefaultLength));
@@ -676,8 +774,8 @@ AcpiTbSetupFadtRegisters (
AcpiTbInitGenericAddress (FadtPmInfoTable[i].Target,
Source64->SpaceId, Pm1RegisterByteWidth,
Source64->Address +
- (FadtPmInfoTable[i].RegisterNum * Pm1RegisterByteWidth));
+ (FadtPmInfoTable[i].RegisterNum * Pm1RegisterByteWidth),
+ "PmRegisters", 0);
}
}
}
-
diff --git a/usr/src/uts/intel/io/acpica/tables/tbfind.c b/usr/src/uts/intel/io/acpica/tables/tbfind.c
index 3b7c420f01..32839a3cd3 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbfind.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbfind.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __TBFIND_C__
-
#include "acpi.h"
#include "accommon.h"
#include "actables.h"
@@ -75,27 +73,42 @@ AcpiTbFindTable (
char *OemTableId,
UINT32 *TableIndex)
{
- UINT32 i;
ACPI_STATUS Status;
ACPI_TABLE_HEADER Header;
+ UINT32 i;
ACPI_FUNCTION_TRACE (TbFindTable);
+ /* Validate the input table signature */
+
+ if (!AcpiUtValidNameseg (Signature))
+ {
+ return_ACPI_STATUS (AE_BAD_SIGNATURE);
+ }
+
+ /* Don't allow the OEM strings to be too long */
+
+ if ((strlen (OemId) > ACPI_OEM_ID_SIZE) ||
+ (strlen (OemTableId) > ACPI_OEM_TABLE_ID_SIZE))
+ {
+ return_ACPI_STATUS (AE_AML_STRING_LIMIT);
+ }
+
/* Normalize the input strings */
- ACPI_MEMSET (&Header, 0, sizeof (ACPI_TABLE_HEADER));
- ACPI_STRNCPY (Header.Signature, Signature, ACPI_NAME_SIZE);
- ACPI_STRNCPY (Header.OemId, OemId, ACPI_OEM_ID_SIZE);
- ACPI_STRNCPY (Header.OemTableId, OemTableId, ACPI_OEM_TABLE_ID_SIZE);
+ memset (&Header, 0, sizeof (ACPI_TABLE_HEADER));
+ ACPI_MOVE_NAME (Header.Signature, Signature);
+ strncpy (Header.OemId, OemId, ACPI_OEM_ID_SIZE);
+ strncpy (Header.OemTableId, OemTableId, ACPI_OEM_TABLE_ID_SIZE);
/* Search for the table */
for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
{
- if (ACPI_MEMCMP (&(AcpiGbl_RootTableList.Tables[i].Signature),
- Header.Signature, ACPI_NAME_SIZE))
+ if (memcmp (&(AcpiGbl_RootTableList.Tables[i].Signature),
+ Header.Signature, ACPI_NAME_SIZE))
{
/* Not the requested table */
@@ -108,7 +121,7 @@ AcpiTbFindTable (
{
/* Table is not currently mapped, map it */
- Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[i]);
+ Status = AcpiTbValidateTable (&AcpiGbl_RootTableList.Tables[i]);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -122,14 +135,14 @@ AcpiTbFindTable (
/* Check for table match on all IDs */
- if (!ACPI_MEMCMP (AcpiGbl_RootTableList.Tables[i].Pointer->Signature,
- Header.Signature, ACPI_NAME_SIZE) &&
+ if (!memcmp (AcpiGbl_RootTableList.Tables[i].Pointer->Signature,
+ Header.Signature, ACPI_NAME_SIZE) &&
(!OemId[0] ||
- !ACPI_MEMCMP (AcpiGbl_RootTableList.Tables[i].Pointer->OemId,
- Header.OemId, ACPI_OEM_ID_SIZE)) &&
+ !memcmp (AcpiGbl_RootTableList.Tables[i].Pointer->OemId,
+ Header.OemId, ACPI_OEM_ID_SIZE)) &&
(!OemTableId[0] ||
- !ACPI_MEMCMP (AcpiGbl_RootTableList.Tables[i].Pointer->OemTableId,
- Header.OemTableId, ACPI_OEM_TABLE_ID_SIZE)))
+ !memcmp (AcpiGbl_RootTableList.Tables[i].Pointer->OemTableId,
+ Header.OemTableId, ACPI_OEM_TABLE_ID_SIZE)))
{
*TableIndex = i;
diff --git a/usr/src/uts/intel/io/acpica/tables/tbinstal.c b/usr/src/uts/intel/io/acpica/tables/tbinstal.c
index ab69691a1e..78e8b4ec8b 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbinstal.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbinstal.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,691 +41,488 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __TBINSTAL_C__
-
#include "acpi.h"
#include "accommon.h"
-#include "acnamesp.h"
#include "actables.h"
-
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME ("tbinstal")
+/* Local prototypes */
-/******************************************************************************
+static BOOLEAN
+AcpiTbCompareTables (
+ ACPI_TABLE_DESC *TableDesc,
+ UINT32 TableIndex);
+
+
+/*******************************************************************************
*
- * FUNCTION: AcpiTbVerifyTable
+ * FUNCTION: AcpiTbCompareTables
*
- * PARAMETERS: TableDesc - table
+ * PARAMETERS: TableDesc - Table 1 descriptor to be compared
+ * TableIndex - Index of table 2 to be compared
*
- * RETURN: Status
+ * RETURN: TRUE if both tables are identical.
*
- * DESCRIPTION: this function is called to verify and map table
+ * DESCRIPTION: This function compares a table with another table that has
+ * already been installed in the root table list.
*
- *****************************************************************************/
+ ******************************************************************************/
-ACPI_STATUS
-AcpiTbVerifyTable (
- ACPI_TABLE_DESC *TableDesc)
+static BOOLEAN
+AcpiTbCompareTables (
+ ACPI_TABLE_DESC *TableDesc,
+ UINT32 TableIndex)
{
ACPI_STATUS Status = AE_OK;
+ BOOLEAN IsIdentical;
+ ACPI_TABLE_HEADER *Table;
+ UINT32 TableLength;
+ UINT8 TableFlags;
- ACPI_FUNCTION_TRACE (TbVerifyTable);
-
-
- /* Map the table if necessary */
-
- if (!TableDesc->Pointer)
+ Status = AcpiTbAcquireTable (&AcpiGbl_RootTableList.Tables[TableIndex],
+ &Table, &TableLength, &TableFlags);
+ if (ACPI_FAILURE (Status))
{
- if ((TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK) ==
- ACPI_TABLE_ORIGIN_MAPPED)
- {
- TableDesc->Pointer = AcpiOsMapMemory (
- TableDesc->Address, TableDesc->Length);
- }
-
- if (!TableDesc->Pointer)
- {
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
+ return (FALSE);
}
- /* FACS is the odd table, has no standard ACPI header and no checksum */
-
- if (!ACPI_COMPARE_NAME (&TableDesc->Signature, ACPI_SIG_FACS))
- {
- /* Always calculate checksum, ignore bad checksum if requested */
+ /*
+ * Check for a table match on the entire table length,
+ * not just the header.
+ */
+ IsIdentical = (BOOLEAN)((TableDesc->Length != TableLength ||
+ memcmp (TableDesc->Pointer, Table, TableLength)) ?
+ FALSE : TRUE);
- Status = AcpiTbVerifyChecksum (TableDesc->Pointer, TableDesc->Length);
- }
+ /* Release the acquired table */
- return_ACPI_STATUS (Status);
+ AcpiTbReleaseTable (Table, TableLength, TableFlags);
+ return (IsIdentical);
}
/*******************************************************************************
*
- * FUNCTION: AcpiTbAddTable
+ * FUNCTION: AcpiTbInstallTableWithOverride
*
- * PARAMETERS: TableDesc - Table descriptor
- * TableIndex - Where the table index is returned
+ * PARAMETERS: NewTableDesc - New table descriptor to install
+ * Override - Whether override should be performed
+ * TableIndex - Where the table index is returned
*
- * RETURN: Status
+ * RETURN: None
*
- * DESCRIPTION: This function is called to add an ACPI table. It is used to
- * dynamically load tables via the Load and LoadTable AML
- * operators.
+ * DESCRIPTION: Install an ACPI table into the global data structure. The
+ * table override mechanism is called to allow the host
+ * OS to replace any table before it is installed in the root
+ * table array.
*
******************************************************************************/
-ACPI_STATUS
-AcpiTbAddTable (
- ACPI_TABLE_DESC *TableDesc,
+void
+AcpiTbInstallTableWithOverride (
+ ACPI_TABLE_DESC *NewTableDesc,
+ BOOLEAN Override,
UINT32 *TableIndex)
{
UINT32 i;
- ACPI_STATUS Status = AE_OK;
- ACPI_TABLE_HEADER *OverrideTable = NULL;
-
-
- ACPI_FUNCTION_TRACE (TbAddTable);
+ ACPI_STATUS Status;
- if (!TableDesc->Pointer)
+ Status = AcpiTbGetNextTableDescriptor (&i, NULL);
+ if (ACPI_FAILURE (Status))
{
- Status = AcpiTbVerifyTable (TableDesc);
- if (ACPI_FAILURE (Status) || !TableDesc->Pointer)
- {
- return_ACPI_STATUS (Status);
- }
+ return;
}
/*
- * Validate the incoming table signature.
+ * ACPI Table Override:
*
- * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
- * 2) We added support for OEMx tables, signature "OEM".
- * 3) Valid tables were encountered with a null signature, so we just
- * gave up on validating the signature, (05/2008).
- * 4) We encountered non-AML tables such as the MADT, which caused
- * interpreter errors and kernel faults. So now, we once again allow
- * only "SSDT", "OEMx", and now, also a null signature. (05/2011).
+ * Before we install the table, let the host OS override it with a new
+ * one if desired. Any table within the RSDT/XSDT can be replaced,
+ * including the DSDT which is pointed to by the FADT.
*/
- if ((TableDesc->Pointer->Signature[0] != 0x00) &&
- (!ACPI_COMPARE_NAME (TableDesc->Pointer->Signature, ACPI_SIG_SSDT)) &&
- (ACPI_STRNCMP (TableDesc->Pointer->Signature, "OEM", 3)))
+ if (Override)
{
- ACPI_ERROR ((AE_INFO,
- "Table has invalid signature [%4.4s] (0x%8.8X), must be SSDT or OEMx",
- AcpiUtValidAcpiName (*(UINT32 *) TableDesc->Pointer->Signature) ?
- TableDesc->Pointer->Signature : "????",
- *(UINT32 *) TableDesc->Pointer->Signature));
-
- return_ACPI_STATUS (AE_BAD_SIGNATURE);
+ AcpiTbOverrideTable (NewTableDesc);
}
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
-
- /* Check if table is already registered */
-
- for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
- {
- if (!AcpiGbl_RootTableList.Tables[i].Pointer)
- {
- Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[i]);
- if (ACPI_FAILURE (Status) ||
- !AcpiGbl_RootTableList.Tables[i].Pointer)
- {
- continue;
- }
- }
-
- /*
- * Check for a table match on the entire table length,
- * not just the header.
- */
- if (TableDesc->Length != AcpiGbl_RootTableList.Tables[i].Length)
- {
- continue;
- }
+ AcpiTbInitTableDescriptor (&AcpiGbl_RootTableList.Tables[i],
+ NewTableDesc->Address, NewTableDesc->Flags, NewTableDesc->Pointer);
- if (ACPI_MEMCMP (TableDesc->Pointer,
- AcpiGbl_RootTableList.Tables[i].Pointer,
- AcpiGbl_RootTableList.Tables[i].Length))
- {
- continue;
- }
+ AcpiTbPrintTableHeader (NewTableDesc->Address, NewTableDesc->Pointer);
- /*
- * Note: the current mechanism does not unregister a table if it is
- * dynamically unloaded. The related namespace entries are deleted,
- * but the table remains in the root table list.
- *
- * The assumption here is that the number of different tables that
- * will be loaded is actually small, and there is minimal overhead
- * in just keeping the table in case it is needed again.
- *
- * If this assumption changes in the future (perhaps on large
- * machines with many table load/unload operations), tables will
- * need to be unregistered when they are unloaded, and slots in the
- * root table list should be reused when empty.
- */
+ /* This synchronizes AcpiGbl_DsdtIndex */
- /*
- * Table is already registered.
- * We can delete the table that was passed as a parameter.
- */
- AcpiTbDeleteTable (TableDesc);
- *TableIndex = i;
+ *TableIndex = i;
- if (AcpiGbl_RootTableList.Tables[i].Flags & ACPI_TABLE_IS_LOADED)
- {
- /* Table is still loaded, this is an error */
+ /* Set the global integer width (based upon revision of the DSDT) */
- Status = AE_ALREADY_EXISTS;
- goto Release;
- }
- else
- {
- /* Table was unloaded, allow it to be reloaded */
-
- TableDesc->Pointer = AcpiGbl_RootTableList.Tables[i].Pointer;
- TableDesc->Address = AcpiGbl_RootTableList.Tables[i].Address;
- Status = AE_OK;
- goto PrintHeader;
- }
- }
-
- /*
- * ACPI Table Override:
- * Allow the host to override dynamically loaded tables.
- */
- Status = AcpiOsTableOverride (TableDesc->Pointer, &OverrideTable);
- if (ACPI_SUCCESS (Status) && OverrideTable)
- {
- ACPI_INFO ((AE_INFO,
- "%4.4s @ 0x%p Table override, replaced with:",
- TableDesc->Pointer->Signature,
- ACPI_CAST_PTR (void, TableDesc->Address)));
-
- /* We can delete the table that was passed as a parameter */
-
- AcpiTbDeleteTable (TableDesc);
-
- /* Setup descriptor for the new table */
-
- TableDesc->Address = ACPI_PTR_TO_PHYSADDR (OverrideTable);
- TableDesc->Pointer = OverrideTable;
- TableDesc->Length = OverrideTable->Length;
- TableDesc->Flags = ACPI_TABLE_ORIGIN_OVERRIDE;
- }
-
- /* Add the table to the global root table list */
-
- Status = AcpiTbStoreTable (TableDesc->Address, TableDesc->Pointer,
- TableDesc->Length, TableDesc->Flags, TableIndex);
- if (ACPI_FAILURE (Status))
+ if (i == AcpiGbl_DsdtIndex)
{
- goto Release;
+ AcpiUtSetIntegerWidth (NewTableDesc->Pointer->Revision);
}
-
-PrintHeader:
- AcpiTbPrintTableHeader (TableDesc->Address, TableDesc->Pointer);
-
-Release:
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return_ACPI_STATUS (Status);
}
/*******************************************************************************
*
- * FUNCTION: AcpiTbResizeRootTableList
+ * FUNCTION: AcpiTbInstallFixedTable
*
- * PARAMETERS: None
+ * PARAMETERS: Address - Physical address of DSDT or FACS
+ * Signature - Table signature, NULL if no need to
+ * match
+ * TableIndex - Where the table index is returned
*
* RETURN: Status
*
- * DESCRIPTION: Expand the size of global table array
+ * DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data
+ * structure.
*
******************************************************************************/
ACPI_STATUS
-AcpiTbResizeRootTableList (
- void)
+AcpiTbInstallFixedTable (
+ ACPI_PHYSICAL_ADDRESS Address,
+ char *Signature,
+ UINT32 *TableIndex)
{
- ACPI_TABLE_DESC *Tables;
-
+ ACPI_TABLE_DESC NewTableDesc;
+ ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (TbResizeRootTableList);
+ ACPI_FUNCTION_TRACE (TbInstallFixedTable);
- /* AllowResize flag is a parameter to AcpiInitializeTables */
- if (!(AcpiGbl_RootTableList.Flags & ACPI_ROOT_ALLOW_RESIZE))
+ if (!Address)
{
- ACPI_ERROR ((AE_INFO, "Resize of Root Table Array is not allowed"));
- return_ACPI_STATUS (AE_SUPPORT);
+ ACPI_ERROR ((AE_INFO, "Null physical address for ACPI table [%s]",
+ Signature));
+ return (AE_NO_MEMORY);
}
- /* Increase the Table Array size */
+ /* Fill a table descriptor for validation */
- Tables = ACPI_ALLOCATE_ZEROED (
- ((ACPI_SIZE) AcpiGbl_RootTableList.MaxTableCount +
- ACPI_ROOT_TABLE_SIZE_INCREMENT) *
- sizeof (ACPI_TABLE_DESC));
- if (!Tables)
+ Status = AcpiTbAcquireTempTable (&NewTableDesc, Address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
+ if (ACPI_FAILURE (Status))
{
- ACPI_ERROR ((AE_INFO, "Could not allocate new root table array"));
- return_ACPI_STATUS (AE_NO_MEMORY);
+ ACPI_ERROR ((AE_INFO, "Could not acquire table length at %8.8X%8.8X",
+ ACPI_FORMAT_UINT64 (Address)));
+ return_ACPI_STATUS (Status);
}
- /* Copy and free the previous table array */
+ /* Validate and verify a table before installation */
- if (AcpiGbl_RootTableList.Tables)
+ Status = AcpiTbVerifyTempTable (&NewTableDesc, Signature);
+ if (ACPI_FAILURE (Status))
{
- ACPI_MEMCPY (Tables, AcpiGbl_RootTableList.Tables,
- (ACPI_SIZE) AcpiGbl_RootTableList.MaxTableCount * sizeof (ACPI_TABLE_DESC));
-
- if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
- {
- ACPI_FREE (AcpiGbl_RootTableList.Tables);
- }
+ goto ReleaseAndExit;
}
- AcpiGbl_RootTableList.Tables = Tables;
- AcpiGbl_RootTableList.MaxTableCount += ACPI_ROOT_TABLE_SIZE_INCREMENT;
- AcpiGbl_RootTableList.Flags |= (UINT8) ACPI_ROOT_ORIGIN_ALLOCATED;
+ /* Add the table to the global root table list */
+
+ AcpiTbInstallTableWithOverride (&NewTableDesc, TRUE, TableIndex);
- return_ACPI_STATUS (AE_OK);
+ReleaseAndExit:
+
+ /* Release the temporary table descriptor */
+
+ AcpiTbReleaseTempTable (&NewTableDesc);
+ return_ACPI_STATUS (Status);
}
/*******************************************************************************
*
- * FUNCTION: AcpiTbStoreTable
+ * FUNCTION: AcpiTbInstallStandardTable
*
- * PARAMETERS: Address - Table address
- * Table - Table header
- * Length - Table length
- * Flags - flags
+ * PARAMETERS: Address - Address of the table (might be a virtual
+ * address depending on the TableFlags)
+ * Flags - Flags for the table
+ * Reload - Whether reload should be performed
+ * Override - Whether override should be performed
+ * TableIndex - Where the table index is returned
*
- * RETURN: Status and table index.
+ * RETURN: Status
*
- * DESCRIPTION: Add an ACPI table to the global table list
+ * DESCRIPTION: This function is called to install an ACPI table that is
+ * neither DSDT nor FACS (a "standard" table.)
+ * When this function is called by "Load" or "LoadTable" opcodes,
+ * or by AcpiLoadTable() API, the "Reload" parameter is set.
+ * After sucessfully returning from this function, table is
+ * "INSTALLED" but not "VALIDATED".
*
******************************************************************************/
ACPI_STATUS
-AcpiTbStoreTable (
+AcpiTbInstallStandardTable (
ACPI_PHYSICAL_ADDRESS Address,
- ACPI_TABLE_HEADER *Table,
- UINT32 Length,
UINT8 Flags,
+ BOOLEAN Reload,
+ BOOLEAN Override,
UINT32 *TableIndex)
{
- ACPI_STATUS Status;
- ACPI_TABLE_DESC *NewTable;
-
-
- /* Ensure that there is room for the table in the Root Table List */
-
- if (AcpiGbl_RootTableList.CurrentTableCount >=
- AcpiGbl_RootTableList.MaxTableCount)
- {
- Status = AcpiTbResizeRootTableList();
- if (ACPI_FAILURE (Status))
- {
- return (Status);
- }
- }
-
- NewTable = &AcpiGbl_RootTableList.Tables[AcpiGbl_RootTableList.CurrentTableCount];
-
- /* Initialize added table */
-
- NewTable->Address = Address;
- NewTable->Pointer = Table;
- NewTable->Length = Length;
- NewTable->OwnerId = 0;
- NewTable->Flags = Flags;
-
- ACPI_MOVE_32_TO_32 (&NewTable->Signature, Table->Signature);
-
- *TableIndex = AcpiGbl_RootTableList.CurrentTableCount;
- AcpiGbl_RootTableList.CurrentTableCount++;
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbDeleteTable
- *
- * PARAMETERS: TableIndex - Table index
- *
- * RETURN: None
- *
- * DESCRIPTION: Delete one internal ACPI table
- *
- ******************************************************************************/
-
-void
-AcpiTbDeleteTable (
- ACPI_TABLE_DESC *TableDesc)
-{
-
- /* Table must be mapped or allocated */
-
- if (!TableDesc->Pointer)
- {
- return;
- }
-
- switch (TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK)
- {
- case ACPI_TABLE_ORIGIN_MAPPED:
- AcpiOsUnmapMemory (TableDesc->Pointer, TableDesc->Length);
- break;
-
- case ACPI_TABLE_ORIGIN_ALLOCATED:
- ACPI_FREE (TableDesc->Pointer);
- break;
-
- default:
- break;
- }
-
- TableDesc->Pointer = NULL;
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbTerminate
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Delete all internal ACPI tables
- *
- ******************************************************************************/
-
-void
-AcpiTbTerminate (
- void)
-{
UINT32 i;
+ ACPI_STATUS Status = AE_OK;
+ ACPI_TABLE_DESC NewTableDesc;
- ACPI_FUNCTION_TRACE (TbTerminate);
-
+ ACPI_FUNCTION_TRACE (TbInstallStandardTable);
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- /* Delete the individual tables */
+ /* Acquire a temporary table descriptor for validation */
- for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
+ Status = AcpiTbAcquireTempTable (&NewTableDesc, Address, Flags);
+ if (ACPI_FAILURE (Status))
{
- AcpiTbDeleteTable (&AcpiGbl_RootTableList.Tables[i]);
+ ACPI_ERROR ((AE_INFO,
+ "Could not acquire table length at %8.8X%8.8X",
+ ACPI_FORMAT_UINT64 (Address)));
+ return_ACPI_STATUS (Status);
}
/*
- * Delete the root table array if allocated locally. Array cannot be
- * mapped, so we don't need to check for that flag.
+ * Optionally do not load any SSDTs from the RSDT/XSDT. This can
+ * be useful for debugging ACPI problems on some machines.
*/
- if (AcpiGbl_RootTableList.Flags & ACPI_ROOT_ORIGIN_ALLOCATED)
+ if (!Reload &&
+ AcpiGbl_DisableSsdtTableInstall &&
+ ACPI_COMPARE_NAME (&NewTableDesc.Signature, ACPI_SIG_SSDT))
{
- ACPI_FREE (AcpiGbl_RootTableList.Tables);
+ ACPI_INFO ((
+ "Ignoring installation of %4.4s at %8.8X%8.8X",
+ NewTableDesc.Signature.Ascii, ACPI_FORMAT_UINT64 (Address)));
+ goto ReleaseAndExit;
}
- AcpiGbl_RootTableList.Tables = NULL;
- AcpiGbl_RootTableList.Flags = 0;
- AcpiGbl_RootTableList.CurrentTableCount = 0;
-
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Tables freed\n"));
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
-}
-
+ /* Validate and verify a table before installation */
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbDeleteNamespaceByOwner
- *
- * PARAMETERS: TableIndex - Table index
- *
- * RETURN: Status
- *
- * DESCRIPTION: Delete all namespace objects created when this table was loaded.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiTbDeleteNamespaceByOwner (
- UINT32 TableIndex)
-{
- ACPI_OWNER_ID OwnerId;
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (TbDeleteNamespaceByOwner);
-
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ Status = AcpiTbVerifyTempTable (&NewTableDesc, NULL);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ goto ReleaseAndExit;
}
- if (TableIndex >= AcpiGbl_RootTableList.CurrentTableCount)
+ if (Reload)
{
- /* The table index does not exist */
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return_ACPI_STATUS (AE_NOT_EXIST);
- }
+ /*
+ * Validate the incoming table signature.
+ *
+ * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
+ * 2) We added support for OEMx tables, signature "OEM".
+ * 3) Valid tables were encountered with a null signature, so we just
+ * gave up on validating the signature, (05/2008).
+ * 4) We encountered non-AML tables such as the MADT, which caused
+ * interpreter errors and kernel faults. So now, we once again allow
+ * only "SSDT", "OEMx", and now, also a null signature. (05/2011).
+ */
+ if ((NewTableDesc.Signature.Ascii[0] != 0x00) &&
+ (!ACPI_COMPARE_NAME (&NewTableDesc.Signature, ACPI_SIG_SSDT)) &&
+ (strncmp (NewTableDesc.Signature.Ascii, "OEM", 3)))
+ {
+ ACPI_BIOS_ERROR ((AE_INFO,
+ "Table has invalid signature [%4.4s] (0x%8.8X), "
+ "must be SSDT or OEMx",
+ AcpiUtValidNameseg (NewTableDesc.Signature.Ascii) ?
+ NewTableDesc.Signature.Ascii : "????",
+ NewTableDesc.Signature.Integer));
+
+ Status = AE_BAD_SIGNATURE;
+ goto ReleaseAndExit;
+ }
- /* Get the owner ID for this table, used to delete namespace nodes */
+ /* Check if table is already registered */
- OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
+ {
+ /*
+ * Check for a table match on the entire table length,
+ * not just the header.
+ */
+ if (!AcpiTbCompareTables (&NewTableDesc, i))
+ {
+ continue;
+ }
- /*
- * Need to acquire the namespace writer lock to prevent interference
- * with any concurrent namespace walks. The interpreter must be
- * released during the deletion since the acquisition of the deletion
- * lock may block, and also since the execution of a namespace walk
- * must be allowed to use the interpreter.
- */
- (void) AcpiUtReleaseMutex (ACPI_MTX_INTERPRETER);
- Status = AcpiUtAcquireWriteLock (&AcpiGbl_NamespaceRwLock);
+ /*
+ * Note: the current mechanism does not unregister a table if it is
+ * dynamically unloaded. The related namespace entries are deleted,
+ * but the table remains in the root table list.
+ *
+ * The assumption here is that the number of different tables that
+ * will be loaded is actually small, and there is minimal overhead
+ * in just keeping the table in case it is needed again.
+ *
+ * If this assumption changes in the future (perhaps on large
+ * machines with many table load/unload operations), tables will
+ * need to be unregistered when they are unloaded, and slots in the
+ * root table list should be reused when empty.
+ */
+ if (AcpiGbl_RootTableList.Tables[i].Flags &
+ ACPI_TABLE_IS_LOADED)
+ {
+ /* Table is still loaded, this is an error */
- AcpiNsDeleteNamespaceByOwner (OwnerId);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
+ Status = AE_ALREADY_EXISTS;
+ goto ReleaseAndExit;
+ }
+ else
+ {
+ /*
+ * Table was unloaded, allow it to be reloaded.
+ * As we are going to return AE_OK to the caller, we should
+ * take the responsibility of freeing the input descriptor.
+ * Refill the input descriptor to ensure
+ * AcpiTbInstallTableWithOverride() can be called again to
+ * indicate the re-installation.
+ */
+ AcpiTbUninstallTable (&NewTableDesc);
+ *TableIndex = i;
+ return_ACPI_STATUS (AE_OK);
+ }
+ }
}
- AcpiUtReleaseWriteLock (&AcpiGbl_NamespaceRwLock);
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_INTERPRETER);
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbAllocateOwnerId
- *
- * PARAMETERS: TableIndex - Table index
- *
- * RETURN: Status
- *
- * DESCRIPTION: Allocates OwnerId in TableDesc
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiTbAllocateOwnerId (
- UINT32 TableIndex)
-{
- ACPI_STATUS Status = AE_BAD_PARAMETER;
-
+ /* Add the table to the global root table list */
- ACPI_FUNCTION_TRACE (TbAllocateOwnerId);
+ AcpiTbInstallTableWithOverride (&NewTableDesc, Override, TableIndex);
+ReleaseAndExit:
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
- {
- Status = AcpiUtAllocateOwnerId
- (&(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
- }
+ /* Release the temporary table descriptor */
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ AcpiTbReleaseTempTable (&NewTableDesc);
return_ACPI_STATUS (Status);
}
/*******************************************************************************
*
- * FUNCTION: AcpiTbReleaseOwnerId
+ * FUNCTION: AcpiTbOverrideTable
*
- * PARAMETERS: TableIndex - Table index
+ * PARAMETERS: OldTableDesc - Validated table descriptor to be
+ * overridden
*
- * RETURN: Status
+ * RETURN: None
*
- * DESCRIPTION: Releases OwnerId in TableDesc
+ * DESCRIPTION: Attempt table override by calling the OSL override functions.
+ * Note: If the table is overridden, then the entire new table
+ * is acquired and returned by this function.
+ * Before/after invocation, the table descriptor is in a state
+ * that is "VALIDATED".
*
******************************************************************************/
-ACPI_STATUS
-AcpiTbReleaseOwnerId (
- UINT32 TableIndex)
+void
+AcpiTbOverrideTable (
+ ACPI_TABLE_DESC *OldTableDesc)
{
- ACPI_STATUS Status = AE_BAD_PARAMETER;
-
+ ACPI_STATUS Status;
+ char *OverrideType;
+ ACPI_TABLE_DESC NewTableDesc;
+ ACPI_TABLE_HEADER *Table;
+ ACPI_PHYSICAL_ADDRESS Address;
+ UINT32 Length;
- ACPI_FUNCTION_TRACE (TbReleaseOwnerId);
+ /* (1) Attempt logical override (returns a logical address) */
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ Status = AcpiOsTableOverride (OldTableDesc->Pointer, &Table);
+ if (ACPI_SUCCESS (Status) && Table)
{
- AcpiUtReleaseOwnerId (
- &(AcpiGbl_RootTableList.Tables[TableIndex].OwnerId));
- Status = AE_OK;
+ AcpiTbAcquireTempTable (&NewTableDesc, ACPI_PTR_TO_PHYSADDR (Table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL);
+ OverrideType = "Logical";
+ goto FinishOverride;
}
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return_ACPI_STATUS (Status);
-}
-
+ /* (2) Attempt physical override (returns a physical address) */
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbGetOwnerId
- *
- * PARAMETERS: TableIndex - Table index
- * OwnerId - Where the table OwnerId is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: returns OwnerId for the ACPI table
- *
- ******************************************************************************/
+ Status = AcpiOsPhysicalTableOverride (OldTableDesc->Pointer,
+ &Address, &Length);
+ if (ACPI_SUCCESS (Status) && Address && Length)
+ {
+ AcpiTbAcquireTempTable (&NewTableDesc, Address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
+ OverrideType = "Physical";
+ goto FinishOverride;
+ }
-ACPI_STATUS
-AcpiTbGetOwnerId (
- UINT32 TableIndex,
- ACPI_OWNER_ID *OwnerId)
-{
- ACPI_STATUS Status = AE_BAD_PARAMETER;
+ return; /* There was no override */
- ACPI_FUNCTION_TRACE (TbGetOwnerId);
+FinishOverride:
+ /* Validate and verify a table before overriding */
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ Status = AcpiTbVerifyTempTable (&NewTableDesc, NULL);
+ if (ACPI_FAILURE (Status))
{
- *OwnerId = AcpiGbl_RootTableList.Tables[TableIndex].OwnerId;
- Status = AE_OK;
+ return;
}
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return_ACPI_STATUS (Status);
-}
+ ACPI_INFO (("%4.4s 0x%8.8X%8.8X"
+ " %s table override, new table: 0x%8.8X%8.8X",
+ OldTableDesc->Signature.Ascii,
+ ACPI_FORMAT_UINT64 (OldTableDesc->Address),
+ OverrideType, ACPI_FORMAT_UINT64 (NewTableDesc.Address)));
+ /* We can now uninstall the original table */
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbIsTableLoaded
- *
- * PARAMETERS: TableIndex - Table index
- *
- * RETURN: Table Loaded Flag
- *
- ******************************************************************************/
+ AcpiTbUninstallTable (OldTableDesc);
-BOOLEAN
-AcpiTbIsTableLoaded (
- UINT32 TableIndex)
-{
- BOOLEAN IsLoaded = FALSE;
+ /*
+ * Replace the original table descriptor and keep its state as
+ * "VALIDATED".
+ */
+ AcpiTbInitTableDescriptor (OldTableDesc, NewTableDesc.Address,
+ NewTableDesc.Flags, NewTableDesc.Pointer);
+ AcpiTbValidateTempTable (OldTableDesc);
+ /* Release the temporary table descriptor */
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
- {
- IsLoaded = (BOOLEAN)
- (AcpiGbl_RootTableList.Tables[TableIndex].Flags &
- ACPI_TABLE_IS_LOADED);
- }
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return (IsLoaded);
+ AcpiTbReleaseTempTable (&NewTableDesc);
}
/*******************************************************************************
*
- * FUNCTION: AcpiTbSetTableLoadedFlag
+ * FUNCTION: AcpiTbUninstallTable
*
- * PARAMETERS: TableIndex - Table index
- * IsLoaded - TRUE if table is loaded, FALSE otherwise
+ * PARAMETERS: TableDesc - Table descriptor
*
* RETURN: None
*
- * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
+ * DESCRIPTION: Delete one internal ACPI table
*
******************************************************************************/
void
-AcpiTbSetTableLoadedFlag (
- UINT32 TableIndex,
- BOOLEAN IsLoaded)
+AcpiTbUninstallTable (
+ ACPI_TABLE_DESC *TableDesc)
{
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- if (TableIndex < AcpiGbl_RootTableList.CurrentTableCount)
+ ACPI_FUNCTION_TRACE (TbUninstallTable);
+
+
+ /* Table must be installed */
+
+ if (!TableDesc->Address)
{
- if (IsLoaded)
- {
- AcpiGbl_RootTableList.Tables[TableIndex].Flags |=
- ACPI_TABLE_IS_LOADED;
- }
- else
- {
- AcpiGbl_RootTableList.Tables[TableIndex].Flags &=
- ~ACPI_TABLE_IS_LOADED;
- }
+ return_VOID;
}
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
-}
+ AcpiTbInvalidateTable (TableDesc);
+ if ((TableDesc->Flags & ACPI_TABLE_ORIGIN_MASK) ==
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL)
+ {
+ ACPI_FREE (ACPI_PHYSADDR_TO_PTR (TableDesc->Address));
+ }
+
+ TableDesc->Address = ACPI_PTR_TO_PHYSADDR (NULL);
+ return_VOID;
+}
diff --git a/usr/src/uts/intel/io/acpica/tables/tbprint.c b/usr/src/uts/intel/io/acpica/tables/tbprint.c
new file mode 100644
index 0000000000..9dab6d9d4a
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/tables/tbprint.c
@@ -0,0 +1,272 @@
+/******************************************************************************
+ *
+ * Module Name: tbprint - Table output utilities
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_TABLES
+ ACPI_MODULE_NAME ("tbprint")
+
+
+/* Local prototypes */
+
+static void
+AcpiTbFixString (
+ char *String,
+ ACPI_SIZE Length);
+
+static void
+AcpiTbCleanupTableHeader (
+ ACPI_TABLE_HEADER *OutHeader,
+ ACPI_TABLE_HEADER *Header);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbFixString
+ *
+ * PARAMETERS: String - String to be repaired
+ * Length - Maximum length
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Replace every non-printable or non-ascii byte in the string
+ * with a question mark '?'.
+ *
+ ******************************************************************************/
+
+static void
+AcpiTbFixString (
+ char *String,
+ ACPI_SIZE Length)
+{
+
+ while (Length && *String)
+ {
+ if (!isprint ((int) *String))
+ {
+ *String = '?';
+ }
+
+ String++;
+ Length--;
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbCleanupTableHeader
+ *
+ * PARAMETERS: OutHeader - Where the cleaned header is returned
+ * Header - Input ACPI table header
+ *
+ * RETURN: Returns the cleaned header in OutHeader
+ *
+ * DESCRIPTION: Copy the table header and ensure that all "string" fields in
+ * the header consist of printable characters.
+ *
+ ******************************************************************************/
+
+static void
+AcpiTbCleanupTableHeader (
+ ACPI_TABLE_HEADER *OutHeader,
+ ACPI_TABLE_HEADER *Header)
+{
+
+ memcpy (OutHeader, Header, sizeof (ACPI_TABLE_HEADER));
+
+ AcpiTbFixString (OutHeader->Signature, ACPI_NAME_SIZE);
+ AcpiTbFixString (OutHeader->OemId, ACPI_OEM_ID_SIZE);
+ AcpiTbFixString (OutHeader->OemTableId, ACPI_OEM_TABLE_ID_SIZE);
+ AcpiTbFixString (OutHeader->AslCompilerId, ACPI_NAME_SIZE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbPrintTableHeader
+ *
+ * PARAMETERS: Address - Table physical address
+ * Header - Table header
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP.
+ *
+ ******************************************************************************/
+
+void
+AcpiTbPrintTableHeader (
+ ACPI_PHYSICAL_ADDRESS Address,
+ ACPI_TABLE_HEADER *Header)
+{
+ ACPI_TABLE_HEADER LocalHeader;
+
+
+ if (ACPI_COMPARE_NAME (Header->Signature, ACPI_SIG_FACS))
+ {
+ /* FACS only has signature and length fields */
+
+ ACPI_INFO (("%-4.4s 0x%8.8X%8.8X %06X",
+ Header->Signature, ACPI_FORMAT_UINT64 (Address),
+ Header->Length));
+ }
+ else if (ACPI_VALIDATE_RSDP_SIG (Header->Signature))
+ {
+ /* RSDP has no common fields */
+
+ memcpy (LocalHeader.OemId, ACPI_CAST_PTR (ACPI_TABLE_RSDP,
+ Header)->OemId, ACPI_OEM_ID_SIZE);
+ AcpiTbFixString (LocalHeader.OemId, ACPI_OEM_ID_SIZE);
+
+ ACPI_INFO (("RSDP 0x%8.8X%8.8X %06X (v%.2d %-6.6s)",
+ ACPI_FORMAT_UINT64 (Address),
+ (ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Revision > 0) ?
+ ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Length : 20,
+ ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Revision,
+ LocalHeader.OemId));
+ }
+ else
+ {
+ /* Standard ACPI table with full common header */
+
+ AcpiTbCleanupTableHeader (&LocalHeader, Header);
+
+ ACPI_INFO ((
+ "%-4.4s 0x%8.8X%8.8X"
+ " %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)",
+ LocalHeader.Signature, ACPI_FORMAT_UINT64 (Address),
+ LocalHeader.Length, LocalHeader.Revision, LocalHeader.OemId,
+ LocalHeader.OemTableId, LocalHeader.OemRevision,
+ LocalHeader.AslCompilerId, LocalHeader.AslCompilerRevision));
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbValidateChecksum
+ *
+ * PARAMETERS: Table - ACPI table to verify
+ * Length - Length of entire table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns
+ * exception on bad checksum.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbVerifyChecksum (
+ ACPI_TABLE_HEADER *Table,
+ UINT32 Length)
+{
+ UINT8 Checksum;
+
+
+ /*
+ * FACS/S3PT:
+ * They are the odd tables, have no standard ACPI header and no checksum
+ */
+
+ if (ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_S3PT) ||
+ ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_FACS))
+ {
+ return (AE_OK);
+ }
+
+ /* Compute the checksum on the table */
+
+ Checksum = AcpiTbChecksum (ACPI_CAST_PTR (UINT8, Table), Length);
+
+ /* Checksum ok? (should be zero) */
+
+ if (Checksum)
+ {
+ ACPI_BIOS_WARNING ((AE_INFO,
+ "Incorrect checksum in table [%4.4s] - 0x%2.2X, "
+ "should be 0x%2.2X",
+ Table->Signature, Table->Checksum,
+ (UINT8) (Table->Checksum - Checksum)));
+
+#if (ACPI_CHECKSUM_ABORT)
+ return (AE_BAD_CHECKSUM);
+#endif
+ }
+
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbChecksum
+ *
+ * PARAMETERS: Buffer - Pointer to memory region to be checked
+ * Length - Length of this memory region
+ *
+ * RETURN: Checksum (UINT8)
+ *
+ * DESCRIPTION: Calculates circular checksum of memory region.
+ *
+ ******************************************************************************/
+
+UINT8
+AcpiTbChecksum (
+ UINT8 *Buffer,
+ UINT32 Length)
+{
+ UINT8 Sum = 0;
+ UINT8 *End = Buffer + Length;
+
+
+ while (Buffer < End)
+ {
+ Sum = (UINT8) (Sum + *(Buffer++));
+ }
+
+ return (Sum);
+}
diff --git a/usr/src/uts/intel/io/acpica/tables/tbutils.c b/usr/src/uts/intel/io/acpica/tables/tbutils.c
index bff6540e07..5fc07b76e0 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbutils.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbutils.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: tbutils - table utilities
+ * Module Name: tbutils - ACPI Table utilities
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __TBUTILS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "actables.h"
@@ -50,17 +48,8 @@
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME ("tbutils")
-/* Local prototypes */
-
-static void
-AcpiTbFixString (
- char *String,
- ACPI_SIZE Length);
-static void
-AcpiTbCleanupTableHeader (
- ACPI_TABLE_HEADER *OutHeader,
- ACPI_TABLE_HEADER *Header);
+/* Local prototypes */
static ACPI_PHYSICAL_ADDRESS
AcpiTbGetRootTableEntry (
@@ -68,6 +57,7 @@ AcpiTbGetRootTableEntry (
UINT32 TableEntrySize);
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: AcpiTbInitializeFacs
@@ -85,240 +75,35 @@ ACPI_STATUS
AcpiTbInitializeFacs (
void)
{
- ACPI_STATUS Status;
-
+ ACPI_TABLE_FACS *Facs;
- Status = AcpiGetTableByIndex (ACPI_TABLE_INDEX_FACS,
- ACPI_CAST_INDIRECT_PTR (ACPI_TABLE_HEADER, &AcpiGbl_FACS));
- return (Status);
-}
+ /* If Hardware Reduced flag is set, there is no FACS */
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbTablesLoaded
- *
- * PARAMETERS: None
- *
- * RETURN: TRUE if required ACPI tables are loaded
- *
- * DESCRIPTION: Determine if the minimum required ACPI tables are present
- * (FADT, FACS, DSDT)
- *
- ******************************************************************************/
-
-BOOLEAN
-AcpiTbTablesLoaded (
- void)
-{
-
- if (AcpiGbl_RootTableList.CurrentTableCount >= 3)
+ if (AcpiGbl_ReducedHardware)
{
- return (TRUE);
+ AcpiGbl_FACS = NULL;
+ return (AE_OK);
}
-
- return (FALSE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbFixString
- *
- * PARAMETERS: String - String to be repaired
- * Length - Maximum length
- *
- * RETURN: None
- *
- * DESCRIPTION: Replace every non-printable or non-ascii byte in the string
- * with a question mark '?'.
- *
- ******************************************************************************/
-
-static void
-AcpiTbFixString (
- char *String,
- ACPI_SIZE Length)
-{
-
- while (Length && *String)
+ else if (AcpiGbl_FADT.XFacs &&
+ (!AcpiGbl_FADT.Facs || !AcpiGbl_Use32BitFacsAddresses))
{
- if (!ACPI_IS_PRINT (*String))
- {
- *String = '?';
- }
- String++;
- Length--;
+ (void) AcpiGetTableByIndex (AcpiGbl_XFacsIndex,
+ ACPI_CAST_INDIRECT_PTR (ACPI_TABLE_HEADER, &Facs));
+ AcpiGbl_FACS = Facs;
}
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbCleanupTableHeader
- *
- * PARAMETERS: OutHeader - Where the cleaned header is returned
- * Header - Input ACPI table header
- *
- * RETURN: Returns the cleaned header in OutHeader
- *
- * DESCRIPTION: Copy the table header and ensure that all "string" fields in
- * the header consist of printable characters.
- *
- ******************************************************************************/
-
-static void
-AcpiTbCleanupTableHeader (
- ACPI_TABLE_HEADER *OutHeader,
- ACPI_TABLE_HEADER *Header)
-{
-
- ACPI_MEMCPY (OutHeader, Header, sizeof (ACPI_TABLE_HEADER));
-
- AcpiTbFixString (OutHeader->Signature, ACPI_NAME_SIZE);
- AcpiTbFixString (OutHeader->OemId, ACPI_OEM_ID_SIZE);
- AcpiTbFixString (OutHeader->OemTableId, ACPI_OEM_TABLE_ID_SIZE);
- AcpiTbFixString (OutHeader->AslCompilerId, ACPI_NAME_SIZE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbPrintTableHeader
- *
- * PARAMETERS: Address - Table physical address
- * Header - Table header
- *
- * RETURN: None
- *
- * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP.
- *
- ******************************************************************************/
-
-void
-AcpiTbPrintTableHeader (
- ACPI_PHYSICAL_ADDRESS Address,
- ACPI_TABLE_HEADER *Header)
-{
- ACPI_TABLE_HEADER LocalHeader;
-
-
- /*
- * The reason that the Address is cast to a void pointer is so that we
- * can use %p which will work properly on both 32-bit and 64-bit hosts.
- */
- if (ACPI_COMPARE_NAME (Header->Signature, ACPI_SIG_FACS))
- {
- /* FACS only has signature and length fields */
-
- ACPI_INFO ((AE_INFO, "%4.4s %p %05X",
- Header->Signature, ACPI_CAST_PTR (void, Address),
- Header->Length));
- }
- else if (ACPI_COMPARE_NAME (Header->Signature, ACPI_SIG_RSDP))
- {
- /* RSDP has no common fields */
-
- ACPI_MEMCPY (LocalHeader.OemId,
- ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->OemId, ACPI_OEM_ID_SIZE);
- AcpiTbFixString (LocalHeader.OemId, ACPI_OEM_ID_SIZE);
-
- ACPI_INFO ((AE_INFO, "RSDP %p %05X (v%.2d %6.6s)",
- ACPI_CAST_PTR (void, Address),
- (ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Revision > 0) ?
- ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Length : 20,
- ACPI_CAST_PTR (ACPI_TABLE_RSDP, Header)->Revision,
- LocalHeader.OemId));
- }
- else
+ else if (AcpiGbl_FADT.Facs)
{
- /* Standard ACPI table with full common header */
-
- AcpiTbCleanupTableHeader (&LocalHeader, Header);
-
- ACPI_INFO ((AE_INFO,
- "%4.4s %p %05X (v%.2d %6.6s %8.8s %08X %4.4s %08X)",
- LocalHeader.Signature, ACPI_CAST_PTR (void, Address),
- LocalHeader.Length, LocalHeader.Revision, LocalHeader.OemId,
- LocalHeader.OemTableId, LocalHeader.OemRevision,
- LocalHeader.AslCompilerId, LocalHeader.AslCompilerRevision));
+ (void) AcpiGetTableByIndex (AcpiGbl_FacsIndex,
+ ACPI_CAST_INDIRECT_PTR (ACPI_TABLE_HEADER, &Facs));
+ AcpiGbl_FACS = Facs;
}
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbValidateChecksum
- *
- * PARAMETERS: Table - ACPI table to verify
- * Length - Length of entire table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns
- * exception on bad checksum.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiTbVerifyChecksum (
- ACPI_TABLE_HEADER *Table,
- UINT32 Length)
-{
- UINT8 Checksum;
-
- /* Compute the checksum on the table */
-
- Checksum = AcpiTbChecksum (ACPI_CAST_PTR (UINT8, Table), Length);
-
- /* Checksum ok? (should be zero) */
-
- if (Checksum)
- {
- ACPI_WARNING ((AE_INFO,
- "Incorrect checksum in table [%4.4s] - 0x%2.2X, should be 0x%2.2X",
- Table->Signature, Table->Checksum,
- (UINT8) (Table->Checksum - Checksum)));
-
-#if (ACPI_CHECKSUM_ABORT)
- return (AE_BAD_CHECKSUM);
-#endif
- }
+ /* If there is no FACS, just continue. There was already an error msg */
return (AE_OK);
}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiTbChecksum
- *
- * PARAMETERS: Buffer - Pointer to memory region to be checked
- * Length - Length of this memory region
- *
- * RETURN: Checksum (UINT8)
- *
- * DESCRIPTION: Calculates circular checksum of memory region.
- *
- ******************************************************************************/
-
-UINT8
-AcpiTbChecksum (
- UINT8 *Buffer,
- UINT32 Length)
-{
- UINT8 Sum = 0;
- UINT8 *End = Buffer + Length;
-
-
- while (Buffer < End)
- {
- Sum = (UINT8) (Sum + *(Buffer++));
- }
-
- return Sum;
-}
+#endif /* !ACPI_REDUCED_HARDWARE */
/*******************************************************************************
@@ -345,8 +130,10 @@ AcpiTbCheckDsdtHeader (
if (AcpiGbl_OriginalDsdtHeader.Length != AcpiGbl_DSDT->Length ||
AcpiGbl_OriginalDsdtHeader.Checksum != AcpiGbl_DSDT->Checksum)
{
- ACPI_ERROR ((AE_INFO,
- "The DSDT has been corrupted or replaced - old, new headers below"));
+ ACPI_BIOS_ERROR ((AE_INFO,
+ "The DSDT has been corrupted or replaced - "
+ "old, new headers below"));
+
AcpiTbPrintTableHeader (0, &AcpiGbl_OriginalDsdtHeader);
AcpiTbPrintTableHeader (0, AcpiGbl_DSDT);
@@ -390,12 +177,15 @@ AcpiTbCopyDsdt (
return (NULL);
}
- ACPI_MEMCPY (NewTable, TableDesc->Pointer, TableDesc->Length);
- AcpiTbDeleteTable (TableDesc);
- TableDesc->Pointer = NewTable;
- TableDesc->Flags = ACPI_TABLE_ORIGIN_ALLOCATED;
+ memcpy (NewTable, TableDesc->Pointer, TableDesc->Length);
+ AcpiTbUninstallTable (TableDesc);
- ACPI_INFO ((AE_INFO,
+ AcpiTbInitTableDescriptor (
+ &AcpiGbl_RootTableList.Tables[AcpiGbl_DsdtIndex],
+ ACPI_PTR_TO_PHYSADDR (NewTable),
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, NewTable);
+
+ ACPI_INFO ((
"Forced DSDT copy: length 0x%05X copied locally, original unmapped",
NewTable->Length));
@@ -405,113 +195,6 @@ AcpiTbCopyDsdt (
/*******************************************************************************
*
- * FUNCTION: AcpiTbInstallTable
- *
- * PARAMETERS: Address - Physical address of DSDT or FACS
- * Signature - Table signature, NULL if no need to
- * match
- * TableIndex - Index into root table array
- *
- * RETURN: None
- *
- * DESCRIPTION: Install an ACPI table into the global data structure. The
- * table override mechanism is implemented here to allow the host
- * OS to replace any table before it is installed in the root
- * table array.
- *
- ******************************************************************************/
-
-void
-AcpiTbInstallTable (
- ACPI_PHYSICAL_ADDRESS Address,
- char *Signature,
- UINT32 TableIndex)
-{
- UINT8 Flags;
- ACPI_STATUS Status;
- ACPI_TABLE_HEADER *TableToInstall;
- ACPI_TABLE_HEADER *MappedTable;
- ACPI_TABLE_HEADER *OverrideTable = NULL;
-
-
- if (!Address)
- {
- ACPI_ERROR ((AE_INFO, "Null physical address for ACPI table [%s]",
- Signature));
- return;
- }
-
- /* Map just the table header */
-
- MappedTable = AcpiOsMapMemory (Address, sizeof (ACPI_TABLE_HEADER));
- if (!MappedTable)
- {
- return;
- }
-
- /* If a particular signature is expected (DSDT/FACS), it must match */
-
- if (Signature &&
- !ACPI_COMPARE_NAME (MappedTable->Signature, Signature))
- {
- ACPI_ERROR ((AE_INFO,
- "Invalid signature 0x%X for ACPI table, expected [%s]",
- *ACPI_CAST_PTR (UINT32, MappedTable->Signature), Signature));
- goto UnmapAndExit;
- }
-
- /*
- * ACPI Table Override:
- *
- * Before we install the table, let the host OS override it with a new
- * one if desired. Any table within the RSDT/XSDT can be replaced,
- * including the DSDT which is pointed to by the FADT.
- */
- Status = AcpiOsTableOverride (MappedTable, &OverrideTable);
- if (ACPI_SUCCESS (Status) && OverrideTable)
- {
- ACPI_INFO ((AE_INFO,
- "%4.4s @ 0x%p Table override, replaced with:",
- MappedTable->Signature, ACPI_CAST_PTR (void, Address)));
-
- AcpiGbl_RootTableList.Tables[TableIndex].Pointer = OverrideTable;
- Address = ACPI_PTR_TO_PHYSADDR (OverrideTable);
-
- TableToInstall = OverrideTable;
- Flags = ACPI_TABLE_ORIGIN_OVERRIDE;
- }
- else
- {
- TableToInstall = MappedTable;
- Flags = ACPI_TABLE_ORIGIN_MAPPED;
- }
-
- /* Initialize the table entry */
-
- AcpiGbl_RootTableList.Tables[TableIndex].Address = Address;
- AcpiGbl_RootTableList.Tables[TableIndex].Length = TableToInstall->Length;
- AcpiGbl_RootTableList.Tables[TableIndex].Flags = Flags;
-
- ACPI_MOVE_32_TO_32 (
- &(AcpiGbl_RootTableList.Tables[TableIndex].Signature),
- TableToInstall->Signature);
-
- AcpiTbPrintTableHeader (Address, TableToInstall);
-
- if (TableIndex == ACPI_TABLE_INDEX_DSDT)
- {
- /* Global integer width is based upon revision of the DSDT */
-
- AcpiUtSetIntegerWidth (TableToInstall->Revision);
- }
-
-UnmapAndExit:
- AcpiOsUnmapMemory (MappedTable, sizeof (ACPI_TABLE_HEADER));
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiTbGetRootTableEntry
*
* PARAMETERS: TableEntry - Pointer to the RSDT/XSDT table entry
@@ -539,13 +222,14 @@ AcpiTbGetRootTableEntry (
* Get the table physical address (32-bit for RSDT, 64-bit for XSDT):
* Note: Addresses are 32-bit aligned (not 64) in both RSDT and XSDT
*/
- if (TableEntrySize == sizeof (UINT32))
+ if (TableEntrySize == ACPI_RSDT_ENTRY_SIZE)
{
/*
* 32-bit platform, RSDT: Return 32-bit table entry
* 64-bit platform, RSDT: Expand 32-bit to 64-bit and return
*/
- return ((ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST_PTR (UINT32, TableEntry)));
+ return ((ACPI_PHYSICAL_ADDRESS) (*ACPI_CAST_PTR (
+ UINT32, TableEntry)));
}
else
{
@@ -561,7 +245,7 @@ AcpiTbGetRootTableEntry (
{
/* Will truncate 64-bit address to 32 bits, issue warning */
- ACPI_WARNING ((AE_INFO,
+ ACPI_BIOS_WARNING ((AE_INFO,
"64-bit Physical Address in XSDT is too large (0x%8.8X%8.8X),"
" truncating",
ACPI_FORMAT_UINT64 (Address64)));
@@ -602,14 +286,14 @@ AcpiTbParseRootTable (
UINT32 Length;
UINT8 *TableEntry;
ACPI_STATUS Status;
+ UINT32 TableIndex;
ACPI_FUNCTION_TRACE (TbParseRootTable);
- /*
- * Map the entire RSDP and extract the address of the RSDT or XSDT
- */
+ /* Map the entire RSDP and extract the address of the RSDT or XSDT */
+
Rsdp = AcpiOsMapMemory (RsdpAddress, sizeof (ACPI_TABLE_RSDP));
if (!Rsdp)
{
@@ -619,24 +303,26 @@ AcpiTbParseRootTable (
AcpiTbPrintTableHeader (RsdpAddress,
ACPI_CAST_PTR (ACPI_TABLE_HEADER, Rsdp));
- /* Differentiate between RSDT and XSDT root tables */
+ /* Use XSDT if present and not overridden. Otherwise, use RSDT */
- if (Rsdp->Revision > 1 && Rsdp->XsdtPhysicalAddress)
+ if ((Rsdp->Revision > 1) &&
+ Rsdp->XsdtPhysicalAddress &&
+ !AcpiGbl_DoNotUseXsdt)
{
/*
- * Root table is an XSDT (64-bit physical addresses). We must use the
- * XSDT if the revision is > 1 and the XSDT pointer is present, as per
- * the ACPI specification.
+ * RSDP contains an XSDT (64-bit physical addresses). We must use
+ * the XSDT if the revision is > 1 and the XSDT pointer is present,
+ * as per the ACPI specification.
*/
Address = (ACPI_PHYSICAL_ADDRESS) Rsdp->XsdtPhysicalAddress;
- TableEntrySize = sizeof (UINT64);
+ TableEntrySize = ACPI_XSDT_ENTRY_SIZE;
}
else
{
/* Root table is an RSDT (32-bit physical addresses) */
Address = (ACPI_PHYSICAL_ADDRESS) Rsdp->RsdtPhysicalAddress;
- TableEntrySize = sizeof (UINT32);
+ TableEntrySize = ACPI_RSDT_ENTRY_SIZE;
}
/*
@@ -645,7 +331,6 @@ AcpiTbParseRootTable (
*/
AcpiOsUnmapMemory (Rsdp, sizeof (ACPI_TABLE_RSDP));
-
/* Map the RSDT/XSDT table header to get the full table length */
Table = AcpiOsMapMemory (Address, sizeof (ACPI_TABLE_HEADER));
@@ -656,14 +341,17 @@ AcpiTbParseRootTable (
AcpiTbPrintTableHeader (Address, Table);
- /* Get the length of the full table, verify length and map entire table */
-
+ /*
+ * Validate length of the table, and map entire table.
+ * Minimum length table must contain at least one entry.
+ */
Length = Table->Length;
AcpiOsUnmapMemory (Table, sizeof (ACPI_TABLE_HEADER));
- if (Length < sizeof (ACPI_TABLE_HEADER))
+ if (Length < (sizeof (ACPI_TABLE_HEADER) + TableEntrySize))
{
- ACPI_ERROR ((AE_INFO, "Invalid length 0x%X in RSDT/XSDT", Length));
+ ACPI_BIOS_ERROR ((AE_INFO,
+ "Invalid table length 0x%X in RSDT/XSDT", Length));
return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH);
}
@@ -682,71 +370,44 @@ AcpiTbParseRootTable (
return_ACPI_STATUS (Status);
}
- /* Calculate the number of tables described in the root table */
+ /* Get the number of entries and pointer to first entry */
TableCount = (UINT32) ((Table->Length - sizeof (ACPI_TABLE_HEADER)) /
TableEntrySize);
+ TableEntry = ACPI_ADD_PTR (UINT8, Table, sizeof (ACPI_TABLE_HEADER));
- /*
- * First two entries in the table array are reserved for the DSDT
- * and FACS, which are not actually present in the RSDT/XSDT - they
- * come from the FADT
- */
- TableEntry = ACPI_CAST_PTR (UINT8, Table) + sizeof (ACPI_TABLE_HEADER);
- AcpiGbl_RootTableList.CurrentTableCount = 2;
+ /* Initialize the root table array from the RSDT/XSDT */
- /*
- * Initialize the root table array from the RSDT/XSDT
- */
for (i = 0; i < TableCount; i++)
{
- if (AcpiGbl_RootTableList.CurrentTableCount >=
- AcpiGbl_RootTableList.MaxTableCount)
- {
- /* There is no more room in the root table array, attempt resize */
-
- Status = AcpiTbResizeRootTableList ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_WARNING ((AE_INFO, "Truncating %u table entries!",
- (unsigned) (TableCount -
- (AcpiGbl_RootTableList.CurrentTableCount - 2))));
- break;
- }
- }
-
/* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */
- AcpiGbl_RootTableList.Tables[AcpiGbl_RootTableList.CurrentTableCount].Address =
- AcpiTbGetRootTableEntry (TableEntry, TableEntrySize);
-
- TableEntry += TableEntrySize;
- AcpiGbl_RootTableList.CurrentTableCount++;
- }
+ Address = AcpiTbGetRootTableEntry (TableEntry, TableEntrySize);
- /*
- * It is not possible to map more than one entry in some environments,
- * so unmap the root table here before mapping other tables
- */
- AcpiOsUnmapMemory (Table, Length);
+ /* Skip NULL entries in RSDT/XSDT */
- /*
- * Complete the initialization of the root table array by examining
- * the header of each table
- */
- for (i = 2; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
- {
- AcpiTbInstallTable (AcpiGbl_RootTableList.Tables[i].Address,
- NULL, i);
+ if (!Address)
+ {
+ goto NextTable;
+ }
- /* Special case for FADT - get the DSDT and FACS */
+ Status = AcpiTbInstallStandardTable (Address,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, FALSE, TRUE, &TableIndex);
- if (ACPI_COMPARE_NAME (
- &AcpiGbl_RootTableList.Tables[i].Signature, ACPI_SIG_FADT))
+ if (ACPI_SUCCESS (Status) &&
+ ACPI_COMPARE_NAME (
+ &AcpiGbl_RootTableList.Tables[TableIndex].Signature,
+ ACPI_SIG_FADT))
{
- AcpiTbParseFadt (i);
+ AcpiGbl_FadtIndex = TableIndex;
+ AcpiTbParseFadt ();
}
+
+NextTable:
+
+ TableEntry += TableEntrySize;
}
+ AcpiOsUnmapMemory (Table, Length);
return_ACPI_STATUS (AE_OK);
}
diff --git a/usr/src/uts/intel/io/acpica/tables/tbxface.c b/usr/src/uts/intel/io/acpica/tables/tbxface.c
index 465bf95c64..8c0caaee75 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbxface.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbxface.c
@@ -1,12 +1,11 @@
/******************************************************************************
*
- * Module Name: tbxface - Public interfaces to the ACPI subsystem
- * ACPI table oriented interfaces
+ * Module Name: tbxface - ACPI table-oriented external interfaces
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,22 +41,15 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __TBXFACE_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
-#include "acnamesp.h"
#include "actables.h"
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME ("tbxface")
-/* Local prototypes */
-
-static ACPI_STATUS
-AcpiTbLoadNamespace (
- void);
-
/*******************************************************************************
*
@@ -94,7 +86,7 @@ AcpiAllocateRootTable (
* array is dynamically allocated.
* InitialTableCount - Size of InitialTableArray, in number of
* ACPI_TABLE_DESC structures
- * AllowRealloc - Flag to tell Table Manager if resize of
+ * AllowResize - Flag to tell Table Manager if resize of
* pre-allocated array is allowed. Ignored
* if InitialTableArray is NULL.
*
@@ -125,8 +117,8 @@ AcpiInitializeTables (
/*
- * Set up the Root Table Array
- * Allocate the table array if requested
+ * Setup the Root Table Array and allocate the table array
+ * if requested
*/
if (!InitialTableArray)
{
@@ -140,7 +132,7 @@ AcpiInitializeTables (
{
/* Root Table Array has been statically allocated by the host */
- ACPI_MEMSET (InitialTableArray, 0,
+ memset (InitialTableArray, 0,
(ACPI_SIZE) InitialTableCount * sizeof (ACPI_TABLE_DESC));
AcpiGbl_RootTableList.Tables = InitialTableArray;
@@ -169,7 +161,7 @@ AcpiInitializeTables (
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiInitializeTables)
+ACPI_EXPORT_SYMBOL_INIT (AcpiInitializeTables)
/*******************************************************************************
@@ -183,7 +175,7 @@ ACPI_EXPORT_SYMBOL (AcpiInitializeTables)
* DESCRIPTION: Reallocate Root Table List into dynamic memory. Copies the
* root list from the previously provided scratch area. Should
* be called once dynamic memory allocation is available in the
- * kernel
+ * kernel.
*
******************************************************************************/
@@ -191,9 +183,7 @@ ACPI_STATUS
AcpiReallocateRootTable (
void)
{
- ACPI_TABLE_DESC *Tables;
- ACPI_SIZE NewSize;
- ACPI_SIZE CurrentSize;
+ ACPI_STATUS Status;
ACPI_FUNCTION_TRACE (AcpiReallocateRootTable);
@@ -208,41 +198,13 @@ AcpiReallocateRootTable (
return_ACPI_STATUS (AE_SUPPORT);
}
- /*
- * Get the current size of the root table and add the default
- * increment to create the new table size.
- */
- CurrentSize = (ACPI_SIZE)
- AcpiGbl_RootTableList.CurrentTableCount * sizeof (ACPI_TABLE_DESC);
-
- NewSize = CurrentSize +
- (ACPI_ROOT_TABLE_SIZE_INCREMENT * sizeof (ACPI_TABLE_DESC));
-
- /* Create new array and copy the old array */
-
- Tables = ACPI_ALLOCATE_ZEROED (NewSize);
- if (!Tables)
- {
- return_ACPI_STATUS (AE_NO_MEMORY);
- }
-
- ACPI_MEMCPY (Tables, AcpiGbl_RootTableList.Tables, CurrentSize);
-
- /*
- * Update the root table descriptor. The new size will be the current
- * number of tables plus the increment, independent of the reserved
- * size of the original table list.
- */
- AcpiGbl_RootTableList.Tables = Tables;
- AcpiGbl_RootTableList.MaxTableCount =
- AcpiGbl_RootTableList.CurrentTableCount + ACPI_ROOT_TABLE_SIZE_INCREMENT;
- AcpiGbl_RootTableList.Flags =
- ACPI_ROOT_ORIGIN_ALLOCATED | ACPI_ROOT_ALLOW_RESIZE;
+ AcpiGbl_RootTableList.Flags |= ACPI_ROOT_ALLOW_RESIZE;
- return_ACPI_STATUS (AE_OK);
+ Status = AcpiTbResizeRootTableList ();
+ return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiReallocateRootTable)
+ACPI_EXPORT_SYMBOL_INIT (AcpiReallocateRootTable)
/*******************************************************************************
@@ -284,8 +246,8 @@ AcpiGetTableHeader (
for (i = 0, j = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
{
- if (!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
- Signature))
+ if (!ACPI_COMPARE_NAME (
+ &(AcpiGbl_RootTableList.Tables[i].Signature), Signature))
{
continue;
}
@@ -299,29 +261,29 @@ AcpiGetTableHeader (
{
if ((AcpiGbl_RootTableList.Tables[i].Flags &
ACPI_TABLE_ORIGIN_MASK) ==
- ACPI_TABLE_ORIGIN_MAPPED)
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL)
{
Header = AcpiOsMapMemory (
- AcpiGbl_RootTableList.Tables[i].Address,
- sizeof (ACPI_TABLE_HEADER));
+ AcpiGbl_RootTableList.Tables[i].Address,
+ sizeof (ACPI_TABLE_HEADER));
if (!Header)
{
- return AE_NO_MEMORY;
+ return (AE_NO_MEMORY);
}
- ACPI_MEMCPY (OutTableHeader, Header, sizeof(ACPI_TABLE_HEADER));
- AcpiOsUnmapMemory (Header, sizeof(ACPI_TABLE_HEADER));
+ memcpy (OutTableHeader, Header, sizeof (ACPI_TABLE_HEADER));
+ AcpiOsUnmapMemory (Header, sizeof (ACPI_TABLE_HEADER));
}
else
{
- return AE_NOT_FOUND;
+ return (AE_NOT_FOUND);
}
}
else
{
- ACPI_MEMCPY (OutTableHeader,
+ memcpy (OutTableHeader,
AcpiGbl_RootTableList.Tables[i].Pointer,
- sizeof(ACPI_TABLE_HEADER));
+ sizeof (ACPI_TABLE_HEADER));
}
return (AE_OK);
@@ -341,9 +303,10 @@ ACPI_EXPORT_SYMBOL (AcpiGetTableHeader)
* Instance - Which instance (for SSDTs)
* OutTable - Where the pointer to the table is returned
*
- * RETURN: Status and pointer to table
+ * RETURN: Status and pointer to the requested table
*
- * DESCRIPTION: Finds and verifies an ACPI table.
+ * DESCRIPTION: Finds and verifies an ACPI table. Table must be in the
+ * RSDT/XSDT.
*
******************************************************************************/
@@ -369,8 +332,8 @@ AcpiGetTable (
for (i = 0, j = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
{
- if (!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
- Signature))
+ if (!ACPI_COMPARE_NAME (
+ &(AcpiGbl_RootTableList.Tables[i].Signature), Signature))
{
continue;
}
@@ -380,7 +343,7 @@ AcpiGetTable (
continue;
}
- Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[i]);
+ Status = AcpiTbValidateTable (&AcpiGbl_RootTableList.Tables[i]);
if (ACPI_SUCCESS (Status))
{
*OutTable = AcpiGbl_RootTableList.Tables[i].Pointer;
@@ -402,9 +365,10 @@ ACPI_EXPORT_SYMBOL (AcpiGetTable)
* PARAMETERS: TableIndex - Table index
* Table - Where the pointer to the table is returned
*
- * RETURN: Status and pointer to the table
+ * RETURN: Status and pointer to the requested table
*
- * DESCRIPTION: Obtain a table by an index into the global table list.
+ * DESCRIPTION: Obtain a table by an index into the global table list. Used
+ * internally also.
*
******************************************************************************/
@@ -440,7 +404,8 @@ AcpiGetTableByIndex (
{
/* Table is not mapped, map it */
- Status = AcpiTbVerifyTable (&AcpiGbl_RootTableList.Tables[TableIndex]);
+ Status = AcpiTbValidateTable (
+ &AcpiGbl_RootTableList.Tables[TableIndex]);
if (ACPI_FAILURE (Status))
{
(void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
@@ -458,155 +423,6 @@ ACPI_EXPORT_SYMBOL (AcpiGetTableByIndex)
/*******************************************************************************
*
- * FUNCTION: AcpiTbLoadNamespace
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in
- * the RSDT/XSDT.
- *
- ******************************************************************************/
-
-static ACPI_STATUS
-AcpiTbLoadNamespace (
- void)
-{
- ACPI_STATUS Status;
- UINT32 i;
- ACPI_TABLE_HEADER *NewDsdt;
-
-
- ACPI_FUNCTION_TRACE (TbLoadNamespace);
-
-
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
-
- /*
- * Load the namespace. The DSDT is required, but any SSDT and
- * PSDT tables are optional. Verify the DSDT.
- */
- if (!AcpiGbl_RootTableList.CurrentTableCount ||
- !ACPI_COMPARE_NAME (
- &(AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT].Signature),
- ACPI_SIG_DSDT) ||
- ACPI_FAILURE (AcpiTbVerifyTable (
- &AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT])))
- {
- Status = AE_NO_ACPI_TABLES;
- goto UnlockAndExit;
- }
-
- /*
- * Save the DSDT pointer for simple access. This is the mapped memory
- * address. We must take care here because the address of the .Tables
- * array can change dynamically as tables are loaded at run-time. Note:
- * .Pointer field is not validated until after call to AcpiTbVerifyTable.
- */
- AcpiGbl_DSDT = AcpiGbl_RootTableList.Tables[ACPI_TABLE_INDEX_DSDT].Pointer;
-
- /*
- * Optionally copy the entire DSDT to local memory (instead of simply
- * mapping it.) There are some BIOSs that corrupt or replace the original
- * DSDT, creating the need for this option. Default is FALSE, do not copy
- * the DSDT.
- */
- if (AcpiGbl_CopyDsdtLocally)
- {
- NewDsdt = AcpiTbCopyDsdt (ACPI_TABLE_INDEX_DSDT);
- if (NewDsdt)
- {
- AcpiGbl_DSDT = NewDsdt;
- }
- }
-
- /*
- * Save the original DSDT header for detection of table corruption
- * and/or replacement of the DSDT from outside the OS.
- */
- ACPI_MEMCPY (&AcpiGbl_OriginalDsdtHeader, AcpiGbl_DSDT,
- sizeof (ACPI_TABLE_HEADER));
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
-
- /* Load and parse tables */
-
- Status = AcpiNsLoadTable (ACPI_TABLE_INDEX_DSDT, AcpiGbl_RootNode);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
-
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
- {
- if ((!ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
- ACPI_SIG_SSDT) &&
- !ACPI_COMPARE_NAME (&(AcpiGbl_RootTableList.Tables[i].Signature),
- ACPI_SIG_PSDT)) ||
- ACPI_FAILURE (AcpiTbVerifyTable (
- &AcpiGbl_RootTableList.Tables[i])))
- {
- continue;
- }
-
- /* Ignore errors while loading tables, get as many as possible */
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- (void) AcpiNsLoadTable (i, AcpiGbl_RootNode);
- (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
- }
-
- ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "ACPI Tables successfully acquired\n"));
-
-UnlockAndExit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiLoadTables
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiLoadTables (
- void)
-{
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (AcpiLoadTables);
-
-
- /* Load the namespace from the tables */
-
- Status = AcpiTbLoadNamespace ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "While loading namespace from ACPI tables"));
- }
-
- return_ACPI_STATUS (Status);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiLoadTables)
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiInstallTableHandler
*
* PARAMETERS: Handler - Table event handler
@@ -614,7 +430,7 @@ ACPI_EXPORT_SYMBOL (AcpiLoadTables)
*
* RETURN: Status
*
- * DESCRIPTION: Install table event handler
+ * DESCRIPTION: Install a global table event handler.
*
******************************************************************************/
@@ -670,7 +486,7 @@ ACPI_EXPORT_SYMBOL (AcpiInstallTableHandler)
*
* RETURN: Status
*
- * DESCRIPTION: Remove table event handler
+ * DESCRIPTION: Remove a table event handler
*
******************************************************************************/
@@ -709,4 +525,3 @@ Cleanup:
}
ACPI_EXPORT_SYMBOL (AcpiRemoveTableHandler)
-
diff --git a/usr/src/uts/intel/io/acpica/tables/tbxfload.c b/usr/src/uts/intel/io/acpica/tables/tbxfload.c
new file mode 100644
index 0000000000..645b9d57ac
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/tables/tbxfload.c
@@ -0,0 +1,538 @@
+/******************************************************************************
+ *
+ * Module Name: tbxfload - Table load/unload external interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define EXPORT_ACPI_INTERFACES
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+#include "actables.h"
+#include "acevents.h"
+
+#define _COMPONENT ACPI_TABLES
+ ACPI_MODULE_NAME ("tbxfload")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiLoadTables
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiLoadTables (
+ void)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiLoadTables);
+
+
+ /*
+ * Install the default operation region handlers. These are the
+ * handlers that are defined by the ACPI specification to be
+ * "always accessible" -- namely, SystemMemory, SystemIO, and
+ * PCI_Config. This also means that no _REG methods need to be
+ * run for these address spaces. We need to have these handlers
+ * installed before any AML code can be executed, especially any
+ * module-level code (11/2015).
+ * Note that we allow OSPMs to install their own region handlers
+ * between AcpiInitializeSubsystem() and AcpiLoadTables() to use
+ * their customized default region handlers.
+ */
+ Status = AcpiEvInstallRegionHandlers ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During Region initialization"));
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Load the namespace from the tables */
+
+ Status = AcpiTbLoadNamespace ();
+
+ /* Don't let single failures abort the load */
+
+ if (Status == AE_CTRL_TERMINATE)
+ {
+ Status = AE_OK;
+ }
+
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status,
+ "While loading namespace from ACPI tables"));
+ }
+
+ if (!AcpiGbl_GroupModuleLevelCode)
+ {
+ /*
+ * Initialize the objects that remain uninitialized. This
+ * runs the executable AML that may be part of the
+ * declaration of these objects:
+ * OperationRegions, BufferFields, Buffers, and Packages.
+ */
+ Status = AcpiNsInitializeObjects ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ AcpiGbl_NamespaceInitialized = TRUE;
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT (AcpiLoadTables)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbLoadNamespace
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in
+ * the RSDT/XSDT.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiTbLoadNamespace (
+ void)
+{
+ ACPI_STATUS Status;
+ UINT32 i;
+ ACPI_TABLE_HEADER *NewDsdt;
+ ACPI_TABLE_DESC *Table;
+ UINT32 TablesLoaded = 0;
+ UINT32 TablesFailed = 0;
+
+
+ ACPI_FUNCTION_TRACE (TbLoadNamespace);
+
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+
+ /*
+ * Load the namespace. The DSDT is required, but any SSDT and
+ * PSDT tables are optional. Verify the DSDT.
+ */
+ Table = &AcpiGbl_RootTableList.Tables[AcpiGbl_DsdtIndex];
+
+ if (!AcpiGbl_RootTableList.CurrentTableCount ||
+ !ACPI_COMPARE_NAME (Table->Signature.Ascii, ACPI_SIG_DSDT) ||
+ ACPI_FAILURE (AcpiTbValidateTable (Table)))
+ {
+ Status = AE_NO_ACPI_TABLES;
+ goto UnlockAndExit;
+ }
+
+ /*
+ * Save the DSDT pointer for simple access. This is the mapped memory
+ * address. We must take care here because the address of the .Tables
+ * array can change dynamically as tables are loaded at run-time. Note:
+ * .Pointer field is not validated until after call to AcpiTbValidateTable.
+ */
+ AcpiGbl_DSDT = Table->Pointer;
+
+ /*
+ * Optionally copy the entire DSDT to local memory (instead of simply
+ * mapping it.) There are some BIOSs that corrupt or replace the original
+ * DSDT, creating the need for this option. Default is FALSE, do not copy
+ * the DSDT.
+ */
+ if (AcpiGbl_CopyDsdtLocally)
+ {
+ NewDsdt = AcpiTbCopyDsdt (AcpiGbl_DsdtIndex);
+ if (NewDsdt)
+ {
+ AcpiGbl_DSDT = NewDsdt;
+ }
+ }
+
+ /*
+ * Save the original DSDT header for detection of table corruption
+ * and/or replacement of the DSDT from outside the OS.
+ */
+ memcpy (&AcpiGbl_OriginalDsdtHeader, AcpiGbl_DSDT,
+ sizeof (ACPI_TABLE_HEADER));
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+
+ /* Load and parse tables */
+
+ Status = AcpiNsLoadTable (AcpiGbl_DsdtIndex, AcpiGbl_RootNode);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "[DSDT] table load failed"));
+ TablesFailed++;
+ }
+ else
+ {
+ TablesLoaded++;
+ }
+
+ /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; ++i)
+ {
+ Table = &AcpiGbl_RootTableList.Tables[i];
+
+ if (!AcpiGbl_RootTableList.Tables[i].Address ||
+ (!ACPI_COMPARE_NAME (Table->Signature.Ascii, ACPI_SIG_SSDT) &&
+ !ACPI_COMPARE_NAME (Table->Signature.Ascii, ACPI_SIG_PSDT) &&
+ !ACPI_COMPARE_NAME (Table->Signature.Ascii, ACPI_SIG_OSDT)) ||
+ ACPI_FAILURE (AcpiTbValidateTable (Table)))
+ {
+ continue;
+ }
+
+ /* Ignore errors while loading tables, get as many as possible */
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ Status = AcpiNsLoadTable (i, AcpiGbl_RootNode);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "(%4.4s:%8.8s) while loading table",
+ Table->Signature.Ascii, Table->Pointer->OemTableId));
+
+ TablesFailed++;
+
+ ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
+ "Table [%4.4s:%8.8s] (id FF) - Table namespace load failed\n\n",
+ Table->Signature.Ascii, Table->Pointer->OemTableId));
+ }
+ else
+ {
+ TablesLoaded++;
+ }
+
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+ }
+
+ if (!TablesFailed)
+ {
+ ACPI_INFO ((
+ "%u ACPI AML tables successfully acquired and loaded\n",
+ TablesLoaded));
+ }
+ else
+ {
+ ACPI_ERROR ((AE_INFO,
+ "%u table load failures, %u successful",
+ TablesFailed, TablesLoaded));
+
+ /* Indicate at least one failure */
+
+ Status = AE_CTRL_TERMINATE;
+ }
+
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiInstallTable
+ *
+ * PARAMETERS: Address - Address of the ACPI table to be installed.
+ * Physical - Whether the address is a physical table
+ * address or not
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dynamically install an ACPI table.
+ * Note: This function should only be invoked after
+ * AcpiInitializeTables() and before AcpiLoadTables().
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiInstallTable (
+ ACPI_PHYSICAL_ADDRESS Address,
+ BOOLEAN Physical)
+{
+ ACPI_STATUS Status;
+ UINT8 Flags;
+ UINT32 TableIndex;
+
+
+ ACPI_FUNCTION_TRACE (AcpiInstallTable);
+
+
+ if (Physical)
+ {
+ Flags = ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL;
+ }
+ else
+ {
+ Flags = ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL;
+ }
+
+ Status = AcpiTbInstallStandardTable (Address, Flags,
+ FALSE, FALSE, &TableIndex);
+
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT (AcpiInstallTable)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiLoadTable
+ *
+ * PARAMETERS: Table - Pointer to a buffer containing the ACPI
+ * table to be loaded.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dynamically load an ACPI table from the caller's buffer. Must
+ * be a valid ACPI table with a valid ACPI table header.
+ * Note1: Mainly intended to support hotplug addition of SSDTs.
+ * Note2: Does not copy the incoming table. User is responsible
+ * to ensure that the table is not deleted or unmapped.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiLoadTable (
+ ACPI_TABLE_HEADER *Table)
+{
+ ACPI_STATUS Status;
+ UINT32 TableIndex;
+
+
+ ACPI_FUNCTION_TRACE (AcpiLoadTable);
+
+
+ /* Parameter validation */
+
+ if (!Table)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ /* Must acquire the interpreter lock during this operation */
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_INTERPRETER);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Install the table and load it into the namespace */
+
+ ACPI_INFO (("Host-directed Dynamic ACPI Table Load:"));
+ (void) AcpiUtAcquireMutex (ACPI_MTX_TABLES);
+
+ Status = AcpiTbInstallStandardTable (ACPI_PTR_TO_PHYSADDR (Table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL, TRUE, FALSE,
+ &TableIndex);
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_TABLES);
+ if (ACPI_FAILURE (Status))
+ {
+ goto UnlockAndExit;
+ }
+
+ /*
+ * Note: Now table is "INSTALLED", it must be validated before
+ * using.
+ */
+ Status = AcpiTbValidateTable (
+ &AcpiGbl_RootTableList.Tables[TableIndex]);
+ if (ACPI_FAILURE (Status))
+ {
+ goto UnlockAndExit;
+ }
+
+ Status = AcpiNsLoadTable (TableIndex, AcpiGbl_RootNode);
+
+ /* Invoke table handler if present */
+
+ if (AcpiGbl_TableHandler)
+ {
+ (void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_LOAD, Table,
+ AcpiGbl_TableHandlerContext);
+ }
+
+UnlockAndExit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_INTERPRETER);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiLoadTable)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUnloadParentTable
+ *
+ * PARAMETERS: Object - Handle to any namespace object owned by
+ * the table to be unloaded
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Via any namespace object within an SSDT or OEMx table, unloads
+ * the table and deletes all namespace objects associated with
+ * that table. Unloading of the DSDT is not allowed.
+ * Note: Mainly intended to support hotplug removal of SSDTs.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUnloadParentTable (
+ ACPI_HANDLE Object)
+{
+ ACPI_NAMESPACE_NODE *Node = ACPI_CAST_PTR (ACPI_NAMESPACE_NODE, Object);
+ ACPI_STATUS Status = AE_NOT_EXIST;
+ ACPI_OWNER_ID OwnerId;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_TRACE (AcpiUnloadParentTable);
+
+
+ /* Parameter validation */
+
+ if (!Object)
+ {
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ /*
+ * The node OwnerId is currently the same as the parent table ID.
+ * However, this could change in the future.
+ */
+ OwnerId = Node->OwnerId;
+ if (!OwnerId)
+ {
+ /* OwnerId==0 means DSDT is the owner. DSDT cannot be unloaded */
+
+ return_ACPI_STATUS (AE_TYPE);
+ }
+
+ /* Must acquire the interpreter lock during this operation */
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_INTERPRETER);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Find the table in the global table list */
+
+ for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++)
+ {
+ if (OwnerId != AcpiGbl_RootTableList.Tables[i].OwnerId)
+ {
+ continue;
+ }
+
+ /*
+ * Allow unload of SSDT and OEMx tables only. Do not allow unload
+ * of the DSDT. No other types of tables should get here, since
+ * only these types can contain AML and thus are the only types
+ * that can create namespace objects.
+ */
+ if (ACPI_COMPARE_NAME (
+ AcpiGbl_RootTableList.Tables[i].Signature.Ascii,
+ ACPI_SIG_DSDT))
+ {
+ Status = AE_TYPE;
+ break;
+ }
+
+ /* Ensure the table is actually loaded */
+
+ if (!AcpiTbIsTableLoaded (i))
+ {
+ Status = AE_NOT_EXIST;
+ break;
+ }
+
+ /* Invoke table handler if present */
+
+ if (AcpiGbl_TableHandler)
+ {
+ (void) AcpiGbl_TableHandler (ACPI_TABLE_EVENT_UNLOAD,
+ AcpiGbl_RootTableList.Tables[i].Pointer,
+ AcpiGbl_TableHandlerContext);
+ }
+
+ /*
+ * Delete all namespace objects owned by this table. Note that
+ * these objects can appear anywhere in the namespace by virtue
+ * of the AML "Scope" operator. Thus, we need to track ownership
+ * by an ID, not simply a position within the hierarchy.
+ */
+ Status = AcpiTbDeleteNamespaceByOwner (i);
+ if (ACPI_FAILURE (Status))
+ {
+ break;
+ }
+
+ Status = AcpiTbReleaseOwnerId (i);
+ AcpiTbSetTableLoadedFlag (i, FALSE);
+ break;
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_INTERPRETER);
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiUnloadParentTable)
diff --git a/usr/src/uts/intel/io/acpica/tables/tbxfroot.c b/usr/src/uts/intel/io/acpica/tables/tbxfroot.c
index e447108ebe..aaa24c470f 100644
--- a/usr/src/uts/intel/io/acpica/tables/tbxfroot.c
+++ b/usr/src/uts/intel/io/acpica/tables/tbxfroot.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __TBXFROOT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "actables.h"
@@ -51,16 +49,42 @@
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME ("tbxfroot")
-/* Local prototypes */
-static UINT8 *
-AcpiTbScanMemoryForRsdp (
- UINT8 *StartAddress,
- UINT32 Length);
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTbGetRsdpLength
+ *
+ * PARAMETERS: Rsdp - Pointer to RSDP
+ *
+ * RETURN: Table length
+ *
+ * DESCRIPTION: Get the length of the RSDP
+ *
+ ******************************************************************************/
-static ACPI_STATUS
-AcpiTbValidateRsdp (
- ACPI_TABLE_RSDP *Rsdp);
+UINT32
+AcpiTbGetRsdpLength (
+ ACPI_TABLE_RSDP *Rsdp)
+{
+
+ if (!ACPI_VALIDATE_RSDP_SIG (Rsdp->Signature))
+ {
+ /* BAD Signature */
+
+ return (0);
+ }
+
+ /* "Length" field is available if table version >= 2 */
+
+ if (Rsdp->Revision >= 2)
+ {
+ return (Rsdp->Length);
+ }
+ else
+ {
+ return (ACPI_RSDP_CHECKSUM_LENGTH);
+ }
+}
/*******************************************************************************
@@ -75,12 +99,10 @@ AcpiTbValidateRsdp (
*
******************************************************************************/
-static ACPI_STATUS
+ACPI_STATUS
AcpiTbValidateRsdp (
ACPI_TABLE_RSDP *Rsdp)
{
- ACPI_FUNCTION_ENTRY ();
-
/*
* The signature and checksum must both be correct
@@ -88,8 +110,7 @@ AcpiTbValidateRsdp (
* Note: Sometimes there exists more than one RSDP in memory; the valid
* RSDP has a valid checksum, all others have an invalid checksum.
*/
- if (ACPI_STRNCMP ((char *) Rsdp, ACPI_SIG_RSDP,
- sizeof (ACPI_SIG_RSDP)-1) != 0)
+ if (!ACPI_VALIDATE_RSDP_SIG (Rsdp->Signature))
{
/* Nope, BAD Signature */
@@ -124,7 +145,7 @@ AcpiTbValidateRsdp (
* RETURN: Status, RSDP physical address
*
* DESCRIPTION: Search lower 1Mbyte of memory for the root system descriptor
- * pointer structure. If it is found, set *RSDP to point to it.
+ * pointer structure. If it is found, set *RSDP to point to it.
*
* NOTE1: The RSDP must be either in the first 1K of the Extended
* BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.)
@@ -137,7 +158,7 @@ AcpiTbValidateRsdp (
ACPI_STATUS
AcpiFindRootPointer (
- ACPI_SIZE *TableAddress)
+ ACPI_PHYSICAL_ADDRESS *TableAddress)
{
UINT8 *TablePtr;
UINT8 *MemRover;
@@ -150,8 +171,8 @@ AcpiFindRootPointer (
/* 1a) Get the location of the Extended BIOS Data Area (EBDA) */
TablePtr = AcpiOsMapMemory (
- (ACPI_PHYSICAL_ADDRESS) ACPI_EBDA_PTR_LOCATION,
- ACPI_EBDA_PTR_LENGTH);
+ (ACPI_PHYSICAL_ADDRESS) ACPI_EBDA_PTR_LOCATION,
+ ACPI_EBDA_PTR_LENGTH);
if (!TablePtr)
{
ACPI_ERROR ((AE_INFO,
@@ -177,8 +198,8 @@ AcpiFindRootPointer (
* minimum of 1K length)
*/
TablePtr = AcpiOsMapMemory (
- (ACPI_PHYSICAL_ADDRESS) PhysicalAddress,
- ACPI_EBDA_WINDOW_SIZE);
+ (ACPI_PHYSICAL_ADDRESS) PhysicalAddress,
+ ACPI_EBDA_WINDOW_SIZE);
if (!TablePtr)
{
ACPI_ERROR ((AE_INFO,
@@ -188,16 +209,18 @@ AcpiFindRootPointer (
return_ACPI_STATUS (AE_NO_MEMORY);
}
- MemRover = AcpiTbScanMemoryForRsdp (TablePtr, ACPI_EBDA_WINDOW_SIZE);
+ MemRover = AcpiTbScanMemoryForRsdp (
+ TablePtr, ACPI_EBDA_WINDOW_SIZE);
AcpiOsUnmapMemory (TablePtr, ACPI_EBDA_WINDOW_SIZE);
if (MemRover)
{
/* Return the physical address */
- PhysicalAddress += (UINT32) ACPI_PTR_DIFF (MemRover, TablePtr);
+ PhysicalAddress +=
+ (UINT32) ACPI_PTR_DIFF (MemRover, TablePtr);
- *TableAddress = PhysicalAddress;
+ *TableAddress = (ACPI_PHYSICAL_ADDRESS) PhysicalAddress;
return_ACPI_STATUS (AE_OK);
}
}
@@ -206,8 +229,8 @@ AcpiFindRootPointer (
* 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh
*/
TablePtr = AcpiOsMapMemory (
- (ACPI_PHYSICAL_ADDRESS) ACPI_HI_RSDP_WINDOW_BASE,
- ACPI_HI_RSDP_WINDOW_SIZE);
+ (ACPI_PHYSICAL_ADDRESS) ACPI_HI_RSDP_WINDOW_BASE,
+ ACPI_HI_RSDP_WINDOW_SIZE);
if (!TablePtr)
{
@@ -218,7 +241,8 @@ AcpiFindRootPointer (
return_ACPI_STATUS (AE_NO_MEMORY);
}
- MemRover = AcpiTbScanMemoryForRsdp (TablePtr, ACPI_HI_RSDP_WINDOW_SIZE);
+ MemRover = AcpiTbScanMemoryForRsdp (
+ TablePtr, ACPI_HI_RSDP_WINDOW_SIZE);
AcpiOsUnmapMemory (TablePtr, ACPI_HI_RSDP_WINDOW_SIZE);
if (MemRover)
@@ -228,13 +252,13 @@ AcpiFindRootPointer (
PhysicalAddress = (UINT32)
(ACPI_HI_RSDP_WINDOW_BASE + ACPI_PTR_DIFF (MemRover, TablePtr));
- *TableAddress = PhysicalAddress;
+ *TableAddress = (ACPI_PHYSICAL_ADDRESS) PhysicalAddress;
return_ACPI_STATUS (AE_OK);
}
/* A valid RSDP was not found */
- ACPI_ERROR ((AE_INFO, "A valid RSDP was not found"));
+ ACPI_BIOS_ERROR ((AE_INFO, "A valid RSDP was not found"));
return_ACPI_STATUS (AE_NOT_FOUND);
}
@@ -254,7 +278,7 @@ ACPI_EXPORT_SYMBOL (AcpiFindRootPointer)
*
******************************************************************************/
-static UINT8 *
+UINT8 *
AcpiTbScanMemoryForRsdp (
UINT8 *StartAddress,
UINT32 Length)
@@ -276,7 +300,8 @@ AcpiTbScanMemoryForRsdp (
{
/* The RSDP signature and checksum must both be correct */
- Status = AcpiTbValidateRsdp (ACPI_CAST_PTR (ACPI_TABLE_RSDP, MemRover));
+ Status = AcpiTbValidateRsdp (
+ ACPI_CAST_PTR (ACPI_TABLE_RSDP, MemRover));
if (ACPI_SUCCESS (Status))
{
/* Sig and checksum valid, we have found a real RSDP */
@@ -296,4 +321,3 @@ AcpiTbScanMemoryForRsdp (
StartAddress));
return_PTR (NULL);
}
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utaddress.c b/usr/src/uts/intel/io/acpica/utilities/utaddress.c
new file mode 100644
index 0000000000..4b36391e24
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utaddress.c
@@ -0,0 +1,324 @@
+/******************************************************************************
+ *
+ * Module Name: utaddress - OpRegion address range check
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utaddress")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtAddAddressRange
+ *
+ * PARAMETERS: SpaceId - Address space ID
+ * Address - OpRegion start address
+ * Length - OpRegion length
+ * RegionNode - OpRegion namespace node
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add the Operation Region address range to the global list.
+ * The only supported Space IDs are Memory and I/O. Called when
+ * the OpRegion address/length operands are fully evaluated.
+ *
+ * MUTEX: Locks the namespace
+ *
+ * NOTE: Because this interface is only called when an OpRegion argument
+ * list is evaluated, there cannot be any duplicate RegionNodes.
+ * Duplicate Address/Length values are allowed, however, so that multiple
+ * address conflicts can be detected.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtAddAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 Length,
+ ACPI_NAMESPACE_NODE *RegionNode)
+{
+ ACPI_ADDRESS_RANGE *RangeInfo;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (UtAddAddressRange);
+
+
+ if ((SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
+ {
+ return_ACPI_STATUS (AE_OK);
+ }
+
+ /* Allocate/init a new info block, add it to the appropriate list */
+
+ RangeInfo = ACPI_ALLOCATE (sizeof (ACPI_ADDRESS_RANGE));
+ if (!RangeInfo)
+ {
+ return_ACPI_STATUS (AE_NO_MEMORY);
+ }
+
+ RangeInfo->StartAddress = Address;
+ RangeInfo->EndAddress = (Address + Length - 1);
+ RangeInfo->RegionNode = RegionNode;
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_FREE (RangeInfo);
+ return_ACPI_STATUS (Status);
+ }
+
+ RangeInfo->Next = AcpiGbl_AddressRangeList[SpaceId];
+ AcpiGbl_AddressRangeList[SpaceId] = RangeInfo;
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
+ "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
+ AcpiUtGetNodeName (RangeInfo->RegionNode),
+ ACPI_FORMAT_UINT64 (Address),
+ ACPI_FORMAT_UINT64 (RangeInfo->EndAddress)));
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtRemoveAddressRange
+ *
+ * PARAMETERS: SpaceId - Address space ID
+ * RegionNode - OpRegion namespace node
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Remove the Operation Region from the global list. The only
+ * supported Space IDs are Memory and I/O. Called when an
+ * OpRegion is deleted.
+ *
+ * MUTEX: Assumes the namespace is locked
+ *
+ ******************************************************************************/
+
+void
+AcpiUtRemoveAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_NAMESPACE_NODE *RegionNode)
+{
+ ACPI_ADDRESS_RANGE *RangeInfo;
+ ACPI_ADDRESS_RANGE *Prev;
+
+
+ ACPI_FUNCTION_TRACE (UtRemoveAddressRange);
+
+
+ if ((SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
+ {
+ return_VOID;
+ }
+
+ /* Get the appropriate list head and check the list */
+
+ RangeInfo = Prev = AcpiGbl_AddressRangeList[SpaceId];
+ while (RangeInfo)
+ {
+ if (RangeInfo->RegionNode == RegionNode)
+ {
+ if (RangeInfo == Prev) /* Found at list head */
+ {
+ AcpiGbl_AddressRangeList[SpaceId] = RangeInfo->Next;
+ }
+ else
+ {
+ Prev->Next = RangeInfo->Next;
+ }
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES,
+ "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
+ AcpiUtGetNodeName (RangeInfo->RegionNode),
+ ACPI_FORMAT_UINT64 (RangeInfo->StartAddress),
+ ACPI_FORMAT_UINT64 (RangeInfo->EndAddress)));
+
+ ACPI_FREE (RangeInfo);
+ return_VOID;
+ }
+
+ Prev = RangeInfo;
+ RangeInfo = RangeInfo->Next;
+ }
+
+ return_VOID;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtCheckAddressRange
+ *
+ * PARAMETERS: SpaceId - Address space ID
+ * Address - Start address
+ * Length - Length of address range
+ * Warn - TRUE if warning on overlap desired
+ *
+ * RETURN: Count of the number of conflicts detected. Zero is always
+ * returned for Space IDs other than Memory or I/O.
+ *
+ * DESCRIPTION: Check if the input address range overlaps any of the
+ * ASL operation region address ranges. The only supported
+ * Space IDs are Memory and I/O.
+ *
+ * MUTEX: Assumes the namespace is locked.
+ *
+ ******************************************************************************/
+
+UINT32
+AcpiUtCheckAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 Length,
+ BOOLEAN Warn)
+{
+ ACPI_ADDRESS_RANGE *RangeInfo;
+ ACPI_PHYSICAL_ADDRESS EndAddress;
+ char *Pathname;
+ UINT32 OverlapCount = 0;
+
+
+ ACPI_FUNCTION_TRACE (UtCheckAddressRange);
+
+
+ if ((SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
+ (SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
+ {
+ return_UINT32 (0);
+ }
+
+ RangeInfo = AcpiGbl_AddressRangeList[SpaceId];
+ EndAddress = Address + Length - 1;
+
+ /* Check entire list for all possible conflicts */
+
+ while (RangeInfo)
+ {
+ /*
+ * Check if the requested address/length overlaps this
+ * address range. There are four cases to consider:
+ *
+ * 1) Input address/length is contained completely in the
+ * address range
+ * 2) Input address/length overlaps range at the range start
+ * 3) Input address/length overlaps range at the range end
+ * 4) Input address/length completely encompasses the range
+ */
+ if ((Address <= RangeInfo->EndAddress) &&
+ (EndAddress >= RangeInfo->StartAddress))
+ {
+ /* Found an address range overlap */
+
+ OverlapCount++;
+ if (Warn) /* Optional warning message */
+ {
+ Pathname = AcpiNsGetNormalizedPathname (RangeInfo->RegionNode, TRUE);
+
+ ACPI_WARNING ((AE_INFO,
+ "%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)",
+ AcpiUtGetRegionName (SpaceId),
+ ACPI_FORMAT_UINT64 (Address),
+ ACPI_FORMAT_UINT64 (EndAddress),
+ ACPI_FORMAT_UINT64 (RangeInfo->StartAddress),
+ ACPI_FORMAT_UINT64 (RangeInfo->EndAddress),
+ Pathname));
+ ACPI_FREE (Pathname);
+ }
+ }
+
+ RangeInfo = RangeInfo->Next;
+ }
+
+ return_UINT32 (OverlapCount);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDeleteAddressLists
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Delete all global address range lists (called during
+ * subsystem shutdown).
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDeleteAddressLists (
+ void)
+{
+ ACPI_ADDRESS_RANGE *Next;
+ ACPI_ADDRESS_RANGE *RangeInfo;
+ int i;
+
+
+ /* Delete all elements in all address range lists */
+
+ for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++)
+ {
+ Next = AcpiGbl_AddressRangeList[i];
+
+ while (Next)
+ {
+ RangeInfo = Next;
+ Next = RangeInfo->Next;
+ ACPI_FREE (RangeInfo);
+ }
+
+ AcpiGbl_AddressRangeList[i] = NULL;
+ }
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utalloc.c b/usr/src/uts/intel/io/acpica/utilities/utalloc.c
index 4c024f1a30..bb29586e89 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utalloc.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utalloc.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTALLOC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acdebug.h"
@@ -51,6 +49,45 @@
ACPI_MODULE_NAME ("utalloc")
+#if !defined (USE_NATIVE_ALLOCATE_ZEROED)
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiOsAllocateZeroed
+ *
+ * PARAMETERS: Size - Size of the allocation
+ *
+ * RETURN: Address of the allocated memory on success, NULL on failure.
+ *
+ * DESCRIPTION: Subsystem equivalent of calloc. Allocate and zero memory.
+ * This is the default implementation. Can be overridden via the
+ * USE_NATIVE_ALLOCATE_ZEROED flag.
+ *
+ ******************************************************************************/
+
+void *
+AcpiOsAllocateZeroed (
+ ACPI_SIZE Size)
+{
+ void *Allocation;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ Allocation = AcpiOsAllocate (Size);
+ if (Allocation)
+ {
+ /* Clear the memory block */
+
+ memset (Allocation, 0, Size);
+ }
+
+ return (Allocation);
+}
+
+#endif /* !USE_NATIVE_ALLOCATE_ZEROED */
+
+
/*******************************************************************************
*
* FUNCTION: AcpiUtCreateCaches
@@ -73,35 +110,35 @@ AcpiUtCreateCaches (
/* Object Caches, for frequently used objects */
Status = AcpiOsCreateCache ("Acpi-Namespace", sizeof (ACPI_NAMESPACE_NODE),
- ACPI_MAX_NAMESPACE_CACHE_DEPTH, &AcpiGbl_NamespaceCache);
+ ACPI_MAX_NAMESPACE_CACHE_DEPTH, &AcpiGbl_NamespaceCache);
if (ACPI_FAILURE (Status))
{
return (Status);
}
Status = AcpiOsCreateCache ("Acpi-State", sizeof (ACPI_GENERIC_STATE),
- ACPI_MAX_STATE_CACHE_DEPTH, &AcpiGbl_StateCache);
+ ACPI_MAX_STATE_CACHE_DEPTH, &AcpiGbl_StateCache);
if (ACPI_FAILURE (Status))
{
return (Status);
}
Status = AcpiOsCreateCache ("Acpi-Parse", sizeof (ACPI_PARSE_OBJ_COMMON),
- ACPI_MAX_PARSE_CACHE_DEPTH, &AcpiGbl_PsNodeCache);
+ ACPI_MAX_PARSE_CACHE_DEPTH, &AcpiGbl_PsNodeCache);
if (ACPI_FAILURE (Status))
{
return (Status);
}
Status = AcpiOsCreateCache ("Acpi-ParseExt", sizeof (ACPI_PARSE_OBJ_NAMED),
- ACPI_MAX_EXTPARSE_CACHE_DEPTH, &AcpiGbl_PsNodeExtCache);
+ ACPI_MAX_EXTPARSE_CACHE_DEPTH, &AcpiGbl_PsNodeExtCache);
if (ACPI_FAILURE (Status))
{
return (Status);
}
Status = AcpiOsCreateCache ("Acpi-Operand", sizeof (ACPI_OPERAND_OBJECT),
- ACPI_MAX_OBJECT_CACHE_DEPTH, &AcpiGbl_OperandCache);
+ ACPI_MAX_OBJECT_CACHE_DEPTH, &AcpiGbl_OperandCache);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -113,14 +150,14 @@ AcpiUtCreateCaches (
/* Memory allocation lists */
Status = AcpiUtCreateList ("Acpi-Global", 0,
- &AcpiGbl_GlobalList);
+ &AcpiGbl_GlobalList);
if (ACPI_FAILURE (Status))
{
return (Status);
}
Status = AcpiUtCreateList ("Acpi-Namespace", sizeof (ACPI_NAMESPACE_NODE),
- &AcpiGbl_NsNodeList);
+ &AcpiGbl_NsNodeList);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -150,9 +187,10 @@ AcpiUtDeleteCaches (
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
char Buffer[7];
+
if (AcpiGbl_DisplayFinalMemStats)
{
- ACPI_STRCPY (Buffer, "MEMORY");
+ strcpy (Buffer, "MEMORY");
(void) AcpiDbDisplayStatistics (Buffer);
}
#endif
@@ -285,9 +323,13 @@ AcpiUtInitializeBuffer (
return (AE_BUFFER_OVERFLOW);
case ACPI_ALLOCATE_BUFFER:
-
- /* Allocate a new buffer */
-
+ /*
+ * Allocate a new buffer. We directectly call AcpiOsAllocate here to
+ * purposefully bypass the (optionally enabled) internal allocation
+ * tracking mechanism since we only want to track internal
+ * allocations. Note: The caller should use AcpiOsFree to free this
+ * buffer created via ACPI_ALLOCATE_BUFFER.
+ */
Buffer->Pointer = AcpiOsAllocate (RequiredLength);
break;
@@ -318,99 +360,6 @@ AcpiUtInitializeBuffer (
/* Have a valid buffer, clear it */
- ACPI_MEMSET (Buffer->Pointer, 0, RequiredLength);
+ memset (Buffer->Pointer, 0, RequiredLength);
return (AE_OK);
}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtAllocate
- *
- * PARAMETERS: Size - Size of the allocation
- * Component - Component type of caller
- * Module - Source file name of caller
- * Line - Line number of caller
- *
- * RETURN: Address of the allocated memory on success, NULL on failure.
- *
- * DESCRIPTION: Subsystem equivalent of malloc.
- *
- ******************************************************************************/
-
-void *
-AcpiUtAllocate (
- ACPI_SIZE Size,
- UINT32 Component,
- const char *Module,
- UINT32 Line)
-{
- void *Allocation;
-
-
- ACPI_FUNCTION_TRACE_U32 (UtAllocate, Size);
-
-
- /* Check for an inadvertent size of zero bytes */
-
- if (!Size)
- {
- ACPI_WARNING ((Module, Line,
- "Attempt to allocate zero bytes, allocating 1 byte"));
- Size = 1;
- }
-
- Allocation = AcpiOsAllocate (Size);
- if (!Allocation)
- {
- /* Report allocation error */
-
- ACPI_WARNING ((Module, Line,
- "Could not allocate size %u", (UINT32) Size));
-
- return_PTR (NULL);
- }
-
- return_PTR (Allocation);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtAllocateZeroed
- *
- * PARAMETERS: Size - Size of the allocation
- * Component - Component type of caller
- * Module - Source file name of caller
- * Line - Line number of caller
- *
- * RETURN: Address of the allocated memory on success, NULL on failure.
- *
- * DESCRIPTION: Subsystem equivalent of calloc. Allocate and zero memory.
- *
- ******************************************************************************/
-
-void *
-AcpiUtAllocateZeroed (
- ACPI_SIZE Size,
- UINT32 Component,
- const char *Module,
- UINT32 Line)
-{
- void *Allocation;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- Allocation = AcpiUtAllocate (Size, Component, Module, Line);
- if (Allocation)
- {
- /* Clear the memory block */
-
- ACPI_MEMSET (Allocation, 0, Size);
- }
-
- return (Allocation);
-}
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utascii.c b/usr/src/uts/intel/io/acpica/utilities/utascii.c
new file mode 100644
index 0000000000..25c02e674e
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utascii.c
@@ -0,0 +1,161 @@
+/******************************************************************************
+ *
+ * Module Name: utascii - Utility ascii functions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtValidNameseg
+ *
+ * PARAMETERS: Name - The name or table signature to be examined.
+ * Four characters, does not have to be a
+ * NULL terminated string.
+ *
+ * RETURN: TRUE if signature is has 4 valid ACPI characters
+ *
+ * DESCRIPTION: Validate an ACPI table signature.
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiUtValidNameseg (
+ char *Name)
+{
+ UINT32 i;
+
+
+ /* Validate each character in the signature */
+
+ for (i = 0; i < ACPI_NAME_SIZE; i++)
+ {
+ if (!AcpiUtValidNameChar (Name[i], i))
+ {
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtValidNameChar
+ *
+ * PARAMETERS: Char - The character to be examined
+ * Position - Byte position (0-3)
+ *
+ * RETURN: TRUE if the character is valid, FALSE otherwise
+ *
+ * DESCRIPTION: Check for a valid ACPI character. Must be one of:
+ * 1) Upper case alpha
+ * 2) numeric
+ * 3) underscore
+ *
+ * We allow a '!' as the last character because of the ASF! table
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiUtValidNameChar (
+ char Character,
+ UINT32 Position)
+{
+
+ if (!((Character >= 'A' && Character <= 'Z') ||
+ (Character >= '0' && Character <= '9') ||
+ (Character == '_')))
+ {
+ /* Allow a '!' in the last position */
+
+ if (Character == '!' && Position == 3)
+ {
+ return (TRUE);
+ }
+
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtCheckAndRepairAscii
+ *
+ * PARAMETERS: Name - Ascii string
+ * Count - Number of characters to check
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Ensure that the requested number of characters are printable
+ * Ascii characters. Sets non-printable and null chars to <space>.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtCheckAndRepairAscii (
+ UINT8 *Name,
+ char *RepairedName,
+ UINT32 Count)
+{
+ UINT32 i;
+
+
+ for (i = 0; i < Count; i++)
+ {
+ RepairedName[i] = (char) Name[i];
+
+ if (!Name[i])
+ {
+ return;
+ }
+ if (!isprint (Name[i]))
+ {
+ RepairedName[i] = ' ';
+ }
+ }
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utbuffer.c b/usr/src/uts/intel/io/acpica/utilities/utbuffer.c
new file mode 100644
index 0000000000..863055f737
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utbuffer.c
@@ -0,0 +1,362 @@
+/******************************************************************************
+ *
+ * Module Name: utbuffer - Buffer dump routines
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utbuffer")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDumpBuffer
+ *
+ * PARAMETERS: Buffer - Buffer to dump
+ * Count - Amount to dump, in bytes
+ * Display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * BaseOffset - Beginning buffer offset (display only)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDumpBuffer (
+ UINT8 *Buffer,
+ UINT32 Count,
+ UINT32 Display,
+ UINT32 BaseOffset)
+{
+ UINT32 i = 0;
+ UINT32 j;
+ UINT32 Temp32;
+ UINT8 BufChar;
+
+
+ if (!Buffer)
+ {
+ AcpiOsPrintf ("Null Buffer Pointer in DumpBuffer!\n");
+ return;
+ }
+
+ if ((Count < 4) || (Count & 0x01))
+ {
+ Display = DB_BYTE_DISPLAY;
+ }
+
+ /* Nasty little dump buffer routine! */
+
+ while (i < Count)
+ {
+ /* Print current offset */
+
+ AcpiOsPrintf ("%6.4X: ", (BaseOffset + i));
+
+ /* Print 16 hex chars */
+
+ for (j = 0; j < 16;)
+ {
+ if (i + j >= Count)
+ {
+ /* Dump fill spaces */
+
+ AcpiOsPrintf ("%*s", ((Display * 2) + 1), " ");
+ j += Display;
+ continue;
+ }
+
+ switch (Display)
+ {
+ case DB_BYTE_DISPLAY:
+ default: /* Default is BYTE display */
+
+ AcpiOsPrintf ("%02X ", Buffer[(ACPI_SIZE) i + j]);
+ break;
+
+ case DB_WORD_DISPLAY:
+
+ ACPI_MOVE_16_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiOsPrintf ("%04X ", Temp32);
+ break;
+
+ case DB_DWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiOsPrintf ("%08X ", Temp32);
+ break;
+
+ case DB_QWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiOsPrintf ("%08X", Temp32);
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j + 4]);
+ AcpiOsPrintf ("%08X ", Temp32);
+ break;
+ }
+
+ j += Display;
+ }
+
+ /*
+ * Print the ASCII equivalent characters but watch out for the bad
+ * unprintable ones (printable chars are 0x20 through 0x7E)
+ */
+ AcpiOsPrintf (" ");
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j >= Count)
+ {
+ AcpiOsPrintf ("\n");
+ return;
+ }
+
+ /*
+ * Add comment characters so rest of line is ignored when
+ * compiled
+ */
+ if (j == 0)
+ {
+ AcpiOsPrintf ("// ");
+ }
+
+ BufChar = Buffer[(ACPI_SIZE) i + j];
+ if (isprint (BufChar))
+ {
+ AcpiOsPrintf ("%c", BufChar);
+ }
+ else
+ {
+ AcpiOsPrintf (".");
+ }
+ }
+
+ /* Done with that line. */
+
+ AcpiOsPrintf ("\n");
+ i += 16;
+ }
+
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDebugDumpBuffer
+ *
+ * PARAMETERS: Buffer - Buffer to dump
+ * Count - Amount to dump, in bytes
+ * Display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * ComponentID - Caller's component ID
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDebugDumpBuffer (
+ UINT8 *Buffer,
+ UINT32 Count,
+ UINT32 Display,
+ UINT32 ComponentId)
+{
+
+ /* Only dump the buffer if tracing is enabled */
+
+ if (!((ACPI_LV_TABLES & AcpiDbgLevel) &&
+ (ComponentId & AcpiDbgLayer)))
+ {
+ return;
+ }
+
+ AcpiUtDumpBuffer (Buffer, Count, Display, 0);
+}
+
+
+#ifdef ACPI_APPLICATION
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDumpBufferToFile
+ *
+ * PARAMETERS: File - File descriptor
+ * Buffer - Buffer to dump
+ * Count - Amount to dump, in bytes
+ * Display - BYTE, WORD, DWORD, or QWORD display:
+ * DB_BYTE_DISPLAY
+ * DB_WORD_DISPLAY
+ * DB_DWORD_DISPLAY
+ * DB_QWORD_DISPLAY
+ * BaseOffset - Beginning buffer offset (display only)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Generic dump buffer in both hex and ascii to a file.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDumpBufferToFile (
+ ACPI_FILE File,
+ UINT8 *Buffer,
+ UINT32 Count,
+ UINT32 Display,
+ UINT32 BaseOffset)
+{
+ UINT32 i = 0;
+ UINT32 j;
+ UINT32 Temp32;
+ UINT8 BufChar;
+
+
+ if (!Buffer)
+ {
+ AcpiUtFilePrintf (File, "Null Buffer Pointer in DumpBuffer!\n");
+ return;
+ }
+
+ if ((Count < 4) || (Count & 0x01))
+ {
+ Display = DB_BYTE_DISPLAY;
+ }
+
+ /* Nasty little dump buffer routine! */
+
+ while (i < Count)
+ {
+ /* Print current offset */
+
+ AcpiUtFilePrintf (File, "%6.4X: ", (BaseOffset + i));
+
+ /* Print 16 hex chars */
+
+ for (j = 0; j < 16;)
+ {
+ if (i + j >= Count)
+ {
+ /* Dump fill spaces */
+
+ AcpiUtFilePrintf (File, "%*s", ((Display * 2) + 1), " ");
+ j += Display;
+ continue;
+ }
+
+ switch (Display)
+ {
+ case DB_BYTE_DISPLAY:
+ default: /* Default is BYTE display */
+
+ AcpiUtFilePrintf (File, "%02X ", Buffer[(ACPI_SIZE) i + j]);
+ break;
+
+ case DB_WORD_DISPLAY:
+
+ ACPI_MOVE_16_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiUtFilePrintf (File, "%04X ", Temp32);
+ break;
+
+ case DB_DWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiUtFilePrintf (File, "%08X ", Temp32);
+ break;
+
+ case DB_QWORD_DISPLAY:
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
+ AcpiUtFilePrintf (File, "%08X", Temp32);
+
+ ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j + 4]);
+ AcpiUtFilePrintf (File, "%08X ", Temp32);
+ break;
+ }
+
+ j += Display;
+ }
+
+ /*
+ * Print the ASCII equivalent characters but watch out for the bad
+ * unprintable ones (printable chars are 0x20 through 0x7E)
+ */
+ AcpiUtFilePrintf (File, " ");
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j >= Count)
+ {
+ AcpiUtFilePrintf (File, "\n");
+ return;
+ }
+
+ BufChar = Buffer[(ACPI_SIZE) i + j];
+ if (isprint (BufChar))
+ {
+ AcpiUtFilePrintf (File, "%c", BufChar);
+ }
+ else
+ {
+ AcpiUtFilePrintf (File, ".");
+ }
+ }
+
+ /* Done with that line. */
+
+ AcpiUtFilePrintf (File, "\n");
+ i += 16;
+ }
+
+ return;
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utcache.c b/usr/src/uts/intel/io/acpica/utilities/utcache.c
index 96d64b05a5..049dae8e98 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utcache.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utcache.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTCACHE_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -94,11 +92,10 @@ AcpiOsCreateCache (
/* Populate the cache object and return it */
- ACPI_MEMSET (Cache, 0, sizeof (ACPI_MEMORY_LIST));
- Cache->LinkOffset = 8;
- Cache->ListName = CacheName;
+ memset (Cache, 0, sizeof (ACPI_MEMORY_LIST));
+ Cache->ListName = CacheName;
Cache->ObjectSize = ObjectSize;
- Cache->MaxDepth = MaxDepth;
+ Cache->MaxDepth = MaxDepth;
*ReturnCache = Cache;
return (AE_OK);
@@ -121,7 +118,7 @@ ACPI_STATUS
AcpiOsPurgeCache (
ACPI_MEMORY_LIST *Cache)
{
- char *Next;
+ void *Next;
ACPI_STATUS Status;
@@ -145,8 +142,7 @@ AcpiOsPurgeCache (
{
/* Delete and unlink one cached state object */
- Next = *(ACPI_CAST_INDIRECT_PTR (char,
- &(((char *) Cache->ListHead)[Cache->LinkOffset])));
+ Next = ACPI_GET_DESCRIPTOR_PTR (Cache->ListHead);
ACPI_FREE (Cache->ListHead);
Cache->ListHead = Next;
@@ -205,7 +201,7 @@ AcpiOsDeleteCache (
*
* RETURN: None
*
- * DESCRIPTION: Release an object to the specified cache. If cache is full,
+ * DESCRIPTION: Release an object to the specified cache. If cache is full,
* the object is deleted.
*
******************************************************************************/
@@ -246,13 +242,12 @@ AcpiOsReleaseObject (
/* Mark the object as cached */
- ACPI_MEMSET (Object, 0xCA, Cache->ObjectSize);
+ memset (Object, 0xCA, Cache->ObjectSize);
ACPI_SET_DESCRIPTOR_TYPE (Object, ACPI_DESC_TYPE_CACHED);
/* Put the object at the head of the cache list */
- * (ACPI_CAST_INDIRECT_PTR (char,
- &(((char *) Object)[Cache->LinkOffset]))) = Cache->ListHead;
+ ACPI_SET_DESCRIPTOR_PTR (Object, Cache->ListHead);
Cache->ListHead = Object;
Cache->CurrentDepth++;
@@ -269,9 +264,9 @@ AcpiOsReleaseObject (
*
* PARAMETERS: Cache - Handle to cache object
*
- * RETURN: the acquired object. NULL on error
+ * RETURN: the acquired object. NULL on error
*
- * DESCRIPTION: Get an object from the specified cache. If cache is empty,
+ * DESCRIPTION: Get an object from the specified cache. If cache is empty,
* the object is allocated.
*
******************************************************************************/
@@ -284,18 +279,18 @@ AcpiOsAcquireObject (
void *Object;
- ACPI_FUNCTION_NAME (OsAcquireObject);
+ ACPI_FUNCTION_TRACE (OsAcquireObject);
if (!Cache)
{
- return (NULL);
+ return_PTR (NULL);
}
Status = AcpiUtAcquireMutex (ACPI_MTX_CACHES);
if (ACPI_FAILURE (Status))
{
- return (NULL);
+ return_PTR (NULL);
}
ACPI_MEM_TRACKING (Cache->Requests++);
@@ -307,8 +302,7 @@ AcpiOsAcquireObject (
/* There is an object available, use it */
Object = Cache->ListHead;
- Cache->ListHead = *(ACPI_CAST_INDIRECT_PTR (char,
- &(((char *) Object)[Cache->LinkOffset])));
+ Cache->ListHead = ACPI_GET_DESCRIPTOR_PTR (Object);
Cache->CurrentDepth--;
@@ -319,12 +313,12 @@ AcpiOsAcquireObject (
Status = AcpiUtReleaseMutex (ACPI_MTX_CACHES);
if (ACPI_FAILURE (Status))
{
- return (NULL);
+ return_PTR (NULL);
}
/* Clear (zero) the previously used Object */
- ACPI_MEMSET (Object, 0, Cache->ObjectSize);
+ memset (Object, 0, Cache->ObjectSize);
}
else
{
@@ -344,18 +338,16 @@ AcpiOsAcquireObject (
Status = AcpiUtReleaseMutex (ACPI_MTX_CACHES);
if (ACPI_FAILURE (Status))
{
- return (NULL);
+ return_PTR (NULL);
}
Object = ACPI_ALLOCATE_ZEROED (Cache->ObjectSize);
if (!Object)
{
- return (NULL);
+ return_PTR (NULL);
}
}
- return (Object);
+ return_PTR (Object);
}
#endif /* ACPI_USE_LOCAL_CACHE */
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utclib.c b/usr/src/uts/intel/io/acpica/utilities/utclib.c
index 70d17bb1be..8c188d00a4 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utclib.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utclib.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: cmclib - Local implementation of C library functions
+ * Module Name: utclib - ACPICA implementations of C library functions
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,31 +41,62 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __CMCLIB_C__
-
+#define ACPI_CLIBRARY
#include "acpi.h"
#include "accommon.h"
/*
- * These implementations of standard C Library routines can optionally be
- * used if a C library is not available. In general, they are less efficient
- * than an inline or assembly implementation
+ * This module contains implementations of the standard C library functions
+ * that are required by the ACPICA code at both application level and kernel
+ * level.
+ *
+ * The module is an optional feature that can be used if a local/system
+ * C library is not available. Some operating system kernels may not have
+ * an internal C library.
+ *
+ * In general, these functions are less efficient than an inline or assembly
+ * code implementation.
+ *
+ * These C functions and the associated prototypes are enabled by default
+ * unless the ACPI_USE_SYSTEM_CLIBRARY symbol is defined. This is usually
+ * automatically defined for the ACPICA applications such as iASL and
+ * AcpiExec, so that these user-level applications use the local C library
+ * instead of the functions in this module.
*/
-#define _COMPONENT ACPI_UTILITIES
- ACPI_MODULE_NAME ("cmclib")
+/*******************************************************************************
+ *
+ * Functions implemented in this module:
+ *
+ * FUNCTION: memcmp
+ * FUNCTION: memcpy
+ * FUNCTION: memset
+ * FUNCTION: strlen
+ * FUNCTION: strcpy
+ * FUNCTION: strncpy
+ * FUNCTION: strcmp
+ * FUNCTION: strchr
+ * FUNCTION: strncmp
+ * FUNCTION: strcat
+ * FUNCTION: strncat
+ * FUNCTION: strstr
+ * FUNCTION: strtoul
+ * FUNCTION: toupper
+ * FUNCTION: tolower
+ * FUNCTION: is* functions
+ *
+ ******************************************************************************/
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utclib")
-#ifndef ACPI_USE_SYSTEM_CLIBRARY
-#define NEGATIVE 1
-#define POSITIVE 0
+#ifndef ACPI_USE_SYSTEM_CLIBRARY /* Entire module */
/*******************************************************************************
*
- * FUNCTION: AcpiUtMemcmp (memcmp)
+ * FUNCTION: memcmp
*
* PARAMETERS: Buffer1 - First Buffer
* Buffer2 - Second Buffer
@@ -78,11 +109,14 @@
******************************************************************************/
int
-AcpiUtMemcmp (
- const char *Buffer1,
- const char *Buffer2,
+memcmp (
+ void *VBuffer1,
+ void *VBuffer2,
ACPI_SIZE Count)
{
+ char *Buffer1 = (char *) VBuffer1;
+ char *Buffer2 = (char *) VBuffer2;
+
for ( ; Count-- && (*Buffer1 == *Buffer2); Buffer1++, Buffer2++)
{
@@ -95,7 +129,7 @@ AcpiUtMemcmp (
/*******************************************************************************
*
- * FUNCTION: AcpiUtMemcpy (memcpy)
+ * FUNCTION: memcpy
*
* PARAMETERS: Dest - Target of the copy
* Src - Source buffer to copy
@@ -108,7 +142,7 @@ AcpiUtMemcmp (
******************************************************************************/
void *
-AcpiUtMemcpy (
+memcpy (
void *Dest,
const void *Src,
ACPI_SIZE Count)
@@ -131,7 +165,7 @@ AcpiUtMemcpy (
/*******************************************************************************
*
- * FUNCTION: AcpiUtMemset (memset)
+ * FUNCTION: memset
*
* PARAMETERS: Dest - Buffer to set
* Value - Value to set each byte of memory
@@ -144,9 +178,9 @@ AcpiUtMemcpy (
******************************************************************************/
void *
-AcpiUtMemset (
+memset (
void *Dest,
- UINT8 Value,
+ int Value,
ACPI_SIZE Count)
{
char *New = (char *) Dest;
@@ -165,7 +199,7 @@ AcpiUtMemset (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrlen (strlen)
+ * FUNCTION: strlen
*
* PARAMETERS: String - Null terminated string
*
@@ -177,7 +211,7 @@ AcpiUtMemset (
ACPI_SIZE
-AcpiUtStrlen (
+strlen (
const char *String)
{
UINT32 Length = 0;
@@ -197,7 +231,7 @@ AcpiUtStrlen (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrcpy (strcpy)
+ * FUNCTION: strcpy
*
* PARAMETERS: DstString - Target of the copy
* SrcString - The source string to copy
@@ -209,7 +243,7 @@ AcpiUtStrlen (
******************************************************************************/
char *
-AcpiUtStrcpy (
+strcpy (
char *DstString,
const char *SrcString)
{
@@ -235,7 +269,7 @@ AcpiUtStrcpy (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrncpy (strncpy)
+ * FUNCTION: strncpy
*
* PARAMETERS: DstString - Target of the copy
* SrcString - The source string to copy
@@ -248,7 +282,7 @@ AcpiUtStrcpy (
******************************************************************************/
char *
-AcpiUtStrncpy (
+strncpy (
char *DstString,
const char *SrcString,
ACPI_SIZE Count)
@@ -278,7 +312,7 @@ AcpiUtStrncpy (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrcmp (strcmp)
+ * FUNCTION: strcmp
*
* PARAMETERS: String1 - First string
* String2 - Second string
@@ -290,7 +324,7 @@ AcpiUtStrncpy (
******************************************************************************/
int
-AcpiUtStrcmp (
+strcmp (
const char *String1,
const char *String2)
{
@@ -308,11 +342,9 @@ AcpiUtStrcmp (
}
-#ifdef ACPI_FUTURE_IMPLEMENTATION
-/* Not used at this time */
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrchr (strchr)
+ * FUNCTION: strchr
*
* PARAMETERS: String - Search string
* ch - character to search for
@@ -324,7 +356,7 @@ AcpiUtStrcmp (
******************************************************************************/
char *
-AcpiUtStrchr (
+strchr (
const char *String,
int ch)
{
@@ -340,11 +372,11 @@ AcpiUtStrchr (
return (NULL);
}
-#endif
+
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrncmp (strncmp)
+ * FUNCTION: strncmp
*
* PARAMETERS: String1 - First string
* String2 - Second string
@@ -357,7 +389,7 @@ AcpiUtStrchr (
******************************************************************************/
int
-AcpiUtStrncmp (
+strncmp (
const char *String1,
const char *String2,
ACPI_SIZE Count)
@@ -379,7 +411,7 @@ AcpiUtStrncmp (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrcat (Strcat)
+ * FUNCTION: strcat
*
* PARAMETERS: DstString - Target of the copy
* SrcString - The source string to copy
@@ -391,7 +423,7 @@ AcpiUtStrncmp (
******************************************************************************/
char *
-AcpiUtStrcat (
+strcat (
char *DstString,
const char *SrcString)
{
@@ -414,7 +446,7 @@ AcpiUtStrcat (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrncat (strncat)
+ * FUNCTION: strncat
*
* PARAMETERS: DstString - Target of the copy
* SrcString - The source string to copy
@@ -428,7 +460,7 @@ AcpiUtStrcat (
******************************************************************************/
char *
-AcpiUtStrncat (
+strncat (
char *DstString,
const char *SrcString,
ACPI_SIZE Count)
@@ -462,7 +494,7 @@ AcpiUtStrncat (
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrstr (strstr)
+ * FUNCTION: strstr
*
* PARAMETERS: String1 - Target string
* String2 - Substring to search for
@@ -476,38 +508,35 @@ AcpiUtStrncat (
******************************************************************************/
char *
-AcpiUtStrstr (
+strstr (
char *String1,
char *String2)
{
- char *String;
+ UINT32 Length;
- if (AcpiUtStrlen (String2) > AcpiUtStrlen (String1))
+ Length = strlen (String2);
+ if (!Length)
{
- return (NULL);
+ return (String1);
}
- /* Walk entire string, comparing the letters */
-
- for (String = String1; *String2; )
+ while (strlen (String1) >= Length)
{
- if (*String2 != *String)
+ if (memcmp (String1, String2, Length) == 0)
{
- return (NULL);
+ return (String1);
}
-
- String2++;
- String++;
+ String1++;
}
- return (String1);
+ return (NULL);
}
/*******************************************************************************
*
- * FUNCTION: AcpiUtStrtoul (strtoul)
+ * FUNCTION: strtoul
*
* PARAMETERS: String - Null terminated string
* Terminater - Where a pointer to the terminating byte is
@@ -517,12 +546,12 @@ AcpiUtStrstr (
* RETURN: Converted value
*
* DESCRIPTION: Convert a string into a 32-bit unsigned value.
- * Note: use AcpiUtStrtoul64 for 64-bit integers.
+ * Note: use strtoul64 for 64-bit integers.
*
******************************************************************************/
UINT32
-AcpiUtStrtoul (
+strtoul (
const char *String,
char **Terminator,
UINT32 Base)
@@ -541,7 +570,7 @@ AcpiUtStrtoul (
* skip over any white space in the buffer:
*/
StringStart = String;
- while (ACPI_IS_SPACE (*String) || *String == '\t')
+ while (isspace (*String) || *String == '\t')
{
++String;
}
@@ -552,17 +581,17 @@ AcpiUtStrtoul (
*/
if (*String == '-')
{
- sign = NEGATIVE;
+ sign = ACPI_SIGN_NEGATIVE;
++String;
}
else if (*String == '+')
{
++String;
- sign = POSITIVE;
+ sign = ACPI_SIGN_POSITIVE;
}
else
{
- sign = POSITIVE;
+ sign = ACPI_SIGN_POSITIVE;
}
/*
@@ -573,7 +602,7 @@ AcpiUtStrtoul (
{
if (*String == '0')
{
- if (AcpiUtToLower (*(++String)) == 'x')
+ if (tolower (*(++String)) == 'x')
{
Base = 16;
++String;
@@ -608,7 +637,7 @@ AcpiUtStrtoul (
if (Base == 16 &&
*String == '0' &&
- AcpiUtToLower (*(++String)) == 'x')
+ tolower (*(++String)) == 'x')
{
String++;
}
@@ -618,14 +647,14 @@ AcpiUtStrtoul (
*/
while (*String)
{
- if (ACPI_IS_DIGIT (*String))
+ if (isdigit (*String))
{
index = (UINT32) ((UINT8) *String - '0');
}
else
{
- index = (UINT32) AcpiUtToUpper (*String);
- if (ACPI_IS_UPPER (index))
+ index = (UINT32) toupper (*String);
+ if (isupper (index))
{
index = index - 'A' + 10;
}
@@ -685,7 +714,7 @@ done:
/*
* If a minus sign was present, then "the conversion is negated":
*/
- if (sign == NEGATIVE)
+ if (sign == ACPI_SIGN_NEGATIVE)
{
ReturnValue = (ACPI_UINT32_MAX - ReturnValue) + 1;
}
@@ -696,7 +725,7 @@ done:
/*******************************************************************************
*
- * FUNCTION: AcpiUtToUpper (TOUPPER)
+ * FUNCTION: toupper
*
* PARAMETERS: c - Character to convert
*
@@ -707,17 +736,17 @@ done:
******************************************************************************/
int
-AcpiUtToUpper (
+toupper (
int c)
{
- return (ACPI_IS_LOWER(c) ? ((c)-0x20) : (c));
+ return (islower(c) ? ((c)-0x20) : (c));
}
/*******************************************************************************
*
- * FUNCTION: AcpiUtToLower (TOLOWER)
+ * FUNCTION: tolower
*
* PARAMETERS: c - Character to convert
*
@@ -728,151 +757,151 @@ AcpiUtToUpper (
******************************************************************************/
int
-AcpiUtToLower (
+tolower (
int c)
{
- return (ACPI_IS_UPPER(c) ? ((c)+0x20) : (c));
+ return (isupper(c) ? ((c)+0x20) : (c));
}
/*******************************************************************************
*
- * FUNCTION: is* functions
+ * FUNCTION: is* function array
*
* DESCRIPTION: is* functions use the ctype table below
*
******************************************************************************/
-const UINT8 _acpi_ctype[257] = {
- _ACPI_CN, /* 0x0 0. */
- _ACPI_CN, /* 0x1 1. */
- _ACPI_CN, /* 0x2 2. */
- _ACPI_CN, /* 0x3 3. */
- _ACPI_CN, /* 0x4 4. */
- _ACPI_CN, /* 0x5 5. */
- _ACPI_CN, /* 0x6 6. */
- _ACPI_CN, /* 0x7 7. */
- _ACPI_CN, /* 0x8 8. */
- _ACPI_CN|_ACPI_SP, /* 0x9 9. */
- _ACPI_CN|_ACPI_SP, /* 0xA 10. */
- _ACPI_CN|_ACPI_SP, /* 0xB 11. */
- _ACPI_CN|_ACPI_SP, /* 0xC 12. */
- _ACPI_CN|_ACPI_SP, /* 0xD 13. */
- _ACPI_CN, /* 0xE 14. */
- _ACPI_CN, /* 0xF 15. */
- _ACPI_CN, /* 0x10 16. */
- _ACPI_CN, /* 0x11 17. */
- _ACPI_CN, /* 0x12 18. */
- _ACPI_CN, /* 0x13 19. */
- _ACPI_CN, /* 0x14 20. */
- _ACPI_CN, /* 0x15 21. */
- _ACPI_CN, /* 0x16 22. */
- _ACPI_CN, /* 0x17 23. */
- _ACPI_CN, /* 0x18 24. */
- _ACPI_CN, /* 0x19 25. */
- _ACPI_CN, /* 0x1A 26. */
- _ACPI_CN, /* 0x1B 27. */
- _ACPI_CN, /* 0x1C 28. */
- _ACPI_CN, /* 0x1D 29. */
- _ACPI_CN, /* 0x1E 30. */
- _ACPI_CN, /* 0x1F 31. */
- _ACPI_XS|_ACPI_SP, /* 0x20 32. ' ' */
- _ACPI_PU, /* 0x21 33. '!' */
- _ACPI_PU, /* 0x22 34. '"' */
- _ACPI_PU, /* 0x23 35. '#' */
- _ACPI_PU, /* 0x24 36. '$' */
- _ACPI_PU, /* 0x25 37. '%' */
- _ACPI_PU, /* 0x26 38. '&' */
- _ACPI_PU, /* 0x27 39. ''' */
- _ACPI_PU, /* 0x28 40. '(' */
- _ACPI_PU, /* 0x29 41. ')' */
- _ACPI_PU, /* 0x2A 42. '*' */
- _ACPI_PU, /* 0x2B 43. '+' */
- _ACPI_PU, /* 0x2C 44. ',' */
- _ACPI_PU, /* 0x2D 45. '-' */
- _ACPI_PU, /* 0x2E 46. '.' */
- _ACPI_PU, /* 0x2F 47. '/' */
- _ACPI_XD|_ACPI_DI, /* 0x30 48. '0' */
- _ACPI_XD|_ACPI_DI, /* 0x31 49. '1' */
- _ACPI_XD|_ACPI_DI, /* 0x32 50. '2' */
- _ACPI_XD|_ACPI_DI, /* 0x33 51. '3' */
- _ACPI_XD|_ACPI_DI, /* 0x34 52. '4' */
- _ACPI_XD|_ACPI_DI, /* 0x35 53. '5' */
- _ACPI_XD|_ACPI_DI, /* 0x36 54. '6' */
- _ACPI_XD|_ACPI_DI, /* 0x37 55. '7' */
- _ACPI_XD|_ACPI_DI, /* 0x38 56. '8' */
- _ACPI_XD|_ACPI_DI, /* 0x39 57. '9' */
- _ACPI_PU, /* 0x3A 58. ':' */
- _ACPI_PU, /* 0x3B 59. ';' */
- _ACPI_PU, /* 0x3C 60. '<' */
- _ACPI_PU, /* 0x3D 61. '=' */
- _ACPI_PU, /* 0x3E 62. '>' */
- _ACPI_PU, /* 0x3F 63. '?' */
- _ACPI_PU, /* 0x40 64. '@' */
- _ACPI_XD|_ACPI_UP, /* 0x41 65. 'A' */
- _ACPI_XD|_ACPI_UP, /* 0x42 66. 'B' */
- _ACPI_XD|_ACPI_UP, /* 0x43 67. 'C' */
- _ACPI_XD|_ACPI_UP, /* 0x44 68. 'D' */
- _ACPI_XD|_ACPI_UP, /* 0x45 69. 'E' */
- _ACPI_XD|_ACPI_UP, /* 0x46 70. 'F' */
- _ACPI_UP, /* 0x47 71. 'G' */
- _ACPI_UP, /* 0x48 72. 'H' */
- _ACPI_UP, /* 0x49 73. 'I' */
- _ACPI_UP, /* 0x4A 74. 'J' */
- _ACPI_UP, /* 0x4B 75. 'K' */
- _ACPI_UP, /* 0x4C 76. 'L' */
- _ACPI_UP, /* 0x4D 77. 'M' */
- _ACPI_UP, /* 0x4E 78. 'N' */
- _ACPI_UP, /* 0x4F 79. 'O' */
- _ACPI_UP, /* 0x50 80. 'P' */
- _ACPI_UP, /* 0x51 81. 'Q' */
- _ACPI_UP, /* 0x52 82. 'R' */
- _ACPI_UP, /* 0x53 83. 'S' */
- _ACPI_UP, /* 0x54 84. 'T' */
- _ACPI_UP, /* 0x55 85. 'U' */
- _ACPI_UP, /* 0x56 86. 'V' */
- _ACPI_UP, /* 0x57 87. 'W' */
- _ACPI_UP, /* 0x58 88. 'X' */
- _ACPI_UP, /* 0x59 89. 'Y' */
- _ACPI_UP, /* 0x5A 90. 'Z' */
- _ACPI_PU, /* 0x5B 91. '[' */
- _ACPI_PU, /* 0x5C 92. '\' */
- _ACPI_PU, /* 0x5D 93. ']' */
- _ACPI_PU, /* 0x5E 94. '^' */
- _ACPI_PU, /* 0x5F 95. '_' */
- _ACPI_PU, /* 0x60 96. '`' */
- _ACPI_XD|_ACPI_LO, /* 0x61 97. 'a' */
- _ACPI_XD|_ACPI_LO, /* 0x62 98. 'b' */
- _ACPI_XD|_ACPI_LO, /* 0x63 99. 'c' */
- _ACPI_XD|_ACPI_LO, /* 0x64 100. 'd' */
- _ACPI_XD|_ACPI_LO, /* 0x65 101. 'e' */
- _ACPI_XD|_ACPI_LO, /* 0x66 102. 'f' */
- _ACPI_LO, /* 0x67 103. 'g' */
- _ACPI_LO, /* 0x68 104. 'h' */
- _ACPI_LO, /* 0x69 105. 'i' */
- _ACPI_LO, /* 0x6A 106. 'j' */
- _ACPI_LO, /* 0x6B 107. 'k' */
- _ACPI_LO, /* 0x6C 108. 'l' */
- _ACPI_LO, /* 0x6D 109. 'm' */
- _ACPI_LO, /* 0x6E 110. 'n' */
- _ACPI_LO, /* 0x6F 111. 'o' */
- _ACPI_LO, /* 0x70 112. 'p' */
- _ACPI_LO, /* 0x71 113. 'q' */
- _ACPI_LO, /* 0x72 114. 'r' */
- _ACPI_LO, /* 0x73 115. 's' */
- _ACPI_LO, /* 0x74 116. 't' */
- _ACPI_LO, /* 0x75 117. 'u' */
- _ACPI_LO, /* 0x76 118. 'v' */
- _ACPI_LO, /* 0x77 119. 'w' */
- _ACPI_LO, /* 0x78 120. 'x' */
- _ACPI_LO, /* 0x79 121. 'y' */
- _ACPI_LO, /* 0x7A 122. 'z' */
- _ACPI_PU, /* 0x7B 123. '{' */
- _ACPI_PU, /* 0x7C 124. '|' */
- _ACPI_PU, /* 0x7D 125. '}' */
- _ACPI_PU, /* 0x7E 126. '~' */
- _ACPI_CN, /* 0x7F 127. */
+const UINT8 AcpiGbl_Ctypes[257] = {
+ _ACPI_CN, /* 0x00 0 NUL */
+ _ACPI_CN, /* 0x01 1 SOH */
+ _ACPI_CN, /* 0x02 2 STX */
+ _ACPI_CN, /* 0x03 3 ETX */
+ _ACPI_CN, /* 0x04 4 EOT */
+ _ACPI_CN, /* 0x05 5 ENQ */
+ _ACPI_CN, /* 0x06 6 ACK */
+ _ACPI_CN, /* 0x07 7 BEL */
+ _ACPI_CN, /* 0x08 8 BS */
+ _ACPI_CN|_ACPI_SP, /* 0x09 9 TAB */
+ _ACPI_CN|_ACPI_SP, /* 0x0A 10 LF */
+ _ACPI_CN|_ACPI_SP, /* 0x0B 11 VT */
+ _ACPI_CN|_ACPI_SP, /* 0x0C 12 FF */
+ _ACPI_CN|_ACPI_SP, /* 0x0D 13 CR */
+ _ACPI_CN, /* 0x0E 14 SO */
+ _ACPI_CN, /* 0x0F 15 SI */
+ _ACPI_CN, /* 0x10 16 DLE */
+ _ACPI_CN, /* 0x11 17 DC1 */
+ _ACPI_CN, /* 0x12 18 DC2 */
+ _ACPI_CN, /* 0x13 19 DC3 */
+ _ACPI_CN, /* 0x14 20 DC4 */
+ _ACPI_CN, /* 0x15 21 NAK */
+ _ACPI_CN, /* 0x16 22 SYN */
+ _ACPI_CN, /* 0x17 23 ETB */
+ _ACPI_CN, /* 0x18 24 CAN */
+ _ACPI_CN, /* 0x19 25 EM */
+ _ACPI_CN, /* 0x1A 26 SUB */
+ _ACPI_CN, /* 0x1B 27 ESC */
+ _ACPI_CN, /* 0x1C 28 FS */
+ _ACPI_CN, /* 0x1D 29 GS */
+ _ACPI_CN, /* 0x1E 30 RS */
+ _ACPI_CN, /* 0x1F 31 US */
+ _ACPI_XS|_ACPI_SP, /* 0x20 32 ' ' */
+ _ACPI_PU, /* 0x21 33 '!' */
+ _ACPI_PU, /* 0x22 34 '"' */
+ _ACPI_PU, /* 0x23 35 '#' */
+ _ACPI_PU, /* 0x24 36 '$' */
+ _ACPI_PU, /* 0x25 37 '%' */
+ _ACPI_PU, /* 0x26 38 '&' */
+ _ACPI_PU, /* 0x27 39 ''' */
+ _ACPI_PU, /* 0x28 40 '(' */
+ _ACPI_PU, /* 0x29 41 ')' */
+ _ACPI_PU, /* 0x2A 42 '*' */
+ _ACPI_PU, /* 0x2B 43 '+' */
+ _ACPI_PU, /* 0x2C 44 ',' */
+ _ACPI_PU, /* 0x2D 45 '-' */
+ _ACPI_PU, /* 0x2E 46 '.' */
+ _ACPI_PU, /* 0x2F 47 '/' */
+ _ACPI_XD|_ACPI_DI, /* 0x30 48 '0' */
+ _ACPI_XD|_ACPI_DI, /* 0x31 49 '1' */
+ _ACPI_XD|_ACPI_DI, /* 0x32 50 '2' */
+ _ACPI_XD|_ACPI_DI, /* 0x33 51 '3' */
+ _ACPI_XD|_ACPI_DI, /* 0x34 52 '4' */
+ _ACPI_XD|_ACPI_DI, /* 0x35 53 '5' */
+ _ACPI_XD|_ACPI_DI, /* 0x36 54 '6' */
+ _ACPI_XD|_ACPI_DI, /* 0x37 55 '7' */
+ _ACPI_XD|_ACPI_DI, /* 0x38 56 '8' */
+ _ACPI_XD|_ACPI_DI, /* 0x39 57 '9' */
+ _ACPI_PU, /* 0x3A 58 ':' */
+ _ACPI_PU, /* 0x3B 59 ';' */
+ _ACPI_PU, /* 0x3C 60 '<' */
+ _ACPI_PU, /* 0x3D 61 '=' */
+ _ACPI_PU, /* 0x3E 62 '>' */
+ _ACPI_PU, /* 0x3F 63 '?' */
+ _ACPI_PU, /* 0x40 64 '@' */
+ _ACPI_XD|_ACPI_UP, /* 0x41 65 'A' */
+ _ACPI_XD|_ACPI_UP, /* 0x42 66 'B' */
+ _ACPI_XD|_ACPI_UP, /* 0x43 67 'C' */
+ _ACPI_XD|_ACPI_UP, /* 0x44 68 'D' */
+ _ACPI_XD|_ACPI_UP, /* 0x45 69 'E' */
+ _ACPI_XD|_ACPI_UP, /* 0x46 70 'F' */
+ _ACPI_UP, /* 0x47 71 'G' */
+ _ACPI_UP, /* 0x48 72 'H' */
+ _ACPI_UP, /* 0x49 73 'I' */
+ _ACPI_UP, /* 0x4A 74 'J' */
+ _ACPI_UP, /* 0x4B 75 'K' */
+ _ACPI_UP, /* 0x4C 76 'L' */
+ _ACPI_UP, /* 0x4D 77 'M' */
+ _ACPI_UP, /* 0x4E 78 'N' */
+ _ACPI_UP, /* 0x4F 79 'O' */
+ _ACPI_UP, /* 0x50 80 'P' */
+ _ACPI_UP, /* 0x51 81 'Q' */
+ _ACPI_UP, /* 0x52 82 'R' */
+ _ACPI_UP, /* 0x53 83 'S' */
+ _ACPI_UP, /* 0x54 84 'T' */
+ _ACPI_UP, /* 0x55 85 'U' */
+ _ACPI_UP, /* 0x56 86 'V' */
+ _ACPI_UP, /* 0x57 87 'W' */
+ _ACPI_UP, /* 0x58 88 'X' */
+ _ACPI_UP, /* 0x59 89 'Y' */
+ _ACPI_UP, /* 0x5A 90 'Z' */
+ _ACPI_PU, /* 0x5B 91 '[' */
+ _ACPI_PU, /* 0x5C 92 '\' */
+ _ACPI_PU, /* 0x5D 93 ']' */
+ _ACPI_PU, /* 0x5E 94 '^' */
+ _ACPI_PU, /* 0x5F 95 '_' */
+ _ACPI_PU, /* 0x60 96 '`' */
+ _ACPI_XD|_ACPI_LO, /* 0x61 97 'a' */
+ _ACPI_XD|_ACPI_LO, /* 0x62 98 'b' */
+ _ACPI_XD|_ACPI_LO, /* 0x63 99 'c' */
+ _ACPI_XD|_ACPI_LO, /* 0x64 100 'd' */
+ _ACPI_XD|_ACPI_LO, /* 0x65 101 'e' */
+ _ACPI_XD|_ACPI_LO, /* 0x66 102 'f' */
+ _ACPI_LO, /* 0x67 103 'g' */
+ _ACPI_LO, /* 0x68 104 'h' */
+ _ACPI_LO, /* 0x69 105 'i' */
+ _ACPI_LO, /* 0x6A 106 'j' */
+ _ACPI_LO, /* 0x6B 107 'k' */
+ _ACPI_LO, /* 0x6C 108 'l' */
+ _ACPI_LO, /* 0x6D 109 'm' */
+ _ACPI_LO, /* 0x6E 110 'n' */
+ _ACPI_LO, /* 0x6F 111 'o' */
+ _ACPI_LO, /* 0x70 112 'p' */
+ _ACPI_LO, /* 0x71 113 'q' */
+ _ACPI_LO, /* 0x72 114 'r' */
+ _ACPI_LO, /* 0x73 115 's' */
+ _ACPI_LO, /* 0x74 116 't' */
+ _ACPI_LO, /* 0x75 117 'u' */
+ _ACPI_LO, /* 0x76 118 'v' */
+ _ACPI_LO, /* 0x77 119 'w' */
+ _ACPI_LO, /* 0x78 120 'x' */
+ _ACPI_LO, /* 0x79 121 'y' */
+ _ACPI_LO, /* 0x7A 122 'z' */
+ _ACPI_PU, /* 0x7B 123 '{' */
+ _ACPI_PU, /* 0x7C 124 '|' */
+ _ACPI_PU, /* 0x7D 125 '}' */
+ _ACPI_PU, /* 0x7E 126 '~' */
+ _ACPI_CN, /* 0x7F 127 DEL */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 to 0x8F */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90 to 0x9F */
@@ -881,9 +910,9 @@ const UINT8 _acpi_ctype[257] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xC0 to 0xCF */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xD0 to 0xDF */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xE0 to 0xEF */
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xF0 to 0x100 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xF0 to 0xFF */
+ 0 /* 0x100 */
};
#endif /* ACPI_USE_SYSTEM_CLIBRARY */
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utcopy.c b/usr/src/uts/intel/io/acpica/utilities/utcopy.c
index 907f272f7e..40abdfb76b 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utcopy.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utcopy.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTCOPY_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -148,7 +146,7 @@ AcpiUtCopyIsimpleToEsimple (
/* Always clear the external object */
- ACPI_MEMSET (ExternalObject, 0, sizeof (ACPI_OBJECT));
+ memset (ExternalObject, 0, sizeof (ACPI_OBJECT));
/*
* In general, the external object will be the same type as
@@ -165,33 +163,30 @@ AcpiUtCopyIsimpleToEsimple (
ExternalObject->String.Pointer = (char *) DataSpace;
ExternalObject->String.Length = InternalObject->String.Length;
*BufferSpaceUsed = ACPI_ROUND_UP_TO_NATIVE_WORD (
- (ACPI_SIZE) InternalObject->String.Length + 1);
+ (ACPI_SIZE) InternalObject->String.Length + 1);
- ACPI_MEMCPY ((void *) DataSpace,
+ memcpy ((void *) DataSpace,
(void *) InternalObject->String.Pointer,
(ACPI_SIZE) InternalObject->String.Length + 1);
break;
-
case ACPI_TYPE_BUFFER:
ExternalObject->Buffer.Pointer = DataSpace;
ExternalObject->Buffer.Length = InternalObject->Buffer.Length;
*BufferSpaceUsed = ACPI_ROUND_UP_TO_NATIVE_WORD (
- InternalObject->String.Length);
+ InternalObject->String.Length);
- ACPI_MEMCPY ((void *) DataSpace,
+ memcpy ((void *) DataSpace,
(void *) InternalObject->Buffer.Pointer,
InternalObject->Buffer.Length);
break;
-
case ACPI_TYPE_INTEGER:
ExternalObject->Integer.Value = InternalObject->Integer.Value;
break;
-
case ACPI_TYPE_LOCAL_REFERENCE:
/* This is an object reference. */
@@ -199,7 +194,6 @@ AcpiUtCopyIsimpleToEsimple (
switch (InternalObject->Reference.Class)
{
case ACPI_REFCLASS_NAME:
-
/*
* For namepath, return the object handle ("reference")
* We are referring to the namespace node
@@ -218,7 +212,6 @@ AcpiUtCopyIsimpleToEsimple (
}
break;
-
case ACPI_TYPE_PROCESSOR:
ExternalObject->Processor.ProcId =
@@ -229,7 +222,6 @@ AcpiUtCopyIsimpleToEsimple (
InternalObject->Processor.Length;
break;
-
case ACPI_TYPE_POWER:
ExternalObject->PowerResource.SystemLevel =
@@ -239,7 +231,6 @@ AcpiUtCopyIsimpleToEsimple (
InternalObject->PowerResource.ResourceOrder;
break;
-
default:
/*
* There is no corresponding external object type
@@ -284,34 +275,31 @@ AcpiUtCopyIelementToEelement (
ACPI_FUNCTION_ENTRY ();
- ThisIndex = State->Pkg.Index;
- TargetObject = (ACPI_OBJECT *)
- &((ACPI_OBJECT *)(State->Pkg.DestObject))->Package.Elements[ThisIndex];
+ ThisIndex = State->Pkg.Index;
+ TargetObject = (ACPI_OBJECT *) &((ACPI_OBJECT *)
+ (State->Pkg.DestObject))->Package.Elements[ThisIndex];
switch (ObjectType)
{
case ACPI_COPY_TYPE_SIMPLE:
-
/*
* This is a simple or null object
*/
Status = AcpiUtCopyIsimpleToEsimple (SourceObject,
- TargetObject, Info->FreeSpace, &ObjectSpace);
+ TargetObject, Info->FreeSpace, &ObjectSpace);
if (ACPI_FAILURE (Status))
{
return (Status);
}
break;
-
case ACPI_COPY_TYPE_PACKAGE:
-
/*
* Build the package object
*/
- TargetObject->Type = ACPI_TYPE_PACKAGE;
- TargetObject->Package.Count = SourceObject->Package.Count;
- TargetObject->Package.Elements =
+ TargetObject->Type = ACPI_TYPE_PACKAGE;
+ TargetObject->Package.Count = SourceObject->Package.Count;
+ TargetObject->Package.Elements =
ACPI_CAST_PTR (ACPI_OBJECT, Info->FreeSpace);
/*
@@ -324,17 +312,17 @@ AcpiUtCopyIelementToEelement (
* update the buffer length counter
*/
ObjectSpace = ACPI_ROUND_UP_TO_NATIVE_WORD (
- (ACPI_SIZE) TargetObject->Package.Count *
- sizeof (ACPI_OBJECT));
+ (ACPI_SIZE) TargetObject->Package.Count *
+ sizeof (ACPI_OBJECT));
break;
-
default:
+
return (AE_BAD_PARAMETER);
}
- Info->FreeSpace += ObjectSpace;
- Info->Length += ObjectSpace;
+ Info->FreeSpace += ObjectSpace;
+ Info->Length += ObjectSpace;
return (Status);
}
@@ -380,28 +368,28 @@ AcpiUtCopyIpackageToEpackage (
/*
* Free space begins right after the first package
*/
- Info.Length = ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
- Info.FreeSpace = Buffer + ACPI_ROUND_UP_TO_NATIVE_WORD (
- sizeof (ACPI_OBJECT));
+ Info.Length = ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
+ Info.FreeSpace = Buffer +
+ ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
Info.ObjectSpace = 0;
Info.NumPackages = 1;
- ExternalObject->Type = InternalObject->Common.Type;
- ExternalObject->Package.Count = InternalObject->Package.Count;
- ExternalObject->Package.Elements = ACPI_CAST_PTR (ACPI_OBJECT,
- Info.FreeSpace);
+ ExternalObject->Type = InternalObject->Common.Type;
+ ExternalObject->Package.Count = InternalObject->Package.Count;
+ ExternalObject->Package.Elements =
+ ACPI_CAST_PTR (ACPI_OBJECT, Info.FreeSpace);
/*
* Leave room for an array of ACPI_OBJECTS in the buffer
* and move the free space past it
*/
- Info.Length += (ACPI_SIZE) ExternalObject->Package.Count *
- ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
+ Info.Length += (ACPI_SIZE) ExternalObject->Package.Count *
+ ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
Info.FreeSpace += ExternalObject->Package.Count *
- ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
+ ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT));
Status = AcpiUtWalkPackageTree (InternalObject, ExternalObject,
- AcpiUtCopyIelementToEelement, &Info);
+ AcpiUtCopyIelementToEelement, &Info);
*SpaceUsed = Info.Length;
return_ACPI_STATUS (Status);
@@ -440,7 +428,7 @@ AcpiUtCopyIobjectToEobject (
* nested packages)
*/
Status = AcpiUtCopyIpackageToEpackage (InternalObject,
- RetBuffer->Pointer, &RetBuffer->Length);
+ RetBuffer->Pointer, &RetBuffer->Length);
}
else
{
@@ -448,10 +436,10 @@ AcpiUtCopyIobjectToEobject (
* Build a simple object (no nested objects)
*/
Status = AcpiUtCopyIsimpleToEsimple (InternalObject,
- ACPI_CAST_PTR (ACPI_OBJECT, RetBuffer->Pointer),
- ACPI_ADD_PTR (UINT8, RetBuffer->Pointer,
- ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT))),
- &RetBuffer->Length);
+ ACPI_CAST_PTR (ACPI_OBJECT, RetBuffer->Pointer),
+ ACPI_ADD_PTR (UINT8, RetBuffer->Pointer,
+ ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT))),
+ &RetBuffer->Length);
/*
* build simple does not include the object size in the length
* so we add it in here
@@ -501,7 +489,7 @@ AcpiUtCopyEsimpleToIsimple (
case ACPI_TYPE_LOCAL_REFERENCE:
InternalObject = AcpiUtCreateInternalObject (
- (UINT8) ExternalObject->Type);
+ (UINT8) ExternalObject->Type);
if (!InternalObject)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -514,6 +502,7 @@ AcpiUtCopyEsimpleToIsimple (
return_ACPI_STATUS (AE_OK);
default:
+
/* All other types are not supported */
ACPI_ERROR ((AE_INFO,
@@ -539,14 +528,13 @@ AcpiUtCopyEsimpleToIsimple (
goto ErrorExit;
}
- ACPI_MEMCPY (InternalObject->String.Pointer,
- ExternalObject->String.Pointer,
- ExternalObject->String.Length);
+ memcpy (InternalObject->String.Pointer,
+ ExternalObject->String.Pointer,
+ ExternalObject->String.Length);
- InternalObject->String.Length = ExternalObject->String.Length;
+ InternalObject->String.Length = ExternalObject->String.Length;
break;
-
case ACPI_TYPE_BUFFER:
InternalObject->Buffer.Pointer =
@@ -556,33 +544,34 @@ AcpiUtCopyEsimpleToIsimple (
goto ErrorExit;
}
- ACPI_MEMCPY (InternalObject->Buffer.Pointer,
- ExternalObject->Buffer.Pointer,
- ExternalObject->Buffer.Length);
+ memcpy (InternalObject->Buffer.Pointer,
+ ExternalObject->Buffer.Pointer,
+ ExternalObject->Buffer.Length);
- InternalObject->Buffer.Length = ExternalObject->Buffer.Length;
+ InternalObject->Buffer.Length = ExternalObject->Buffer.Length;
/* Mark buffer data valid */
InternalObject->Buffer.Flags |= AOPOBJ_DATA_VALID;
break;
-
case ACPI_TYPE_INTEGER:
- InternalObject->Integer.Value = ExternalObject->Integer.Value;
+ InternalObject->Integer.Value = ExternalObject->Integer.Value;
break;
case ACPI_TYPE_LOCAL_REFERENCE:
- /* TBD: should validate incoming handle */
+ /* An incoming reference is defined to be a namespace node */
- InternalObject->Reference.Class = ACPI_REFCLASS_NAME;
- InternalObject->Reference.Node = ExternalObject->Reference.Handle;
+ InternalObject->Reference.Class = ACPI_REFCLASS_REFOF;
+ InternalObject->Reference.Object = ExternalObject->Reference.Handle;
break;
default:
+
/* Other types can't get here */
+
break;
}
@@ -626,7 +615,8 @@ AcpiUtCopyEpackageToIpackage (
/* Create the package object */
- PackageObject = AcpiUtCreatePackageObject (ExternalObject->Package.Count);
+ PackageObject = AcpiUtCreatePackageObject (
+ ExternalObject->Package.Count);
if (!PackageObject)
{
return_ACPI_STATUS (AE_NO_MEMORY);
@@ -635,14 +625,14 @@ AcpiUtCopyEpackageToIpackage (
PackageElements = PackageObject->Package.Elements;
/*
- * Recursive implementation. Probably ok, since nested external packages
- * as parameters should be very rare.
+ * Recursive implementation. Probably ok, since nested external
+ * packages as parameters should be very rare.
*/
for (i = 0; i < ExternalObject->Package.Count; i++)
{
Status = AcpiUtCopyEobjectToIobject (
- &ExternalObject->Package.Elements[i],
- &PackageElements[i]);
+ &ExternalObject->Package.Elements[i],
+ &PackageElements[i]);
if (ACPI_FAILURE (Status))
{
/* Truncate package and delete it */
@@ -689,14 +679,16 @@ AcpiUtCopyEobjectToIobject (
if (ExternalObject->Type == ACPI_TYPE_PACKAGE)
{
- Status = AcpiUtCopyEpackageToIpackage (ExternalObject, InternalObject);
+ Status = AcpiUtCopyEpackageToIpackage (
+ ExternalObject, InternalObject);
}
else
{
/*
* Build a simple object (no nested objects)
*/
- Status = AcpiUtCopyEsimpleToIsimple (ExternalObject, InternalObject);
+ Status = AcpiUtCopyEsimpleToIsimple (ExternalObject,
+ InternalObject);
}
return_ACPI_STATUS (Status);
@@ -743,7 +735,7 @@ AcpiUtCopySimpleObject (
CopySize = sizeof (ACPI_NAMESPACE_NODE);
}
- ACPI_MEMCPY (ACPI_CAST_PTR (char, DestDesc),
+ memcpy (ACPI_CAST_PTR (char, DestDesc),
ACPI_CAST_PTR (char, SourceDesc), CopySize);
/* Restore the saved fields */
@@ -777,7 +769,7 @@ AcpiUtCopySimpleObject (
/* Copy the actual buffer data */
- ACPI_MEMCPY (DestDesc->Buffer.Pointer,
+ memcpy (DestDesc->Buffer.Pointer,
SourceDesc->Buffer.Pointer, SourceDesc->Buffer.Length);
}
break;
@@ -799,7 +791,7 @@ AcpiUtCopySimpleObject (
/* Copy the actual string data */
- ACPI_MEMCPY (DestDesc->String.Pointer, SourceDesc->String.Pointer,
+ memcpy (DestDesc->String.Pointer, SourceDesc->String.Pointer,
(ACPI_SIZE) SourceDesc->String.Length + 1);
}
break;
@@ -847,7 +839,7 @@ AcpiUtCopySimpleObject (
case ACPI_TYPE_EVENT:
Status = AcpiOsCreateSemaphore (ACPI_NO_UNIT_LIMIT, 0,
- &DestDesc->Event.OsSemaphore);
+ &DestDesc->Event.OsSemaphore);
if (ACPI_FAILURE (Status))
{
return (Status);
@@ -855,7 +847,9 @@ AcpiUtCopySimpleObject (
break;
default:
+
/* Nothing to do for other simple objects */
+
break;
}
@@ -891,9 +885,9 @@ AcpiUtCopyIelementToIelement (
ACPI_FUNCTION_ENTRY ();
- ThisIndex = State->Pkg.Index;
+ ThisIndex = State->Pkg.Index;
ThisTargetPtr = (ACPI_OPERAND_OBJECT **)
- &State->Pkg.DestObject->Package.Elements[ThisIndex];
+ &State->Pkg.DestObject->Package.Elements[ThisIndex];
switch (ObjectType)
{
@@ -907,7 +901,7 @@ AcpiUtCopyIelementToIelement (
* This is a simple object, just copy it
*/
TargetObject = AcpiUtCreateInternalObject (
- SourceObject->Common.Type);
+ SourceObject->Common.Type);
if (!TargetObject)
{
return (AE_NO_MEMORY);
@@ -929,14 +923,13 @@ AcpiUtCopyIelementToIelement (
}
break;
-
case ACPI_COPY_TYPE_PACKAGE:
-
/*
* This object is a package - go down another nesting level
* Create and build the package object
*/
- TargetObject = AcpiUtCreatePackageObject (SourceObject->Package.Count);
+ TargetObject = AcpiUtCreatePackageObject (
+ SourceObject->Package.Count);
if (!TargetObject)
{
return (AE_NO_MEMORY);
@@ -953,8 +946,8 @@ AcpiUtCopyIelementToIelement (
*ThisTargetPtr = TargetObject;
break;
-
default:
+
return (AE_BAD_PARAMETER);
}
@@ -993,16 +986,16 @@ AcpiUtCopyIpackageToIpackage (
ACPI_FUNCTION_TRACE (UtCopyIpackageToIpackage);
- DestObj->Common.Type = SourceObj->Common.Type;
- DestObj->Common.Flags = SourceObj->Common.Flags;
- DestObj->Package.Count = SourceObj->Package.Count;
+ DestObj->Common.Type = SourceObj->Common.Type;
+ DestObj->Common.Flags = SourceObj->Common.Flags;
+ DestObj->Package.Count = SourceObj->Package.Count;
/*
* Create the object array and walk the source package tree
*/
DestObj->Package.Elements = ACPI_ALLOCATE_ZEROED (
- ((ACPI_SIZE) SourceObj->Package.Count + 1) *
- sizeof (void *));
+ ((ACPI_SIZE) SourceObj->Package.Count + 1) *
+ sizeof (void *));
if (!DestObj->Package.Elements)
{
ACPI_ERROR ((AE_INFO, "Package allocation failure"));
@@ -1014,7 +1007,7 @@ AcpiUtCopyIpackageToIpackage (
* This handles nested packages of arbitrary depth.
*/
Status = AcpiUtWalkPackageTree (SourceObj, DestObj,
- AcpiUtCopyIelementToIelement, WalkState);
+ AcpiUtCopyIelementToIelement, WalkState);
if (ACPI_FAILURE (Status))
{
/* On failure, delete the destination package object */
@@ -1064,15 +1057,20 @@ AcpiUtCopyIobjectToIobject (
if (SourceDesc->Common.Type == ACPI_TYPE_PACKAGE)
{
- Status = AcpiUtCopyIpackageToIpackage (SourceDesc, *DestDesc,
- WalkState);
+ Status = AcpiUtCopyIpackageToIpackage (
+ SourceDesc, *DestDesc, WalkState);
}
else
{
Status = AcpiUtCopySimpleObject (SourceDesc, *DestDesc);
}
- return_ACPI_STATUS (Status);
-}
+ /* Delete the allocated object if copy failed */
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiUtRemoveReference (*DestDesc);
+ }
+ return_ACPI_STATUS (Status);
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utdebug.c b/usr/src/uts/intel/io/acpica/utilities/utdebug.c
index 24d2eb5cfa..6a298d175b 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utdebug.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utdebug.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: utdebug - Debug print routines
+ * Module Name: utdebug - Debug print/trace routines
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,10 +41,11 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTDEBUG_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
+#include "acinterp.h"
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME ("utdebug")
@@ -52,15 +53,9 @@
#ifdef ACPI_DEBUG_OUTPUT
-static ACPI_THREAD_ID AcpiGbl_PrevThreadId = (ACPI_THREAD_ID) 0xFFFFFFFF;
-static char *AcpiGbl_FnEntryStr = "----Entry";
-static char *AcpiGbl_FnExitStr = "----Exit-";
-
-/* Local prototypes */
-
-static const char *
-AcpiUtTrimFunctionName (
- const char *FunctionName);
+static ACPI_THREAD_ID AcpiGbl_PreviousThreadId = (ACPI_THREAD_ID) 0xFFFFFFFF;
+static const char *AcpiGbl_FunctionEntryPrefix = "----Entry";
+static const char *AcpiGbl_FunctionExitPrefix = "----Exit-";
/*******************************************************************************
@@ -189,11 +184,9 @@ AcpiDebugPrint (
va_list args;
- /*
- * Stay silent if the debug level or component ID is disabled
- */
- if (!(RequestedDebugLevel & AcpiDbgLevel) ||
- !(ComponentId & AcpiDbgLayer))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (RequestedDebugLevel, ComponentId))
{
return;
}
@@ -202,31 +195,41 @@ AcpiDebugPrint (
* Thread tracking and context switch notification
*/
ThreadId = AcpiOsGetThreadId ();
- if (ThreadId != AcpiGbl_PrevThreadId)
+ if (ThreadId != AcpiGbl_PreviousThreadId)
{
if (ACPI_LV_THREADS & AcpiDbgLevel)
{
AcpiOsPrintf (
"\n**** Context Switch from TID %u to TID %u ****\n\n",
- (UINT32) AcpiGbl_PrevThreadId, (UINT32) ThreadId);
+ (UINT32) AcpiGbl_PreviousThreadId, (UINT32) ThreadId);
}
- AcpiGbl_PrevThreadId = ThreadId;
+ AcpiGbl_PreviousThreadId = ThreadId;
+ AcpiGbl_NestingLevel = 0;
}
/*
* Display the module name, current line number, thread ID (if requested),
* current procedure nesting level, and the current procedure name
*/
- AcpiOsPrintf ("%8s-%04ld ", ModuleName, LineNumber);
+ AcpiOsPrintf ("%9s-%04ld ", ModuleName, LineNumber);
+#ifdef ACPI_APPLICATION
+ /*
+ * For AcpiExec/iASL only, emit the thread ID and nesting level.
+ * Note: nesting level is really only useful during a single-thread
+ * execution. Otherwise, multiple threads will keep resetting the
+ * level.
+ */
if (ACPI_LV_THREADS & AcpiDbgLevel)
{
AcpiOsPrintf ("[%u] ", (UINT32) ThreadId);
}
- AcpiOsPrintf ("[%02ld] %-22.22s: ",
- AcpiGbl_NestingLevel, AcpiUtTrimFunctionName (FunctionName));
+ AcpiOsPrintf ("[%02ld] ", AcpiGbl_NestingLevel);
+#endif
+
+ AcpiOsPrintf ("%-22.22s: ", AcpiUtTrimFunctionName (FunctionName));
va_start (args, Format);
AcpiOsVprintf (Format, args);
@@ -250,7 +253,7 @@ ACPI_EXPORT_SYMBOL (AcpiDebugPrint)
*
* RETURN: None
*
- * DESCRIPTION: Print message with no headers. Has same interface as
+ * DESCRIPTION: Print message with no headers. Has same interface as
* DebugPrint so that the same macros can be used.
*
******************************************************************************/
@@ -268,8 +271,9 @@ AcpiDebugPrintRaw (
va_list args;
- if (!(RequestedDebugLevel & AcpiDbgLevel) ||
- !(ComponentId & AcpiDbgLayer))
+ /* Check if debug output enabled */
+
+ if (!ACPI_IS_DEBUG_ENABLED (RequestedDebugLevel, ComponentId))
{
return;
}
@@ -293,7 +297,7 @@ ACPI_EXPORT_SYMBOL (AcpiDebugPrintRaw)
*
* RETURN: None
*
- * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
* set in DebugLevel
*
******************************************************************************/
@@ -309,9 +313,14 @@ AcpiUtTrace (
AcpiGbl_NestingLevel++;
AcpiUtTrackStackPtr ();
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s\n", AcpiGbl_FnEntryStr);
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s\n", AcpiGbl_FunctionEntryPrefix);
+ }
}
ACPI_EXPORT_SYMBOL (AcpiUtTrace)
@@ -329,7 +338,7 @@ ACPI_EXPORT_SYMBOL (AcpiUtTrace)
*
* RETURN: None
*
- * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
* set in DebugLevel
*
******************************************************************************/
@@ -340,14 +349,20 @@ AcpiUtTracePtr (
const char *FunctionName,
const char *ModuleName,
UINT32 ComponentId,
- void *Pointer)
+ const void *Pointer)
{
+
AcpiGbl_NestingLevel++;
AcpiUtTrackStackPtr ();
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %p\n", AcpiGbl_FnEntryStr, Pointer);
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %p\n", AcpiGbl_FunctionEntryPrefix, Pointer);
+ }
}
@@ -363,7 +378,7 @@ AcpiUtTracePtr (
*
* RETURN: None
*
- * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
* set in DebugLevel
*
******************************************************************************/
@@ -374,15 +389,20 @@ AcpiUtTraceStr (
const char *FunctionName,
const char *ModuleName,
UINT32 ComponentId,
- char *String)
+ const char *String)
{
AcpiGbl_NestingLevel++;
AcpiUtTrackStackPtr ();
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %s\n", AcpiGbl_FnEntryStr, String);
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %s\n", AcpiGbl_FunctionEntryPrefix, String);
+ }
}
@@ -398,7 +418,7 @@ AcpiUtTraceStr (
*
* RETURN: None
*
- * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
+ * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is
* set in DebugLevel
*
******************************************************************************/
@@ -415,9 +435,14 @@ AcpiUtTraceU32 (
AcpiGbl_NestingLevel++;
AcpiUtTrackStackPtr ();
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %08X\n", AcpiGbl_FnEntryStr, Integer);
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %08X\n", AcpiGbl_FunctionEntryPrefix, Integer);
+ }
}
@@ -432,7 +457,7 @@ AcpiUtTraceU32 (
*
* RETURN: None
*
- * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
* set in DebugLevel
*
******************************************************************************/
@@ -445,11 +470,19 @@ AcpiUtExit (
UINT32 ComponentId)
{
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s\n", AcpiGbl_FnExitStr);
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s\n", AcpiGbl_FunctionExitPrefix);
+ }
- AcpiGbl_NestingLevel--;
+ if (AcpiGbl_NestingLevel)
+ {
+ AcpiGbl_NestingLevel--;
+ }
}
ACPI_EXPORT_SYMBOL (AcpiUtExit)
@@ -467,8 +500,8 @@ ACPI_EXPORT_SYMBOL (AcpiUtExit)
*
* RETURN: None
*
- * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
- * set in DebugLevel. Prints exit status also.
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in DebugLevel. Prints exit status also.
*
******************************************************************************/
@@ -481,22 +514,30 @@ AcpiUtStatusExit (
ACPI_STATUS Status)
{
- if (ACPI_SUCCESS (Status))
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
{
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %s\n", AcpiGbl_FnExitStr,
- AcpiFormatException (Status));
+ if (ACPI_SUCCESS (Status))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %s\n", AcpiGbl_FunctionExitPrefix,
+ AcpiFormatException (Status));
+ }
+ else
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s ****Exception****: %s\n", AcpiGbl_FunctionExitPrefix,
+ AcpiFormatException (Status));
+ }
}
- else
+
+ if (AcpiGbl_NestingLevel)
{
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s ****Exception****: %s\n", AcpiGbl_FnExitStr,
- AcpiFormatException (Status));
+ AcpiGbl_NestingLevel--;
}
-
- AcpiGbl_NestingLevel--;
}
ACPI_EXPORT_SYMBOL (AcpiUtStatusExit)
@@ -514,8 +555,8 @@ ACPI_EXPORT_SYMBOL (AcpiUtStatusExit)
*
* RETURN: None
*
- * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
- * set in DebugLevel. Prints exit value also.
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in DebugLevel. Prints exit value also.
*
******************************************************************************/
@@ -528,12 +569,20 @@ AcpiUtValueExit (
UINT64 Value)
{
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %8.8X%8.8X\n", AcpiGbl_FnExitStr,
- ACPI_FORMAT_UINT64 (Value));
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %8.8X%8.8X\n", AcpiGbl_FunctionExitPrefix,
+ ACPI_FORMAT_UINT64 (Value));
+ }
- AcpiGbl_NestingLevel--;
+ if (AcpiGbl_NestingLevel)
+ {
+ AcpiGbl_NestingLevel--;
+ }
}
ACPI_EXPORT_SYMBOL (AcpiUtValueExit)
@@ -551,8 +600,8 @@ ACPI_EXPORT_SYMBOL (AcpiUtValueExit)
*
* RETURN: None
*
- * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
- * set in DebugLevel. Prints exit value also.
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in DebugLevel. Prints exit value also.
*
******************************************************************************/
@@ -565,177 +614,127 @@ AcpiUtPtrExit (
UINT8 *Ptr)
{
- AcpiDebugPrint (ACPI_LV_FUNCTIONS,
- LineNumber, FunctionName, ModuleName, ComponentId,
- "%s %p\n", AcpiGbl_FnExitStr, Ptr);
+ /* Check if enabled up-front for performance */
- AcpiGbl_NestingLevel--;
-}
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
+ {
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %p\n", AcpiGbl_FunctionExitPrefix, Ptr);
+ }
-#endif
+ if (AcpiGbl_NestingLevel)
+ {
+ AcpiGbl_NestingLevel--;
+ }
+}
/*******************************************************************************
*
- * FUNCTION: AcpiUtDumpBuffer
+ * FUNCTION: AcpiUtStrExit
*
- * PARAMETERS: Buffer - Buffer to dump
- * Count - Amount to dump, in bytes
- * Display - BYTE, WORD, DWORD, or QWORD display
- * ComponentID - Caller's component ID
+ * PARAMETERS: LineNumber - Caller's line number
+ * FunctionName - Caller's procedure name
+ * ModuleName - Caller's module name
+ * ComponentId - Caller's component ID
+ * String - String to display
*
* RETURN: None
*
- * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in DebugLevel. Prints exit value also.
*
******************************************************************************/
void
-AcpiUtDumpBuffer2 (
- UINT8 *Buffer,
- UINT32 Count,
- UINT32 Display)
+AcpiUtStrExit (
+ UINT32 LineNumber,
+ const char *FunctionName,
+ const char *ModuleName,
+ UINT32 ComponentId,
+ const char *String)
{
- UINT32 i = 0;
- UINT32 j;
- UINT32 Temp32;
- UINT8 BufChar;
+ /* Check if enabled up-front for performance */
- if (!Buffer)
+ if (ACPI_IS_DEBUG_ENABLED (ACPI_LV_FUNCTIONS, ComponentId))
{
- AcpiOsPrintf ("Null Buffer Pointer in DumpBuffer!\n");
- return;
+ AcpiDebugPrint (ACPI_LV_FUNCTIONS,
+ LineNumber, FunctionName, ModuleName, ComponentId,
+ "%s %s\n", AcpiGbl_FunctionExitPrefix, String);
}
- if ((Count < 4) || (Count & 0x01))
+ if (AcpiGbl_NestingLevel)
{
- Display = DB_BYTE_DISPLAY;
+ AcpiGbl_NestingLevel--;
}
+}
- /* Nasty little dump buffer routine! */
-
- while (i < Count)
- {
- /* Print current offset */
-
- AcpiOsPrintf ("%6.4X: ", i);
-
- /* Print 16 hex chars */
-
- for (j = 0; j < 16;)
- {
- if (i + j >= Count)
- {
- /* Dump fill spaces */
-
- AcpiOsPrintf ("%*s", ((Display * 2) + 1), " ");
- j += Display;
- continue;
- }
-
- switch (Display)
- {
- case DB_BYTE_DISPLAY:
- default: /* Default is BYTE display */
-
- AcpiOsPrintf ("%02X ", Buffer[(ACPI_SIZE) i + j]);
- break;
-
-
- case DB_WORD_DISPLAY:
-
- ACPI_MOVE_16_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
- AcpiOsPrintf ("%04X ", Temp32);
- break;
-
-
- case DB_DWORD_DISPLAY:
-
- ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
- AcpiOsPrintf ("%08X ", Temp32);
- break;
-
-
- case DB_QWORD_DISPLAY:
- ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j]);
- AcpiOsPrintf ("%08X", Temp32);
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiTracePoint
+ *
+ * PARAMETERS: Type - Trace event type
+ * Begin - TRUE if before execution
+ * Aml - Executed AML address
+ * Pathname - Object path
+ * Pointer - Pointer to the related object
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Interpreter execution trace.
+ *
+ ******************************************************************************/
- ACPI_MOVE_32_TO_32 (&Temp32, &Buffer[(ACPI_SIZE) i + j + 4]);
- AcpiOsPrintf ("%08X ", Temp32);
- break;
- }
+void
+AcpiTracePoint (
+ ACPI_TRACE_EVENT_TYPE Type,
+ BOOLEAN Begin,
+ UINT8 *Aml,
+ char *Pathname)
+{
- j += Display;
- }
+ ACPI_FUNCTION_ENTRY ();
- /*
- * Print the ASCII equivalent characters but watch out for the bad
- * unprintable ones (printable chars are 0x20 through 0x7E)
- */
- AcpiOsPrintf (" ");
- for (j = 0; j < 16; j++)
- {
- if (i + j >= Count)
- {
- AcpiOsPrintf ("\n");
- return;
- }
-
- BufChar = Buffer[(ACPI_SIZE) i + j];
- if (ACPI_IS_PRINT (BufChar))
- {
- AcpiOsPrintf ("%c", BufChar);
- }
- else
- {
- AcpiOsPrintf (".");
- }
- }
+ AcpiExTracePoint (Type, Begin, Aml, Pathname);
- /* Done with that line. */
+#ifdef ACPI_USE_SYSTEM_TRACER
+ AcpiOsTracePoint (Type, Begin, Aml, Pathname);
+#endif
+}
- AcpiOsPrintf ("\n");
- i += 16;
- }
+ACPI_EXPORT_SYMBOL (AcpiTracePoint)
- return;
-}
+#endif
+#ifdef ACPI_APPLICATION
/*******************************************************************************
*
- * FUNCTION: AcpiUtDumpBuffer
+ * FUNCTION: AcpiLogError
*
- * PARAMETERS: Buffer - Buffer to dump
- * Count - Amount to dump, in bytes
- * Display - BYTE, WORD, DWORD, or QWORD display
- * ComponentID - Caller's component ID
+ * PARAMETERS: Format - Printf format field
+ * ... - Optional printf arguments
*
* RETURN: None
*
- * DESCRIPTION: Generic dump buffer in both hex and ascii.
+ * DESCRIPTION: Print error message to the console, used by applications.
*
******************************************************************************/
-void
-AcpiUtDumpBuffer (
- UINT8 *Buffer,
- UINT32 Count,
- UINT32 Display,
- UINT32 ComponentId)
+void ACPI_INTERNAL_VAR_XFACE
+AcpiLogError (
+ const char *Format,
+ ...)
{
+ va_list Args;
- /* Only dump the buffer if tracing is enabled */
-
- if (!((ACPI_LV_TABLES & AcpiDbgLevel) &&
- (ComponentId & AcpiDbgLayer)))
- {
- return;
- }
-
- AcpiUtDumpBuffer2 (Buffer, Count, Display);
+ va_start (Args, Format);
+ (void) AcpiUtFileVprintf (ACPI_FILE_ERR, Format, Args);
+ va_end (Args);
}
-
+ACPI_EXPORT_SYMBOL (AcpiLogError)
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utdecode.c b/usr/src/uts/intel/io/acpica/utilities/utdecode.c
index feca6c725c..5c8f2cec80 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utdecode.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utdecode.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTDECODE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -51,47 +49,6 @@
ACPI_MODULE_NAME ("utdecode")
-/*******************************************************************************
- *
- * FUNCTION: AcpiFormatException
- *
- * PARAMETERS: Status - The ACPI_STATUS code to be formatted
- *
- * RETURN: A string containing the exception text. A valid pointer is
- * always returned.
- *
- * DESCRIPTION: This function translates an ACPI exception into an ASCII string
- * It is here instead of utxface.c so it is always present.
- *
- ******************************************************************************/
-
-const char *
-AcpiFormatException (
- ACPI_STATUS Status)
-{
- const char *Exception = NULL;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- Exception = AcpiUtValidateException (Status);
- if (!Exception)
- {
- /* Exception code was not recognized */
-
- ACPI_ERROR ((AE_INFO,
- "Unknown exception code: 0x%8.8X", Status));
-
- Exception = "UNKNOWN_STATUS_CODE";
- }
-
- return (ACPI_CAST_PTR (const char, Exception));
-}
-
-ACPI_EXPORT_SYMBOL (AcpiFormatException)
-
-
/*
* Properties of the ACPI Object Types, both internal and external.
* The table is indexed by values of ACPI_OBJECT_TYPE
@@ -134,38 +91,6 @@ const UINT8 AcpiGbl_NsProperties[ACPI_NUM_NS_TYPES] =
/*******************************************************************************
*
- * FUNCTION: AcpiUtHexToAsciiChar
- *
- * PARAMETERS: Integer - Contains the hex digit
- * Position - bit position of the digit within the
- * integer (multiple of 4)
- *
- * RETURN: The converted Ascii character
- *
- * DESCRIPTION: Convert a hex digit to an Ascii character
- *
- ******************************************************************************/
-
-/* Hex to ASCII conversion table */
-
-static const char AcpiGbl_HexToAscii[] =
-{
- '0','1','2','3','4','5','6','7',
- '8','9','A','B','C','D','E','F'
-};
-
-char
-AcpiUtHexToAsciiChar (
- UINT64 Integer,
- UINT32 Position)
-{
-
- return (AcpiGbl_HexToAscii[(Integer >> Position) & 0xF]);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiUtGetRegionName
*
* PARAMETERS: Space ID - ID for the region
@@ -180,18 +105,21 @@ AcpiUtHexToAsciiChar (
const char *AcpiGbl_RegionTypes[ACPI_NUM_PREDEFINED_REGIONS] =
{
- "SystemMemory",
- "SystemIO",
- "PCI_Config",
- "EmbeddedControl",
- "SMBus",
- "SystemCMOS",
- "PCIBARTarget",
- "IPMI"
+ "SystemMemory", /* 0x00 */
+ "SystemIO", /* 0x01 */
+ "PCI_Config", /* 0x02 */
+ "EmbeddedControl", /* 0x03 */
+ "SMBus", /* 0x04 */
+ "SystemCMOS", /* 0x05 */
+ "PCIBARTarget", /* 0x06 */
+ "IPMI", /* 0x07 */
+ "GeneralPurposeIo", /* 0x08 */
+ "GenericSerialBus", /* 0x09 */
+ "PCC" /* 0x0A */
};
-char *
+const char *
AcpiUtGetRegionName (
UINT8 SpaceId)
{
@@ -213,7 +141,7 @@ AcpiUtGetRegionName (
return ("InvalidSpaceId");
}
- return (ACPI_CAST_PTR (char, AcpiGbl_RegionTypes[SpaceId]));
+ return (AcpiGbl_RegionTypes[SpaceId]);
}
@@ -241,7 +169,7 @@ static const char *AcpiGbl_EventTypes[ACPI_NUM_FIXED_EVENTS] =
};
-char *
+const char *
AcpiUtGetEventName (
UINT32 EventId)
{
@@ -251,7 +179,7 @@ AcpiUtGetEventName (
return ("InvalidEventID");
}
- return (ACPI_CAST_PTR (char, AcpiGbl_EventTypes[EventId]));
+ return (AcpiGbl_EventTypes[EventId]);
}
@@ -273,7 +201,8 @@ AcpiUtGetEventName (
*
* The type ACPI_TYPE_ANY (Untyped) is used as a "don't care" when searching;
* when stored in a table it really means that we have thus far seen no
- * evidence to indicate what type is actually going to be stored for this entry.
+ * evidence to indicate what type is actually going to be stored for this
+ & entry.
*/
static const char AcpiGbl_BadType[] = "UNDEFINED";
@@ -315,31 +244,47 @@ static const char *AcpiGbl_NsTypeNames[] =
};
-char *
+const char *
AcpiUtGetTypeName (
ACPI_OBJECT_TYPE Type)
{
if (Type > ACPI_TYPE_INVALID)
{
- return (ACPI_CAST_PTR (char, AcpiGbl_BadType));
+ return (AcpiGbl_BadType);
}
- return (ACPI_CAST_PTR (char, AcpiGbl_NsTypeNames[Type]));
+ return (AcpiGbl_NsTypeNames[Type]);
}
-char *
+const char *
AcpiUtGetObjectTypeName (
ACPI_OPERAND_OBJECT *ObjDesc)
{
+ ACPI_FUNCTION_TRACE (UtGetObjectTypeName);
+
if (!ObjDesc)
{
- return ("[NULL Object Descriptor]");
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Null Object Descriptor\n"));
+ return_PTR ("[NULL Object Descriptor]");
+ }
+
+ /* These descriptor types share a common area */
+
+ if ((ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) != ACPI_DESC_TYPE_OPERAND) &&
+ (ACPI_GET_DESCRIPTOR_TYPE (ObjDesc) != ACPI_DESC_TYPE_NAMED))
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "Invalid object descriptor type: 0x%2.2X [%s] (%p)\n",
+ ACPI_GET_DESCRIPTOR_TYPE (ObjDesc),
+ AcpiUtGetDescriptorName (ObjDesc), ObjDesc));
+
+ return_PTR ("Invalid object");
}
- return (AcpiUtGetTypeName (ObjDesc->Common.Type));
+ return_STR (AcpiUtGetTypeName (ObjDesc->Common.Type));
}
@@ -355,7 +300,7 @@ AcpiUtGetObjectTypeName (
*
******************************************************************************/
-char *
+const char *
AcpiUtGetNodeName (
void *Object)
{
@@ -431,7 +376,7 @@ static const char *AcpiGbl_DescTypeNames[] =
};
-char *
+const char *
AcpiUtGetDescriptorName (
void *Object)
{
@@ -446,9 +391,7 @@ AcpiUtGetDescriptorName (
return ("Not a Descriptor");
}
- return (ACPI_CAST_PTR (char,
- AcpiGbl_DescTypeNames[ACPI_GET_DESCRIPTOR_TYPE (Object)]));
-
+ return (AcpiGbl_DescTypeNames[ACPI_GET_DESCRIPTOR_TYPE (Object)]);
}
@@ -525,7 +468,7 @@ AcpiUtGetReferenceName (
/* Names for internal mutex objects, used for debug output */
-static char *AcpiGbl_MutexNames[ACPI_NUM_MUTEX] =
+static const char *AcpiGbl_MutexNames[ACPI_NUM_MUTEX] =
{
"ACPI_MTX_Interpreter",
"ACPI_MTX_Namespace",
@@ -533,11 +476,9 @@ static char *AcpiGbl_MutexNames[ACPI_NUM_MUTEX] =
"ACPI_MTX_Events",
"ACPI_MTX_Caches",
"ACPI_MTX_Memory",
- "ACPI_MTX_CommandComplete",
- "ACPI_MTX_CommandReady"
};
-char *
+const char *
AcpiUtGetMutexName (
UINT32 MutexId)
{
@@ -565,39 +506,103 @@ AcpiUtGetMutexName (
/* Names for Notify() values, used for debug output */
-static const char *AcpiGbl_NotifyValueNames[] =
+static const char *AcpiGbl_GenericNotify[ACPI_GENERIC_NOTIFY_MAX + 1] =
+{
+ /* 00 */ "Bus Check",
+ /* 01 */ "Device Check",
+ /* 02 */ "Device Wake",
+ /* 03 */ "Eject Request",
+ /* 04 */ "Device Check Light",
+ /* 05 */ "Frequency Mismatch",
+ /* 06 */ "Bus Mode Mismatch",
+ /* 07 */ "Power Fault",
+ /* 08 */ "Capabilities Check",
+ /* 09 */ "Device PLD Check",
+ /* 0A */ "Reserved",
+ /* 0B */ "System Locality Update",
+ /* 0C */ "Shutdown Request", /* Reserved in ACPI 6.0 */
+ /* 0D */ "System Resource Affinity Update"
+};
+
+static const char *AcpiGbl_DeviceNotify[5] =
+{
+ /* 80 */ "Status Change",
+ /* 81 */ "Information Change",
+ /* 82 */ "Device-Specific Change",
+ /* 83 */ "Device-Specific Change",
+ /* 84 */ "Reserved"
+};
+
+static const char *AcpiGbl_ProcessorNotify[5] =
{
- "Bus Check",
- "Device Check",
- "Device Wake",
- "Eject Request",
- "Device Check Light",
- "Frequency Mismatch",
- "Bus Mode Mismatch",
- "Power Fault",
- "Capabilities Check",
- "Device PLD Check",
- "Reserved",
- "System Locality Update"
+ /* 80 */ "Performance Capability Change",
+ /* 81 */ "C-State Change",
+ /* 82 */ "Throttling Capability Change",
+ /* 83 */ "Guaranteed Change",
+ /* 84 */ "Minimum Excursion"
};
+static const char *AcpiGbl_ThermalNotify[5] =
+{
+ /* 80 */ "Thermal Status Change",
+ /* 81 */ "Thermal Trip Point Change",
+ /* 82 */ "Thermal Device List Change",
+ /* 83 */ "Thermal Relationship Change",
+ /* 84 */ "Reserved"
+};
+
+
const char *
AcpiUtGetNotifyName (
- UINT32 NotifyValue)
+ UINT32 NotifyValue,
+ ACPI_OBJECT_TYPE Type)
{
- if (NotifyValue <= ACPI_NOTIFY_MAX)
+ /* 00 - 0D are "common to all object types" (from ACPI Spec) */
+
+ if (NotifyValue <= ACPI_GENERIC_NOTIFY_MAX)
{
- return (AcpiGbl_NotifyValueNames[NotifyValue]);
+ return (AcpiGbl_GenericNotify[NotifyValue]);
}
- else if (NotifyValue <= ACPI_MAX_SYS_NOTIFY)
+
+ /* 0E - 7F are reserved */
+
+ if (NotifyValue <= ACPI_MAX_SYS_NOTIFY)
{
return ("Reserved");
}
- else /* Greater or equal to 0x80 */
+
+ /* 80 - 84 are per-object-type */
+
+ if (NotifyValue <= ACPI_SPECIFIC_NOTIFY_MAX)
+ {
+ switch (Type)
+ {
+ case ACPI_TYPE_ANY:
+ case ACPI_TYPE_DEVICE:
+ return (AcpiGbl_DeviceNotify [NotifyValue - 0x80]);
+
+ case ACPI_TYPE_PROCESSOR:
+ return (AcpiGbl_ProcessorNotify [NotifyValue - 0x80]);
+
+ case ACPI_TYPE_THERMAL:
+ return (AcpiGbl_ThermalNotify [NotifyValue - 0x80]);
+
+ default:
+ return ("Target object type does not support notifies");
+ }
+ }
+
+ /* 84 - BF are device-specific */
+
+ if (NotifyValue <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY)
{
- return ("**Device Specific**");
+ return ("Device-Specific");
}
+
+ /* C0 and above are hardware-specific */
+
+ return ("Hardware-Specific");
}
#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utdelete.c b/usr/src/uts/intel/io/acpica/utilities/utdelete.c
index b87d2f0d59..95a98f5681 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utdelete.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utdelete.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTDELETE_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -86,6 +84,7 @@ AcpiUtDeleteInternalObj (
ACPI_OPERAND_OBJECT *HandlerDesc;
ACPI_OPERAND_OBJECT *SecondDesc;
ACPI_OPERAND_OBJECT *NextDesc;
+ ACPI_OPERAND_OBJECT *StartDesc;
ACPI_OPERAND_OBJECT **LastObjPtr;
@@ -118,7 +117,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_BUFFER:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "**** Buffer %p, ptr %p\n",
@@ -134,7 +132,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_PACKAGE:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, " **** Package of count %X\n",
@@ -150,7 +147,6 @@ AcpiUtDeleteInternalObj (
ObjPointer = Object->Package.Elements;
break;
-
/*
* These objects have a possible list of notify handlers.
* Device object also may have a GPE block.
@@ -167,7 +163,7 @@ AcpiUtDeleteInternalObj (
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_THERMAL:
- /* Walk the notify handler list for this object */
+ /* Walk the address handler list for this object */
HandlerDesc = Object->CommonNotify.Handler;
while (HandlerDesc)
@@ -178,7 +174,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_MUTEX:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -202,7 +197,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_EVENT:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -213,7 +207,6 @@ AcpiUtDeleteInternalObj (
Object->Event.OsSemaphore = NULL;
break;
-
case ACPI_TYPE_METHOD:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -227,14 +220,28 @@ AcpiUtDeleteInternalObj (
AcpiUtDeleteObjectDesc (Object->Method.Mutex);
Object->Method.Mutex = NULL;
}
- break;
+ if (Object->Method.Node)
+ {
+ Object->Method.Node = NULL;
+ }
+ break;
case ACPI_TYPE_REGION:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
"***** Region %p\n", Object));
+ /*
+ * Update AddressRange list. However, only permanent regions
+ * are installed in this list. (Not created within a method)
+ */
+ if (!(Object->Region.Node->Flags & ANOBJ_TEMPORARY))
+ {
+ AcpiUtRemoveAddressRange (Object->Region.SpaceId,
+ Object->Region.Node);
+ }
+
SecondDesc = AcpiNsGetSecondaryObject (Object);
if (SecondDesc)
{
@@ -247,9 +254,10 @@ AcpiUtDeleteInternalObj (
if (HandlerDesc)
{
NextDesc = HandlerDesc->AddressSpace.RegionList;
+ StartDesc = NextDesc;
LastObjPtr = &HandlerDesc->AddressSpace.RegionList;
- /* Remove the region object from the handler's list */
+ /* Remove the region object from the handler list */
while (NextDesc)
{
@@ -259,10 +267,20 @@ AcpiUtDeleteInternalObj (
break;
}
- /* Walk the linked list of handler */
+ /* Walk the linked list of handlers */
LastObjPtr = &NextDesc->Region.Next;
NextDesc = NextDesc->Region.Next;
+
+ /* Prevent infinite loop if list is corrupted */
+
+ if (NextDesc == StartDesc)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Circular region list in address handler object %p",
+ HandlerDesc));
+ return_VOID;
+ }
}
if (HandlerDesc->AddressSpace.HandlerFlags &
@@ -288,7 +306,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_BUFFER_FIELD:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -301,7 +318,6 @@ AcpiUtDeleteInternalObj (
}
break;
-
case ACPI_TYPE_LOCAL_BANK_FIELD:
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -314,8 +330,8 @@ AcpiUtDeleteInternalObj (
}
break;
-
default:
+
break;
}
@@ -358,7 +374,7 @@ AcpiUtDeleteInternalObjectList (
ACPI_OPERAND_OBJECT **InternalObj;
- ACPI_FUNCTION_TRACE (UtDeleteInternalObjectList);
+ ACPI_FUNCTION_ENTRY ();
/* Walk the null-terminated internal list */
@@ -371,7 +387,7 @@ AcpiUtDeleteInternalObjectList (
/* Free the combined parameter pointer list and object array */
ACPI_FREE (ObjList);
- return_VOID;
+ return;
}
@@ -380,11 +396,11 @@ AcpiUtDeleteInternalObjectList (
* FUNCTION: AcpiUtUpdateRefCount
*
* PARAMETERS: Object - Object whose ref count is to be updated
- * Action - What to do
+ * Action - What to do (REF_INCREMENT or REF_DECREMENT)
*
- * RETURN: New ref count
+ * RETURN: None. Sets new reference count within the object
*
- * DESCRIPTION: Modify the ref count and return it.
+ * DESCRIPTION: Modify the reference count for an internal acpi object
*
******************************************************************************/
@@ -393,8 +409,9 @@ AcpiUtUpdateRefCount (
ACPI_OPERAND_OBJECT *Object,
UINT32 Action)
{
- UINT16 Count;
- UINT16 NewCount;
+ UINT16 OriginalCount;
+ UINT16 NewCount = 0;
+ ACPI_CPU_FLAGS LockFlags;
ACPI_FUNCTION_NAME (UtUpdateRefCount);
@@ -405,80 +422,85 @@ AcpiUtUpdateRefCount (
return;
}
- Count = Object->Common.ReferenceCount;
- NewCount = Count;
-
/*
- * Perform the reference count action (increment, decrement, force delete)
+ * Always get the reference count lock. Note: Interpreter and/or
+ * Namespace is not always locked when this function is called.
*/
+ LockFlags = AcpiOsAcquireLock (AcpiGbl_ReferenceCountLock);
+ OriginalCount = Object->Common.ReferenceCount;
+
+ /* Perform the reference count action (increment, decrement) */
+
switch (Action)
{
case REF_INCREMENT:
- NewCount++;
+ NewCount = OriginalCount + 1;
Object->Common.ReferenceCount = NewCount;
+ AcpiOsReleaseLock (AcpiGbl_ReferenceCountLock, LockFlags);
+
+ /* The current reference count should never be zero here */
+
+ if (!OriginalCount)
+ {
+ ACPI_WARNING ((AE_INFO,
+ "Obj %p, Reference Count was zero before increment\n",
+ Object));
+ }
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
- "Obj %p Refs=%X, [Incremented]\n",
- Object, NewCount));
+ "Obj %p Type %.2X Refs %.2X [Incremented]\n",
+ Object, Object->Common.Type, NewCount));
break;
case REF_DECREMENT:
- if (Count < 1)
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
- "Obj %p Refs=%X, can't decrement! (Set to 0)\n",
- Object, NewCount));
+ /* The current reference count must be non-zero */
- NewCount = 0;
- }
- else
+ if (OriginalCount)
{
- NewCount--;
-
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
- "Obj %p Refs=%X, [Decremented]\n",
- Object, NewCount));
+ NewCount = OriginalCount - 1;
+ Object->Common.ReferenceCount = NewCount;
}
- if (Object->Common.Type == ACPI_TYPE_METHOD)
+ AcpiOsReleaseLock (AcpiGbl_ReferenceCountLock, LockFlags);
+
+ if (!OriginalCount)
{
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
- "Method Obj %p Refs=%X, [Decremented]\n", Object, NewCount));
+ ACPI_WARNING ((AE_INFO,
+ "Obj %p, Reference Count is already zero, cannot decrement\n",
+ Object));
}
- Object->Common.ReferenceCount = NewCount;
+ ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
+ "Obj %p Type %.2X Refs %.2X [Decremented]\n",
+ Object, Object->Common.Type, NewCount));
+
+ /* Actually delete the object on a reference count of zero */
+
if (NewCount == 0)
{
AcpiUtDeleteInternalObj (Object);
}
break;
- case REF_FORCE_DELETE:
-
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
- "Obj %p Refs=%X, Force delete! (Set to 0)\n", Object, Count));
-
- NewCount = 0;
- Object->Common.ReferenceCount = NewCount;
- AcpiUtDeleteInternalObj (Object);
- break;
-
default:
- ACPI_ERROR ((AE_INFO, "Unknown action (0x%X)", Action));
- break;
+ AcpiOsReleaseLock (AcpiGbl_ReferenceCountLock, LockFlags);
+ ACPI_ERROR ((AE_INFO, "Unknown Reference Count action (0x%X)",
+ Action));
+ return;
}
/*
* Sanity check the reference count, for debug purposes only.
* (A deleted object will have a huge reference count)
*/
- if (Count > ACPI_MAX_REFERENCE_COUNT)
+ if (NewCount > ACPI_MAX_REFERENCE_COUNT)
{
ACPI_WARNING ((AE_INFO,
- "Large Reference Count (0x%X) in object %p", Count, Object));
+ "Large Reference Count (0x%X) in object %p, Type=0x%.2X",
+ NewCount, Object, Object->Common.Type));
}
}
@@ -489,8 +511,7 @@ AcpiUtUpdateRefCount (
*
* PARAMETERS: Object - Increment ref count for this object
* and all sub-objects
- * Action - Either REF_INCREMENT or REF_DECREMENT or
- * REF_FORCE_DELETE
+ * Action - Either REF_INCREMENT or REF_DECREMENT
*
* RETURN: Status
*
@@ -513,11 +534,12 @@ AcpiUtUpdateObjectReference (
ACPI_STATUS Status = AE_OK;
ACPI_GENERIC_STATE *StateList = NULL;
ACPI_OPERAND_OBJECT *NextObject = NULL;
+ ACPI_OPERAND_OBJECT *PrevObject;
ACPI_GENERIC_STATE *State;
UINT32 i;
- ACPI_FUNCTION_TRACE_PTR (UtUpdateObjectReference, Object);
+ ACPI_FUNCTION_NAME (UtUpdateObjectReference);
while (Object)
@@ -528,12 +550,12 @@ AcpiUtUpdateObjectReference (
{
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
"Object %p is NS handle\n", Object));
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
/*
- * All sub-objects must have their reference count incremented also.
- * Different object types have different subobjects.
+ * All sub-objects must have their reference count incremented
+ * also. Different object types have different subobjects.
*/
switch (Object->Common.Type)
{
@@ -541,11 +563,20 @@ AcpiUtUpdateObjectReference (
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_POWER:
case ACPI_TYPE_THERMAL:
-
- /* Update the notify objects for these types (if present) */
-
- AcpiUtUpdateRefCount (Object->CommonNotify.SystemNotify, Action);
- AcpiUtUpdateRefCount (Object->CommonNotify.DeviceNotify, Action);
+ /*
+ * Update the notify objects for these types (if present)
+ * Two lists, system and device notify handlers.
+ */
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++)
+ {
+ PrevObject = Object->CommonNotify.NotifyList[i];
+ while (PrevObject)
+ {
+ NextObject = PrevObject->Notify.Next[i];
+ AcpiUtUpdateRefCount (PrevObject, Action);
+ PrevObject = NextObject;
+ }
+ }
break;
case ACPI_TYPE_PACKAGE:
@@ -556,17 +587,43 @@ AcpiUtUpdateObjectReference (
for (i = 0; i < Object->Package.Count; i++)
{
/*
- * Push each element onto the stack for later processing.
- * Note: There can be null elements within the package,
- * these are simply ignored
+ * Null package elements are legal and can be simply
+ * ignored.
*/
- Status = AcpiUtCreateUpdateStateAndPush (
- Object->Package.Elements[i], Action, &StateList);
- if (ACPI_FAILURE (Status))
+ NextObject = Object->Package.Elements[i];
+ if (!NextObject)
+ {
+ continue;
+ }
+
+ switch (NextObject->Common.Type)
{
- goto ErrorExit;
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+ /*
+ * For these very simple sub-objects, we can just
+ * update the reference count here and continue.
+ * Greatly increases performance of this operation.
+ */
+ AcpiUtUpdateRefCount (NextObject, Action);
+ break;
+
+ default:
+ /*
+ * For complex sub-objects, push them onto the stack
+ * for later processing (this eliminates recursion.)
+ */
+ Status = AcpiUtCreateUpdateStateAndPush (
+ NextObject, Action, &StateList);
+ if (ACPI_FAILURE (Status))
+ {
+ goto ErrorExit;
+ }
+ break;
}
}
+ NextObject = NULL;
break;
case ACPI_TYPE_BUFFER_FIELD:
@@ -583,7 +640,7 @@ AcpiUtUpdateObjectReference (
NextObject = Object->BankField.BankObj;
Status = AcpiUtCreateUpdateStateAndPush (
- Object->BankField.RegionObj, Action, &StateList);
+ Object->BankField.RegionObj, Action, &StateList);
if (ACPI_FAILURE (Status))
{
goto ErrorExit;
@@ -594,7 +651,7 @@ AcpiUtUpdateObjectReference (
NextObject = Object->IndexField.IndexObj;
Status = AcpiUtCreateUpdateStateAndPush (
- Object->IndexField.DataObj, Action, &StateList);
+ Object->IndexField.DataObj, Action, &StateList);
if (ACPI_FAILURE (Status))
{
goto ErrorExit;
@@ -616,6 +673,7 @@ AcpiUtUpdateObjectReference (
case ACPI_TYPE_REGION:
default:
+
break; /* No subobjects for all other types */
}
@@ -642,7 +700,7 @@ AcpiUtUpdateObjectReference (
}
}
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
ErrorExit:
@@ -658,7 +716,7 @@ ErrorExit:
AcpiUtDeleteGenericState (State);
}
- return_ACPI_STATUS (Status);
+ return (Status);
}
@@ -680,14 +738,14 @@ AcpiUtAddReference (
ACPI_OPERAND_OBJECT *Object)
{
- ACPI_FUNCTION_TRACE_PTR (UtAddReference, Object);
+ ACPI_FUNCTION_NAME (UtAddReference);
/* Ensure that we have a valid object */
if (!AcpiUtValidInternalObject (Object))
{
- return_VOID;
+ return;
}
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -697,7 +755,7 @@ AcpiUtAddReference (
/* Increment the reference count */
(void) AcpiUtUpdateObjectReference (Object, REF_INCREMENT);
- return_VOID;
+ return;
}
@@ -718,26 +776,25 @@ AcpiUtRemoveReference (
ACPI_OPERAND_OBJECT *Object)
{
- ACPI_FUNCTION_TRACE_PTR (UtRemoveReference, Object);
+ ACPI_FUNCTION_NAME (UtRemoveReference);
/*
* Allow a NULL pointer to be passed in, just ignore it. This saves
* each caller from having to check. Also, ignore NS nodes.
- *
*/
if (!Object ||
(ACPI_GET_DESCRIPTOR_TYPE (Object) == ACPI_DESC_TYPE_NAMED))
{
- return_VOID;
+ return;
}
/* Ensure that we have a valid object */
if (!AcpiUtValidInternalObject (Object))
{
- return_VOID;
+ return;
}
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS,
@@ -750,7 +807,5 @@ AcpiUtRemoveReference (
* of all subobjects!)
*/
(void) AcpiUtUpdateObjectReference (Object, REF_DECREMENT);
- return_VOID;
+ return;
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/uterror.c b/usr/src/uts/intel/io/acpica/utilities/uterror.c
new file mode 100644
index 0000000000..86d2368521
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/uterror.c
@@ -0,0 +1,325 @@
+/*******************************************************************************
+ *
+ * Module Name: uterror - Various internal error/warning output functions
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("uterror")
+
+
+/*
+ * This module contains internal error functions that may
+ * be configured out.
+ */
+#if !defined (ACPI_NO_ERROR_MESSAGES)
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPredefinedWarning
+ *
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * NodeFlags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Warnings for the predefined validation module. Messages are
+ * only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of error
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+AcpiUtPredefinedWarning (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ char *Pathname,
+ UINT8 NodeFlags,
+ const char *Format,
+ ...)
+{
+ va_list ArgList;
+
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (NodeFlags & ANOBJ_EVALUATED)
+ {
+ return;
+ }
+
+ AcpiOsPrintf (ACPI_MSG_WARNING "%s: ", Pathname);
+
+ va_start (ArgList, Format);
+ AcpiOsVprintf (Format, ArgList);
+ ACPI_MSG_SUFFIX;
+ va_end (ArgList);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPredefinedInfo
+ *
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * NodeFlags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Info messages for the predefined validation module. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+AcpiUtPredefinedInfo (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ char *Pathname,
+ UINT8 NodeFlags,
+ const char *Format,
+ ...)
+{
+ va_list ArgList;
+
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (NodeFlags & ANOBJ_EVALUATED)
+ {
+ return;
+ }
+
+ AcpiOsPrintf (ACPI_MSG_INFO "%s: ", Pathname);
+
+ va_start (ArgList, Format);
+ AcpiOsVprintf (Format, ArgList);
+ ACPI_MSG_SUFFIX;
+ va_end (ArgList);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPredefinedBiosError
+ *
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * NodeFlags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: BIOS error message for predefined names. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+AcpiUtPredefinedBiosError (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ char *Pathname,
+ UINT8 NodeFlags,
+ const char *Format,
+ ...)
+{
+ va_list ArgList;
+
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (NodeFlags & ANOBJ_EVALUATED)
+ {
+ return;
+ }
+
+ AcpiOsPrintf (ACPI_MSG_BIOS_ERROR "%s: ", Pathname);
+
+ va_start (ArgList, Format);
+ AcpiOsVprintf (Format, ArgList);
+ ACPI_MSG_SUFFIX;
+ va_end (ArgList);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtNamespaceError
+ *
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * InternalName - Name or path of the namespace node
+ * LookupStatus - Exception code from NS lookup
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the NS node.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtNamespaceError (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ const char *InternalName,
+ ACPI_STATUS LookupStatus)
+{
+ ACPI_STATUS Status;
+ UINT32 BadName;
+ char *Name = NULL;
+
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ AcpiOsPrintf (ACPI_MSG_ERROR);
+
+ if (LookupStatus == AE_BAD_CHARACTER)
+ {
+ /* There is a non-ascii character in the name */
+
+ ACPI_MOVE_32_TO_32 (&BadName, ACPI_CAST_PTR (UINT32, InternalName));
+ AcpiOsPrintf ("[0x%.8X] (NON-ASCII)", BadName);
+ }
+ else
+ {
+ /* Convert path to external format */
+
+ Status = AcpiNsExternalizeName (
+ ACPI_UINT32_MAX, InternalName, NULL, &Name);
+
+ /* Print target name */
+
+ if (ACPI_SUCCESS (Status))
+ {
+ AcpiOsPrintf ("[%s]", Name);
+ }
+ else
+ {
+ AcpiOsPrintf ("[COULD NOT EXTERNALIZE NAME]");
+ }
+
+ if (Name)
+ {
+ ACPI_FREE (Name);
+ }
+ }
+
+ AcpiOsPrintf (" Namespace lookup failure, %s",
+ AcpiFormatException (LookupStatus));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtMethodError
+ *
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Message - Error message to use on failure
+ * PrefixNode - Prefix relative to the path
+ * Path - Path to the node (optional)
+ * MethodStatus - Execution status
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Print error message with the full pathname for the method.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtMethodError (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ const char *Message,
+ ACPI_NAMESPACE_NODE *PrefixNode,
+ const char *Path,
+ ACPI_STATUS MethodStatus)
+{
+ ACPI_STATUS Status;
+ ACPI_NAMESPACE_NODE *Node = PrefixNode;
+
+
+ ACPI_MSG_REDIRECT_BEGIN;
+ AcpiOsPrintf (ACPI_MSG_ERROR);
+
+ if (Path)
+ {
+ Status = AcpiNsGetNode (PrefixNode, Path,
+ ACPI_NS_NO_UPSEARCH, &Node);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiOsPrintf ("[Could not get node by pathname]");
+ }
+ }
+
+ AcpiNsPrintNodePathname (Node, Message);
+ AcpiOsPrintf (", %s", AcpiFormatException (MethodStatus));
+
+ ACPI_MSG_SUFFIX;
+ ACPI_MSG_REDIRECT_END;
+}
+
+#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/usr/src/uts/intel/io/acpica/utilities/uteval.c b/usr/src/uts/intel/io/acpica/utilities/uteval.c
index 0042f411ff..d4864f3813 100644
--- a/usr/src/uts/intel/io/acpica/utilities/uteval.c
+++ b/usr/src/uts/intel/io/acpica/utilities/uteval.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTEVAL_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -74,7 +72,7 @@
ACPI_STATUS
AcpiUtEvaluateObject (
ACPI_NAMESPACE_NODE *PrefixNode,
- char *Path,
+ const char *Path,
UINT32 ExpectedReturnBtypes,
ACPI_OPERAND_OBJECT **ReturnDesc)
{
@@ -95,7 +93,7 @@ AcpiUtEvaluateObject (
}
Info->PrefixNode = PrefixNode;
- Info->Pathname = Path;
+ Info->RelativePathname = Path;
/* Evaluate the object/method */
@@ -136,22 +134,27 @@ AcpiUtEvaluateObject (
switch ((Info->ReturnObject)->Common.Type)
{
case ACPI_TYPE_INTEGER:
+
ReturnBtype = ACPI_BTYPE_INTEGER;
break;
case ACPI_TYPE_BUFFER:
+
ReturnBtype = ACPI_BTYPE_BUFFER;
break;
case ACPI_TYPE_STRING:
+
ReturnBtype = ACPI_BTYPE_STRING;
break;
case ACPI_TYPE_PACKAGE:
+
ReturnBtype = ACPI_BTYPE_PACKAGE;
break;
default:
+
ReturnBtype = 0;
break;
}
@@ -216,7 +219,7 @@ Cleanup:
ACPI_STATUS
AcpiUtEvaluateNumericObject (
- char *ObjectName,
+ const char *ObjectName,
ACPI_NAMESPACE_NODE *DeviceNode,
UINT64 *Value)
{
@@ -228,7 +231,7 @@ AcpiUtEvaluateNumericObject (
Status = AcpiUtEvaluateObject (DeviceNode, ObjectName,
- ACPI_BTYPE_INTEGER, &ObjDesc);
+ ACPI_BTYPE_INTEGER, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -255,7 +258,8 @@ AcpiUtEvaluateNumericObject (
* RETURN: Status
*
* DESCRIPTION: Executes _STA for selected device and stores results in
- * *Flags.
+ * *Flags. If _STA does not exist, then the device is assumed
+ * to be present/functional/enabled (as per the ACPI spec).
*
* NOTE: Internal function, no parameter validation
*
@@ -274,11 +278,16 @@ AcpiUtExecute_STA (
Status = AcpiUtEvaluateObject (DeviceNode, METHOD_NAME__STA,
- ACPI_BTYPE_INTEGER, &ObjDesc);
+ ACPI_BTYPE_INTEGER, &ObjDesc);
if (ACPI_FAILURE (Status))
{
if (AE_NOT_FOUND == Status)
{
+ /*
+ * if _STA does not exist, then (as per the ACPI specification),
+ * the returned flags will indicate that the device is present,
+ * functional, and enabled.
+ */
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
"_STA on %4.4s was not found, assuming device is present\n",
AcpiUtGetNodeName (DeviceNode)));
@@ -342,8 +351,8 @@ AcpiUtExecutePowerMethods (
* return type is an Integer.
*/
Status = AcpiUtEvaluateObject (DeviceNode,
- ACPI_CAST_PTR (char, MethodNames[i]),
- ACPI_BTYPE_INTEGER, &ObjDesc);
+ ACPI_CAST_PTR (char, MethodNames[i]),
+ ACPI_BTYPE_INTEGER, &ObjDesc);
if (ACPI_SUCCESS (Status))
{
OutValues[i] = (UINT8) ObjDesc->Integer.Value;
diff --git a/usr/src/uts/intel/io/acpica/utilities/utexcep.c b/usr/src/uts/intel/io/acpica/utilities/utexcep.c
new file mode 100644
index 0000000000..5be8efd5f2
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utexcep.c
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ *
+ * Module Name: utexcep - Exception code support
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define EXPORT_ACPI_INTERFACES
+
+#define ACPI_DEFINE_EXCEPTION_TABLE
+#include "acpi.h"
+#include "accommon.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utexcep")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiFormatException
+ *
+ * PARAMETERS: Status - The ACPI_STATUS code to be formatted
+ *
+ * RETURN: A string containing the exception text. A valid pointer is
+ * always returned.
+ *
+ * DESCRIPTION: This function translates an ACPI exception into an ASCII
+ * string. Returns "unknown status" string for invalid codes.
+ *
+ ******************************************************************************/
+
+const char *
+AcpiFormatException (
+ ACPI_STATUS Status)
+{
+ const ACPI_EXCEPTION_INFO *Exception;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ Exception = AcpiUtValidateException (Status);
+ if (!Exception)
+ {
+ /* Exception code was not recognized */
+
+ ACPI_ERROR ((AE_INFO,
+ "Unknown exception code: 0x%8.8X", Status));
+
+ return ("UNKNOWN_STATUS_CODE");
+ }
+
+ return (Exception->Name);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiFormatException)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtValidateException
+ *
+ * PARAMETERS: Status - The ACPI_STATUS code to be formatted
+ *
+ * RETURN: A string containing the exception text. NULL if exception is
+ * not valid.
+ *
+ * DESCRIPTION: This function validates and translates an ACPI exception into
+ * an ASCII string.
+ *
+ ******************************************************************************/
+
+const ACPI_EXCEPTION_INFO *
+AcpiUtValidateException (
+ ACPI_STATUS Status)
+{
+ UINT32 SubStatus;
+ const ACPI_EXCEPTION_INFO *Exception = NULL;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /*
+ * Status is composed of two parts, a "type" and an actual code
+ */
+ SubStatus = (Status & ~AE_CODE_MASK);
+
+ switch (Status & AE_CODE_MASK)
+ {
+ case AE_CODE_ENVIRONMENTAL:
+
+ if (SubStatus <= AE_CODE_ENV_MAX)
+ {
+ Exception = &AcpiGbl_ExceptionNames_Env [SubStatus];
+ }
+ break;
+
+ case AE_CODE_PROGRAMMER:
+
+ if (SubStatus <= AE_CODE_PGM_MAX)
+ {
+ Exception = &AcpiGbl_ExceptionNames_Pgm [SubStatus];
+ }
+ break;
+
+ case AE_CODE_ACPI_TABLES:
+
+ if (SubStatus <= AE_CODE_TBL_MAX)
+ {
+ Exception = &AcpiGbl_ExceptionNames_Tbl [SubStatus];
+ }
+ break;
+
+ case AE_CODE_AML:
+
+ if (SubStatus <= AE_CODE_AML_MAX)
+ {
+ Exception = &AcpiGbl_ExceptionNames_Aml [SubStatus];
+ }
+ break;
+
+ case AE_CODE_CONTROL:
+
+ if (SubStatus <= AE_CODE_CTRL_MAX)
+ {
+ Exception = &AcpiGbl_ExceptionNames_Ctrl [SubStatus];
+ }
+ break;
+
+ default:
+
+ break;
+ }
+
+ if (!Exception || !Exception->Name)
+ {
+ return (NULL);
+ }
+
+ return (Exception);
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utglobal.c b/usr/src/uts/intel/io/acpica/utilities/utglobal.c
index 336f6706c1..0d8dff88c9 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utglobal.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utglobal.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,7 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTGLOBAL_C__
+#define EXPORT_ACPI_INTERFACES
#define DEFINE_ACPI_GLOBALS
#include "acpi.h"
@@ -57,37 +57,7 @@
*
******************************************************************************/
-/*
- * We want the debug switches statically initialized so they
- * are already set when the debugger is entered.
- */
-
-/* Debug switch - level and trace mask */
-
-#ifdef ACPI_DEBUG_OUTPUT
-UINT32 AcpiDbgLevel = ACPI_DEBUG_DEFAULT;
-#else
-UINT32 AcpiDbgLevel = ACPI_NORMAL_DEFAULT;
-#endif
-
-/* Debug switch - layer (component) mask */
-
-UINT32 AcpiDbgLayer = ACPI_COMPONENT_DEFAULT;
-UINT32 AcpiGbl_NestingLevel = 0;
-
-/* Debugger globals */
-
-BOOLEAN AcpiGbl_DbTerminateThreads = FALSE;
-BOOLEAN AcpiGbl_AbortMethod = FALSE;
-BOOLEAN AcpiGbl_MethodExecuting = FALSE;
-
-/* System flags */
-
-UINT32 AcpiGbl_StartupFlags = 0;
-
-/* System starts uninitialized */
-
-BOOLEAN AcpiGbl_Shutdown = TRUE;
+/* Various state name strings */
const char *AcpiGbl_SleepStateNames[ACPI_S_STATE_COUNT] =
{
@@ -117,6 +87,12 @@ const char *AcpiGbl_HighestDstateNames[ACPI_NUM_SxD_METHODS] =
};
+/* Hex-to-ascii */
+
+const char AcpiGbl_LowerHexDigits[] = "0123456789abcdef";
+const char AcpiGbl_UpperHexDigits[] = "0123456789ABCDEF";
+
+
/*******************************************************************************
*
* Namespace globals
@@ -141,12 +117,19 @@ const ACPI_PREDEFINED_NAMES AcpiGbl_PreDefinedNames[] =
{"_SB_", ACPI_TYPE_DEVICE, NULL},
{"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL},
{"_TZ_", ACPI_TYPE_DEVICE, NULL},
- {"_REV", ACPI_TYPE_INTEGER, (char *) ACPI_CA_SUPPORT_LEVEL},
+ /*
+ * March, 2015:
+ * The _REV object is in the process of being deprecated, because
+ * other ACPI implementations permanently return 2. Thus, it
+ * has little or no value. Return 2 for compatibility with
+ * other ACPI implementations.
+ */
+ {"_REV", ACPI_TYPE_INTEGER, ACPI_CAST_PTR (char, 2)},
{"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME},
- {"_GL_", ACPI_TYPE_MUTEX, (char *) 1},
+ {"_GL_", ACPI_TYPE_MUTEX, ACPI_CAST_PTR (char, 1)},
#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY)
- {"_OSI", ACPI_TYPE_METHOD, (char *) 1},
+ {"_OSI", ACPI_TYPE_METHOD, ACPI_CAST_PTR (char, 1)},
#endif
/* Table terminator */
@@ -155,6 +138,7 @@ const ACPI_PREDEFINED_NAMES AcpiGbl_PreDefinedNames[] =
};
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
* Event and Hardware globals
@@ -199,148 +183,56 @@ ACPI_FIXED_EVENT_INFO AcpiGbl_FixedEventInfo[ACPI_NUM_FIXED_EVENTS] =
/* ACPI_EVENT_SLEEP_BUTTON */ {ACPI_BITREG_SLEEP_BUTTON_STATUS, ACPI_BITREG_SLEEP_BUTTON_ENABLE, ACPI_BITMASK_SLEEP_BUTTON_STATUS, ACPI_BITMASK_SLEEP_BUTTON_ENABLE},
/* ACPI_EVENT_RTC */ {ACPI_BITREG_RT_CLOCK_STATUS, ACPI_BITREG_RT_CLOCK_ENABLE, ACPI_BITMASK_RT_CLOCK_STATUS, ACPI_BITMASK_RT_CLOCK_ENABLE},
};
+#endif /* !ACPI_REDUCED_HARDWARE */
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtInitGlobals
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Init ACPICA globals. All globals that require specific
- * initialization should be initialized here!
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiUtInitGlobals (
- void)
-{
- ACPI_STATUS Status;
- UINT32 i;
-
-
- ACPI_FUNCTION_TRACE (UtInitGlobals);
-
-
- /* Create all memory caches */
-
- Status = AcpiUtCreateCaches ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
+#if defined (ACPI_DISASSEMBLER) || defined (ACPI_ASL_COMPILER)
- /* Mutex locked flags */
+/* ToPld macro: compile/disassemble strings */
- for (i = 0; i < ACPI_NUM_MUTEX; i++)
- {
- AcpiGbl_MutexInfo[i].Mutex = NULL;
- AcpiGbl_MutexInfo[i].ThreadId = ACPI_MUTEX_NOT_ACQUIRED;
- AcpiGbl_MutexInfo[i].UseCount = 0;
- }
-
- for (i = 0; i < ACPI_NUM_OWNERID_MASKS; i++)
- {
- AcpiGbl_OwnerIdMask[i] = 0;
- }
-
- /* Last OwnerID is never valid */
-
- AcpiGbl_OwnerIdMask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000;
-
- /* Event counters */
-
- AcpiMethodCount = 0;
- AcpiSciCount = 0;
- AcpiGpeCount = 0;
-
- for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++)
- {
- AcpiFixedEventCount[i] = 0;
- }
-
- /* GPE support */
-
- AcpiGbl_AllGpesInitialized = FALSE;
- AcpiGbl_GpeXruptListHead = NULL;
- AcpiGbl_GpeFadtBlocks[0] = NULL;
- AcpiGbl_GpeFadtBlocks[1] = NULL;
- AcpiCurrentGpeCount = 0;
-
- /* Global handlers */
-
- AcpiGbl_SystemNotify.Handler = NULL;
- AcpiGbl_DeviceNotify.Handler = NULL;
- AcpiGbl_ExceptionHandler = NULL;
- AcpiGbl_InitHandler = NULL;
- AcpiGbl_TableHandler = NULL;
- AcpiGbl_InterfaceHandler = NULL;
- AcpiGbl_GlobalEventHandler = NULL;
-
- /* Global Lock support */
-
- AcpiGbl_GlobalLockSemaphore = NULL;
- AcpiGbl_GlobalLockMutex = NULL;
- AcpiGbl_GlobalLockAcquired = FALSE;
- AcpiGbl_GlobalLockHandle = 0;
- AcpiGbl_GlobalLockPresent = FALSE;
-
- /* Miscellaneous variables */
-
- AcpiGbl_DSDT = NULL;
- AcpiGbl_CmSingleStep = FALSE;
- AcpiGbl_DbTerminateThreads = FALSE;
- AcpiGbl_Shutdown = FALSE;
- AcpiGbl_NsLookupCount = 0;
- AcpiGbl_PsFindCount = 0;
- AcpiGbl_AcpiHardwarePresent = TRUE;
- AcpiGbl_LastOwnerIdIndex = 0;
- AcpiGbl_NextOwnerIdOffset = 0;
- AcpiGbl_TraceMethodName = 0;
- AcpiGbl_TraceDbgLevel = 0;
- AcpiGbl_TraceDbgLayer = 0;
- AcpiGbl_DebuggerConfiguration = DEBUGGER_THREADING;
- AcpiGbl_DbOutputFlags = ACPI_DB_CONSOLE_OUTPUT;
- AcpiGbl_OsiData = 0;
- AcpiGbl_OsiMutex = NULL;
- AcpiGbl_RegMethodsExecuted = FALSE;
-
- /* Hardware oriented */
-
- AcpiGbl_EventsInitialized = FALSE;
- AcpiGbl_SystemAwakeAndRunning = TRUE;
-
- /* Namespace */
+const char *AcpiGbl_PldPanelList[] =
+{
+ "TOP",
+ "BOTTOM",
+ "LEFT",
+ "RIGHT",
+ "FRONT",
+ "BACK",
+ "UNKNOWN",
+ NULL
+};
- AcpiGbl_ModuleCodeList = NULL;
- AcpiGbl_RootNode = NULL;
- AcpiGbl_RootNodeStruct.Name.Integer = ACPI_ROOT_NAME;
- AcpiGbl_RootNodeStruct.DescriptorType = ACPI_DESC_TYPE_NAMED;
- AcpiGbl_RootNodeStruct.Type = ACPI_TYPE_DEVICE;
- AcpiGbl_RootNodeStruct.Parent = NULL;
- AcpiGbl_RootNodeStruct.Child = NULL;
- AcpiGbl_RootNodeStruct.Peer = NULL;
- AcpiGbl_RootNodeStruct.Object = NULL;
-
-
-#ifdef ACPI_DISASSEMBLER
- AcpiGbl_ExternalList = NULL;
-#endif
+const char *AcpiGbl_PldVerticalPositionList[] =
+{
+ "UPPER",
+ "CENTER",
+ "LOWER",
+ NULL
+};
-#ifdef ACPI_DEBUG_OUTPUT
- AcpiGbl_LowestStackPointer = ACPI_CAST_PTR (ACPI_SIZE, ACPI_SIZE_MAX);
-#endif
+const char *AcpiGbl_PldHorizontalPositionList[] =
+{
+ "LEFT",
+ "CENTER",
+ "RIGHT",
+ NULL
+};
-#ifdef ACPI_DBG_TRACK_ALLOCATIONS
- AcpiGbl_DisplayFinalMemStats = FALSE;
- AcpiGbl_DisableMemTracking = FALSE;
+const char *AcpiGbl_PldShapeList[] =
+{
+ "ROUND",
+ "OVAL",
+ "SQUARE",
+ "VERTICALRECTANGLE",
+ "HORIZONTALRECTANGLE",
+ "VERTICALTRAPEZOID",
+ "HORIZONTALTRAPEZOID",
+ "UNKNOWN",
+ "CHAMFERED",
+ NULL
+};
#endif
- return_ACPI_STATUS (AE_OK);
-}
/* Public globals */
diff --git a/usr/src/uts/intel/io/acpica/utilities/uthex.c b/usr/src/uts/intel/io/acpica/utilities/uthex.c
new file mode 100644
index 0000000000..b93013b200
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/uthex.c
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ * Module Name: uthex -- Hex/ASCII support functions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_COMPILER
+ ACPI_MODULE_NAME ("uthex")
+
+
+/* Hex to ASCII conversion table */
+
+static const char AcpiGbl_HexToAscii[] =
+{
+ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtHexToAsciiChar
+ *
+ * PARAMETERS: Integer - Contains the hex digit
+ * Position - bit position of the digit within the
+ * integer (multiple of 4)
+ *
+ * RETURN: The converted Ascii character
+ *
+ * DESCRIPTION: Convert a hex digit to an Ascii character
+ *
+ ******************************************************************************/
+
+char
+AcpiUtHexToAsciiChar (
+ UINT64 Integer,
+ UINT32 Position)
+{
+
+ return (AcpiGbl_HexToAscii[(Integer >> Position) & 0xF]);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtAsciiCharToHex
+ *
+ * PARAMETERS: HexChar - Hex character in Ascii
+ *
+ * RETURN: The binary value of the ascii/hex character
+ *
+ * DESCRIPTION: Perform ascii-to-hex translation
+ *
+ ******************************************************************************/
+
+UINT8
+AcpiUtAsciiCharToHex (
+ int HexChar)
+{
+
+ if (HexChar <= 0x39)
+ {
+ return ((UINT8) (HexChar - 0x30));
+ }
+
+ if (HexChar <= 0x46)
+ {
+ return ((UINT8) (HexChar - 0x37));
+ }
+
+ return ((UINT8) (HexChar - 0x57));
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utids.c b/usr/src/uts/intel/io/acpica/utilities/utids.c
index 36dc3afce6..7182a91ae8 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utids.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utids.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: utids - support for device IDs - HID, UID, CID
+ * Module Name: utids - support for device IDs - HID, UID, CID, SUB, CLS
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTIDS_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acinterp.h"
@@ -73,10 +71,10 @@
ACPI_STATUS
AcpiUtExecute_HID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID **ReturnId)
+ ACPI_PNP_DEVICE_ID **ReturnId)
{
ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_DEVICE_ID *Hid;
+ ACPI_PNP_DEVICE_ID *Hid;
UINT32 Length;
ACPI_STATUS Status;
@@ -85,7 +83,7 @@ AcpiUtExecute_HID (
Status = AcpiUtEvaluateObject (DeviceNode, METHOD_NAME__HID,
- ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &ObjDesc);
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -104,16 +102,17 @@ AcpiUtExecute_HID (
/* Allocate a buffer for the HID */
- Hid = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_DEVICE_ID) + (ACPI_SIZE) Length);
+ Hid = ACPI_ALLOCATE_ZEROED (
+ sizeof (ACPI_PNP_DEVICE_ID) + (ACPI_SIZE) Length);
if (!Hid)
{
Status = AE_NO_MEMORY;
goto Cleanup;
}
- /* Area for the string starts after DEVICE_ID struct */
+ /* Area for the string starts after PNP_DEVICE_ID struct */
- Hid->String = ACPI_ADD_PTR (char, Hid, sizeof (ACPI_DEVICE_ID));
+ Hid->String = ACPI_ADD_PTR (char, Hid, sizeof (ACPI_PNP_DEVICE_ID));
/* Convert EISAID to a string or simply copy existing string */
@@ -123,7 +122,7 @@ AcpiUtExecute_HID (
}
else
{
- ACPI_STRCPY (Hid->String, ObjDesc->String.Pointer);
+ strcpy (Hid->String, ObjDesc->String.Pointer);
}
Hid->Length = Length;
@@ -160,10 +159,10 @@ Cleanup:
ACPI_STATUS
AcpiUtExecute_UID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID **ReturnId)
+ ACPI_PNP_DEVICE_ID **ReturnId)
{
ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_DEVICE_ID *Uid;
+ ACPI_PNP_DEVICE_ID *Uid;
UINT32 Length;
ACPI_STATUS Status;
@@ -172,7 +171,7 @@ AcpiUtExecute_UID (
Status = AcpiUtEvaluateObject (DeviceNode, METHOD_NAME__UID,
- ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &ObjDesc);
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -191,16 +190,17 @@ AcpiUtExecute_UID (
/* Allocate a buffer for the UID */
- Uid = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_DEVICE_ID) + (ACPI_SIZE) Length);
+ Uid = ACPI_ALLOCATE_ZEROED (
+ sizeof (ACPI_PNP_DEVICE_ID) + (ACPI_SIZE) Length);
if (!Uid)
{
Status = AE_NO_MEMORY;
goto Cleanup;
}
- /* Area for the string starts after DEVICE_ID struct */
+ /* Area for the string starts after PNP_DEVICE_ID struct */
- Uid->String = ACPI_ADD_PTR (char, Uid, sizeof (ACPI_DEVICE_ID));
+ Uid->String = ACPI_ADD_PTR (char, Uid, sizeof (ACPI_PNP_DEVICE_ID));
/* Convert an Integer to string, or just copy an existing string */
@@ -210,7 +210,7 @@ AcpiUtExecute_UID (
}
else
{
- ACPI_STRCPY (Uid->String, ObjDesc->String.Pointer);
+ strcpy (Uid->String, ObjDesc->String.Pointer);
}
Uid->Length = Length;
@@ -252,11 +252,11 @@ Cleanup:
ACPI_STATUS
AcpiUtExecute_CID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID_LIST **ReturnCidList)
+ ACPI_PNP_DEVICE_ID_LIST **ReturnCidList)
{
ACPI_OPERAND_OBJECT **CidObjects;
ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_DEVICE_ID_LIST *CidList;
+ ACPI_PNP_DEVICE_ID_LIST *CidList;
char *NextIdString;
UINT32 StringAreaSize;
UINT32 Length;
@@ -272,8 +272,8 @@ AcpiUtExecute_CID (
/* Evaluate the _CID method for this device */
Status = AcpiUtEvaluateObject (DeviceNode, METHOD_NAME__CID,
- ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_PACKAGE,
- &ObjDesc);
+ ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_PACKAGE,
+ &ObjDesc);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -304,14 +304,17 @@ AcpiUtExecute_CID (
switch (CidObjects[i]->Common.Type)
{
case ACPI_TYPE_INTEGER:
+
StringAreaSize += ACPI_EISAID_STRING_SIZE;
break;
case ACPI_TYPE_STRING:
+
StringAreaSize += CidObjects[i]->String.Length + 1;
break;
default:
+
Status = AE_TYPE;
goto Cleanup;
}
@@ -320,11 +323,11 @@ AcpiUtExecute_CID (
/*
* Now that we know the length of the CIDs, allocate return buffer:
* 1) Size of the base structure +
- * 2) Size of the CID DEVICE_ID array +
+ * 2) Size of the CID PNP_DEVICE_ID array +
* 3) Size of the actual CID strings
*/
- CidListSize = sizeof (ACPI_DEVICE_ID_LIST) +
- ((Count - 1) * sizeof (ACPI_DEVICE_ID)) +
+ CidListSize = sizeof (ACPI_PNP_DEVICE_ID_LIST) +
+ ((Count - 1) * sizeof (ACPI_PNP_DEVICE_ID)) +
StringAreaSize;
CidList = ACPI_ALLOCATE_ZEROED (CidListSize);
@@ -334,10 +337,10 @@ AcpiUtExecute_CID (
goto Cleanup;
}
- /* Area for CID strings starts after the CID DEVICE_ID array */
+ /* Area for CID strings starts after the CID PNP_DEVICE_ID array */
NextIdString = ACPI_CAST_PTR (char, CidList->Ids) +
- ((ACPI_SIZE) Count * sizeof (ACPI_DEVICE_ID));
+ ((ACPI_SIZE) Count * sizeof (ACPI_PNP_DEVICE_ID));
/* Copy/convert the CIDs to the return buffer */
@@ -347,14 +350,15 @@ AcpiUtExecute_CID (
{
/* Convert the Integer (EISAID) CID to a string */
- AcpiExEisaIdToString (NextIdString, CidObjects[i]->Integer.Value);
+ AcpiExEisaIdToString (
+ NextIdString, CidObjects[i]->Integer.Value);
Length = ACPI_EISAID_STRING_SIZE;
}
else /* ACPI_TYPE_STRING */
{
/* Copy the String CID from the returned object */
- ACPI_STRCPY (NextIdString, CidObjects[i]->String.Pointer);
+ strcpy (NextIdString, CidObjects[i]->String.Pointer);
Length = CidObjects[i]->String.Length + 1;
}
@@ -378,3 +382,97 @@ Cleanup:
return_ACPI_STATUS (Status);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtExecute_CLS
+ *
+ * PARAMETERS: DeviceNode - Node for the device
+ * ReturnId - Where the _CLS is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Executes the _CLS control method that returns PCI-defined
+ * class code of the device. The _CLS value is always a package
+ * containing PCI class information as a list of integers.
+ * The returned string has format "BBSSPP", where:
+ * BB = Base-class code
+ * SS = Sub-class code
+ * PP = Programming Interface code
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtExecute_CLS (
+ ACPI_NAMESPACE_NODE *DeviceNode,
+ ACPI_PNP_DEVICE_ID **ReturnId)
+{
+ ACPI_OPERAND_OBJECT *ObjDesc;
+ ACPI_OPERAND_OBJECT **ClsObjects;
+ UINT32 Count;
+ ACPI_PNP_DEVICE_ID *Cls;
+ UINT32 Length;
+ ACPI_STATUS Status;
+ UINT8 ClassCode[3] = {0, 0, 0};
+
+
+ ACPI_FUNCTION_TRACE (UtExecute_CLS);
+
+
+ Status = AcpiUtEvaluateObject (DeviceNode, METHOD_NAME__CLS,
+ ACPI_BTYPE_PACKAGE, &ObjDesc);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Get the size of the String to be returned, includes null terminator */
+
+ Length = ACPI_PCICLS_STRING_SIZE;
+ ClsObjects = ObjDesc->Package.Elements;
+ Count = ObjDesc->Package.Count;
+
+ if (ObjDesc->Common.Type == ACPI_TYPE_PACKAGE)
+ {
+ if (Count > 0 && ClsObjects[0]->Common.Type == ACPI_TYPE_INTEGER)
+ {
+ ClassCode[0] = (UINT8) ClsObjects[0]->Integer.Value;
+ }
+ if (Count > 1 && ClsObjects[1]->Common.Type == ACPI_TYPE_INTEGER)
+ {
+ ClassCode[1] = (UINT8) ClsObjects[1]->Integer.Value;
+ }
+ if (Count > 2 && ClsObjects[2]->Common.Type == ACPI_TYPE_INTEGER)
+ {
+ ClassCode[2] = (UINT8) ClsObjects[2]->Integer.Value;
+ }
+ }
+
+ /* Allocate a buffer for the CLS */
+
+ Cls = ACPI_ALLOCATE_ZEROED (
+ sizeof (ACPI_PNP_DEVICE_ID) + (ACPI_SIZE) Length);
+ if (!Cls)
+ {
+ Status = AE_NO_MEMORY;
+ goto Cleanup;
+ }
+
+ /* Area for the string starts after PNP_DEVICE_ID struct */
+
+ Cls->String = ACPI_ADD_PTR (char, Cls, sizeof (ACPI_PNP_DEVICE_ID));
+
+ /* Simply copy existing string */
+
+ AcpiExPciClsToString (Cls->String, ClassCode);
+ Cls->Length = Length;
+ *ReturnId = Cls;
+
+
+Cleanup:
+
+ /* On exit, we must delete the return object */
+
+ AcpiUtRemoveReference (ObjDesc);
+ return_ACPI_STATUS (Status);
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utinit.c b/usr/src/uts/intel/io/acpica/utilities/utinit.c
index 9e253f01d3..03c3e510b8 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utinit.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utinit.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTINIT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -58,21 +55,33 @@
static void AcpiUtTerminate (
void);
+#if (!ACPI_REDUCED_HARDWARE)
+
+static void
+AcpiUtFreeGpeLists (
+ void);
+
+#else
+
+#define AcpiUtFreeGpeLists()
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
- * FUNCTION: AcpiUtTerminate
+ * FUNCTION: AcpiUtFreeGpeLists
*
* PARAMETERS: none
*
* RETURN: none
*
- * DESCRIPTION: Free global memory
+ * DESCRIPTION: Free global GPE lists
*
******************************************************************************/
static void
-AcpiUtTerminate (
+AcpiUtFreeGpeLists (
void)
{
ACPI_GPE_BLOCK_INFO *GpeBlock;
@@ -81,9 +90,6 @@ AcpiUtTerminate (
ACPI_GPE_XRUPT_INFO *NextGpeXruptInfo;
- ACPI_FUNCTION_TRACE (UtTerminate);
-
-
/* Free global GPE blocks and related info structures */
GpeXruptInfo = AcpiGbl_GpeXruptListHead;
@@ -103,7 +109,182 @@ AcpiUtTerminate (
ACPI_FREE (GpeXruptInfo);
GpeXruptInfo = NextGpeXruptInfo;
}
+}
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtInitGlobals
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize ACPICA globals. All globals that require specific
+ * initialization should be initialized here. This allows for
+ * a warm restart.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtInitGlobals (
+ void)
+{
+ ACPI_STATUS Status;
+ UINT32 i;
+
+
+ ACPI_FUNCTION_TRACE (UtInitGlobals);
+
+
+ /* Create all memory caches */
+
+ Status = AcpiUtCreateCaches ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Address Range lists */
+
+ for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++)
+ {
+ AcpiGbl_AddressRangeList[i] = NULL;
+ }
+
+ /* Mutex locked flags */
+
+ for (i = 0; i < ACPI_NUM_MUTEX; i++)
+ {
+ AcpiGbl_MutexInfo[i].Mutex = NULL;
+ AcpiGbl_MutexInfo[i].ThreadId = ACPI_MUTEX_NOT_ACQUIRED;
+ AcpiGbl_MutexInfo[i].UseCount = 0;
+ }
+
+ for (i = 0; i < ACPI_NUM_OWNERID_MASKS; i++)
+ {
+ AcpiGbl_OwnerIdMask[i] = 0;
+ }
+
+ /* Last OwnerID is never valid */
+
+ AcpiGbl_OwnerIdMask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000;
+
+ /* Event counters */
+
+ AcpiMethodCount = 0;
+ AcpiSciCount = 0;
+ AcpiGpeCount = 0;
+
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++)
+ {
+ AcpiFixedEventCount[i] = 0;
+ }
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /* GPE/SCI support */
+
+ AcpiGbl_AllGpesInitialized = FALSE;
+ AcpiGbl_GpeXruptListHead = NULL;
+ AcpiGbl_GpeFadtBlocks[0] = NULL;
+ AcpiGbl_GpeFadtBlocks[1] = NULL;
+ AcpiCurrentGpeCount = 0;
+
+ AcpiGbl_GlobalEventHandler = NULL;
+ AcpiGbl_SciHandlerList = NULL;
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+ /* Global handlers */
+
+ AcpiGbl_GlobalNotify[0].Handler = NULL;
+ AcpiGbl_GlobalNotify[1].Handler = NULL;
+ AcpiGbl_ExceptionHandler = NULL;
+ AcpiGbl_InitHandler = NULL;
+ AcpiGbl_TableHandler = NULL;
+ AcpiGbl_InterfaceHandler = NULL;
+
+ /* Global Lock support */
+
+ AcpiGbl_GlobalLockSemaphore = NULL;
+ AcpiGbl_GlobalLockMutex = NULL;
+ AcpiGbl_GlobalLockAcquired = FALSE;
+ AcpiGbl_GlobalLockHandle = 0;
+ AcpiGbl_GlobalLockPresent = FALSE;
+
+ /* Miscellaneous variables */
+
+ AcpiGbl_DSDT = NULL;
+ AcpiGbl_CmSingleStep = FALSE;
+ AcpiGbl_Shutdown = FALSE;
+ AcpiGbl_NsLookupCount = 0;
+ AcpiGbl_PsFindCount = 0;
+ AcpiGbl_AcpiHardwarePresent = TRUE;
+ AcpiGbl_LastOwnerIdIndex = 0;
+ AcpiGbl_NextOwnerIdOffset = 0;
+ AcpiGbl_DebuggerConfiguration = DEBUGGER_THREADING;
+ AcpiGbl_OsiMutex = NULL;
+ AcpiGbl_MaxLoopIterations = 0xFFFF;
+
+ /* Hardware oriented */
+
+ AcpiGbl_EventsInitialized = FALSE;
+ AcpiGbl_SystemAwakeAndRunning = TRUE;
+
+ /* Namespace */
+
+ AcpiGbl_ModuleCodeList = NULL;
+ AcpiGbl_RootNode = NULL;
+ AcpiGbl_RootNodeStruct.Name.Integer = ACPI_ROOT_NAME;
+ AcpiGbl_RootNodeStruct.DescriptorType = ACPI_DESC_TYPE_NAMED;
+ AcpiGbl_RootNodeStruct.Type = ACPI_TYPE_DEVICE;
+ AcpiGbl_RootNodeStruct.Parent = NULL;
+ AcpiGbl_RootNodeStruct.Child = NULL;
+ AcpiGbl_RootNodeStruct.Peer = NULL;
+ AcpiGbl_RootNodeStruct.Object = NULL;
+
+
+#ifdef ACPI_DISASSEMBLER
+ AcpiGbl_ExternalList = NULL;
+ AcpiGbl_NumExternalMethods = 0;
+ AcpiGbl_ResolvedExternalMethods = 0;
+#endif
+
+#ifdef ACPI_DEBUG_OUTPUT
+ AcpiGbl_LowestStackPointer = ACPI_CAST_PTR (ACPI_SIZE, ACPI_SIZE_MAX);
+#endif
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+ AcpiGbl_DisplayFinalMemStats = FALSE;
+ AcpiGbl_DisableMemTracking = FALSE;
+#endif
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiUtTerminate
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Free global memory
+ *
+ ******************************************************************************/
+
+static void
+AcpiUtTerminate (
+ void)
+{
+ ACPI_FUNCTION_TRACE (UtTerminate);
+
+ AcpiUtFreeGpeLists ();
+ AcpiUtDeleteAddressLists ();
return_VOID;
}
@@ -128,6 +309,20 @@ AcpiUtSubsystemShutdown (
ACPI_FUNCTION_TRACE (UtSubsystemShutdown);
+ /* Just exit if subsystem is already shutdown */
+
+ if (AcpiGbl_Shutdown)
+ {
+ ACPI_ERROR ((AE_INFO, "ACPI Subsystem is already terminated"));
+ return_VOID;
+ }
+
+ /* Subsystem appears active, go ahead and shut it down */
+
+ AcpiGbl_Shutdown = TRUE;
+ AcpiGbl_StartupFlags = 0;
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n"));
+
#ifndef ACPI_ASL_COMPILER
/* Close the AcpiEvent Handling */
@@ -156,5 +351,3 @@ AcpiUtSubsystemShutdown (
(void) AcpiUtDeleteCaches ();
return_VOID;
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utlock.c b/usr/src/uts/intel/io/acpica/utilities/utlock.c
index fd2fb2bb1d..c62a37d9f5 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utlock.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utlock.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTLOCK_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -202,4 +200,3 @@ AcpiUtReleaseWriteLock (
AcpiOsReleaseMutex (Lock->WriterMutex);
}
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utmath.c b/usr/src/uts/intel/io/acpica/utilities/utmath.c
index 032b72d074..aa3d762c0d 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utmath.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utmath.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTMATH_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -90,7 +87,7 @@ typedef union uint64_overlay
* RETURN: Status (Checks for divide-by-zero)
*
* DESCRIPTION: Perform a short (maximum 64 bits divided by 32 bits)
- * divide and modulo. The result is a 64-bit quotient and a
+ * divide and modulo. The result is a 64-bit quotient and a
* 32-bit remainder.
*
******************************************************************************/
@@ -125,9 +122,10 @@ AcpiUtShortDivide (
* and is generated by the second divide.
*/
ACPI_DIV_64_BY_32 (0, DividendOvl.Part.Hi, Divisor,
- Quotient.Part.Hi, Remainder32);
+ Quotient.Part.Hi, Remainder32);
+
ACPI_DIV_64_BY_32 (Remainder32, DividendOvl.Part.Lo, Divisor,
- Quotient.Part.Lo, Remainder32);
+ Quotient.Part.Lo, Remainder32);
/* Return only what was requested */
@@ -203,9 +201,10 @@ AcpiUtDivide (
* and is generated by the second divide.
*/
ACPI_DIV_64_BY_32 (0, Dividend.Part.Hi, Divisor.Part.Lo,
- Quotient.Part.Hi, Partial1);
+ Quotient.Part.Hi, Partial1);
+
ACPI_DIV_64_BY_32 (Partial1, Dividend.Part.Lo, Divisor.Part.Lo,
- Quotient.Part.Lo, Remainder.Part.Lo);
+ Quotient.Part.Lo, Remainder.Part.Lo);
}
else
@@ -222,25 +221,24 @@ AcpiUtDivide (
do
{
- ACPI_SHIFT_RIGHT_64 (NormalizedDivisor.Part.Hi,
- NormalizedDivisor.Part.Lo);
- ACPI_SHIFT_RIGHT_64 (NormalizedDividend.Part.Hi,
- NormalizedDividend.Part.Lo);
+ ACPI_SHIFT_RIGHT_64 (
+ NormalizedDivisor.Part.Hi, NormalizedDivisor.Part.Lo);
+ ACPI_SHIFT_RIGHT_64 (
+ NormalizedDividend.Part.Hi, NormalizedDividend.Part.Lo);
} while (NormalizedDivisor.Part.Hi != 0);
/* Partial divide */
- ACPI_DIV_64_BY_32 (NormalizedDividend.Part.Hi,
- NormalizedDividend.Part.Lo,
- NormalizedDivisor.Part.Lo,
- Quotient.Part.Lo, Partial1);
+ ACPI_DIV_64_BY_32 (
+ NormalizedDividend.Part.Hi, NormalizedDividend.Part.Lo,
+ NormalizedDivisor.Part.Lo, Quotient.Part.Lo, Partial1);
/*
- * The quotient is always 32 bits, and simply requires adjustment.
- * The 64-bit remainder must be generated.
+ * The quotient is always 32 bits, and simply requires
+ * adjustment. The 64-bit remainder must be generated.
*/
- Partial1 = Quotient.Part.Lo * Divisor.Part.Hi;
+ Partial1 = Quotient.Part.Lo * Divisor.Part.Hi;
Partial2.Full = (UINT64) Quotient.Part.Lo * Divisor.Part.Lo;
Partial3.Full = (UINT64) Partial2.Part.Hi + Partial1;
@@ -266,7 +264,7 @@ AcpiUtDivide (
}
}
- Remainder.Full = Remainder.Full - Dividend.Full;
+ Remainder.Full = Remainder.Full - Dividend.Full;
Remainder.Part.Hi = (UINT32) -((INT32) Remainder.Part.Hi);
Remainder.Part.Lo = (UINT32) -((INT32) Remainder.Part.Lo);
@@ -375,5 +373,3 @@ AcpiUtDivide (
}
#endif
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utmisc.c b/usr/src/uts/intel/io/acpica/utilities/utmisc.c
index d20ad7e3a1..ce8f25426d 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utmisc.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utmisc.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTMISC_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -55,86 +52,6 @@
/*******************************************************************************
*
- * FUNCTION: AcpiUtValidateException
- *
- * PARAMETERS: Status - The ACPI_STATUS code to be formatted
- *
- * RETURN: A string containing the exception text. NULL if exception is
- * not valid.
- *
- * DESCRIPTION: This function validates and translates an ACPI exception into
- * an ASCII string.
- *
- ******************************************************************************/
-
-const char *
-AcpiUtValidateException (
- ACPI_STATUS Status)
-{
- UINT32 SubStatus;
- const char *Exception = NULL;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- /*
- * Status is composed of two parts, a "type" and an actual code
- */
- SubStatus = (Status & ~AE_CODE_MASK);
-
- switch (Status & AE_CODE_MASK)
- {
- case AE_CODE_ENVIRONMENTAL:
-
- if (SubStatus <= AE_CODE_ENV_MAX)
- {
- Exception = AcpiGbl_ExceptionNames_Env [SubStatus];
- }
- break;
-
- case AE_CODE_PROGRAMMER:
-
- if (SubStatus <= AE_CODE_PGM_MAX)
- {
- Exception = AcpiGbl_ExceptionNames_Pgm [SubStatus];
- }
- break;
-
- case AE_CODE_ACPI_TABLES:
-
- if (SubStatus <= AE_CODE_TBL_MAX)
- {
- Exception = AcpiGbl_ExceptionNames_Tbl [SubStatus];
- }
- break;
-
- case AE_CODE_AML:
-
- if (SubStatus <= AE_CODE_AML_MAX)
- {
- Exception = AcpiGbl_ExceptionNames_Aml [SubStatus];
- }
- break;
-
- case AE_CODE_CONTROL:
-
- if (SubStatus <= AE_CODE_CTRL_MAX)
- {
- Exception = AcpiGbl_ExceptionNames_Ctrl [SubStatus];
- }
- break;
-
- default:
- break;
- }
-
- return (ACPI_CAST_PTR (const char, Exception));
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiUtIsPciRootBridge
*
* PARAMETERS: Id - The HID/CID in string format
@@ -154,11 +71,11 @@ AcpiUtIsPciRootBridge (
* Check if this is a PCI root bridge.
* ACPI 3.0+: check for a PCI Express root also.
*/
- if (!(ACPI_STRCMP (Id,
- PCI_ROOT_HID_STRING)) ||
+ if (!(strcmp (Id,
+ PCI_ROOT_HID_STRING)) ||
- !(ACPI_STRCMP (Id,
- PCI_EXPRESS_ROOT_HID_STRING)))
+ !(strcmp (Id,
+ PCI_EXPRESS_ROOT_HID_STRING)))
{
return (TRUE);
}
@@ -167,6 +84,7 @@ AcpiUtIsPciRootBridge (
}
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_NAMES_APP)
/*******************************************************************************
*
* FUNCTION: AcpiUtIsAmlTable
@@ -190,384 +108,19 @@ AcpiUtIsAmlTable (
if (ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_DSDT) ||
ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_PSDT) ||
- ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_SSDT))
+ ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_SSDT) ||
+ ACPI_COMPARE_NAME (Table->Signature, ACPI_SIG_OSDT))
{
return (TRUE);
}
return (FALSE);
}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtAllocateOwnerId
- *
- * PARAMETERS: OwnerId - Where the new owner ID is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Allocate a table or method owner ID. The owner ID is used to
- * track objects created by the table or method, to be deleted
- * when the method exits or the table is unloaded.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiUtAllocateOwnerId (
- ACPI_OWNER_ID *OwnerId)
-{
- UINT32 i;
- UINT32 j;
- UINT32 k;
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (UtAllocateOwnerId);
-
-
- /* Guard against multiple allocations of ID to the same location */
-
- if (*OwnerId)
- {
- ACPI_ERROR ((AE_INFO, "Owner ID [0x%2.2X] already exists", *OwnerId));
- return_ACPI_STATUS (AE_ALREADY_EXISTS);
- }
-
- /* Mutex for the global ID mask */
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_CACHES);
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
-
- /*
- * Find a free owner ID, cycle through all possible IDs on repeated
- * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index may have
- * to be scanned twice.
- */
- for (i = 0, j = AcpiGbl_LastOwnerIdIndex;
- i < (ACPI_NUM_OWNERID_MASKS + 1);
- i++, j++)
- {
- if (j >= ACPI_NUM_OWNERID_MASKS)
- {
- j = 0; /* Wraparound to start of mask array */
- }
-
- for (k = AcpiGbl_NextOwnerIdOffset; k < 32; k++)
- {
- if (AcpiGbl_OwnerIdMask[j] == ACPI_UINT32_MAX)
- {
- /* There are no free IDs in this mask */
-
- break;
- }
-
- if (!(AcpiGbl_OwnerIdMask[j] & (1 << k)))
- {
- /*
- * Found a free ID. The actual ID is the bit index plus one,
- * making zero an invalid Owner ID. Save this as the last ID
- * allocated and update the global ID mask.
- */
- AcpiGbl_OwnerIdMask[j] |= (1 << k);
-
- AcpiGbl_LastOwnerIdIndex = (UINT8) j;
- AcpiGbl_NextOwnerIdOffset = (UINT8) (k + 1);
-
- /*
- * Construct encoded ID from the index and bit position
- *
- * Note: Last [j].k (bit 255) is never used and is marked
- * permanently allocated (prevents +1 overflow)
- */
- *OwnerId = (ACPI_OWNER_ID) ((k + 1) + ACPI_MUL_32 (j));
-
- ACPI_DEBUG_PRINT ((ACPI_DB_VALUES,
- "Allocated OwnerId: %2.2X\n", (unsigned int) *OwnerId));
- goto Exit;
- }
- }
-
- AcpiGbl_NextOwnerIdOffset = 0;
- }
-
- /*
- * All OwnerIds have been allocated. This typically should
- * not happen since the IDs are reused after deallocation. The IDs are
- * allocated upon table load (one per table) and method execution, and
- * they are released when a table is unloaded or a method completes
- * execution.
- *
- * If this error happens, there may be very deep nesting of invoked control
- * methods, or there may be a bug where the IDs are not released.
- */
- Status = AE_OWNER_ID_LIMIT;
- ACPI_ERROR ((AE_INFO,
- "Could not allocate new OwnerId (255 max), AE_OWNER_ID_LIMIT"));
-
-Exit:
- (void) AcpiUtReleaseMutex (ACPI_MTX_CACHES);
- return_ACPI_STATUS (Status);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtReleaseOwnerId
- *
- * PARAMETERS: OwnerIdPtr - Pointer to a previously allocated OwnerID
- *
- * RETURN: None. No error is returned because we are either exiting a
- * control method or unloading a table. Either way, we would
- * ignore any error anyway.
- *
- * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 255
- *
- ******************************************************************************/
-
-void
-AcpiUtReleaseOwnerId (
- ACPI_OWNER_ID *OwnerIdPtr)
-{
- ACPI_OWNER_ID OwnerId = *OwnerIdPtr;
- ACPI_STATUS Status;
- UINT32 Index;
- UINT32 Bit;
-
-
- ACPI_FUNCTION_TRACE_U32 (UtReleaseOwnerId, OwnerId);
-
-
- /* Always clear the input OwnerId (zero is an invalid ID) */
-
- *OwnerIdPtr = 0;
-
- /* Zero is not a valid OwnerID */
-
- if (OwnerId == 0)
- {
- ACPI_ERROR ((AE_INFO, "Invalid OwnerId: 0x%2.2X", OwnerId));
- return_VOID;
- }
-
- /* Mutex for the global ID mask */
-
- Status = AcpiUtAcquireMutex (ACPI_MTX_CACHES);
- if (ACPI_FAILURE (Status))
- {
- return_VOID;
- }
-
- /* Normalize the ID to zero */
-
- OwnerId--;
-
- /* Decode ID to index/offset pair */
-
- Index = ACPI_DIV_32 (OwnerId);
- Bit = 1 << ACPI_MOD_32 (OwnerId);
-
- /* Free the owner ID only if it is valid */
-
- if (AcpiGbl_OwnerIdMask[Index] & Bit)
- {
- AcpiGbl_OwnerIdMask[Index] ^= Bit;
- }
- else
- {
- ACPI_ERROR ((AE_INFO,
- "Release of non-allocated OwnerId: 0x%2.2X", OwnerId + 1));
- }
-
- (void) AcpiUtReleaseMutex (ACPI_MTX_CACHES);
- return_VOID;
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtStrupr (strupr)
- *
- * PARAMETERS: SrcString - The source string to convert
- *
- * RETURN: None
- *
- * DESCRIPTION: Convert string to uppercase
- *
- * NOTE: This is not a POSIX function, so it appears here, not in utclib.c
- *
- ******************************************************************************/
-
-void
-AcpiUtStrupr (
- char *SrcString)
-{
- char *String;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- if (!SrcString)
- {
- return;
- }
-
- /* Walk entire string, uppercasing the letters */
-
- for (String = SrcString; *String; String++)
- {
- *String = (char) ACPI_TOUPPER (*String);
- }
-
- return;
-}
-
-
-#ifdef ACPI_ASL_COMPILER
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtStrlwr (strlwr)
- *
- * PARAMETERS: SrcString - The source string to convert
- *
- * RETURN: None
- *
- * DESCRIPTION: Convert string to lowercase
- *
- * NOTE: This is not a POSIX function, so it appears here, not in utclib.c
- *
- ******************************************************************************/
-
-void
-AcpiUtStrlwr (
- char *SrcString)
-{
- char *String;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- if (!SrcString)
- {
- return;
- }
-
- /* Walk entire string, lowercasing the letters */
-
- for (String = SrcString; *String; String++)
- {
- *String = (char) ACPI_TOLOWER (*String);
- }
-
- return;
-}
#endif
/*******************************************************************************
*
- * FUNCTION: AcpiUtPrintString
- *
- * PARAMETERS: String - Null terminated ASCII string
- * MaxLength - Maximum output length
- *
- * RETURN: None
- *
- * DESCRIPTION: Dump an ASCII string with support for ACPI-defined escape
- * sequences.
- *
- ******************************************************************************/
-
-void
-AcpiUtPrintString (
- char *String,
- UINT8 MaxLength)
-{
- UINT32 i;
-
-
- if (!String)
- {
- AcpiOsPrintf ("<\"NULL STRING PTR\">");
- return;
- }
-
- AcpiOsPrintf ("\"");
- for (i = 0; String[i] && (i < MaxLength); i++)
- {
- /* Escape sequences */
-
- switch (String[i])
- {
- case 0x07:
- AcpiOsPrintf ("\\a"); /* BELL */
- break;
-
- case 0x08:
- AcpiOsPrintf ("\\b"); /* BACKSPACE */
- break;
-
- case 0x0C:
- AcpiOsPrintf ("\\f"); /* FORMFEED */
- break;
-
- case 0x0A:
- AcpiOsPrintf ("\\n"); /* LINEFEED */
- break;
-
- case 0x0D:
- AcpiOsPrintf ("\\r"); /* CARRIAGE RETURN*/
- break;
-
- case 0x09:
- AcpiOsPrintf ("\\t"); /* HORIZONTAL TAB */
- break;
-
- case 0x0B:
- AcpiOsPrintf ("\\v"); /* VERTICAL TAB */
- break;
-
- case '\'': /* Single Quote */
- case '\"': /* Double Quote */
- case '\\': /* Backslash */
- AcpiOsPrintf ("\\%c", (int) String[i]);
- break;
-
- default:
-
- /* Check for printable character or hex escape */
-
- if (ACPI_IS_PRINT (String[i]))
- {
- /* This is a normal character */
-
- AcpiOsPrintf ("%c", (int) String[i]);
- }
- else
- {
- /* All others will be Hex escapes */
-
- AcpiOsPrintf ("\\x%2.2X", (INT32) String[i]);
- }
- break;
- }
- }
- AcpiOsPrintf ("\"");
-
- if (i == MaxLength && String[i])
- {
- AcpiOsPrintf ("...");
- }
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiUtDwordByteSwap
*
* PARAMETERS: Value - Value to be converted
@@ -617,8 +170,8 @@ AcpiUtDwordByteSwap (
* RETURN: None
*
* DESCRIPTION: Set the global integer bit width based upon the revision
- * of the DSDT. For Revision 1 and 0, Integers are 32 bits.
- * For Revision 2 and above, Integers are 64 bits. Yes, this
+ * of the DSDT. For Revision 1 and 0, Integers are 32 bits.
+ * For Revision 2 and above, Integers are 64 bits. Yes, this
* makes a difference.
*
******************************************************************************/
@@ -632,450 +185,17 @@ AcpiUtSetIntegerWidth (
{
/* 32-bit case */
- AcpiGbl_IntegerBitWidth = 32;
+ AcpiGbl_IntegerBitWidth = 32;
AcpiGbl_IntegerNybbleWidth = 8;
- AcpiGbl_IntegerByteWidth = 4;
+ AcpiGbl_IntegerByteWidth = 4;
}
else
{
/* 64-bit case (ACPI 2.0+) */
- AcpiGbl_IntegerBitWidth = 64;
+ AcpiGbl_IntegerBitWidth = 64;
AcpiGbl_IntegerNybbleWidth = 16;
- AcpiGbl_IntegerByteWidth = 8;
- }
-}
-
-
-#ifdef ACPI_DEBUG_OUTPUT
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtDisplayInitPathname
- *
- * PARAMETERS: Type - Object type of the node
- * ObjHandle - Handle whose pathname will be displayed
- * Path - Additional path string to be appended.
- * (NULL if no extra path)
- *
- * RETURN: ACPI_STATUS
- *
- * DESCRIPTION: Display full pathname of an object, DEBUG ONLY
- *
- ******************************************************************************/
-
-void
-AcpiUtDisplayInitPathname (
- UINT8 Type,
- ACPI_NAMESPACE_NODE *ObjHandle,
- char *Path)
-{
- ACPI_STATUS Status;
- ACPI_BUFFER Buffer;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- /* Only print the path if the appropriate debug level is enabled */
-
- if (!(AcpiDbgLevel & ACPI_LV_INIT_NAMES))
- {
- return;
- }
-
- /* Get the full pathname to the node */
-
- Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
- Status = AcpiNsHandleToPathname (ObjHandle, &Buffer);
- if (ACPI_FAILURE (Status))
- {
- return;
- }
-
- /* Print what we're doing */
-
- switch (Type)
- {
- case ACPI_TYPE_METHOD:
- AcpiOsPrintf ("Executing ");
- break;
-
- default:
- AcpiOsPrintf ("Initializing ");
- break;
- }
-
- /* Print the object type and pathname */
-
- AcpiOsPrintf ("%-12s %s",
- AcpiUtGetTypeName (Type), (char *) Buffer.Pointer);
-
- /* Extra path is used to append names like _STA, _INI, etc. */
-
- if (Path)
- {
- AcpiOsPrintf (".%s", Path);
- }
- AcpiOsPrintf ("\n");
-
- ACPI_FREE (Buffer.Pointer);
-}
-#endif
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtValidAcpiChar
- *
- * PARAMETERS: Char - The character to be examined
- * Position - Byte position (0-3)
- *
- * RETURN: TRUE if the character is valid, FALSE otherwise
- *
- * DESCRIPTION: Check for a valid ACPI character. Must be one of:
- * 1) Upper case alpha
- * 2) numeric
- * 3) underscore
- *
- * We allow a '!' as the last character because of the ASF! table
- *
- ******************************************************************************/
-
-BOOLEAN
-AcpiUtValidAcpiChar (
- char Character,
- UINT32 Position)
-{
-
- if (!((Character >= 'A' && Character <= 'Z') ||
- (Character >= '0' && Character <= '9') ||
- (Character == '_')))
- {
- /* Allow a '!' in the last position */
-
- if (Character == '!' && Position == 3)
- {
- return (TRUE);
- }
-
- return (FALSE);
- }
-
- return (TRUE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtValidAcpiName
- *
- * PARAMETERS: Name - The name to be examined
- *
- * RETURN: TRUE if the name is valid, FALSE otherwise
- *
- * DESCRIPTION: Check for a valid ACPI name. Each character must be one of:
- * 1) Upper case alpha
- * 2) numeric
- * 3) underscore
- *
- ******************************************************************************/
-
-BOOLEAN
-AcpiUtValidAcpiName (
- UINT32 Name)
-{
- UINT32 i;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- for (i = 0; i < ACPI_NAME_SIZE; i++)
- {
- if (!AcpiUtValidAcpiChar ((ACPI_CAST_PTR (char, &Name))[i], i))
- {
- return (FALSE);
- }
- }
-
- return (TRUE);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtRepairName
- *
- * PARAMETERS: Name - The ACPI name to be repaired
- *
- * RETURN: Repaired version of the name
- *
- * DESCRIPTION: Repair an ACPI name: Change invalid characters to '*' and
- * return the new name. NOTE: the Name parameter must reside in
- * read/write memory, cannot be a const.
- *
- * An ACPI Name must consist of valid ACPI characters. We will repair the name
- * if necessary because we don't want to abort because of this, but we want
- * all namespace names to be printable. A warning message is appropriate.
- *
- * This issue came up because there are in fact machines that exhibit
- * this problem, and we want to be able to enable ACPI support for them,
- * even though there are a few bad names.
- *
- ******************************************************************************/
-
-void
-AcpiUtRepairName (
- char *Name)
-{
- UINT32 i;
- BOOLEAN FoundBadChar = FALSE;
-
-
- ACPI_FUNCTION_NAME (UtRepairName);
-
-
- /* Check each character in the name */
-
- for (i = 0; i < ACPI_NAME_SIZE; i++)
- {
- if (AcpiUtValidAcpiChar (Name[i], i))
- {
- continue;
- }
-
- /*
- * Replace a bad character with something printable, yet technically
- * still invalid. This prevents any collisions with existing "good"
- * names in the namespace.
- */
- Name[i] = '*';
- FoundBadChar = TRUE;
- }
-
- if (FoundBadChar)
- {
- /* Report warning only if in strict mode or debug mode */
-
- if (!AcpiGbl_EnableInterpreterSlack)
- {
- ACPI_WARNING ((AE_INFO,
- "Found bad character(s) in name, repaired: [%4.4s]\n", Name));
- }
- else
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
- "Found bad character(s) in name, repaired: [%4.4s]\n", Name));
- }
- }
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtStrtoul64
- *
- * PARAMETERS: String - Null terminated string
- * Base - Radix of the string: 16 or ACPI_ANY_BASE;
- * ACPI_ANY_BASE means 'in behalf of ToInteger'
- * RetInteger - Where the converted integer is returned
- *
- * RETURN: Status and Converted value
- *
- * DESCRIPTION: Convert a string into an unsigned value. Performs either a
- * 32-bit or 64-bit conversion, depending on the current mode
- * of the interpreter.
- * NOTE: Does not support Octal strings, not needed.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiUtStrtoul64 (
- char *String,
- UINT32 Base,
- UINT64 *RetInteger)
-{
- UINT32 ThisDigit = 0;
- UINT64 ReturnValue = 0;
- UINT64 Quotient;
- UINT64 Dividend;
- UINT32 ToIntegerOp = (Base == ACPI_ANY_BASE);
- UINT32 Mode32 = (AcpiGbl_IntegerByteWidth == 4);
- UINT8 ValidDigits = 0;
- UINT8 SignOf0x = 0;
- UINT8 Term = 0;
-
-
- ACPI_FUNCTION_TRACE_STR (UtStroul64, String);
-
-
- switch (Base)
- {
- case ACPI_ANY_BASE:
- case 16:
- break;
-
- default:
- /* Invalid Base */
- return_ACPI_STATUS (AE_BAD_PARAMETER);
- }
-
- if (!String)
- {
- goto ErrorExit;
- }
-
- /* Skip over any white space in the buffer */
-
- while ((*String) && (ACPI_IS_SPACE (*String) || *String == '\t'))
- {
- String++;
- }
-
- if (ToIntegerOp)
- {
- /*
- * Base equal to ACPI_ANY_BASE means 'ToInteger operation case'.
- * We need to determine if it is decimal or hexadecimal.
- */
- if ((*String == '0') && (ACPI_TOLOWER (*(String + 1)) == 'x'))
- {
- SignOf0x = 1;
- Base = 16;
-
- /* Skip over the leading '0x' */
- String += 2;
- }
- else
- {
- Base = 10;
- }
- }
-
- /* Any string left? Check that '0x' is not followed by white space. */
-
- if (!(*String) || ACPI_IS_SPACE (*String) || *String == '\t')
- {
- if (ToIntegerOp)
- {
- goto ErrorExit;
- }
- else
- {
- goto AllDone;
- }
- }
-
- /*
- * Perform a 32-bit or 64-bit conversion, depending upon the current
- * execution mode of the interpreter
- */
- Dividend = (Mode32) ? ACPI_UINT32_MAX : ACPI_UINT64_MAX;
-
- /* Main loop: convert the string to a 32- or 64-bit integer */
-
- while (*String)
- {
- if (ACPI_IS_DIGIT (*String))
- {
- /* Convert ASCII 0-9 to Decimal value */
-
- ThisDigit = ((UINT8) *String) - '0';
- }
- else if (Base == 10)
- {
- /* Digit is out of range; possible in ToInteger case only */
-
- Term = 1;
- }
- else
- {
- ThisDigit = (UINT8) ACPI_TOUPPER (*String);
- if (ACPI_IS_XDIGIT ((char) ThisDigit))
- {
- /* Convert ASCII Hex char to value */
-
- ThisDigit = ThisDigit - 'A' + 10;
- }
- else
- {
- Term = 1;
- }
- }
-
- if (Term)
- {
- if (ToIntegerOp)
- {
- goto ErrorExit;
- }
- else
- {
- break;
- }
- }
- else if ((ValidDigits == 0) && (ThisDigit == 0) && !SignOf0x)
- {
- /* Skip zeros */
- String++;
- continue;
- }
-
- ValidDigits++;
-
- if (SignOf0x && ((ValidDigits > 16) || ((ValidDigits > 8) && Mode32)))
- {
- /*
- * This is ToInteger operation case.
- * No any restrictions for string-to-integer conversion,
- * see ACPI spec.
- */
- goto ErrorExit;
- }
-
- /* Divide the digit into the correct position */
-
- (void) AcpiUtShortDivide ((Dividend - (UINT64) ThisDigit),
- Base, &Quotient, NULL);
-
- if (ReturnValue > Quotient)
- {
- if (ToIntegerOp)
- {
- goto ErrorExit;
- }
- else
- {
- break;
- }
- }
-
- ReturnValue *= Base;
- ReturnValue += ThisDigit;
- String++;
- }
-
- /* All done, normal exit */
-
-AllDone:
-
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
- ACPI_FORMAT_UINT64 (ReturnValue)));
-
- *RetInteger = ReturnValue;
- return_ACPI_STATUS (AE_OK);
-
-
-ErrorExit:
- /* Base was set/validated above */
-
- if (Base == 10)
- {
- return_ACPI_STATUS (AE_BAD_DECIMAL_CONSTANT);
- }
- else
- {
- return_ACPI_STATUS (AE_BAD_HEX_CONSTANT);
+ AcpiGbl_IntegerByteWidth = 8;
}
}
@@ -1166,20 +286,21 @@ AcpiUtWalkPackageTree (
{
/* Get one element of the package */
- ThisIndex = State->Pkg.Index;
+ ThisIndex = State->Pkg.Index;
ThisSourceObj = (ACPI_OPERAND_OBJECT *)
- State->Pkg.SourceObject->Package.Elements[ThisIndex];
+ State->Pkg.SourceObject->Package.Elements[ThisIndex];
/*
* Check for:
- * 1) An uninitialized package element. It is completely
+ * 1) An uninitialized package element. It is completely
* legal to declare a package and leave it uninitialized
* 2) Not an internal object - can be a namespace node instead
- * 3) Any type other than a package. Packages are handled in else
+ * 3) Any type other than a package. Packages are handled in else
* case below.
*/
if ((!ThisSourceObj) ||
- (ACPI_GET_DESCRIPTOR_TYPE (ThisSourceObj) != ACPI_DESC_TYPE_OPERAND) ||
+ (ACPI_GET_DESCRIPTOR_TYPE (ThisSourceObj) !=
+ ACPI_DESC_TYPE_OPERAND) ||
(ThisSourceObj->Common.Type != ACPI_TYPE_PACKAGE))
{
Status = WalkCallback (ACPI_COPY_TYPE_SIMPLE, ThisSourceObj,
@@ -1190,11 +311,12 @@ AcpiUtWalkPackageTree (
}
State->Pkg.Index++;
- while (State->Pkg.Index >= State->Pkg.SourceObject->Package.Count)
+ while (State->Pkg.Index >=
+ State->Pkg.SourceObject->Package.Count)
{
/*
* We've handled all of the objects at this level, This means
- * that we have just completed a package. That package may
+ * that we have just completed a package. That package may
* have contained one or more packages itself.
*
* Delete this state and pop the previous state (package).
@@ -1225,8 +347,8 @@ AcpiUtWalkPackageTree (
{
/* This is a subobject of type package */
- Status = WalkCallback (ACPI_COPY_TYPE_PACKAGE, ThisSourceObj,
- State, Context);
+ Status = WalkCallback (
+ ACPI_COPY_TYPE_PACKAGE, ThisSourceObj, State, Context);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -1237,8 +359,8 @@ AcpiUtWalkPackageTree (
* The callback above returned a new target package object.
*/
AcpiUtPushGenericState (&StateList, State);
- State = AcpiUtCreatePkgState (ThisSourceObj,
- State->Pkg.ThisTargetObj, 0);
+ State = AcpiUtCreatePkgState (
+ ThisSourceObj, State->Pkg.ThisTargetObj, 0);
if (!State)
{
/* Free any stacked Update State objects */
@@ -1259,3 +381,79 @@ AcpiUtWalkPackageTree (
}
+#ifdef ACPI_DEBUG_OUTPUT
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDisplayInitPathname
+ *
+ * PARAMETERS: Type - Object type of the node
+ * ObjHandle - Handle whose pathname will be displayed
+ * Path - Additional path string to be appended.
+ * (NULL if no extra path)
+ *
+ * RETURN: ACPI_STATUS
+ *
+ * DESCRIPTION: Display full pathname of an object, DEBUG ONLY
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDisplayInitPathname (
+ UINT8 Type,
+ ACPI_NAMESPACE_NODE *ObjHandle,
+ const char *Path)
+{
+ ACPI_STATUS Status;
+ ACPI_BUFFER Buffer;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ /* Only print the path if the appropriate debug level is enabled */
+
+ if (!(AcpiDbgLevel & ACPI_LV_INIT_NAMES))
+ {
+ return;
+ }
+
+ /* Get the full pathname to the node */
+
+ Buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
+ Status = AcpiNsHandleToPathname (ObjHandle, &Buffer, TRUE);
+ if (ACPI_FAILURE (Status))
+ {
+ return;
+ }
+
+ /* Print what we're doing */
+
+ switch (Type)
+ {
+ case ACPI_TYPE_METHOD:
+
+ AcpiOsPrintf ("Executing ");
+ break;
+
+ default:
+
+ AcpiOsPrintf ("Initializing ");
+ break;
+ }
+
+ /* Print the object type and pathname */
+
+ AcpiOsPrintf ("%-12s %s",
+ AcpiUtGetTypeName (Type), (char *) Buffer.Pointer);
+
+ /* Extra path is used to append names like _STA, _INI, etc. */
+
+ if (Path)
+ {
+ AcpiOsPrintf (".%s", Path);
+ }
+ AcpiOsPrintf ("\n");
+
+ ACPI_FREE (Buffer.Pointer);
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utmutex.c b/usr/src/uts/intel/io/acpica/utilities/utmutex.c
index f7af803de1..83b1cee0d7 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utmutex.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utmutex.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTMUTEX_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -96,7 +93,7 @@ AcpiUtMutexInitialize (
}
}
- /* Create the spinlocks for use at interrupt level */
+ /* Create the spinlocks for use at interrupt level or for speed */
Status = AcpiOsCreateLock (&AcpiGbl_GpeLock);
if (ACPI_FAILURE (Status))
@@ -110,7 +107,14 @@ AcpiUtMutexInitialize (
return_ACPI_STATUS (Status);
}
+ Status = AcpiOsCreateLock (&AcpiGbl_ReferenceCountLock);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
/* Mutex for _OSI support */
+
Status = AcpiOsCreateMutex (&AcpiGbl_OsiMutex);
if (ACPI_FAILURE (Status))
{
@@ -120,6 +124,24 @@ AcpiUtMutexInitialize (
/* Create the reader/writer lock for namespace access */
Status = AcpiUtCreateRwLock (&AcpiGbl_NamespaceRwLock);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+#ifdef ACPI_DEBUGGER
+
+ /* Debugger Support */
+
+ Status = AcpiOsCreateMutex (&AcpiGbl_DbCommandReady);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ Status = AcpiOsCreateMutex (&AcpiGbl_DbCommandComplete);
+#endif
+
return_ACPI_STATUS (Status);
}
@@ -160,10 +182,17 @@ AcpiUtMutexTerminate (
AcpiOsDeleteLock (AcpiGbl_GpeLock);
AcpiOsDeleteLock (AcpiGbl_HardwareLock);
+ AcpiOsDeleteLock (AcpiGbl_ReferenceCountLock);
/* Delete the reader/writer lock */
AcpiUtDeleteRwLock (&AcpiGbl_NamespaceRwLock);
+
+#ifdef ACPI_DEBUGGER
+ AcpiOsDeleteMutex (AcpiGbl_DbCommandReady);
+ AcpiOsDeleteMutex (AcpiGbl_DbCommandComplete);
+#endif
+
return_VOID;
}
@@ -225,6 +254,8 @@ AcpiUtDeleteMutex (
AcpiGbl_MutexInfo[MutexId].Mutex = NULL;
AcpiGbl_MutexInfo[MutexId].ThreadId = ACPI_MUTEX_NOT_ACQUIRED;
+
+ return_VOID;
}
@@ -264,9 +295,9 @@ AcpiUtAcquireMutex (
/*
* Mutex debug code, for internal debugging only.
*
- * Deadlock prevention. Check if this thread owns any mutexes of value
- * greater than or equal to this one. If so, the thread has violated
- * the mutex ordering rule. This indicates a coding error somewhere in
+ * Deadlock prevention. Check if this thread owns any mutexes of value
+ * greater than or equal to this one. If so, the thread has violated
+ * the mutex ordering rule. This indicates a coding error somewhere in
* the ACPI subsystem code.
*/
for (i = MutexId; i < ACPI_NUM_MUTEX; i++)
@@ -298,11 +329,12 @@ AcpiUtAcquireMutex (
"Thread %u attempting to acquire Mutex [%s]\n",
(UINT32) ThisThreadId, AcpiUtGetMutexName (MutexId)));
- Status = AcpiOsAcquireMutex (AcpiGbl_MutexInfo[MutexId].Mutex,
- ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (
+ AcpiGbl_MutexInfo[MutexId].Mutex, ACPI_WAIT_FOREVER);
if (ACPI_SUCCESS (Status))
{
- ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Thread %u acquired Mutex [%s]\n",
+ ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX,
+ "Thread %u acquired Mutex [%s]\n",
(UINT32) ThisThreadId, AcpiUtGetMutexName (MutexId)));
AcpiGbl_MutexInfo[MutexId].UseCount++;
@@ -335,15 +367,11 @@ ACPI_STATUS
AcpiUtReleaseMutex (
ACPI_MUTEX_HANDLE MutexId)
{
- ACPI_THREAD_ID ThisThreadId;
-
-
ACPI_FUNCTION_NAME (UtReleaseMutex);
- ThisThreadId = AcpiOsGetThreadId ();
ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n",
- (UINT32) ThisThreadId, AcpiUtGetMutexName (MutexId)));
+ (UINT32) AcpiOsGetThreadId (), AcpiUtGetMutexName (MutexId)));
if (MutexId > ACPI_MAX_MUTEX)
{
@@ -367,14 +395,14 @@ AcpiUtReleaseMutex (
/*
* Mutex debug code, for internal debugging only.
*
- * Deadlock prevention. Check if this thread owns any mutexes of value
- * greater than this one. If so, the thread has violated the mutex
- * ordering rule. This indicates a coding error somewhere in
+ * Deadlock prevention. Check if this thread owns any mutexes of value
+ * greater than this one. If so, the thread has violated the mutex
+ * ordering rule. This indicates a coding error somewhere in
* the ACPI subsystem code.
*/
for (i = MutexId; i < ACPI_NUM_MUTEX; i++)
{
- if (AcpiGbl_MutexInfo[i].ThreadId == ThisThreadId)
+ if (AcpiGbl_MutexInfo[i].ThreadId == AcpiOsGetThreadId ())
{
if (i == MutexId)
{
@@ -398,5 +426,3 @@ AcpiUtReleaseMutex (
AcpiOsReleaseMutex (AcpiGbl_MutexInfo[MutexId].Mutex);
return (AE_OK);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utnonansi.c b/usr/src/uts/intel/io/acpica/utilities/utnonansi.c
new file mode 100644
index 0000000000..70fb33e64c
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utnonansi.c
@@ -0,0 +1,667 @@
+/*******************************************************************************
+ *
+ * Module Name: utnonansi - Non-ansi C library functions
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utnonansi")
+
+
+/*
+ * Non-ANSI C library functions - strlwr, strupr, stricmp, and a 64-bit
+ * version of strtoul.
+ */
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtStrlwr (strlwr)
+ *
+ * PARAMETERS: SrcString - The source string to convert
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert a string to lowercase
+ *
+ ******************************************************************************/
+
+void
+AcpiUtStrlwr (
+ char *SrcString)
+{
+ char *String;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ if (!SrcString)
+ {
+ return;
+ }
+
+ /* Walk entire string, lowercasing the letters */
+
+ for (String = SrcString; *String; String++)
+ {
+ *String = (char) tolower ((int) *String);
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtStrupr (strupr)
+ *
+ * PARAMETERS: SrcString - The source string to convert
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Convert a string to uppercase
+ *
+ ******************************************************************************/
+
+void
+AcpiUtStrupr (
+ char *SrcString)
+{
+ char *String;
+
+
+ ACPI_FUNCTION_ENTRY ();
+
+
+ if (!SrcString)
+ {
+ return;
+ }
+
+ /* Walk entire string, uppercasing the letters */
+
+ for (String = SrcString; *String; String++)
+ {
+ *String = (char) toupper ((int) *String);
+ }
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION: AcpiUtStricmp (stricmp)
+ *
+ * PARAMETERS: String1 - first string to compare
+ * String2 - second string to compare
+ *
+ * RETURN: int that signifies string relationship. Zero means strings
+ * are equal.
+ *
+ * DESCRIPTION: Case-insensitive string compare. Implementation of the
+ * non-ANSI stricmp function.
+ *
+ ******************************************************************************/
+
+int
+AcpiUtStricmp (
+ char *String1,
+ char *String2)
+{
+ int c1;
+ int c2;
+
+
+ do
+ {
+ c1 = tolower ((int) *String1);
+ c2 = tolower ((int) *String2);
+
+ String1++;
+ String2++;
+ }
+ while ((c1 == c2) && (c1));
+
+ return (c1 - c2);
+}
+
+
+#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION)
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtSafeStrcpy, AcpiUtSafeStrcat, AcpiUtSafeStrncat
+ *
+ * PARAMETERS: Adds a "DestSize" parameter to each of the standard string
+ * functions. This is the size of the Destination buffer.
+ *
+ * RETURN: TRUE if the operation would overflow the destination buffer.
+ *
+ * DESCRIPTION: Safe versions of standard Clib string functions. Ensure that
+ * the result of the operation will not overflow the output string
+ * buffer.
+ *
+ * NOTE: These functions are typically only helpful for processing
+ * user input and command lines. For most ACPICA code, the
+ * required buffer length is precisely calculated before buffer
+ * allocation, so the use of these functions is unnecessary.
+ *
+ ******************************************************************************/
+
+BOOLEAN
+AcpiUtSafeStrcpy (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source)
+{
+
+ if (strlen (Source) >= DestSize)
+ {
+ return (TRUE);
+ }
+
+ strcpy (Dest, Source);
+ return (FALSE);
+}
+
+BOOLEAN
+AcpiUtSafeStrcat (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source)
+{
+
+ if ((strlen (Dest) + strlen (Source)) >= DestSize)
+ {
+ return (TRUE);
+ }
+
+ strcat (Dest, Source);
+ return (FALSE);
+}
+
+BOOLEAN
+AcpiUtSafeStrncat (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source,
+ ACPI_SIZE MaxTransferLength)
+{
+ ACPI_SIZE ActualTransferLength;
+
+
+ ActualTransferLength = ACPI_MIN (MaxTransferLength, strlen (Source));
+
+ if ((strlen (Dest) + ActualTransferLength) >= DestSize)
+ {
+ return (TRUE);
+ }
+
+ strncat (Dest, Source, MaxTransferLength);
+ return (FALSE);
+}
+#endif
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtStrtoul64
+ *
+ * PARAMETERS: String - Null terminated string
+ * Base - Radix of the string: 16 or 10 or
+ * ACPI_ANY_BASE
+ * MaxIntegerByteWidth - Maximum allowable integer,in bytes:
+ * 4 or 8 (32 or 64 bits)
+ * RetInteger - Where the converted integer is
+ * returned
+ *
+ * RETURN: Status and Converted value
+ *
+ * DESCRIPTION: Convert a string into an unsigned value. Performs either a
+ * 32-bit or 64-bit conversion, depending on the input integer
+ * size (often the current mode of the interpreter).
+ *
+ * NOTES: Negative numbers are not supported, as they are not supported
+ * by ACPI.
+ *
+ * AcpiGbl_IntegerByteWidth should be set to the proper width.
+ * For the core ACPICA code, this width depends on the DSDT
+ * version. For iASL, the default byte width is always 8 for the
+ * parser, but error checking is performed later to flag cases
+ * where a 64-bit constant is defined in a 32-bit DSDT/SSDT.
+ *
+ * Does not support Octal strings, not needed at this time.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtStrtoul64 (
+ char *String,
+ UINT32 Base,
+ UINT32 MaxIntegerByteWidth,
+ UINT64 *RetInteger)
+{
+ UINT32 ThisDigit = 0;
+ UINT64 ReturnValue = 0;
+ UINT64 Quotient;
+ UINT64 Dividend;
+ UINT8 ValidDigits = 0;
+ UINT8 SignOf0x = 0;
+ UINT8 Term = 0;
+
+
+ ACPI_FUNCTION_TRACE_STR (UtStrtoul64, String);
+
+
+ switch (Base)
+ {
+ case ACPI_ANY_BASE:
+ case 10:
+ case 16:
+
+ break;
+
+ default:
+
+ /* Invalid Base */
+
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
+ }
+
+ if (!String)
+ {
+ goto ErrorExit;
+ }
+
+ /* Skip over any white space in the buffer */
+
+ while ((*String) && (isspace ((int) *String) || *String == '\t'))
+ {
+ String++;
+ }
+
+ if (Base == ACPI_ANY_BASE)
+ {
+ /*
+ * Base equal to ACPI_ANY_BASE means 'Either decimal or hex'.
+ * We need to determine if it is decimal or hexadecimal.
+ */
+ if ((*String == '0') && (tolower ((int) *(String + 1)) == 'x'))
+ {
+ SignOf0x = 1;
+ Base = 16;
+
+ /* Skip over the leading '0x' */
+ String += 2;
+ }
+ else
+ {
+ Base = 10;
+ }
+ }
+
+ /* Any string left? Check that '0x' is not followed by white space. */
+
+ if (!(*String) || isspace ((int) *String) || *String == '\t')
+ {
+ if (Base == ACPI_ANY_BASE)
+ {
+ goto ErrorExit;
+ }
+ else
+ {
+ goto AllDone;
+ }
+ }
+
+ /*
+ * Perform a 32-bit or 64-bit conversion, depending upon the input
+ * byte width
+ */
+ Dividend = (MaxIntegerByteWidth <= ACPI_MAX32_BYTE_WIDTH) ?
+ ACPI_UINT32_MAX : ACPI_UINT64_MAX;
+
+ /* Main loop: convert the string to a 32- or 64-bit integer */
+
+ while (*String)
+ {
+ if (isdigit ((int) *String))
+ {
+ /* Convert ASCII 0-9 to Decimal value */
+
+ ThisDigit = ((UINT8) *String) - '0';
+ }
+ else if (Base == 10)
+ {
+ /* Digit is out of range; possible in ToInteger case only */
+
+ Term = 1;
+ }
+ else
+ {
+ ThisDigit = (UINT8) toupper ((int) *String);
+ if (isxdigit ((int) ThisDigit))
+ {
+ /* Convert ASCII Hex char to value */
+
+ ThisDigit = ThisDigit - 'A' + 10;
+ }
+ else
+ {
+ Term = 1;
+ }
+ }
+
+ if (Term)
+ {
+ if (Base == ACPI_ANY_BASE)
+ {
+ goto ErrorExit;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if ((ValidDigits == 0) && (ThisDigit == 0) && !SignOf0x)
+ {
+ /* Skip zeros */
+ String++;
+ continue;
+ }
+
+ ValidDigits++;
+
+ if (SignOf0x && ((ValidDigits > 16) ||
+ ((ValidDigits > 8) && (MaxIntegerByteWidth <= ACPI_MAX32_BYTE_WIDTH))))
+ {
+ /*
+ * This is ToInteger operation case.
+ * No restrictions for string-to-integer conversion,
+ * see ACPI spec.
+ */
+ goto ErrorExit;
+ }
+
+ /* Divide the digit into the correct position */
+
+ (void) AcpiUtShortDivide (
+ (Dividend - (UINT64) ThisDigit), Base, &Quotient, NULL);
+
+ if (ReturnValue > Quotient)
+ {
+ if (Base == ACPI_ANY_BASE)
+ {
+ goto ErrorExit;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ ReturnValue *= Base;
+ ReturnValue += ThisDigit;
+ String++;
+ }
+
+ /* All done, normal exit */
+
+AllDone:
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64 (ReturnValue)));
+
+ *RetInteger = ReturnValue;
+ return_ACPI_STATUS (AE_OK);
+
+
+ErrorExit:
+
+ /* Base was set/validated above (10 or 16) */
+
+ if (Base == 10)
+ {
+ return_ACPI_STATUS (AE_BAD_DECIMAL_CONSTANT);
+ }
+ else
+ {
+ return_ACPI_STATUS (AE_BAD_HEX_CONSTANT);
+ }
+}
+
+
+#ifdef _OBSOLETE_FUNCTIONS
+/* Removed: 01/2016 */
+
+/*******************************************************************************
+ *
+ * FUNCTION: strtoul64
+ *
+ * PARAMETERS: String - Null terminated string
+ * Terminater - Where a pointer to the terminating byte
+ * is returned
+ * Base - Radix of the string
+ *
+ * RETURN: Converted value
+ *
+ * DESCRIPTION: Convert a string into an unsigned value.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+strtoul64 (
+ char *String,
+ UINT32 Base,
+ UINT64 *RetInteger)
+{
+ UINT32 Index;
+ UINT32 Sign;
+ UINT64 ReturnValue = 0;
+ ACPI_STATUS Status = AE_OK;
+
+
+ *RetInteger = 0;
+
+ switch (Base)
+ {
+ case 0:
+ case 8:
+ case 10:
+ case 16:
+
+ break;
+
+ default:
+ /*
+ * The specified Base parameter is not in the domain of
+ * this function:
+ */
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Skip over any white space in the buffer: */
+
+ while (isspace ((int) *String) || *String == '\t')
+ {
+ ++String;
+ }
+
+ /*
+ * The buffer may contain an optional plus or minus sign.
+ * If it does, then skip over it but remember what is was:
+ */
+ if (*String == '-')
+ {
+ Sign = ACPI_SIGN_NEGATIVE;
+ ++String;
+ }
+ else if (*String == '+')
+ {
+ ++String;
+ Sign = ACPI_SIGN_POSITIVE;
+ }
+ else
+ {
+ Sign = ACPI_SIGN_POSITIVE;
+ }
+
+ /*
+ * If the input parameter Base is zero, then we need to
+ * determine if it is octal, decimal, or hexadecimal:
+ */
+ if (Base == 0)
+ {
+ if (*String == '0')
+ {
+ if (tolower ((int) *(++String)) == 'x')
+ {
+ Base = 16;
+ ++String;
+ }
+ else
+ {
+ Base = 8;
+ }
+ }
+ else
+ {
+ Base = 10;
+ }
+ }
+
+ /*
+ * For octal and hexadecimal bases, skip over the leading
+ * 0 or 0x, if they are present.
+ */
+ if (Base == 8 && *String == '0')
+ {
+ String++;
+ }
+
+ if (Base == 16 &&
+ *String == '0' &&
+ tolower ((int) *(++String)) == 'x')
+ {
+ String++;
+ }
+
+ /* Main loop: convert the string to an unsigned long */
+
+ while (*String)
+ {
+ if (isdigit ((int) *String))
+ {
+ Index = ((UINT8) *String) - '0';
+ }
+ else
+ {
+ Index = (UINT8) toupper ((int) *String);
+ if (isupper ((int) Index))
+ {
+ Index = Index - 'A' + 10;
+ }
+ else
+ {
+ goto ErrorExit;
+ }
+ }
+
+ if (Index >= Base)
+ {
+ goto ErrorExit;
+ }
+
+ /* Check to see if value is out of range: */
+
+ if (ReturnValue > ((ACPI_UINT64_MAX - (UINT64) Index) /
+ (UINT64) Base))
+ {
+ goto ErrorExit;
+ }
+ else
+ {
+ ReturnValue *= Base;
+ ReturnValue += Index;
+ }
+
+ ++String;
+ }
+
+
+ /* If a minus sign was present, then "the conversion is negated": */
+
+ if (Sign == ACPI_SIGN_NEGATIVE)
+ {
+ ReturnValue = (ACPI_UINT32_MAX - ReturnValue) + 1;
+ }
+
+ *RetInteger = ReturnValue;
+ return (Status);
+
+
+ErrorExit:
+ switch (Base)
+ {
+ case 8:
+
+ Status = AE_BAD_OCTAL_CONSTANT;
+ break;
+
+ case 10:
+
+ Status = AE_BAD_DECIMAL_CONSTANT;
+ break;
+
+ case 16:
+
+ Status = AE_BAD_HEX_CONSTANT;
+ break;
+
+ default:
+
+ /* Base validated above */
+
+ break;
+ }
+
+ return (Status);
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utobject.c b/usr/src/uts/intel/io/acpica/utilities/utobject.c
index ea621c741e..ed3b85f1d7 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utobject.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utobject.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTOBJECT_C__
-
#include "acpi.h"
#include "accommon.h"
#include "acnamesp.h"
@@ -86,7 +84,7 @@ AcpiUtGetElementLength (
*
* NOTE: We always allocate the worst-case object descriptor because
* these objects are cached, and we want them to be
- * one-size-satisifies-any-request. This in itself may not be
+ * one-size-satisifies-any-request. This in itself may not be
* the most memory efficient, but the efficiency of the object
* cache should more than make up for this!
*
@@ -109,7 +107,8 @@ AcpiUtCreateInternalObjectDbg (
/* Allocate the raw object descriptor */
- Object = AcpiUtAllocateObjectDescDbg (ModuleName, LineNumber, ComponentId);
+ Object = AcpiUtAllocateObjectDescDbg (
+ ModuleName, LineNumber, ComponentId);
if (!Object)
{
return_PTR (NULL);
@@ -123,8 +122,8 @@ AcpiUtCreateInternalObjectDbg (
/* These types require a secondary object */
- SecondObject = AcpiUtAllocateObjectDescDbg (ModuleName,
- LineNumber, ComponentId);
+ SecondObject = AcpiUtAllocateObjectDescDbg (
+ ModuleName, LineNumber, ComponentId);
if (!SecondObject)
{
AcpiUtDeleteObjectDesc (Object);
@@ -140,6 +139,7 @@ AcpiUtCreateInternalObjectDbg (
break;
default:
+
/* All others have no secondary object */
break;
}
@@ -194,7 +194,7 @@ AcpiUtCreatePackageObject (
* terminated.
*/
PackageElements = ACPI_ALLOCATE_ZEROED (
- ((ACPI_SIZE) Count + 1) * sizeof (void *));
+ ((ACPI_SIZE) Count + 1) * sizeof (void *));
if (!PackageElements)
{
ACPI_FREE (PackageDesc);
@@ -284,6 +284,7 @@ AcpiUtCreateBufferObject (
{
ACPI_ERROR ((AE_INFO, "Could not allocate size %u",
(UINT32) BufferSize));
+
AcpiUtRemoveReference (BufferDesc);
return_PTR (NULL);
}
@@ -343,6 +344,7 @@ AcpiUtCreateStringObject (
{
ACPI_ERROR ((AE_INFO, "Could not allocate size %u",
(UINT32) StringSize));
+
AcpiUtRemoveReference (StringDesc);
return_PTR (NULL);
}
@@ -366,7 +368,7 @@ AcpiUtCreateStringObject (
*
* RETURN: TRUE if object is valid, FALSE otherwise
*
- * DESCRIPTION: Validate a pointer to be an ACPI_OPERAND_OBJECT
+ * DESCRIPTION: Validate a pointer to be of type ACPI_OPERAND_OBJECT
*
******************************************************************************/
@@ -392,14 +394,15 @@ AcpiUtValidInternalObject (
{
case ACPI_DESC_TYPE_OPERAND:
- /* The object appears to be a valid ACPI_OPERAND_OBJECT */
+ /* The object appears to be a valid ACPI_OPERAND_OBJECT */
return (TRUE);
default:
+
ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "%p is not not an ACPI operand obj [%s]\n",
- Object, AcpiUtGetDescriptorName (Object)));
+ "%p is not an ACPI operand obj [%s]\n",
+ Object, AcpiUtGetDescriptorName (Object)));
break;
}
@@ -415,9 +418,9 @@ AcpiUtValidInternalObject (
* LineNumber - Caller's line number (for error output)
* ComponentId - Caller's component ID (for error output)
*
- * RETURN: Pointer to newly allocated object descriptor. Null on error
+ * RETURN: Pointer to newly allocated object descriptor. Null on error
*
- * DESCRIPTION: Allocate a new object descriptor. Gracefully handle
+ * DESCRIPTION: Allocate a new object descriptor. Gracefully handle
* error conditions.
*
******************************************************************************/
@@ -448,7 +451,7 @@ AcpiUtAllocateObjectDescDbg (
ACPI_SET_DESCRIPTOR_TYPE (Object, ACPI_DESC_TYPE_OPERAND);
ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p Size %X\n",
- Object, (UINT32) sizeof (ACPI_OPERAND_OBJECT)));
+ Object, (UINT32) sizeof (ACPI_OPERAND_OBJECT)));
return_PTR (Object);
}
@@ -473,7 +476,7 @@ AcpiUtDeleteObjectDesc (
ACPI_FUNCTION_TRACE_PTR (UtDeleteObjectDesc, Object);
- /* Object must be an ACPI_OPERAND_OBJECT */
+ /* Object must be of type ACPI_OPERAND_OBJECT */
if (ACPI_GET_DESCRIPTOR_TYPE (Object) != ACPI_DESC_TYPE_OPERAND)
{
@@ -556,13 +559,11 @@ AcpiUtGetSimpleObjectSize (
Length += (ACPI_SIZE) InternalObject->String.Length + 1;
break;
-
case ACPI_TYPE_BUFFER:
Length += (ACPI_SIZE) InternalObject->Buffer.Length;
break;
-
case ACPI_TYPE_INTEGER:
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_POWER:
@@ -571,13 +572,11 @@ AcpiUtGetSimpleObjectSize (
break;
-
case ACPI_TYPE_LOCAL_REFERENCE:
switch (InternalObject->Reference.Class)
{
case ACPI_REFCLASS_NAME:
-
/*
* Get the actual length of the full pathname to this object.
* The reference will be converted to the pathname to the object
@@ -592,7 +591,6 @@ AcpiUtGetSimpleObjectSize (
break;
default:
-
/*
* No other reference opcodes are supported.
* Notably, Locals and Args are not supported, but this may be
@@ -607,7 +605,6 @@ AcpiUtGetSimpleObjectSize (
}
break;
-
default:
ACPI_ERROR ((AE_INFO, "Cannot convert to external object - "
@@ -620,7 +617,7 @@ AcpiUtGetSimpleObjectSize (
/*
* Account for the space required by the object rounded up to the next
- * multiple of the machine word size. This keeps each object aligned
+ * multiple of the machine word size. This keeps each object aligned
* on a machine word boundary. (preventing alignment faults on some
* machines.)
*/
@@ -656,7 +653,6 @@ AcpiUtGetElementLength (
switch (ObjectType)
{
case ACPI_COPY_TYPE_SIMPLE:
-
/*
* Simple object - just get the size (Null object/entry is handled
* here also) and sum it into the running package length
@@ -670,7 +666,6 @@ AcpiUtGetElementLength (
Info->Length += ObjectSpace;
break;
-
case ACPI_COPY_TYPE_PACKAGE:
/* Package object - nothing much to do here, let the walk handle it */
@@ -679,7 +674,6 @@ AcpiUtGetElementLength (
State->Pkg.ThisTargetObj = NULL;
break;
-
default:
/* No other types allowed */
@@ -720,12 +714,12 @@ AcpiUtGetPackageObjectSize (
ACPI_FUNCTION_TRACE_PTR (UtGetPackageObjectSize, InternalObject);
- Info.Length = 0;
+ Info.Length = 0;
Info.ObjectSpace = 0;
Info.NumPackages = 1;
- Status = AcpiUtWalkPackageTree (InternalObject, NULL,
- AcpiUtGetElementLength, &Info);
+ Status = AcpiUtWalkPackageTree (
+ InternalObject, NULL, AcpiUtGetElementLength, &Info);
if (ACPI_FAILURE (Status))
{
return_ACPI_STATUS (Status);
@@ -736,8 +730,8 @@ AcpiUtGetPackageObjectSize (
* just add the length of the package objects themselves.
* Round up to the next machine word.
*/
- Info.Length += ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT)) *
- (ACPI_SIZE) Info.NumPackages;
+ Info.Length += ACPI_ROUND_UP_TO_NATIVE_WORD (
+ sizeof (ACPI_OBJECT)) * (ACPI_SIZE) Info.NumPackages;
/* Return the total package length */
@@ -771,7 +765,8 @@ AcpiUtGetObjectSize (
ACPI_FUNCTION_ENTRY ();
- if ((ACPI_GET_DESCRIPTOR_TYPE (InternalObject) == ACPI_DESC_TYPE_OPERAND) &&
+ if ((ACPI_GET_DESCRIPTOR_TYPE (InternalObject) ==
+ ACPI_DESC_TYPE_OPERAND) &&
(InternalObject->Common.Type == ACPI_TYPE_PACKAGE))
{
Status = AcpiUtGetPackageObjectSize (InternalObject, ObjLength);
@@ -783,5 +778,3 @@ AcpiUtGetObjectSize (
return (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utosi.c b/usr/src/uts/intel/io/acpica/utilities/utosi.c
index 053d6adc9b..18656fa017 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utosi.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utosi.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTOSI_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -50,6 +48,34 @@
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME ("utosi")
+
+/******************************************************************************
+ *
+ * ACPICA policy for new _OSI strings:
+ *
+ * It is the stated policy of ACPICA that new _OSI strings will be integrated
+ * into this module as soon as possible after they are defined. It is strongly
+ * recommended that all ACPICA hosts mirror this policy and integrate any
+ * changes to this module as soon as possible. There are several historical
+ * reasons behind this policy:
+ *
+ * 1) New BIOSs tend to test only the case where the host responds TRUE to
+ * the latest version of Windows, which would respond to the latest/newest
+ * _OSI string. Not responding TRUE to the latest version of Windows will
+ * risk executing untested code paths throughout the DSDT and SSDTs.
+ *
+ * 2) If a new _OSI string is recognized only after a significant delay, this
+ * has the potential to cause problems on existing working machines because
+ * of the possibility that a new and different path through the ASL code
+ * will be executed.
+ *
+ * 3) New _OSI strings are tending to come out about once per year. A delay
+ * in recognizing a new string for a significant amount of time risks the
+ * release of another string which only compounds the initial problem.
+ *
+ *****************************************************************************/
+
+
/*
* Strings supported by the _OSI predefined control method (which is
* implemented internally within this module.)
@@ -77,24 +103,36 @@ static ACPI_INTERFACE_INFO AcpiDefaultSupportedInterfaces[] =
{"Windows 2006 SP1", NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */
{"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */
{"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */
+ /*
+ * XXX
+ * The following OSes are temporarily disabled. Windows introduced
+ * support for xhci (USB 3.0) in Windows 8. When we advertise Windows 8
+ * and newer support, some vendors use that as a key to automatically
+ * transition all USB ports to the xhci controller. Until we have
+ * support for the xhci controller, we should not advertise these
+ * operating systems. From a brief survey, there isn't too much other
+ * AML that this impacts at this time.
+ */
+/* {"Windows 2012", NULL, 0, ACPI_OSI_WIN_8},*/ /* Windows 8 and Server 2012 - Added 08/2012 */
+/* {"Windows 2013", NULL, 0, ACPI_OSI_WIN_8},*/ /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */
+/* {"Windows 2015", NULL, 0, ACPI_OSI_WIN_10},*/ /* Windows 10 - Added 03/2015 */
/* Feature Group Strings */
- {"Extended Address Space Descriptor", NULL, 0, 0}
+ {"Extended Address Space Descriptor", NULL, ACPI_OSI_FEATURE, 0},
/*
* All "optional" feature group strings (features that are implemented
- * by the host) should be dynamically added by the host via
- * AcpiInstallInterface and should not be manually added here.
- *
- * Examples of optional feature group strings:
- *
- * "Module Device"
- * "Processor Device"
- * "3.0 Thermal Model"
- * "3.0 _SCP Extensions"
- * "Processor Aggregator Device"
+ * by the host) should be dynamically modified to VALID by the host via
+ * AcpiInstallInterface or AcpiUpdateInterfaces. Such optional feature
+ * group strings are set as INVALID by default here.
*/
+
+ {"Module Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
+ {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}
};
@@ -114,15 +152,23 @@ ACPI_STATUS
AcpiUtInitializeInterfaces (
void)
{
+ ACPI_STATUS Status;
UINT32 i;
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
AcpiGbl_SupportedInterfaces = AcpiDefaultSupportedInterfaces;
/* Link the static list of supported interfaces */
- for (i = 0; i < (ACPI_ARRAY_LENGTH (AcpiDefaultSupportedInterfaces) - 1); i++)
+ for (i = 0;
+ i < (ACPI_ARRAY_LENGTH (AcpiDefaultSupportedInterfaces) - 1);
+ i++)
{
AcpiDefaultSupportedInterfaces[i].Next =
&AcpiDefaultSupportedInterfaces[(ACPI_SIZE) i + 1];
@@ -139,39 +185,58 @@ AcpiUtInitializeInterfaces (
*
* PARAMETERS: None
*
- * RETURN: None
+ * RETURN: Status
*
* DESCRIPTION: Delete all interfaces in the global list. Sets
* AcpiGbl_SupportedInterfaces to NULL.
*
******************************************************************************/
-void
+ACPI_STATUS
AcpiUtInterfaceTerminate (
void)
{
+ ACPI_STATUS Status;
ACPI_INTERFACE_INFO *NextInterface;
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
- NextInterface = AcpiGbl_SupportedInterfaces;
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ NextInterface = AcpiGbl_SupportedInterfaces;
while (NextInterface)
{
AcpiGbl_SupportedInterfaces = NextInterface->Next;
- /* Only interfaces added at runtime can be freed */
-
if (NextInterface->Flags & ACPI_OSI_DYNAMIC)
{
+ /* Only interfaces added at runtime can be freed */
+
ACPI_FREE (NextInterface->Name);
ACPI_FREE (NextInterface);
}
+ else
+ {
+ /* Interface is in static list. Reset it to invalid or valid. */
+
+ if (NextInterface->Flags & ACPI_OSI_DEFAULT_INVALID)
+ {
+ NextInterface->Flags |= ACPI_OSI_INVALID;
+ }
+ else
+ {
+ NextInterface->Flags &= ~ACPI_OSI_INVALID;
+ }
+ }
NextInterface = AcpiGbl_SupportedInterfaces;
}
AcpiOsReleaseMutex (AcpiGbl_OsiMutex);
+ return (AE_OK);
}
@@ -203,7 +268,7 @@ AcpiUtInstallInterface (
return (AE_NO_MEMORY);
}
- InterfaceInfo->Name = ACPI_ALLOCATE_ZEROED (ACPI_STRLEN (InterfaceName) + 1);
+ InterfaceInfo->Name = ACPI_ALLOCATE_ZEROED (strlen (InterfaceName) + 1);
if (!InterfaceInfo->Name)
{
ACPI_FREE (InterfaceInfo);
@@ -212,7 +277,7 @@ AcpiUtInstallInterface (
/* Initialize new info and insert at the head of the global list */
- ACPI_STRCPY (InterfaceInfo->Name, InterfaceName);
+ strcpy (InterfaceInfo->Name, InterfaceName);
InterfaceInfo->Flags = ACPI_OSI_DYNAMIC;
InterfaceInfo->Next = AcpiGbl_SupportedInterfaces;
@@ -245,10 +310,12 @@ AcpiUtRemoveInterface (
PreviousInterface = NextInterface = AcpiGbl_SupportedInterfaces;
while (NextInterface)
{
- if (!ACPI_STRCMP (InterfaceName, NextInterface->Name))
+ if (!strcmp (InterfaceName, NextInterface->Name))
{
- /* Found: name is in either the static list or was added at runtime */
-
+ /*
+ * Found: name is in either the static list
+ * or was added at runtime
+ */
if (NextInterface->Flags & ACPI_OSI_DYNAMIC)
{
/* Interface was added dynamically, remove and free it */
@@ -268,8 +335,8 @@ AcpiUtRemoveInterface (
else
{
/*
- * Interface is in static list. If marked invalid, then it
- * does not actually exist. Else, mark it invalid.
+ * Interface is in static list. If marked invalid, then
+ * it does not actually exist. Else, mark it invalid.
*/
if (NextInterface->Flags & ACPI_OSI_INVALID)
{
@@ -294,6 +361,57 @@ AcpiUtRemoveInterface (
/*******************************************************************************
*
+ * FUNCTION: AcpiUtUpdateInterfaces
+ *
+ * PARAMETERS: Action - Actions to be performed during the
+ * update
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor
+ * strings or/and feature group strings.
+ * Caller MUST hold AcpiGbl_OsiMutex
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtUpdateInterfaces (
+ UINT8 Action)
+{
+ ACPI_INTERFACE_INFO *NextInterface;
+
+
+ NextInterface = AcpiGbl_SupportedInterfaces;
+ while (NextInterface)
+ {
+ if (((NextInterface->Flags & ACPI_OSI_FEATURE) &&
+ (Action & ACPI_FEATURE_STRINGS)) ||
+ (!(NextInterface->Flags & ACPI_OSI_FEATURE) &&
+ (Action & ACPI_VENDOR_STRINGS)))
+ {
+ if (Action & ACPI_DISABLE_INTERFACES)
+ {
+ /* Mark the interfaces as invalid */
+
+ NextInterface->Flags |= ACPI_OSI_INVALID;
+ }
+ else
+ {
+ /* Mark the interfaces as valid */
+
+ NextInterface->Flags &= ~ACPI_OSI_INVALID;
+ }
+ }
+
+ NextInterface = NextInterface->Next;
+ }
+
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiUtGetInterface
*
* PARAMETERS: InterfaceName - The interface to find
@@ -315,7 +433,7 @@ AcpiUtGetInterface (
NextInterface = AcpiGbl_SupportedInterfaces;
while (NextInterface)
{
- if (!ACPI_STRCMP (InterfaceName, NextInterface->Name))
+ if (!strcmp (InterfaceName, NextInterface->Name))
{
return (NextInterface);
}
@@ -349,6 +467,7 @@ AcpiUtOsiImplementation (
ACPI_OPERAND_OBJECT *ReturnDesc;
ACPI_INTERFACE_INFO *InterfaceInfo;
ACPI_INTERFACE_HANDLER InterfaceHandler;
+ ACPI_STATUS Status;
UINT32 ReturnValue;
@@ -375,7 +494,12 @@ AcpiUtOsiImplementation (
/* Default return value is 0, NOT SUPPORTED */
ReturnValue = 0;
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ AcpiUtRemoveReference (ReturnDesc);
+ return_ACPI_STATUS (Status);
+ }
/* Lookup the interface in the global _OSI list */
diff --git a/usr/src/uts/intel/io/acpica/utilities/utownerid.c b/usr/src/uts/intel/io/acpica/utilities/utownerid.c
new file mode 100644
index 0000000000..8fe67bc332
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utownerid.c
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ *
+ * Module Name: utownerid - Support for Table/Method Owner IDs
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utownerid")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtAllocateOwnerId
+ *
+ * PARAMETERS: OwnerId - Where the new owner ID is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Allocate a table or method owner ID. The owner ID is used to
+ * track objects created by the table or method, to be deleted
+ * when the method exits or the table is unloaded.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiUtAllocateOwnerId (
+ ACPI_OWNER_ID *OwnerId)
+{
+ UINT32 i;
+ UINT32 j;
+ UINT32 k;
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (UtAllocateOwnerId);
+
+
+ /* Guard against multiple allocations of ID to the same location */
+
+ if (*OwnerId)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Owner ID [0x%2.2X] already exists", *OwnerId));
+ return_ACPI_STATUS (AE_ALREADY_EXISTS);
+ }
+
+ /* Mutex for the global ID mask */
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_CACHES);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+
+ /*
+ * Find a free owner ID, cycle through all possible IDs on repeated
+ * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index
+ * may have to be scanned twice.
+ */
+ for (i = 0, j = AcpiGbl_LastOwnerIdIndex;
+ i < (ACPI_NUM_OWNERID_MASKS + 1);
+ i++, j++)
+ {
+ if (j >= ACPI_NUM_OWNERID_MASKS)
+ {
+ j = 0; /* Wraparound to start of mask array */
+ }
+
+ for (k = AcpiGbl_NextOwnerIdOffset; k < 32; k++)
+ {
+ if (AcpiGbl_OwnerIdMask[j] == ACPI_UINT32_MAX)
+ {
+ /* There are no free IDs in this mask */
+
+ break;
+ }
+
+ if (!(AcpiGbl_OwnerIdMask[j] & (1 << k)))
+ {
+ /*
+ * Found a free ID. The actual ID is the bit index plus one,
+ * making zero an invalid Owner ID. Save this as the last ID
+ * allocated and update the global ID mask.
+ */
+ AcpiGbl_OwnerIdMask[j] |= (1 << k);
+
+ AcpiGbl_LastOwnerIdIndex = (UINT8) j;
+ AcpiGbl_NextOwnerIdOffset = (UINT8) (k + 1);
+
+ /*
+ * Construct encoded ID from the index and bit position
+ *
+ * Note: Last [j].k (bit 255) is never used and is marked
+ * permanently allocated (prevents +1 overflow)
+ */
+ *OwnerId = (ACPI_OWNER_ID) ((k + 1) + ACPI_MUL_32 (j));
+
+ ACPI_DEBUG_PRINT ((ACPI_DB_VALUES,
+ "Allocated OwnerId: %2.2X\n", (unsigned int) *OwnerId));
+ goto Exit;
+ }
+ }
+
+ AcpiGbl_NextOwnerIdOffset = 0;
+ }
+
+ /*
+ * All OwnerIds have been allocated. This typically should
+ * not happen since the IDs are reused after deallocation. The IDs are
+ * allocated upon table load (one per table) and method execution, and
+ * they are released when a table is unloaded or a method completes
+ * execution.
+ *
+ * If this error happens, there may be very deep nesting of invoked
+ * control methods, or there may be a bug where the IDs are not released.
+ */
+ Status = AE_OWNER_ID_LIMIT;
+ ACPI_ERROR ((AE_INFO,
+ "Could not allocate new OwnerId (255 max), AE_OWNER_ID_LIMIT"));
+
+Exit:
+ (void) AcpiUtReleaseMutex (ACPI_MTX_CACHES);
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtReleaseOwnerId
+ *
+ * PARAMETERS: OwnerIdPtr - Pointer to a previously allocated OwnerID
+ *
+ * RETURN: None. No error is returned because we are either exiting a
+ * control method or unloading a table. Either way, we would
+ * ignore any error anyway.
+ *
+ * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 255
+ *
+ ******************************************************************************/
+
+void
+AcpiUtReleaseOwnerId (
+ ACPI_OWNER_ID *OwnerIdPtr)
+{
+ ACPI_OWNER_ID OwnerId = *OwnerIdPtr;
+ ACPI_STATUS Status;
+ UINT32 Index;
+ UINT32 Bit;
+
+
+ ACPI_FUNCTION_TRACE_U32 (UtReleaseOwnerId, OwnerId);
+
+
+ /* Always clear the input OwnerId (zero is an invalid ID) */
+
+ *OwnerIdPtr = 0;
+
+ /* Zero is not a valid OwnerID */
+
+ if (OwnerId == 0)
+ {
+ ACPI_ERROR ((AE_INFO, "Invalid OwnerId: 0x%2.2X", OwnerId));
+ return_VOID;
+ }
+
+ /* Mutex for the global ID mask */
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_CACHES);
+ if (ACPI_FAILURE (Status))
+ {
+ return_VOID;
+ }
+
+ /* Normalize the ID to zero */
+
+ OwnerId--;
+
+ /* Decode ID to index/offset pair */
+
+ Index = ACPI_DIV_32 (OwnerId);
+ Bit = 1 << ACPI_MOD_32 (OwnerId);
+
+ /* Free the owner ID only if it is valid */
+
+ if (AcpiGbl_OwnerIdMask[Index] & Bit)
+ {
+ AcpiGbl_OwnerIdMask[Index] ^= Bit;
+ }
+ else
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Release of non-allocated OwnerId: 0x%2.2X", OwnerId + 1));
+ }
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_CACHES);
+ return_VOID;
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utpredef.c b/usr/src/uts/intel/io/acpica/utilities/utpredef.c
new file mode 100644
index 0000000000..0f6658ecf7
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utpredef.c
@@ -0,0 +1,452 @@
+/******************************************************************************
+ *
+ * Module Name: utpredef - support functions for predefined names
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acpredef.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utpredef")
+
+
+/*
+ * Names for the types that can be returned by the predefined objects.
+ * Used for warning messages. Must be in the same order as the ACPI_RTYPEs
+ */
+static const char *UtRtypeNames[] =
+{
+ "/Integer",
+ "/String",
+ "/Buffer",
+ "/Package",
+ "/Reference",
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtGetNextPredefinedMethod
+ *
+ * PARAMETERS: ThisName - Entry in the predefined method/name table
+ *
+ * RETURN: Pointer to next entry in predefined table.
+ *
+ * DESCRIPTION: Get the next entry in the predefine method table. Handles the
+ * cases where a package info entry follows a method name that
+ * returns a package.
+ *
+ ******************************************************************************/
+
+const ACPI_PREDEFINED_INFO *
+AcpiUtGetNextPredefinedMethod (
+ const ACPI_PREDEFINED_INFO *ThisName)
+{
+
+ /*
+ * Skip next entry in the table if this name returns a Package
+ * (next entry contains the package info)
+ */
+ if ((ThisName->Info.ExpectedBtypes & ACPI_RTYPE_PACKAGE) &&
+ (ThisName->Info.ExpectedBtypes != ACPI_RTYPE_ALL))
+ {
+ ThisName++;
+ }
+
+ ThisName++;
+ return (ThisName);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtMatchPredefinedMethod
+ *
+ * PARAMETERS: Name - Name to find
+ *
+ * RETURN: Pointer to entry in predefined table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the predefined object list.
+ *
+ ******************************************************************************/
+
+const ACPI_PREDEFINED_INFO *
+AcpiUtMatchPredefinedMethod (
+ char *Name)
+{
+ const ACPI_PREDEFINED_INFO *ThisName;
+
+
+ /* Quick check for a predefined name, first character must be underscore */
+
+ if (Name[0] != '_')
+ {
+ return (NULL);
+ }
+
+ /* Search info table for a predefined method/object name */
+
+ ThisName = AcpiGbl_PredefinedMethods;
+ while (ThisName->Info.Name[0])
+ {
+ if (ACPI_COMPARE_NAME (Name, ThisName->Info.Name))
+ {
+ return (ThisName);
+ }
+
+ ThisName = AcpiUtGetNextPredefinedMethod (ThisName);
+ }
+
+ return (NULL); /* Not found */
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtGetExpectedReturnTypes
+ *
+ * PARAMETERS: Buffer - Where the formatted string is returned
+ * ExpectedBTypes - Bitfield of expected data types
+ *
+ * RETURN: Formatted string in Buffer.
+ *
+ * DESCRIPTION: Format the expected object types into a printable string.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtGetExpectedReturnTypes (
+ char *Buffer,
+ UINT32 ExpectedBtypes)
+{
+ UINT32 ThisRtype;
+ UINT32 i;
+ UINT32 j;
+
+
+ if (!ExpectedBtypes)
+ {
+ strcpy (Buffer, "NONE");
+ return;
+ }
+
+ j = 1;
+ Buffer[0] = 0;
+ ThisRtype = ACPI_RTYPE_INTEGER;
+
+ for (i = 0; i < ACPI_NUM_RTYPES; i++)
+ {
+ /* If one of the expected types, concatenate the name of this type */
+
+ if (ExpectedBtypes & ThisRtype)
+ {
+ strcat (Buffer, &UtRtypeNames[i][j]);
+ j = 0; /* Use name separator from now on */
+ }
+
+ ThisRtype <<= 1; /* Next Rtype */
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * The remaining functions are used by iASL and AcpiHelp only
+ *
+ ******************************************************************************/
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
+#include <stdio.h>
+#include <string.h>
+
+/* Local prototypes */
+
+static UINT32
+AcpiUtGetArgumentTypes (
+ char *Buffer,
+ UINT16 ArgumentTypes);
+
+
+/* Types that can be returned externally by a predefined name */
+
+static const char *UtExternalTypeNames[] = /* Indexed by ACPI_TYPE_* */
+{
+ ", UNSUPPORTED-TYPE",
+ ", Integer",
+ ", String",
+ ", Buffer",
+ ", Package"
+};
+
+/* Bit widths for resource descriptor predefined names */
+
+static const char *UtResourceTypeNames[] =
+{
+ "/1",
+ "/2",
+ "/3",
+ "/8",
+ "/16",
+ "/32",
+ "/64",
+ "/variable",
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtMatchResourceName
+ *
+ * PARAMETERS: Name - Name to find
+ *
+ * RETURN: Pointer to entry in the resource table. NULL indicates not
+ * found.
+ *
+ * DESCRIPTION: Check an object name against the predefined resource
+ * descriptor object list.
+ *
+ ******************************************************************************/
+
+const ACPI_PREDEFINED_INFO *
+AcpiUtMatchResourceName (
+ char *Name)
+{
+ const ACPI_PREDEFINED_INFO *ThisName;
+
+
+ /*
+ * Quick check for a predefined name, first character must
+ * be underscore
+ */
+ if (Name[0] != '_')
+ {
+ return (NULL);
+ }
+
+ /* Search info table for a predefined method/object name */
+
+ ThisName = AcpiGbl_ResourceNames;
+ while (ThisName->Info.Name[0])
+ {
+ if (ACPI_COMPARE_NAME (Name, ThisName->Info.Name))
+ {
+ return (ThisName);
+ }
+
+ ThisName++;
+ }
+
+ return (NULL); /* Not found */
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtDisplayPredefinedMethod
+ *
+ * PARAMETERS: Buffer - Scratch buffer for this function
+ * ThisName - Entry in the predefined method/name table
+ * MultiLine - TRUE if output should be on >1 line
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Display information about a predefined method. Number and
+ * type of the input arguments, and expected type(s) for the
+ * return value, if any.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtDisplayPredefinedMethod (
+ char *Buffer,
+ const ACPI_PREDEFINED_INFO *ThisName,
+ BOOLEAN MultiLine)
+{
+ UINT32 ArgCount;
+
+ /*
+ * Get the argument count and the string buffer
+ * containing all argument types
+ */
+ ArgCount = AcpiUtGetArgumentTypes (Buffer,
+ ThisName->Info.ArgumentList);
+
+ if (MultiLine)
+ {
+ printf (" ");
+ }
+
+ printf ("%4.4s Requires %s%u argument%s",
+ ThisName->Info.Name,
+ (ThisName->Info.ArgumentList & ARG_COUNT_IS_MINIMUM) ?
+ "(at least) " : "",
+ ArgCount, ArgCount != 1 ? "s" : "");
+
+ /* Display the types for any arguments */
+
+ if (ArgCount > 0)
+ {
+ printf (" (%s)", Buffer);
+ }
+
+ if (MultiLine)
+ {
+ printf ("\n ");
+ }
+
+ /* Get the return value type(s) allowed */
+
+ if (ThisName->Info.ExpectedBtypes)
+ {
+ AcpiUtGetExpectedReturnTypes (Buffer, ThisName->Info.ExpectedBtypes);
+ printf (" Return value types: %s\n", Buffer);
+ }
+ else
+ {
+ printf (" No return value\n");
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtGetArgumentTypes
+ *
+ * PARAMETERS: Buffer - Where to return the formatted types
+ * ArgumentTypes - Types field for this method
+ *
+ * RETURN: Count - the number of arguments required for this method
+ *
+ * DESCRIPTION: Format the required data types for this method (Integer,
+ * String, Buffer, or Package) and return the required argument
+ * count.
+ *
+ ******************************************************************************/
+
+static UINT32
+AcpiUtGetArgumentTypes (
+ char *Buffer,
+ UINT16 ArgumentTypes)
+{
+ UINT16 ThisArgumentType;
+ UINT16 SubIndex;
+ UINT16 ArgCount;
+ UINT32 i;
+
+
+ *Buffer = 0;
+ SubIndex = 2;
+
+ /* First field in the types list is the count of args to follow */
+
+ ArgCount = METHOD_GET_ARG_COUNT (ArgumentTypes);
+ if (ArgCount > METHOD_PREDEF_ARGS_MAX)
+ {
+ printf ("**** Invalid argument count (%u) "
+ "in predefined info structure\n", ArgCount);
+ return (ArgCount);
+ }
+
+ /* Get each argument from the list, convert to ascii, store to buffer */
+
+ for (i = 0; i < ArgCount; i++)
+ {
+ ThisArgumentType = METHOD_GET_NEXT_TYPE (ArgumentTypes);
+
+ if (!ThisArgumentType || (ThisArgumentType > METHOD_MAX_ARG_TYPE))
+ {
+ printf ("**** Invalid argument type (%u) "
+ "in predefined info structure\n", ThisArgumentType);
+ return (ArgCount);
+ }
+
+ strcat (Buffer, UtExternalTypeNames[ThisArgumentType] + SubIndex);
+ SubIndex = 0;
+ }
+
+ return (ArgCount);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtGetResourceBitWidth
+ *
+ * PARAMETERS: Buffer - Where the formatted string is returned
+ * Types - Bitfield of expected data types
+ *
+ * RETURN: Count of return types. Formatted string in Buffer.
+ *
+ * DESCRIPTION: Format the resource bit widths into a printable string.
+ *
+ ******************************************************************************/
+
+UINT32
+AcpiUtGetResourceBitWidth (
+ char *Buffer,
+ UINT16 Types)
+{
+ UINT32 i;
+ UINT16 SubIndex;
+ UINT32 Found;
+
+
+ *Buffer = 0;
+ SubIndex = 1;
+ Found = 0;
+
+ for (i = 0; i < NUM_RESOURCE_WIDTHS; i++)
+ {
+ if (Types & 1)
+ {
+ strcat (Buffer, &(UtResourceTypeNames[i][SubIndex]));
+ SubIndex = 0;
+ Found++;
+ }
+
+ Types >>= 1;
+ }
+
+ return (Found);
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utprint.c b/usr/src/uts/intel/io/acpica/utilities/utprint.c
new file mode 100644
index 0000000000..e01f1734a9
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utprint.c
@@ -0,0 +1,812 @@
+/******************************************************************************
+ *
+ * Module Name: utprint - Formatted printing routines
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utprint")
+
+
+#define ACPI_FORMAT_SIGN 0x01
+#define ACPI_FORMAT_SIGN_PLUS 0x02
+#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04
+#define ACPI_FORMAT_ZERO 0x08
+#define ACPI_FORMAT_LEFT 0x10
+#define ACPI_FORMAT_UPPER 0x20
+#define ACPI_FORMAT_PREFIX 0x40
+
+
+/* Local prototypes */
+
+static ACPI_SIZE
+AcpiUtBoundStringLength (
+ const char *String,
+ ACPI_SIZE Count);
+
+static char *
+AcpiUtBoundStringOutput (
+ char *String,
+ const char *End,
+ char c);
+
+static char *
+AcpiUtFormatNumber (
+ char *String,
+ char *End,
+ UINT64 Number,
+ UINT8 Base,
+ INT32 Width,
+ INT32 Precision,
+ UINT8 Type);
+
+static char *
+AcpiUtPutNumber (
+ char *String,
+ UINT64 Number,
+ UINT8 Base,
+ BOOLEAN Upper);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtBoundStringLength
+ *
+ * PARAMETERS: String - String with boundary
+ * Count - Boundary of the string
+ *
+ * RETURN: Length of the string. Less than or equal to Count.
+ *
+ * DESCRIPTION: Calculate the length of a string with boundary.
+ *
+ ******************************************************************************/
+
+static ACPI_SIZE
+AcpiUtBoundStringLength (
+ const char *String,
+ ACPI_SIZE Count)
+{
+ UINT32 Length = 0;
+
+
+ while (*String && Count)
+ {
+ Length++;
+ String++;
+ Count--;
+ }
+
+ return (Length);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtBoundStringOutput
+ *
+ * PARAMETERS: String - String with boundary
+ * End - Boundary of the string
+ * c - Character to be output to the string
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Output a character into a string with boundary check.
+ *
+ ******************************************************************************/
+
+static char *
+AcpiUtBoundStringOutput (
+ char *String,
+ const char *End,
+ char c)
+{
+
+ if (String < End)
+ {
+ *String = c;
+ }
+
+ ++String;
+ return (String);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPutNumber
+ *
+ * PARAMETERS: String - Buffer to hold reverse-ordered string
+ * Number - Integer to be converted
+ * Base - Base of the integer
+ * Upper - Whether or not using upper cased digits
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Convert an integer into a string, note that, the string holds a
+ * reversed ordered number without the trailing zero.
+ *
+ ******************************************************************************/
+
+static char *
+AcpiUtPutNumber (
+ char *String,
+ UINT64 Number,
+ UINT8 Base,
+ BOOLEAN Upper)
+{
+ const char *Digits;
+ UINT64 DigitIndex;
+ char *Pos;
+
+
+ Pos = String;
+ Digits = Upper ? AcpiGbl_UpperHexDigits : AcpiGbl_LowerHexDigits;
+
+ if (Number == 0)
+ {
+ *(Pos++) = '0';
+ }
+ else
+ {
+ while (Number)
+ {
+ (void) AcpiUtDivide (Number, Base, &Number, &DigitIndex);
+ *(Pos++) = Digits[DigitIndex];
+ }
+ }
+
+ /* *(Pos++) = '0'; */
+ return (Pos);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtScanNumber
+ *
+ * PARAMETERS: String - String buffer
+ * NumberPtr - Where the number is returned
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Scan a string for a decimal integer.
+ *
+ ******************************************************************************/
+
+const char *
+AcpiUtScanNumber (
+ const char *String,
+ UINT64 *NumberPtr)
+{
+ UINT64 Number = 0;
+
+
+ while (isdigit ((int) *String))
+ {
+ Number *= 10;
+ Number += *(String++) - '0';
+ }
+
+ *NumberPtr = Number;
+ return (String);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPrintNumber
+ *
+ * PARAMETERS: String - String buffer
+ * Number - The number to be converted
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Print a decimal integer into a string.
+ *
+ ******************************************************************************/
+
+const char *
+AcpiUtPrintNumber (
+ char *String,
+ UINT64 Number)
+{
+ char AsciiString[20];
+ const char *Pos1;
+ char *Pos2;
+
+
+ Pos1 = AcpiUtPutNumber (AsciiString, Number, 10, FALSE);
+ Pos2 = String;
+
+ while (Pos1 != AsciiString)
+ {
+ *(Pos2++) = *(--Pos1);
+ }
+
+ *Pos2 = 0;
+ return (String);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtFormatNumber
+ *
+ * PARAMETERS: String - String buffer with boundary
+ * End - Boundary of the string
+ * Number - The number to be converted
+ * Base - Base of the integer
+ * Width - Field width
+ * Precision - Precision of the integer
+ * Type - Special printing flags
+ *
+ * RETURN: Updated position for next valid character
+ *
+ * DESCRIPTION: Print an integer into a string with any base and any precision.
+ *
+ ******************************************************************************/
+
+static char *
+AcpiUtFormatNumber (
+ char *String,
+ char *End,
+ UINT64 Number,
+ UINT8 Base,
+ INT32 Width,
+ INT32 Precision,
+ UINT8 Type)
+{
+ char *Pos;
+ char Sign;
+ char Zero;
+ BOOLEAN NeedPrefix;
+ BOOLEAN Upper;
+ INT32 i;
+ char ReversedString[66];
+
+
+ /* Parameter validation */
+
+ if (Base < 2 || Base > 16)
+ {
+ return (NULL);
+ }
+
+ if (Type & ACPI_FORMAT_LEFT)
+ {
+ Type &= ~ACPI_FORMAT_ZERO;
+ }
+
+ NeedPrefix = ((Type & ACPI_FORMAT_PREFIX) && Base != 10) ? TRUE : FALSE;
+ Upper = (Type & ACPI_FORMAT_UPPER) ? TRUE : FALSE;
+ Zero = (Type & ACPI_FORMAT_ZERO) ? '0' : ' ';
+
+ /* Calculate size according to sign and prefix */
+
+ Sign = '\0';
+ if (Type & ACPI_FORMAT_SIGN)
+ {
+ if ((INT64) Number < 0)
+ {
+ Sign = '-';
+ Number = - (INT64) Number;
+ Width--;
+ }
+ else if (Type & ACPI_FORMAT_SIGN_PLUS)
+ {
+ Sign = '+';
+ Width--;
+ }
+ else if (Type & ACPI_FORMAT_SIGN_PLUS_SPACE)
+ {
+ Sign = ' ';
+ Width--;
+ }
+ }
+ if (NeedPrefix)
+ {
+ Width--;
+ if (Base == 16)
+ {
+ Width--;
+ }
+ }
+
+ /* Generate full string in reverse order */
+
+ Pos = AcpiUtPutNumber (ReversedString, Number, Base, Upper);
+ i = ACPI_PTR_DIFF (Pos, ReversedString);
+
+ /* Printing 100 using %2d gives "100", not "00" */
+
+ if (i > Precision)
+ {
+ Precision = i;
+ }
+
+ Width -= Precision;
+
+ /* Output the string */
+
+ if (!(Type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT)))
+ {
+ while (--Width >= 0)
+ {
+ String = AcpiUtBoundStringOutput (String, End, ' ');
+ }
+ }
+ if (Sign)
+ {
+ String = AcpiUtBoundStringOutput (String, End, Sign);
+ }
+ if (NeedPrefix)
+ {
+ String = AcpiUtBoundStringOutput (String, End, '0');
+ if (Base == 16)
+ {
+ String = AcpiUtBoundStringOutput (
+ String, End, Upper ? 'X' : 'x');
+ }
+ }
+ if (!(Type & ACPI_FORMAT_LEFT))
+ {
+ while (--Width >= 0)
+ {
+ String = AcpiUtBoundStringOutput (String, End, Zero);
+ }
+ }
+
+ while (i <= --Precision)
+ {
+ String = AcpiUtBoundStringOutput (String, End, '0');
+ }
+ while (--i >= 0)
+ {
+ String = AcpiUtBoundStringOutput (String, End,
+ ReversedString[i]);
+ }
+ while (--Width >= 0)
+ {
+ String = AcpiUtBoundStringOutput (String, End, ' ');
+ }
+
+ return (String);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtVsnprintf
+ *
+ * PARAMETERS: String - String with boundary
+ * Size - Boundary of the string
+ * Format - Standard printf format
+ * Args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string using argument list pointer.
+ *
+ ******************************************************************************/
+
+int
+AcpiUtVsnprintf (
+ char *String,
+ ACPI_SIZE Size,
+ const char *Format,
+ va_list Args)
+{
+ UINT8 Base;
+ UINT8 Type;
+ INT32 Width;
+ INT32 Precision;
+ char Qualifier;
+ UINT64 Number;
+ char *Pos;
+ char *End;
+ char c;
+ const char *s;
+ const void *p;
+ INT32 Length;
+ int i;
+
+
+ Pos = String;
+ End = String + Size;
+
+ for (; *Format; ++Format)
+ {
+ if (*Format != '%')
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, *Format);
+ continue;
+ }
+
+ Type = 0;
+ Base = 10;
+
+ /* Process sign */
+
+ do
+ {
+ ++Format;
+ if (*Format == '#')
+ {
+ Type |= ACPI_FORMAT_PREFIX;
+ }
+ else if (*Format == '0')
+ {
+ Type |= ACPI_FORMAT_ZERO;
+ }
+ else if (*Format == '+')
+ {
+ Type |= ACPI_FORMAT_SIGN_PLUS;
+ }
+ else if (*Format == ' ')
+ {
+ Type |= ACPI_FORMAT_SIGN_PLUS_SPACE;
+ }
+ else if (*Format == '-')
+ {
+ Type |= ACPI_FORMAT_LEFT;
+ }
+ else
+ {
+ break;
+ }
+
+ } while (1);
+
+ /* Process width */
+
+ Width = -1;
+ if (isdigit ((int) *Format))
+ {
+ Format = AcpiUtScanNumber (Format, &Number);
+ Width = (INT32) Number;
+ }
+ else if (*Format == '*')
+ {
+ ++Format;
+ Width = va_arg (Args, int);
+ if (Width < 0)
+ {
+ Width = -Width;
+ Type |= ACPI_FORMAT_LEFT;
+ }
+ }
+
+ /* Process precision */
+
+ Precision = -1;
+ if (*Format == '.')
+ {
+ ++Format;
+ if (isdigit ((int) *Format))
+ {
+ Format = AcpiUtScanNumber (Format, &Number);
+ Precision = (INT32) Number;
+ }
+ else if (*Format == '*')
+ {
+ ++Format;
+ Precision = va_arg (Args, int);
+ }
+
+ if (Precision < 0)
+ {
+ Precision = 0;
+ }
+ }
+
+ /* Process qualifier */
+
+ Qualifier = -1;
+ if (*Format == 'h' || *Format == 'l' || *Format == 'L')
+ {
+ Qualifier = *Format;
+ ++Format;
+
+ if (Qualifier == 'l' && *Format == 'l')
+ {
+ Qualifier = 'L';
+ ++Format;
+ }
+ }
+
+ switch (*Format)
+ {
+ case '%':
+
+ Pos = AcpiUtBoundStringOutput (Pos, End, '%');
+ continue;
+
+ case 'c':
+
+ if (!(Type & ACPI_FORMAT_LEFT))
+ {
+ while (--Width > 0)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, ' ');
+ }
+ }
+
+ c = (char) va_arg (Args, int);
+ Pos = AcpiUtBoundStringOutput (Pos, End, c);
+
+ while (--Width > 0)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, ' ');
+ }
+ continue;
+
+ case 's':
+
+ s = va_arg (Args, char *);
+ if (!s)
+ {
+ s = "<NULL>";
+ }
+ Length = AcpiUtBoundStringLength (s, Precision);
+ if (!(Type & ACPI_FORMAT_LEFT))
+ {
+ while (Length < Width--)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, ' ');
+ }
+ }
+
+ for (i = 0; i < Length; ++i)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, *s);
+ ++s;
+ }
+
+ while (Length < Width--)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, ' ');
+ }
+ continue;
+
+ case 'o':
+
+ Base = 8;
+ break;
+
+ case 'X':
+
+ Type |= ACPI_FORMAT_UPPER;
+
+ case 'x':
+
+ Base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+
+ Type |= ACPI_FORMAT_SIGN;
+
+ case 'u':
+
+ break;
+
+ case 'p':
+
+ if (Width == -1)
+ {
+ Width = 2 * sizeof (void *);
+ Type |= ACPI_FORMAT_ZERO;
+ }
+
+ p = va_arg (Args, void *);
+ Pos = AcpiUtFormatNumber (
+ Pos, End, ACPI_TO_INTEGER (p), 16, Width, Precision, Type);
+ continue;
+
+ default:
+
+ Pos = AcpiUtBoundStringOutput (Pos, End, '%');
+ if (*Format)
+ {
+ Pos = AcpiUtBoundStringOutput (Pos, End, *Format);
+ }
+ else
+ {
+ --Format;
+ }
+ continue;
+ }
+
+ if (Qualifier == 'L')
+ {
+ Number = va_arg (Args, UINT64);
+ if (Type & ACPI_FORMAT_SIGN)
+ {
+ Number = (INT64) Number;
+ }
+ }
+ else if (Qualifier == 'l')
+ {
+ Number = va_arg (Args, unsigned long);
+ if (Type & ACPI_FORMAT_SIGN)
+ {
+ Number = (INT32) Number;
+ }
+ }
+ else if (Qualifier == 'h')
+ {
+ Number = (UINT16) va_arg (Args, int);
+ if (Type & ACPI_FORMAT_SIGN)
+ {
+ Number = (INT16) Number;
+ }
+ }
+ else
+ {
+ Number = va_arg (Args, unsigned int);
+ if (Type & ACPI_FORMAT_SIGN)
+ {
+ Number = (signed int) Number;
+ }
+ }
+
+ Pos = AcpiUtFormatNumber (Pos, End, Number, Base,
+ Width, Precision, Type);
+ }
+
+ if (Size > 0)
+ {
+ if (Pos < End)
+ {
+ *Pos = '\0';
+ }
+ else
+ {
+ End[-1] = '\0';
+ }
+ }
+
+ return (ACPI_PTR_DIFF (Pos, String));
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtSnprintf
+ *
+ * PARAMETERS: String - String with boundary
+ * Size - Boundary of the string
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string.
+ *
+ ******************************************************************************/
+
+int
+AcpiUtSnprintf (
+ char *String,
+ ACPI_SIZE Size,
+ const char *Format,
+ ...)
+{
+ va_list Args;
+ int Length;
+
+
+ va_start (Args, Format);
+ Length = AcpiUtVsnprintf (String, Size, Format, Args);
+ va_end (Args);
+
+ return (Length);
+}
+
+
+#ifdef ACPI_APPLICATION
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtFileVprintf
+ *
+ * PARAMETERS: File - File descriptor
+ * Format - Standard printf format
+ * Args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a file using argument list pointer.
+ *
+ ******************************************************************************/
+
+int
+AcpiUtFileVprintf (
+ ACPI_FILE File,
+ const char *Format,
+ va_list Args)
+{
+ ACPI_CPU_FLAGS Flags;
+ int Length;
+
+
+ Flags = AcpiOsAcquireLock (AcpiGbl_PrintLock);
+ Length = AcpiUtVsnprintf (AcpiGbl_PrintBuffer,
+ sizeof (AcpiGbl_PrintBuffer), Format, Args);
+
+ (void) AcpiOsWriteFile (File, AcpiGbl_PrintBuffer, Length, 1);
+ AcpiOsReleaseLock (AcpiGbl_PrintLock, Flags);
+
+ return (Length);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtFilePrintf
+ *
+ * PARAMETERS: File - File descriptor
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a file.
+ *
+ ******************************************************************************/
+
+int
+AcpiUtFilePrintf (
+ ACPI_FILE File,
+ const char *Format,
+ ...)
+{
+ va_list Args;
+ int Length;
+
+
+ va_start (Args, Format);
+ Length = AcpiUtFileVprintf (File, Format, Args);
+ va_end (Args);
+
+ return (Length);
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utresrc.c b/usr/src/uts/intel/io/acpica/utilities/utresrc.c
index 468ffd6a1a..8efae42977 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utresrc.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utresrc.c
@@ -1,11 +1,11 @@
/*******************************************************************************
*
- * Module Name: utresrc - Resource managment utilities
+ * Module Name: utresrc - Resource management utilities
*
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,23 +41,20 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTRESRC_C__
-
#include "acpi.h"
#include "accommon.h"
-#include "amlresrc.h"
+#include "acresrc.h"
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME ("utresrc")
-#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER)
+#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER)
/*
* Strings used to decode resource descriptors.
- * Used by both the disasssembler and the debugger resource dump routines
+ * Used by both the disassembler and the debugger resource dump routines
*/
const char *AcpiGbl_BmDecode[] =
{
@@ -100,7 +97,9 @@ const char *AcpiGbl_IoDecode[] =
const char *AcpiGbl_LlDecode[] =
{
"ActiveHigh",
- "ActiveLow"
+ "ActiveLow",
+ "ActiveBoth",
+ "Reserved"
};
const char *AcpiGbl_MaxDecode[] =
@@ -148,7 +147,9 @@ const char *AcpiGbl_RwDecode[] =
const char *AcpiGbl_ShrDecode[] =
{
"Exclusive",
- "Shared"
+ "Shared",
+ "ExclusiveAndWake", /* ACPI 5.0 */
+ "SharedAndWake" /* ACPI 5.0 */
};
const char *AcpiGbl_SizDecode[] =
@@ -179,6 +180,154 @@ const char *AcpiGbl_TypDecode[] =
"TypeF"
};
+const char *AcpiGbl_PpcDecode[] =
+{
+ "PullDefault",
+ "PullUp",
+ "PullDown",
+ "PullNone"
+};
+
+const char *AcpiGbl_IorDecode[] =
+{
+ "IoRestrictionNone",
+ "IoRestrictionInputOnly",
+ "IoRestrictionOutputOnly",
+ "IoRestrictionNoneAndPreserve"
+};
+
+const char *AcpiGbl_DtsDecode[] =
+{
+ "Width8bit",
+ "Width16bit",
+ "Width32bit",
+ "Width64bit",
+ "Width128bit",
+ "Width256bit",
+};
+
+/* GPIO connection type */
+
+const char *AcpiGbl_CtDecode[] =
+{
+ "Interrupt",
+ "I/O"
+};
+
+/* Serial bus type */
+
+const char *AcpiGbl_SbtDecode[] =
+{
+ "/* UNKNOWN serial bus type */",
+ "I2C",
+ "SPI",
+ "UART"
+};
+
+/* I2C serial bus access mode */
+
+const char *AcpiGbl_AmDecode[] =
+{
+ "AddressingMode7Bit",
+ "AddressingMode10Bit"
+};
+
+/* I2C serial bus slave mode */
+
+const char *AcpiGbl_SmDecode[] =
+{
+ "ControllerInitiated",
+ "DeviceInitiated"
+};
+
+/* SPI serial bus wire mode */
+
+const char *AcpiGbl_WmDecode[] =
+{
+ "FourWireMode",
+ "ThreeWireMode"
+};
+
+/* SPI serial clock phase */
+
+const char *AcpiGbl_CphDecode[] =
+{
+ "ClockPhaseFirst",
+ "ClockPhaseSecond"
+};
+
+/* SPI serial bus clock polarity */
+
+const char *AcpiGbl_CpoDecode[] =
+{
+ "ClockPolarityLow",
+ "ClockPolarityHigh"
+};
+
+/* SPI serial bus device polarity */
+
+const char *AcpiGbl_DpDecode[] =
+{
+ "PolarityLow",
+ "PolarityHigh"
+};
+
+/* UART serial bus endian */
+
+const char *AcpiGbl_EdDecode[] =
+{
+ "LittleEndian",
+ "BigEndian"
+};
+
+/* UART serial bus bits per byte */
+
+const char *AcpiGbl_BpbDecode[] =
+{
+ "DataBitsFive",
+ "DataBitsSix",
+ "DataBitsSeven",
+ "DataBitsEight",
+ "DataBitsNine",
+ "/* UNKNOWN Bits per byte */",
+ "/* UNKNOWN Bits per byte */",
+ "/* UNKNOWN Bits per byte */"
+};
+
+/* UART serial bus stop bits */
+
+const char *AcpiGbl_SbDecode[] =
+{
+ "StopBitsZero",
+ "StopBitsOne",
+ "StopBitsOnePlusHalf",
+ "StopBitsTwo"
+};
+
+/* UART serial bus flow control */
+
+const char *AcpiGbl_FcDecode[] =
+{
+ "FlowControlNone",
+ "FlowControlHardware",
+ "FlowControlXON",
+ "/* UNKNOWN flow control keyword */"
+};
+
+/* UART serial bus parity type */
+
+const char *AcpiGbl_PtDecode[] =
+{
+ "ParityTypeNone",
+ "ParityTypeEven",
+ "ParityTypeOdd",
+ "ParityTypeMark",
+ "ParityTypeSpace",
+ "/* UNKNOWN parity keyword */",
+ "/* UNKNOWN parity keyword */",
+ "/* UNKNOWN parity keyword */"
+};
+
#endif
@@ -200,7 +349,7 @@ const UINT8 AcpiGbl_ResourceAmlSizes[] =
ACPI_AML_SIZE_SMALL (AML_RESOURCE_END_DEPENDENT),
ACPI_AML_SIZE_SMALL (AML_RESOURCE_IO),
ACPI_AML_SIZE_SMALL (AML_RESOURCE_FIXED_IO),
- 0,
+ ACPI_AML_SIZE_SMALL (AML_RESOURCE_FIXED_DMA),
0,
0,
0,
@@ -220,7 +369,18 @@ const UINT8 AcpiGbl_ResourceAmlSizes[] =
ACPI_AML_SIZE_LARGE (AML_RESOURCE_ADDRESS16),
ACPI_AML_SIZE_LARGE (AML_RESOURCE_EXTENDED_IRQ),
ACPI_AML_SIZE_LARGE (AML_RESOURCE_ADDRESS64),
- ACPI_AML_SIZE_LARGE (AML_RESOURCE_EXTENDED_ADDRESS64)
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_EXTENDED_ADDRESS64),
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_GPIO),
+ 0,
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_COMMON_SERIALBUS),
+};
+
+const UINT8 AcpiGbl_ResourceAmlSerialBusSizes[] =
+{
+ 0,
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_I2C_SERIALBUS),
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_SPI_SERIALBUS),
+ ACPI_AML_SIZE_LARGE (AML_RESOURCE_UART_SERIALBUS),
};
@@ -238,33 +398,36 @@ static const UINT8 AcpiGbl_ResourceTypes[] =
0,
0,
0,
- ACPI_SMALL_VARIABLE_LENGTH,
- ACPI_FIXED_LENGTH,
- ACPI_SMALL_VARIABLE_LENGTH,
- ACPI_FIXED_LENGTH,
- ACPI_FIXED_LENGTH,
- ACPI_FIXED_LENGTH,
- 0,
+ ACPI_SMALL_VARIABLE_LENGTH, /* 04 IRQ */
+ ACPI_FIXED_LENGTH, /* 05 DMA */
+ ACPI_SMALL_VARIABLE_LENGTH, /* 06 StartDependentFunctions */
+ ACPI_FIXED_LENGTH, /* 07 EndDependentFunctions */
+ ACPI_FIXED_LENGTH, /* 08 IO */
+ ACPI_FIXED_LENGTH, /* 09 FixedIO */
+ ACPI_FIXED_LENGTH, /* 0A FixedDMA */
0,
0,
0,
- ACPI_VARIABLE_LENGTH,
- ACPI_FIXED_LENGTH,
+ ACPI_VARIABLE_LENGTH, /* 0E VendorShort */
+ ACPI_FIXED_LENGTH, /* 0F EndTag */
/* Large descriptors */
0,
- ACPI_FIXED_LENGTH,
- ACPI_FIXED_LENGTH,
+ ACPI_FIXED_LENGTH, /* 01 Memory24 */
+ ACPI_FIXED_LENGTH, /* 02 GenericRegister */
0,
- ACPI_VARIABLE_LENGTH,
- ACPI_FIXED_LENGTH,
- ACPI_FIXED_LENGTH,
- ACPI_VARIABLE_LENGTH,
- ACPI_VARIABLE_LENGTH,
- ACPI_VARIABLE_LENGTH,
- ACPI_VARIABLE_LENGTH,
- ACPI_FIXED_LENGTH
+ ACPI_VARIABLE_LENGTH, /* 04 VendorLong */
+ ACPI_FIXED_LENGTH, /* 05 Memory32 */
+ ACPI_FIXED_LENGTH, /* 06 Memory32Fixed */
+ ACPI_VARIABLE_LENGTH, /* 07 Dword* address */
+ ACPI_VARIABLE_LENGTH, /* 08 Word* address */
+ ACPI_VARIABLE_LENGTH, /* 09 ExtendedIRQ */
+ ACPI_VARIABLE_LENGTH, /* 0A Qword* address */
+ ACPI_FIXED_LENGTH, /* 0B Extended* address */
+ ACPI_VARIABLE_LENGTH, /* 0C Gpio* */
+ 0,
+ ACPI_VARIABLE_LENGTH /* 0E *SerialBus */
};
@@ -272,11 +435,12 @@ static const UINT8 AcpiGbl_ResourceTypes[] =
*
* FUNCTION: AcpiUtWalkAmlResources
*
- * PARAMETERS: Aml - Pointer to the raw AML resource template
- * AmlLength - Length of the entire template
- * UserFunction - Called once for each descriptor found. If
- * NULL, a pointer to the EndTag is returned
- * Context - Passed to UserFunction
+ * PARAMETERS: WalkState - Current walk info
+ * PARAMETERS: Aml - Pointer to the raw AML resource template
+ * AmlLength - Length of the entire template
+ * UserFunction - Called once for each descriptor found. If
+ * NULL, a pointer to the EndTag is returned
+ * Context - Passed to UserFunction
*
* RETURN: Status
*
@@ -287,16 +451,18 @@ static const UINT8 AcpiGbl_ResourceTypes[] =
ACPI_STATUS
AcpiUtWalkAmlResources (
+ ACPI_WALK_STATE *WalkState,
UINT8 *Aml,
ACPI_SIZE AmlLength,
ACPI_WALK_AML_CALLBACK UserFunction,
- void *Context)
+ void **Context)
{
ACPI_STATUS Status;
UINT8 *EndAml;
UINT8 ResourceIndex;
UINT32 Length;
UINT32 Offset = 0;
+ UINT8 EndTag[2] = {0x79, 0x00};
ACPI_FUNCTION_TRACE (UtWalkAmlResources);
@@ -319,9 +485,13 @@ AcpiUtWalkAmlResources (
{
/* Validate the Resource Type and Resource Length */
- Status = AcpiUtValidateResource (Aml, &ResourceIndex);
+ Status = AcpiUtValidateResource (WalkState, Aml, &ResourceIndex);
if (ACPI_FAILURE (Status))
{
+ /*
+ * Exit on failure. Cannot continue because the descriptor
+ * length may be bogus also.
+ */
return_ACPI_STATUS (Status);
}
@@ -333,10 +503,11 @@ AcpiUtWalkAmlResources (
if (UserFunction)
{
- Status = UserFunction (Aml, Length, Offset, ResourceIndex, Context);
+ Status = UserFunction (
+ Aml, Length, Offset, ResourceIndex, Context);
if (ACPI_FAILURE (Status))
{
- return (Status);
+ return_ACPI_STATUS (Status);
}
}
@@ -357,7 +528,7 @@ AcpiUtWalkAmlResources (
if (!UserFunction)
{
- *(void **) Context = Aml;
+ *Context = Aml;
}
/* Normal exit */
@@ -371,7 +542,19 @@ AcpiUtWalkAmlResources (
/* Did not find an EndTag descriptor */
- return (AE_AML_NO_RESOURCE_END_TAG);
+ if (UserFunction)
+ {
+ /* Insert an EndTag anyway. AcpiRsGetListLength always leaves room */
+
+ (void) AcpiUtValidateResource (WalkState, EndTag, &ResourceIndex);
+ Status = UserFunction (EndTag, 2, Offset, ResourceIndex, Context);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ return_ACPI_STATUS (AE_AML_NO_RESOURCE_END_TAG);
}
@@ -379,9 +562,10 @@ AcpiUtWalkAmlResources (
*
* FUNCTION: AcpiUtValidateResource
*
- * PARAMETERS: Aml - Pointer to the raw AML resource descriptor
- * ReturnIndex - Where the resource index is returned. NULL
- * if the index is not required.
+ * PARAMETERS: WalkState - Current walk info
+ * Aml - Pointer to the raw AML resource descriptor
+ * ReturnIndex - Where the resource index is returned. NULL
+ * if the index is not required.
*
* RETURN: Status, and optionally the Index into the global resource tables
*
@@ -393,9 +577,11 @@ AcpiUtWalkAmlResources (
ACPI_STATUS
AcpiUtValidateResource (
+ ACPI_WALK_STATE *WalkState,
void *Aml,
UINT8 *ReturnIndex)
{
+ AML_RESOURCE *AmlResource;
UINT8 ResourceType;
UINT8 ResourceIndex;
ACPI_RS_LENGTH ResourceLength;
@@ -420,7 +606,7 @@ AcpiUtValidateResource (
if (ResourceType > ACPI_RESOURCE_NAME_LARGE_MAX)
{
- return (AE_AML_INVALID_RESOURCE_TYPE);
+ goto InvalidResource;
}
/*
@@ -439,17 +625,18 @@ AcpiUtValidateResource (
((ResourceType & ACPI_RESOURCE_NAME_SMALL_MASK) >> 3);
}
- /* Check validity of the resource type, zero indicates name is invalid */
-
+ /*
+ * Check validity of the resource type, via AcpiGbl_ResourceTypes.
+ * Zero indicates an invalid resource.
+ */
if (!AcpiGbl_ResourceTypes[ResourceIndex])
{
- return (AE_AML_INVALID_RESOURCE_TYPE);
+ goto InvalidResource;
}
-
/*
- * 2) Validate the ResourceLength field. This ensures that the length
- * is at least reasonable, and guarantees that it is non-zero.
+ * Validate the ResourceLength field. This ensures that the length
+ * is at least reasonable, and guarantees that it is non-zero.
*/
ResourceLength = AcpiUtGetResourceLength (Aml);
MinimumResourceLength = AcpiGbl_ResourceAmlSizes[ResourceIndex];
@@ -464,7 +651,7 @@ AcpiUtValidateResource (
if (ResourceLength != MinimumResourceLength)
{
- return (AE_AML_BAD_RESOURCE_LENGTH);
+ goto BadResourceLength;
}
break;
@@ -474,7 +661,7 @@ AcpiUtValidateResource (
if (ResourceLength < MinimumResourceLength)
{
- return (AE_AML_BAD_RESOURCE_LENGTH);
+ goto BadResourceLength;
}
break;
@@ -485,7 +672,7 @@ AcpiUtValidateResource (
if ((ResourceLength > MinimumResourceLength) ||
(ResourceLength < (MinimumResourceLength - 1)))
{
- return (AE_AML_BAD_RESOURCE_LENGTH);
+ goto BadResourceLength;
}
break;
@@ -493,7 +680,25 @@ AcpiUtValidateResource (
/* Shouldn't happen (because of validation earlier), but be sure */
- return (AE_AML_INVALID_RESOURCE_TYPE);
+ goto InvalidResource;
+ }
+
+ AmlResource = ACPI_CAST_PTR (AML_RESOURCE, Aml);
+ if (ResourceType == ACPI_RESOURCE_NAME_SERIAL_BUS)
+ {
+ /* Validate the BusType field */
+
+ if ((AmlResource->CommonSerialBus.Type == 0) ||
+ (AmlResource->CommonSerialBus.Type > AML_RESOURCE_MAX_SERIALBUSTYPE))
+ {
+ if (WalkState)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unsupported SerialBus resource descriptor: BusType 0x%2.2X",
+ AmlResource->CommonSerialBus.Type));
+ }
+ return (AE_AML_INVALID_RESOURCE_TYPE);
+ }
}
/* Optionally return the resource table index */
@@ -504,6 +709,28 @@ AcpiUtValidateResource (
}
return (AE_OK);
+
+
+InvalidResource:
+
+ if (WalkState)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid/unsupported resource descriptor: Type 0x%2.2X",
+ ResourceType));
+ }
+ return (AE_AML_INVALID_RESOURCE_TYPE);
+
+BadResourceLength:
+
+ if (WalkState)
+ {
+ ACPI_ERROR ((AE_INFO,
+ "Invalid resource descriptor length: Type "
+ "0x%2.2X, Length 0x%4.4X, MinLength 0x%4.4X",
+ ResourceType, ResourceLength, MinimumResourceLength));
+ }
+ return (AE_AML_BAD_RESOURCE_LENGTH);
}
@@ -587,7 +814,7 @@ AcpiUtGetResourceLength (
/* Small Resource type -- bits 2:0 of byte 0 contain the length */
ResourceLength = (UINT16) (ACPI_GET8 (Aml) &
- ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK);
+ ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK);
}
return (ResourceLength);
@@ -652,7 +879,7 @@ AcpiUtGetDescriptorLength (
* the header length (depends on if this is a small or large resource)
*/
return (AcpiUtGetResourceLength (Aml) +
- AcpiUtGetResourceHeaderLength (Aml));
+ AcpiUtGetResourceHeaderLength (Aml));
}
@@ -691,10 +918,8 @@ AcpiUtGetResourceEndTag (
/* Validate the template and get a pointer to the EndTag */
- Status = AcpiUtWalkAmlResources (ObjDesc->Buffer.Pointer,
- ObjDesc->Buffer.Length, NULL, EndTag);
+ Status = AcpiUtWalkAmlResources (NULL, ObjDesc->Buffer.Pointer,
+ ObjDesc->Buffer.Length, NULL, (void **) EndTag);
return_ACPI_STATUS (Status);
}
-
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utstate.c b/usr/src/uts/intel/io/acpica/utilities/utstate.c
index bc84915ab0..deffd283ca 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utstate.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utstate.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,9 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTSTATE_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -53,44 +50,6 @@
/*******************************************************************************
*
- * FUNCTION: AcpiUtCreatePkgStateAndPush
- *
- * PARAMETERS: Object - Object to be added to the new state
- * Action - Increment/Decrement
- * StateList - List the state will be added to
- *
- * RETURN: Status
- *
- * DESCRIPTION: Create a new state and push it
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiUtCreatePkgStateAndPush (
- void *InternalObject,
- void *ExternalObject,
- UINT16 Index,
- ACPI_GENERIC_STATE **StateList)
-{
- ACPI_GENERIC_STATE *State;
-
-
- ACPI_FUNCTION_ENTRY ();
-
-
- State = AcpiUtCreatePkgState (InternalObject, ExternalObject, Index);
- if (!State)
- {
- return (AE_NO_MEMORY);
- }
-
- AcpiUtPushGenericState (StateList, State);
- return (AE_OK);
-}
-
-
-/*******************************************************************************
- *
* FUNCTION: AcpiUtPushGenericState
*
* PARAMETERS: ListHead - Head of the state stack
@@ -107,15 +66,14 @@ AcpiUtPushGenericState (
ACPI_GENERIC_STATE **ListHead,
ACPI_GENERIC_STATE *State)
{
- ACPI_FUNCTION_TRACE (UtPushGenericState);
+ ACPI_FUNCTION_ENTRY ();
/* Push the state object onto the front of the list (stack) */
State->Common.Next = *ListHead;
*ListHead = State;
-
- return_VOID;
+ return;
}
@@ -138,7 +96,7 @@ AcpiUtPopGenericState (
ACPI_GENERIC_STATE *State;
- ACPI_FUNCTION_TRACE (UtPopGenericState);
+ ACPI_FUNCTION_ENTRY ();
/* Remove the state object at the head of the list (stack) */
@@ -151,7 +109,7 @@ AcpiUtPopGenericState (
*ListHead = State->Common.Next;
}
- return_PTR (State);
+ return (State);
}
@@ -163,7 +121,7 @@ AcpiUtPopGenericState (
*
* RETURN: The new state object. NULL on failure.
*
- * DESCRIPTION: Create a generic state object. Attempt to obtain one from
+ * DESCRIPTION: Create a generic state object. Attempt to obtain one from
* the global state cache; If none available, create a new one.
*
******************************************************************************/
@@ -209,7 +167,7 @@ AcpiUtCreateThreadState (
ACPI_GENERIC_STATE *State;
- ACPI_FUNCTION_TRACE (UtCreateThreadState);
+ ACPI_FUNCTION_ENTRY ();
/* Create the generic state object */
@@ -217,7 +175,7 @@ AcpiUtCreateThreadState (
State = AcpiUtCreateGenericState ();
if (!State)
{
- return_PTR (NULL);
+ return (NULL);
}
/* Init fields specific to the update struct */
@@ -233,7 +191,7 @@ AcpiUtCreateThreadState (
State->Thread.ThreadId = (ACPI_THREAD_ID) 1;
}
- return_PTR ((ACPI_THREAD_STATE *) State);
+ return ((ACPI_THREAD_STATE *) State);
}
@@ -260,7 +218,7 @@ AcpiUtCreateUpdateState (
ACPI_GENERIC_STATE *State;
- ACPI_FUNCTION_TRACE_PTR (UtCreateUpdateState, Object);
+ ACPI_FUNCTION_ENTRY ();
/* Create the generic state object */
@@ -268,7 +226,7 @@ AcpiUtCreateUpdateState (
State = AcpiUtCreateGenericState ();
if (!State)
{
- return_PTR (NULL);
+ return (NULL);
}
/* Init fields specific to the update struct */
@@ -276,8 +234,7 @@ AcpiUtCreateUpdateState (
State->Common.DescriptorType = ACPI_DESC_TYPE_STATE_UPDATE;
State->Update.Object = Object;
State->Update.Value = Action;
-
- return_PTR (State);
+ return (State);
}
@@ -303,7 +260,7 @@ AcpiUtCreatePkgState (
ACPI_GENERIC_STATE *State;
- ACPI_FUNCTION_TRACE_PTR (UtCreatePkgState, InternalObject);
+ ACPI_FUNCTION_ENTRY ();
/* Create the generic state object */
@@ -311,7 +268,7 @@ AcpiUtCreatePkgState (
State = AcpiUtCreateGenericState ();
if (!State)
{
- return_PTR (NULL);
+ return (NULL);
}
/* Init fields specific to the update struct */
@@ -322,7 +279,7 @@ AcpiUtCreatePkgState (
State->Pkg.Index= Index;
State->Pkg.NumPackages = 1;
- return_PTR (State);
+ return (State);
}
@@ -346,7 +303,7 @@ AcpiUtCreateControlState (
ACPI_GENERIC_STATE *State;
- ACPI_FUNCTION_TRACE (UtCreateControlState);
+ ACPI_FUNCTION_ENTRY ();
/* Create the generic state object */
@@ -354,7 +311,7 @@ AcpiUtCreateControlState (
State = AcpiUtCreateGenericState ();
if (!State)
{
- return_PTR (NULL);
+ return (NULL);
}
/* Init fields specific to the control struct */
@@ -362,7 +319,7 @@ AcpiUtCreateControlState (
State->Common.DescriptorType = ACPI_DESC_TYPE_STATE_CONTROL;
State->Common.State = ACPI_CONTROL_CONDITIONAL_EXECUTING;
- return_PTR (State);
+ return (State);
}
@@ -383,7 +340,7 @@ void
AcpiUtDeleteGenericState (
ACPI_GENERIC_STATE *State)
{
- ACPI_FUNCTION_TRACE (UtDeleteGenericState);
+ ACPI_FUNCTION_ENTRY ();
/* Ignore null state */
@@ -392,7 +349,6 @@ AcpiUtDeleteGenericState (
{
(void) AcpiOsReleaseObject (AcpiGbl_StateCache, State);
}
- return_VOID;
-}
-
+ return;
+}
diff --git a/usr/src/uts/intel/io/acpica/utilities/utstring.c b/usr/src/uts/intel/io/acpica/utilities/utstring.c
new file mode 100644
index 0000000000..cb46193e6e
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utstring.c
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ *
+ * Module Name: utstring - Common functions for strings and characters
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utstring")
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtPrintString
+ *
+ * PARAMETERS: String - Null terminated ASCII string
+ * MaxLength - Maximum output length. Used to constrain the
+ * length of strings during debug output only.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump an ASCII string with support for ACPI-defined escape
+ * sequences.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtPrintString (
+ char *String,
+ UINT16 MaxLength)
+{
+ UINT32 i;
+
+
+ if (!String)
+ {
+ AcpiOsPrintf ("<\"NULL STRING PTR\">");
+ return;
+ }
+
+ AcpiOsPrintf ("\"");
+ for (i = 0; (i < MaxLength) && String[i]; i++)
+ {
+ /* Escape sequences */
+
+ switch (String[i])
+ {
+ case 0x07:
+
+ AcpiOsPrintf ("\\a"); /* BELL */
+ break;
+
+ case 0x08:
+
+ AcpiOsPrintf ("\\b"); /* BACKSPACE */
+ break;
+
+ case 0x0C:
+
+ AcpiOsPrintf ("\\f"); /* FORMFEED */
+ break;
+
+ case 0x0A:
+
+ AcpiOsPrintf ("\\n"); /* LINEFEED */
+ break;
+
+ case 0x0D:
+
+ AcpiOsPrintf ("\\r"); /* CARRIAGE RETURN*/
+ break;
+
+ case 0x09:
+
+ AcpiOsPrintf ("\\t"); /* HORIZONTAL TAB */
+ break;
+
+ case 0x0B:
+
+ AcpiOsPrintf ("\\v"); /* VERTICAL TAB */
+ break;
+
+ case '\'': /* Single Quote */
+ case '\"': /* Double Quote */
+ case '\\': /* Backslash */
+
+ AcpiOsPrintf ("\\%c", (int) String[i]);
+ break;
+
+ default:
+
+ /* Check for printable character or hex escape */
+
+ if (isprint ((int) String[i]))
+ {
+ /* This is a normal character */
+
+ AcpiOsPrintf ("%c", (int) String[i]);
+ }
+ else
+ {
+ /* All others will be Hex escapes */
+
+ AcpiOsPrintf ("\\x%2.2X", (INT32) String[i]);
+ }
+ break;
+ }
+ }
+
+ AcpiOsPrintf ("\"");
+
+ if (i == MaxLength && String[i])
+ {
+ AcpiOsPrintf ("...");
+ }
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtRepairName
+ *
+ * PARAMETERS: Name - The ACPI name to be repaired
+ *
+ * RETURN: Repaired version of the name
+ *
+ * DESCRIPTION: Repair an ACPI name: Change invalid characters to '*' and
+ * return the new name. NOTE: the Name parameter must reside in
+ * read/write memory, cannot be a const.
+ *
+ * An ACPI Name must consist of valid ACPI characters. We will repair the name
+ * if necessary because we don't want to abort because of this, but we want
+ * all namespace names to be printable. A warning message is appropriate.
+ *
+ * This issue came up because there are in fact machines that exhibit
+ * this problem, and we want to be able to enable ACPI support for them,
+ * even though there are a few bad names.
+ *
+ ******************************************************************************/
+
+void
+AcpiUtRepairName (
+ char *Name)
+{
+ UINT32 i;
+ BOOLEAN FoundBadChar = FALSE;
+ UINT32 OriginalName;
+
+
+ ACPI_FUNCTION_NAME (UtRepairName);
+
+
+ /*
+ * Special case for the root node. This can happen if we get an
+ * error during the execution of module-level code.
+ */
+ if (ACPI_COMPARE_NAME (Name, "\\___"))
+ {
+ return;
+ }
+
+ ACPI_MOVE_NAME (&OriginalName, Name);
+
+ /* Check each character in the name */
+
+ for (i = 0; i < ACPI_NAME_SIZE; i++)
+ {
+ if (AcpiUtValidNameChar (Name[i], i))
+ {
+ continue;
+ }
+
+ /*
+ * Replace a bad character with something printable, yet technically
+ * still invalid. This prevents any collisions with existing "good"
+ * names in the namespace.
+ */
+ Name[i] = '*';
+ FoundBadChar = TRUE;
+ }
+
+ if (FoundBadChar)
+ {
+ /* Report warning only if in strict mode or debug mode */
+
+ if (!AcpiGbl_EnableInterpreterSlack)
+ {
+ ACPI_WARNING ((AE_INFO,
+ "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
+ OriginalName, Name));
+ }
+ else
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_INFO,
+ "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]",
+ OriginalName, Name));
+ }
+ }
+}
+
+
+#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP
+/*******************************************************************************
+ *
+ * FUNCTION: UtConvertBackslashes
+ *
+ * PARAMETERS: Pathname - File pathname string to be converted
+ *
+ * RETURN: Modifies the input Pathname
+ *
+ * DESCRIPTION: Convert all backslashes (0x5C) to forward slashes (0x2F) within
+ * the entire input file pathname string.
+ *
+ ******************************************************************************/
+
+void
+UtConvertBackslashes (
+ char *Pathname)
+{
+
+ if (!Pathname)
+ {
+ return;
+ }
+
+ while (*Pathname)
+ {
+ if (*Pathname == '\\')
+ {
+ *Pathname = '/';
+ }
+
+ Pathname++;
+ }
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/uttrack.c b/usr/src/uts/intel/io/acpica/utilities/uttrack.c
index 6ecec63f31..9efa9d6ec7 100644
--- a/usr/src/uts/intel/io/acpica/utilities/uttrack.c
+++ b/usr/src/uts/intel/io/acpica/utilities/uttrack.c
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,15 +45,13 @@
* These procedures are used for tracking memory leaks in the subsystem, and
* they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set.
*
- * Each memory allocation is tracked via a doubly linked list. Each
+ * Each memory allocation is tracked via a doubly linked list. Each
* element contains the caller's component, module name, function name, and
- * line number. AcpiUtAllocate and AcpiUtAllocateZeroed call
+ * line number. AcpiUtAllocate and AcpiUtAllocateZeroed call
* AcpiUtTrackAllocation to add an element to the list; deletion
* occurs in the body of AcpiUtFree.
*/
-#define __UTTRACK_C__
-
#include "acpi.h"
#include "accommon.h"
@@ -62,11 +60,12 @@
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME ("uttrack")
+
/* Local prototypes */
static ACPI_DEBUG_MEM_BLOCK *
AcpiUtFindAllocation (
- void *Allocation);
+ ACPI_DEBUG_MEM_BLOCK *Allocation);
static ACPI_STATUS
AcpiUtTrackAllocation (
@@ -101,7 +100,7 @@ AcpiUtRemoveAllocation (
ACPI_STATUS
AcpiUtCreateList (
- char *ListName,
+ const char *ListName,
UINT16 ObjectSize,
ACPI_MEMORY_LIST **ReturnCache)
{
@@ -114,9 +113,9 @@ AcpiUtCreateList (
return (AE_NO_MEMORY);
}
- ACPI_MEMSET (Cache, 0, sizeof (ACPI_MEMORY_LIST));
+ memset (Cache, 0, sizeof (ACPI_MEMORY_LIST));
- Cache->ListName = ListName;
+ Cache->ListName = ListName;
Cache->ObjectSize = ObjectSize;
*ReturnCache = Cache;
@@ -150,15 +149,28 @@ AcpiUtAllocateAndTrack (
ACPI_STATUS Status;
- Allocation = AcpiUtAllocate (Size + sizeof (ACPI_DEBUG_MEM_HEADER),
- Component, Module, Line);
+ /* Check for an inadvertent size of zero bytes */
+
+ if (!Size)
+ {
+ ACPI_WARNING ((Module, Line,
+ "Attempt to allocate zero bytes, allocating 1 byte"));
+ Size = 1;
+ }
+
+ Allocation = AcpiOsAllocate (Size + sizeof (ACPI_DEBUG_MEM_HEADER));
if (!Allocation)
{
+ /* Report allocation error */
+
+ ACPI_WARNING ((Module, Line,
+ "Could not allocate size %u", (UINT32) Size));
+
return (NULL);
}
- Status = AcpiUtTrackAllocation (Allocation, Size,
- ACPI_MEM_MALLOC, Component, Module, Line);
+ Status = AcpiUtTrackAllocation (
+ Allocation, Size, ACPI_MEM_MALLOC, Component, Module, Line);
if (ACPI_FAILURE (Status))
{
AcpiOsFree (Allocation);
@@ -168,9 +180,12 @@ AcpiUtAllocateAndTrack (
AcpiGbl_GlobalList->TotalAllocated++;
AcpiGbl_GlobalList->TotalSize += (UINT32) Size;
AcpiGbl_GlobalList->CurrentTotalSize += (UINT32) Size;
- if (AcpiGbl_GlobalList->CurrentTotalSize > AcpiGbl_GlobalList->MaxOccupied)
+
+ if (AcpiGbl_GlobalList->CurrentTotalSize >
+ AcpiGbl_GlobalList->MaxOccupied)
{
- AcpiGbl_GlobalList->MaxOccupied = AcpiGbl_GlobalList->CurrentTotalSize;
+ AcpiGbl_GlobalList->MaxOccupied =
+ AcpiGbl_GlobalList->CurrentTotalSize;
}
return ((void *) &Allocation->UserSpace);
@@ -203,8 +218,17 @@ AcpiUtAllocateZeroedAndTrack (
ACPI_STATUS Status;
- Allocation = AcpiUtAllocateZeroed (Size + sizeof (ACPI_DEBUG_MEM_HEADER),
- Component, Module, Line);
+ /* Check for an inadvertent size of zero bytes */
+
+ if (!Size)
+ {
+ ACPI_WARNING ((Module, Line,
+ "Attempt to allocate zero bytes, allocating 1 byte"));
+ Size = 1;
+ }
+
+ Allocation = AcpiOsAllocateZeroed (
+ Size + sizeof (ACPI_DEBUG_MEM_HEADER));
if (!Allocation)
{
/* Report allocation error */
@@ -215,7 +239,7 @@ AcpiUtAllocateZeroedAndTrack (
}
Status = AcpiUtTrackAllocation (Allocation, Size,
- ACPI_MEM_CALLOC, Component, Module, Line);
+ ACPI_MEM_CALLOC, Component, Module, Line);
if (ACPI_FAILURE (Status))
{
AcpiOsFree (Allocation);
@@ -225,9 +249,12 @@ AcpiUtAllocateZeroedAndTrack (
AcpiGbl_GlobalList->TotalAllocated++;
AcpiGbl_GlobalList->TotalSize += (UINT32) Size;
AcpiGbl_GlobalList->CurrentTotalSize += (UINT32) Size;
- if (AcpiGbl_GlobalList->CurrentTotalSize > AcpiGbl_GlobalList->MaxOccupied)
+
+ if (AcpiGbl_GlobalList->CurrentTotalSize >
+ AcpiGbl_GlobalList->MaxOccupied)
{
- AcpiGbl_GlobalList->MaxOccupied = AcpiGbl_GlobalList->CurrentTotalSize;
+ AcpiGbl_GlobalList->MaxOccupied =
+ AcpiGbl_GlobalList->CurrentTotalSize;
}
return ((void *) &Allocation->UserSpace);
@@ -272,20 +299,20 @@ AcpiUtFreeAndTrack (
}
DebugBlock = ACPI_CAST_PTR (ACPI_DEBUG_MEM_BLOCK,
- (((char *) Allocation) - sizeof (ACPI_DEBUG_MEM_HEADER)));
+ (((char *) Allocation) - sizeof (ACPI_DEBUG_MEM_HEADER)));
AcpiGbl_GlobalList->TotalFreed++;
AcpiGbl_GlobalList->CurrentTotalSize -= DebugBlock->Size;
- Status = AcpiUtRemoveAllocation (DebugBlock,
- Component, Module, Line);
+ Status = AcpiUtRemoveAllocation (DebugBlock, Component, Module, Line);
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status, "Could not free memory"));
}
AcpiOsFree (DebugBlock);
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p freed\n", Allocation));
+ ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n",
+ Allocation, DebugBlock));
return_VOID;
}
@@ -296,29 +323,52 @@ AcpiUtFreeAndTrack (
*
* PARAMETERS: Allocation - Address of allocated memory
*
- * RETURN: A list element if found; NULL otherwise.
+ * RETURN: Three cases:
+ * 1) List is empty, NULL is returned.
+ * 2) Element was found. Returns Allocation parameter.
+ * 3) Element was not found. Returns position where it should be
+ * inserted into the list.
*
* DESCRIPTION: Searches for an element in the global allocation tracking list.
+ * If the element is not found, returns the location within the
+ * list where the element should be inserted.
+ *
+ * Note: The list is ordered by larger-to-smaller addresses.
+ *
+ * This global list is used to detect memory leaks in ACPICA as
+ * well as other issues such as an attempt to release the same
+ * internal object more than once. Although expensive as far
+ * as cpu time, this list is much more helpful for finding these
+ * types of issues than using memory leak detectors outside of
+ * the ACPICA code.
*
******************************************************************************/
static ACPI_DEBUG_MEM_BLOCK *
AcpiUtFindAllocation (
- void *Allocation)
+ ACPI_DEBUG_MEM_BLOCK *Allocation)
{
ACPI_DEBUG_MEM_BLOCK *Element;
- ACPI_FUNCTION_ENTRY ();
-
-
Element = AcpiGbl_GlobalList->ListHead;
+ if (!Element)
+ {
+ return (NULL);
+ }
- /* Search for the address. */
-
- while (Element)
+ /*
+ * Search for the address.
+ *
+ * Note: List is ordered by larger-to-smaller addresses, on the
+ * assumption that a new allocation usually has a larger address
+ * than previous allocations.
+ */
+ while (Element > Allocation)
{
- if (Element == Allocation)
+ /* Check for end-of-list */
+
+ if (!Element->Next)
{
return (Element);
}
@@ -326,7 +376,12 @@ AcpiUtFindAllocation (
Element = Element->Next;
}
- return (NULL);
+ if (Element == Allocation)
+ {
+ return (Element);
+ }
+
+ return (Element->Previous);
}
@@ -341,7 +396,7 @@ AcpiUtFindAllocation (
* Module - Source file name of caller
* Line - Line number of caller
*
- * RETURN: None.
+ * RETURN: Status
*
* DESCRIPTION: Inserts an element into the global allocation tracking list.
*
@@ -377,43 +432,57 @@ AcpiUtTrackAllocation (
}
/*
- * Search list for this address to make sure it is not already on the list.
- * This will catch several kinds of problems.
+ * Search the global list for this address to make sure it is not
+ * already present. This will catch several kinds of problems.
*/
Element = AcpiUtFindAllocation (Allocation);
- if (Element)
+ if (Element == Allocation)
{
ACPI_ERROR ((AE_INFO,
- "UtTrackAllocation: Allocation already present in list! (%p)",
+ "UtTrackAllocation: Allocation (%p) already present in global list!",
Allocation));
-
- ACPI_ERROR ((AE_INFO, "Element %p Address %p",
- Element, Allocation));
-
goto UnlockAndExit;
}
- /* Fill in the instance data. */
+ /* Fill in the instance data */
- Allocation->Size = (UINT32) Size;
+ Allocation->Size = (UINT32) Size;
Allocation->AllocType = AllocType;
Allocation->Component = Component;
- Allocation->Line = Line;
+ Allocation->Line = Line;
- ACPI_STRNCPY (Allocation->Module, Module, ACPI_MAX_MODULE_NAME);
+ strncpy (Allocation->Module, Module, ACPI_MAX_MODULE_NAME);
Allocation->Module[ACPI_MAX_MODULE_NAME-1] = 0;
- /* Insert at list head */
-
- if (MemList->ListHead)
+ if (!Element)
{
- ((ACPI_DEBUG_MEM_BLOCK *)(MemList->ListHead))->Previous = Allocation;
+ /* Insert at list head */
+
+ if (MemList->ListHead)
+ {
+ ((ACPI_DEBUG_MEM_BLOCK *)(MemList->ListHead))->Previous =
+ Allocation;
+ }
+
+ Allocation->Next = MemList->ListHead;
+ Allocation->Previous = NULL;
+
+ MemList->ListHead = Allocation;
}
+ else
+ {
+ /* Insert after element */
- Allocation->Next = MemList->ListHead;
- Allocation->Previous = NULL;
+ Allocation->Next = Element->Next;
+ Allocation->Previous = Element;
+
+ if (Element->Next)
+ {
+ (Element->Next)->Previous = Allocation;
+ }
- MemList->ListHead = Allocation;
+ Element->Next = Allocation;
+ }
UnlockAndExit:
@@ -431,7 +500,7 @@ UnlockAndExit:
* Module - Source file name of caller
* Line - Line number of caller
*
- * RETURN:
+ * RETURN: Status
*
* DESCRIPTION: Deletes an element from the global allocation tracking list.
*
@@ -448,12 +517,12 @@ AcpiUtRemoveAllocation (
ACPI_STATUS Status;
- ACPI_FUNCTION_TRACE (UtRemoveAllocation);
+ ACPI_FUNCTION_NAME (UtRemoveAllocation);
if (AcpiGbl_DisableMemTracking)
{
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
MemList = AcpiGbl_GlobalList;
@@ -464,13 +533,13 @@ AcpiUtRemoveAllocation (
ACPI_ERROR ((Module, Line,
"Empty allocation list, nothing to free!"));
- return_ACPI_STATUS (AE_OK);
+ return (AE_OK);
}
Status = AcpiUtAcquireMutex (ACPI_MTX_MEMORY);
if (ACPI_FAILURE (Status))
{
- return_ACPI_STATUS (Status);
+ return (Status);
}
/* Unlink */
@@ -489,15 +558,15 @@ AcpiUtRemoveAllocation (
(Allocation->Next)->Previous = Allocation->Previous;
}
- /* Mark the segment as deleted */
+ ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Freeing %p, size 0%X\n",
+ &Allocation->UserSpace, Allocation->Size));
- ACPI_MEMSET (&Allocation->UserSpace, 0xEA, Allocation->Size);
+ /* Mark the segment as deleted */
- ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Freeing size 0%X\n",
- Allocation->Size));
+ memset (&Allocation->UserSpace, 0xEA, Allocation->Size);
Status = AcpiUtReleaseMutex (ACPI_MTX_MEMORY);
- return_ACPI_STATUS (Status);
+ return (Status);
}
@@ -505,7 +574,7 @@ AcpiUtRemoveAllocation (
*
* FUNCTION: AcpiUtDumpAllocationInfo
*
- * PARAMETERS:
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -525,37 +594,37 @@ AcpiUtDumpAllocationInfo (
/*
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Current allocations",
- MemList->CurrentCount,
- ROUND_UP_TO_1K (MemList->CurrentSize)));
+ ("%30s: %4d (%3d Kb)\n", "Current allocations",
+ MemList->CurrentCount,
+ ROUND_UP_TO_1K (MemList->CurrentSize)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",
- MemList->MaxConcurrentCount,
- ROUND_UP_TO_1K (MemList->MaxConcurrentSize)));
+ ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",
+ MemList->MaxConcurrentCount,
+ ROUND_UP_TO_1K (MemList->MaxConcurrentSize)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",
- RunningObjectCount,
- ROUND_UP_TO_1K (RunningObjectSize)));
+ ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",
+ RunningObjectCount,
+ ROUND_UP_TO_1K (RunningObjectSize)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",
- RunningAllocCount,
- ROUND_UP_TO_1K (RunningAllocSize)));
+ ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",
+ RunningAllocCount,
+ ROUND_UP_TO_1K (RunningAllocSize)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Current Nodes",
- AcpiGbl_CurrentNodeCount,
- ROUND_UP_TO_1K (AcpiGbl_CurrentNodeSize)));
+ ("%30s: %4d (%3d Kb)\n", "Current Nodes",
+ AcpiGbl_CurrentNodeCount,
+ ROUND_UP_TO_1K (AcpiGbl_CurrentNodeSize)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Max Nodes",
- AcpiGbl_MaxConcurrentNodeCount,
- ROUND_UP_TO_1K ((AcpiGbl_MaxConcurrentNodeCount *
- sizeof (ACPI_NAMESPACE_NODE)))));
+ ("%30s: %4d (%3d Kb)\n", "Max Nodes",
+ AcpiGbl_MaxConcurrentNodeCount,
+ ROUND_UP_TO_1K ((AcpiGbl_MaxConcurrentNodeCount *
+ sizeof (ACPI_NAMESPACE_NODE)))));
*/
return_VOID;
}
@@ -566,7 +635,7 @@ AcpiUtDumpAllocationInfo (
* FUNCTION: AcpiUtDumpAllocations
*
* PARAMETERS: Component - Component(s) to dump info for.
- * Module - Module to dump info for. NULL means all.
+ * Module - Module to dump info for. NULL means all.
*
* RETURN: None
*
@@ -590,7 +659,7 @@ AcpiUtDumpAllocations (
if (AcpiGbl_DisableMemTracking)
{
- return;
+ return_VOID;
}
/*
@@ -598,16 +667,17 @@ AcpiUtDumpAllocations (
*/
if (ACPI_FAILURE (AcpiUtAcquireMutex (ACPI_MTX_MEMORY)))
{
- return;
+ return_VOID;
}
Element = AcpiGbl_GlobalList->ListHead;
while (Element)
{
if ((Element->Component & Component) &&
- ((Module == NULL) || (0 == ACPI_STRCMP (Module, Element->Module))))
+ ((Module == NULL) || (0 == strcmp (Module, Element->Module))))
{
- Descriptor = ACPI_CAST_PTR (ACPI_DESCRIPTOR, &Element->UserSpace);
+ Descriptor = ACPI_CAST_PTR (
+ ACPI_DESCRIPTOR, &Element->UserSpace);
if (Element->Size < sizeof (ACPI_COMMON_DESCRIPTOR))
{
@@ -620,7 +690,8 @@ AcpiUtDumpAllocations (
{
/* Ignore allocated objects that are in a cache */
- if (ACPI_GET_DESCRIPTOR_TYPE (Descriptor) != ACPI_DESC_TYPE_CACHED)
+ if (ACPI_GET_DESCRIPTOR_TYPE (Descriptor) !=
+ ACPI_DESC_TYPE_CACHED)
{
AcpiOsPrintf ("%p Length 0x%04X %9.9s-%u [%s] ",
Descriptor, Element->Size, Element->Module,
@@ -633,27 +704,31 @@ AcpiUtDumpAllocations (
switch (ACPI_GET_DESCRIPTOR_TYPE (Descriptor))
{
case ACPI_DESC_TYPE_OPERAND:
- if (Element->Size == sizeof (ACPI_DESC_TYPE_OPERAND))
+
+ if (Element->Size == sizeof (ACPI_OPERAND_OBJECT))
{
DescriptorType = ACPI_DESC_TYPE_OPERAND;
}
break;
case ACPI_DESC_TYPE_PARSER:
- if (Element->Size == sizeof (ACPI_DESC_TYPE_PARSER))
+
+ if (Element->Size == sizeof (ACPI_PARSE_OBJECT))
{
DescriptorType = ACPI_DESC_TYPE_PARSER;
}
break;
case ACPI_DESC_TYPE_NAMED:
- if (Element->Size == sizeof (ACPI_DESC_TYPE_NAMED))
+
+ if (Element->Size == sizeof (ACPI_NAMESPACE_NODE))
{
DescriptorType = ACPI_DESC_TYPE_NAMED;
}
break;
default:
+
break;
}
@@ -662,22 +737,26 @@ AcpiUtDumpAllocations (
switch (DescriptorType)
{
case ACPI_DESC_TYPE_OPERAND:
+
AcpiOsPrintf ("%12.12s RefCount 0x%04X\n",
AcpiUtGetTypeName (Descriptor->Object.Common.Type),
Descriptor->Object.Common.ReferenceCount);
break;
case ACPI_DESC_TYPE_PARSER:
+
AcpiOsPrintf ("AmlOpcode 0x%04hX\n",
Descriptor->Op.Asl.AmlOpcode);
break;
case ACPI_DESC_TYPE_NAMED:
+
AcpiOsPrintf ("%4.4s\n",
AcpiUtGetNodeName (&Descriptor->Node));
break;
default:
+
AcpiOsPrintf ( "\n");
break;
}
@@ -696,7 +775,7 @@ AcpiUtDumpAllocations (
if (!NumOutstanding)
{
- ACPI_INFO ((AE_INFO, "No outstanding allocations"));
+ ACPI_INFO (("No outstanding allocations"));
}
else
{
@@ -708,4 +787,3 @@ AcpiUtDumpAllocations (
}
#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
-
diff --git a/usr/src/uts/intel/io/acpica/utilities/utuuid.c b/usr/src/uts/intel/io/acpica/utilities/utuuid.c
new file mode 100644
index 0000000000..df48912a23
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utuuid.c
@@ -0,0 +1,103 @@
+/******************************************************************************
+ *
+ * Module Name: utuuid -- UUID support functions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+
+#define _COMPONENT ACPI_COMPILER
+ ACPI_MODULE_NAME ("utuuid")
+
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
+/*
+ * UUID support functions.
+ *
+ * This table is used to convert an input UUID ascii string to a 16 byte
+ * buffer and the reverse. The table maps a UUID buffer index 0-15 to
+ * the index within the 36-byte UUID string where the associated 2-byte
+ * hex value can be found.
+ *
+ * 36-byte UUID strings are of the form:
+ * aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ * Where aa-pp are one byte hex numbers, made up of two hex digits
+ *
+ * Note: This table is basically the inverse of the string-to-offset table
+ * found in the ACPI spec in the description of the ToUUID macro.
+ */
+const UINT8 AcpiGbl_MapToUuidOffset[UUID_BUFFER_LENGTH] =
+{
+ 6,4,2,0,11,9,16,14,19,21,24,26,28,30,32,34
+};
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtConvertStringToUuid
+ *
+ * PARAMETERS: InString - 36-byte formatted UUID string
+ * UuidBuffer - Where the 16-byte UUID buffer is returned
+ *
+ * RETURN: None. Output data is returned in the UuidBuffer
+ *
+ * DESCRIPTION: Convert a 36-byte formatted UUID string to 16-byte UUID buffer
+ *
+ ******************************************************************************/
+
+void
+AcpiUtConvertStringToUuid (
+ char *InString,
+ UINT8 *UuidBuffer)
+{
+ UINT32 i;
+
+
+ for (i = 0; i < UUID_BUFFER_LENGTH; i++)
+ {
+ UuidBuffer[i] = (AcpiUtAsciiCharToHex (
+ InString[AcpiGbl_MapToUuidOffset[i]]) << 4);
+
+ UuidBuffer[i] |= AcpiUtAsciiCharToHex (
+ InString[AcpiGbl_MapToUuidOffset[i] + 1]);
+ }
+}
+#endif
diff --git a/usr/src/uts/intel/io/acpica/utilities/utxface.c b/usr/src/uts/intel/io/acpica/utilities/utxface.c
index 63b081b758..a8452c3295 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utxface.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utxface.c
@@ -1,11 +1,11 @@
/******************************************************************************
*
- * Module Name: utxface - External interfaces for "global" ACPI functions
+ * Module Name: utxface - External interfaces, miscellaneous utility functions
*
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,320 +41,16 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
-#define __UTXFACE_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
-#include "acevents.h"
-#include "acnamesp.h"
#include "acdebug.h"
-#include "actables.h"
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME ("utxface")
-#ifndef ACPI_ASL_COMPILER
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiInitializeSubsystem
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Initializes all global variables. This is the first function
- * called, so any early initialization belongs here.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiInitializeSubsystem (
- void)
-{
- ACPI_STATUS Status;
-
-
- ACPI_FUNCTION_TRACE (AcpiInitializeSubsystem);
-
-
- AcpiGbl_StartupFlags = ACPI_SUBSYSTEM_INITIALIZE;
- ACPI_DEBUG_EXEC (AcpiUtInitStackPtrTrace ());
-
- /* Initialize the OS-Dependent layer */
-
- Status = AcpiOsInitialize ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During OSL initialization"));
- return_ACPI_STATUS (Status);
- }
-
- /* Initialize all globals used by the subsystem */
-
- Status = AcpiUtInitGlobals ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During initialization of globals"));
- return_ACPI_STATUS (Status);
- }
-
- /* Create the default mutex objects */
-
- Status = AcpiUtMutexInitialize ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Global Mutex creation"));
- return_ACPI_STATUS (Status);
- }
-
- /*
- * Initialize the namespace manager and
- * the root of the namespace tree
- */
- Status = AcpiNsRootInitialize ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During Namespace initialization"));
- return_ACPI_STATUS (Status);
- }
-
- /* Initialize the global OSI interfaces list with the static names */
-
- Status = AcpiUtInitializeInterfaces ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status, "During OSI interfaces initialization"));
- return_ACPI_STATUS (Status);
- }
-
- /* If configured, initialize the AML debugger */
-
- ACPI_DEBUGGER_EXEC (Status = AcpiDbInitialize ());
- return_ACPI_STATUS (Status);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiInitializeSubsystem)
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiEnableSubsystem
- *
- * PARAMETERS: Flags - Init/enable Options
- *
- * RETURN: Status
- *
- * DESCRIPTION: Completes the subsystem initialization including hardware.
- * Puts system into ACPI mode if it isn't already.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiEnableSubsystem (
- UINT32 Flags)
-{
- ACPI_STATUS Status = AE_OK;
-
-
- ACPI_FUNCTION_TRACE (AcpiEnableSubsystem);
-
-
- /* Enable ACPI mode */
-
- if (!(Flags & ACPI_NO_ACPI_ENABLE))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Going into ACPI mode\n"));
-
- AcpiGbl_OriginalMode = AcpiHwGetMode();
-
- Status = AcpiEnable ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_WARNING ((AE_INFO, "AcpiEnable failed"));
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Obtain a permanent mapping for the FACS. This is required for the
- * Global Lock and the Firmware Waking Vector
- */
- Status = AcpiTbInitializeFacs ();
- if (ACPI_FAILURE (Status))
- {
- ACPI_WARNING ((AE_INFO, "Could not map the FACS table"));
- return_ACPI_STATUS (Status);
- }
-
- /*
- * Install the default OpRegion handlers. These are installed unless
- * other handlers have already been installed via the
- * InstallAddressSpaceHandler interface.
- */
- if (!(Flags & ACPI_NO_ADDRESS_SPACE_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Installing default address space handlers\n"));
-
- Status = AcpiEvInstallRegionHandlers ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Initialize ACPI Event handling (Fixed and General Purpose)
- *
- * Note1: We must have the hardware and events initialized before we can
- * execute any control methods safely. Any control method can require
- * ACPI hardware support, so the hardware must be fully initialized before
- * any method execution!
- *
- * Note2: Fixed events are initialized and enabled here. GPEs are
- * initialized, but cannot be enabled until after the hardware is
- * completely initialized (SCI and GlobalLock activated) and the various
- * initialization control methods are run (_REG, _STA, _INI) on the
- * entire namespace.
- */
- if (!(Flags & ACPI_NO_EVENT_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Initializing ACPI events\n"));
-
- Status = AcpiEvInitializeEvents ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Install the SCI handler and Global Lock handler. This completes the
- * hardware initialization.
- */
- if (!(Flags & ACPI_NO_HANDLER_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Installing SCI/GL handlers\n"));
-
- Status = AcpiEvInstallXruptHandlers ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- return_ACPI_STATUS (Status);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiEnableSubsystem)
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiInitializeObjects
- *
- * PARAMETERS: Flags - Init/enable Options
- *
- * RETURN: Status
- *
- * DESCRIPTION: Completes namespace initialization by initializing device
- * objects and executing AML code for Regions, buffers, etc.
- *
- ******************************************************************************/
-
-ACPI_STATUS
-AcpiInitializeObjects (
- UINT32 Flags)
-{
- ACPI_STATUS Status = AE_OK;
-
-
- ACPI_FUNCTION_TRACE (AcpiInitializeObjects);
-
-
- /*
- * Run all _REG methods
- *
- * Note: Any objects accessed by the _REG methods will be automatically
- * initialized, even if they contain executable AML (see the call to
- * AcpiNsInitializeObjects below).
- */
- if (!(Flags & ACPI_NO_ADDRESS_SPACE_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Executing _REG OpRegion methods\n"));
-
- Status = AcpiEvInitializeOpRegions ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Execute any module-level code that was detected during the table load
- * phase. Although illegal since ACPI 2.0, there are many machines that
- * contain this type of code. Each block of detected executable AML code
- * outside of any control method is wrapped with a temporary control
- * method object and placed on a global list. The methods on this list
- * are executed below.
- */
- AcpiNsExecModuleCodeList ();
-
- /*
- * Initialize the objects that remain uninitialized. This runs the
- * executable AML that may be part of the declaration of these objects:
- * OperationRegions, BufferFields, Buffers, and Packages.
- */
- if (!(Flags & ACPI_NO_OBJECT_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Completing Initialization of ACPI Objects\n"));
-
- Status = AcpiNsInitializeObjects ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Initialize all device objects in the namespace. This runs the device
- * _STA and _INI methods.
- */
- if (!(Flags & ACPI_NO_DEVICE_INIT))
- {
- ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
- "[Init] Initializing ACPI Devices\n"));
-
- Status = AcpiNsInitializeDevices ();
- if (ACPI_FAILURE (Status))
- {
- return_ACPI_STATUS (Status);
- }
- }
-
- /*
- * Empty the caches (delete the cached objects) on the assumption that
- * the table load filled them up more than they will be at runtime --
- * thus wasting non-paged memory.
- */
- Status = AcpiPurgeCachedObjects ();
-
- AcpiGbl_StartupFlags |= ACPI_INITIALIZED_OK;
- return_ACPI_STATUS (Status);
-}
-
-ACPI_EXPORT_SYMBOL (AcpiInitializeObjects)
-
-
-#endif
-
/*******************************************************************************
*
* FUNCTION: AcpiTerminate
@@ -377,24 +73,6 @@ AcpiTerminate (
ACPI_FUNCTION_TRACE (AcpiTerminate);
- /* Just exit if subsystem is already shutdown */
-
- if (AcpiGbl_Shutdown)
- {
- ACPI_ERROR ((AE_INFO, "ACPI Subsystem is already terminated"));
- return_ACPI_STATUS (AE_OK);
- }
-
- /* Subsystem appears active, go ahead and shut it down */
-
- AcpiGbl_Shutdown = TRUE;
- AcpiGbl_StartupFlags = 0;
- ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n"));
-
- /* Terminate the AML Debugger if present */
-
- ACPI_DEBUGGER_EXEC (AcpiGbl_DbTerminateThreads = TRUE);
-
/* Shutdown and free all resources */
AcpiUtSubsystemShutdown ();
@@ -403,21 +81,13 @@ AcpiTerminate (
AcpiUtMutexTerminate ();
-
-#ifdef ACPI_DEBUGGER
-
- /* Shut down the debugger */
-
- AcpiDbTerminate ();
-#endif
-
/* Now we can shutdown the OS-dependent layer */
Status = AcpiOsTerminate ();
return_ACPI_STATUS (Status);
}
-ACPI_EXPORT_SYMBOL (AcpiTerminate)
+ACPI_EXPORT_SYMBOL_INIT (AcpiTerminate)
#ifndef ACPI_ASL_COMPILER
@@ -463,7 +133,7 @@ ACPI_EXPORT_SYMBOL (AcpiSubsystemStatus)
* RETURN: Status - the status of the call
*
* DESCRIPTION: This function is called to get information about the current
- * state of the ACPI subsystem. It will return system information
+ * state of the ACPI subsystem. It will return system information
* in the OutBuffer.
*
* If the function fails an appropriate status will be returned
@@ -502,7 +172,6 @@ AcpiGetSystemInfo (
* Populate the return buffer
*/
InfoPtr = (ACPI_SYSTEM_INFO *) OutBuffer->Pointer;
-
InfoPtr->AcpiCaVersion = ACPI_CA_VERSION;
/* System flags (ACPI capabilities) */
@@ -567,14 +236,12 @@ AcpiGetStatistics (
Stats->SciCount = AcpiSciCount;
Stats->GpeCount = AcpiGpeCount;
- ACPI_MEMCPY (Stats->FixedEventCount, AcpiFixedEventCount,
+ memcpy (Stats->FixedEventCount, AcpiFixedEventCount,
sizeof (AcpiFixedEventCount));
-
/* Other counters */
Stats->MethodCount = AcpiMethodCount;
-
return_ACPI_STATUS (AE_OK);
}
@@ -613,7 +280,7 @@ AcpiInstallInitializationHandler (
}
AcpiGbl_InitHandler = Handler;
- return AE_OK;
+ return (AE_OK);
}
ACPI_EXPORT_SYMBOL (AcpiInstallInitializationHandler)
@@ -637,10 +304,12 @@ AcpiPurgeCachedObjects (
{
ACPI_FUNCTION_TRACE (AcpiPurgeCachedObjects);
+
(void) AcpiOsPurgeCache (AcpiGbl_StateCache);
(void) AcpiOsPurgeCache (AcpiGbl_OperandCache);
(void) AcpiOsPurgeCache (AcpiGbl_PsNodeCache);
(void) AcpiOsPurgeCache (AcpiGbl_PsNodeExtCache);
+
return_ACPI_STATUS (AE_OK);
}
@@ -669,12 +338,16 @@ AcpiInstallInterface (
/* Parameter validation */
- if (!InterfaceName || (ACPI_STRLEN (InterfaceName) == 0))
+ if (!InterfaceName || (strlen (InterfaceName) == 0))
{
return (AE_BAD_PARAMETER);
}
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
/* Check if the interface name is already in the global list */
@@ -730,12 +403,16 @@ AcpiRemoveInterface (
/* Parameter validation */
- if (!InterfaceName || (ACPI_STRLEN (InterfaceName) == 0))
+ if (!InterfaceName || (strlen (InterfaceName) == 0))
{
return (AE_BAD_PARAMETER);
}
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
Status = AcpiUtRemoveInterface (InterfaceName);
@@ -765,10 +442,14 @@ ACPI_STATUS
AcpiInstallInterfaceHandler (
ACPI_INTERFACE_HANDLER Handler)
{
- ACPI_STATUS Status = AE_OK;
+ ACPI_STATUS Status;
- (void) AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
if (Handler && AcpiGbl_InterfaceHandler)
{
@@ -785,5 +466,178 @@ AcpiInstallInterfaceHandler (
ACPI_EXPORT_SYMBOL (AcpiInstallInterfaceHandler)
+
+/*****************************************************************************
+ *
+ * FUNCTION: AcpiUpdateInterfaces
+ *
+ * PARAMETERS: Action - Actions to be performed during the
+ * update
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor
+ * string or/and feature group strings.
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+AcpiUpdateInterfaces (
+ UINT8 Action)
+{
+ ACPI_STATUS Status;
+
+
+ Status = AcpiOsAcquireMutex (AcpiGbl_OsiMutex, ACPI_WAIT_FOREVER);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ Status = AcpiUtUpdateInterfaces (Action);
+
+ AcpiOsReleaseMutex (AcpiGbl_OsiMutex);
+ return (Status);
+}
+
+
+/*****************************************************************************
+ *
+ * FUNCTION: AcpiCheckAddressRange
+ *
+ * PARAMETERS: SpaceId - Address space ID
+ * Address - Start address
+ * Length - Length
+ * Warn - TRUE if warning on overlap desired
+ *
+ * RETURN: Count of the number of conflicts detected.
+ *
+ * DESCRIPTION: Check if the input address range overlaps any of the
+ * ASL operation region address ranges.
+ *
+ ****************************************************************************/
+
+UINT32
+AcpiCheckAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ ACPI_SIZE Length,
+ BOOLEAN Warn)
+{
+ UINT32 Overlaps;
+ ACPI_STATUS Status;
+
+
+ Status = AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE (Status))
+ {
+ return (0);
+ }
+
+ Overlaps = AcpiUtCheckAddressRange (SpaceId, Address,
+ (UINT32) Length, Warn);
+
+ (void) AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE);
+ return (Overlaps);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiCheckAddressRange)
+
#endif /* !ACPI_ASL_COMPILER */
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiDecodePldBuffer
+ *
+ * PARAMETERS: InBuffer - Buffer returned by _PLD method
+ * Length - Length of the InBuffer
+ * ReturnBuffer - Where the decode buffer is returned
+ *
+ * RETURN: Status and the decoded _PLD buffer. User must deallocate
+ * the buffer via ACPI_FREE.
+ *
+ * DESCRIPTION: Decode the bit-packed buffer returned by the _PLD method into
+ * a local struct that is much more useful to an ACPI driver.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiDecodePldBuffer (
+ UINT8 *InBuffer,
+ ACPI_SIZE Length,
+ ACPI_PLD_INFO **ReturnBuffer)
+{
+ ACPI_PLD_INFO *PldInfo;
+ UINT32 *Buffer = ACPI_CAST_PTR (UINT32, InBuffer);
+ UINT32 Dword;
+
+
+ /* Parameter validation */
+
+ if (!InBuffer || !ReturnBuffer || (Length < ACPI_PLD_REV1_BUFFER_SIZE))
+ {
+ return (AE_BAD_PARAMETER);
+ }
+
+ PldInfo = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_PLD_INFO));
+ if (!PldInfo)
+ {
+ return (AE_NO_MEMORY);
+ }
+
+ /* First 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32 (&Dword, &Buffer[0]);
+ PldInfo->Revision = ACPI_PLD_GET_REVISION (&Dword);
+ PldInfo->IgnoreColor = ACPI_PLD_GET_IGNORE_COLOR (&Dword);
+ PldInfo->Red = ACPI_PLD_GET_RED (&Dword);
+ PldInfo->Green = ACPI_PLD_GET_GREEN (&Dword);
+ PldInfo->Blue = ACPI_PLD_GET_BLUE (&Dword);
+
+ /* Second 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32 (&Dword, &Buffer[1]);
+ PldInfo->Width = ACPI_PLD_GET_WIDTH (&Dword);
+ PldInfo->Height = ACPI_PLD_GET_HEIGHT(&Dword);
+
+ /* Third 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32 (&Dword, &Buffer[2]);
+ PldInfo->UserVisible = ACPI_PLD_GET_USER_VISIBLE (&Dword);
+ PldInfo->Dock = ACPI_PLD_GET_DOCK (&Dword);
+ PldInfo->Lid = ACPI_PLD_GET_LID (&Dword);
+ PldInfo->Panel = ACPI_PLD_GET_PANEL (&Dword);
+ PldInfo->VerticalPosition = ACPI_PLD_GET_VERTICAL (&Dword);
+ PldInfo->HorizontalPosition = ACPI_PLD_GET_HORIZONTAL (&Dword);
+ PldInfo->Shape = ACPI_PLD_GET_SHAPE (&Dword);
+ PldInfo->GroupOrientation = ACPI_PLD_GET_ORIENTATION (&Dword);
+ PldInfo->GroupToken = ACPI_PLD_GET_TOKEN (&Dword);
+ PldInfo->GroupPosition = ACPI_PLD_GET_POSITION (&Dword);
+ PldInfo->Bay = ACPI_PLD_GET_BAY (&Dword);
+
+ /* Fourth 32-bit DWord */
+
+ ACPI_MOVE_32_TO_32 (&Dword, &Buffer[3]);
+ PldInfo->Ejectable = ACPI_PLD_GET_EJECTABLE (&Dword);
+ PldInfo->OspmEjectRequired = ACPI_PLD_GET_OSPM_EJECT (&Dword);
+ PldInfo->CabinetNumber = ACPI_PLD_GET_CABINET (&Dword);
+ PldInfo->CardCageNumber = ACPI_PLD_GET_CARD_CAGE (&Dword);
+ PldInfo->Reference = ACPI_PLD_GET_REFERENCE (&Dword);
+ PldInfo->Rotation = ACPI_PLD_GET_ROTATION (&Dword);
+ PldInfo->Order = ACPI_PLD_GET_ORDER (&Dword);
+
+ if (Length >= ACPI_PLD_REV2_BUFFER_SIZE)
+ {
+ /* Fifth 32-bit DWord (Revision 2 of _PLD) */
+
+ ACPI_MOVE_32_TO_32 (&Dword, &Buffer[4]);
+ PldInfo->VerticalOffset = ACPI_PLD_GET_VERT_OFFSET (&Dword);
+ PldInfo->HorizontalOffset = ACPI_PLD_GET_HORIZ_OFFSET (&Dword);
+ }
+
+ *ReturnBuffer = PldInfo;
+ return (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL (AcpiDecodePldBuffer)
diff --git a/usr/src/uts/intel/io/acpica/utilities/utxferror.c b/usr/src/uts/intel/io/acpica/utilities/utxferror.c
index a371308d6b..e79ffc94f3 100644
--- a/usr/src/uts/intel/io/acpica/utilities/utxferror.c
+++ b/usr/src/uts/intel/io/acpica/utilities/utxferror.c
@@ -5,7 +5,7 @@
******************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,11 +41,10 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-#define __UTXFERROR_C__
+#define EXPORT_ACPI_INTERFACES
#include "acpi.h"
#include "accommon.h"
-#include "acnamesp.h"
#define _COMPONENT ACPI_UTILITIES
@@ -54,45 +53,9 @@
/*
* This module is used for the in-kernel ACPICA as well as the ACPICA
* tools/applications.
- *
- * For the iASL compiler case, the output is redirected to stderr so that
- * any of the various ACPI errors and warnings do not appear in the output
- * files, for either the compiler or disassembler portions of the tool.
- */
-#ifdef ACPI_ASL_COMPILER
-#include <stdio.h>
-
-extern FILE *AcpiGbl_OutputFile;
-
-#define ACPI_MSG_REDIRECT_BEGIN \
- FILE *OutputFile = AcpiGbl_OutputFile; \
- AcpiOsRedirectOutput (stderr);
-
-#define ACPI_MSG_REDIRECT_END \
- AcpiOsRedirectOutput (OutputFile);
-
-#else
-/*
- * non-iASL case - no redirection, nothing to do
- */
-#define ACPI_MSG_REDIRECT_BEGIN
-#define ACPI_MSG_REDIRECT_END
-#endif
-
-/*
- * Common message prefixes
- */
-#define ACPI_MSG_ERROR "ACPI Error: "
-#define ACPI_MSG_EXCEPTION "ACPI Exception: "
-#define ACPI_MSG_WARNING "ACPI Warning: "
-#define ACPI_MSG_INFO "ACPI: "
-
-/*
- * Common message suffix
*/
-#define ACPI_MSG_SUFFIX \
- AcpiOsPrintf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, ModuleName, LineNumber)
+#ifndef ACPI_NO_ERROR_MESSAGES /* Entire module */
/*******************************************************************************
*
@@ -160,7 +123,19 @@ AcpiException (
ACPI_MSG_REDIRECT_BEGIN;
- AcpiOsPrintf (ACPI_MSG_EXCEPTION "%s, ", AcpiFormatException (Status));
+
+ /* For AE_OK, just print the message */
+
+ if (ACPI_SUCCESS (Status))
+ {
+ AcpiOsPrintf (ACPI_MSG_EXCEPTION);
+
+ }
+ else
+ {
+ AcpiOsPrintf (ACPI_MSG_EXCEPTION "%s, ",
+ AcpiFormatException (Status));
+ }
va_start (ArgList, Format);
AcpiOsVprintf (Format, ArgList);
@@ -230,8 +205,6 @@ ACPI_EXPORT_SYMBOL (AcpiWarning)
void ACPI_INTERNAL_VAR_XFACE
AcpiInfo (
- const char *ModuleName,
- UINT32 LineNumber,
const char *Format,
...)
{
@@ -252,227 +225,81 @@ AcpiInfo (
ACPI_EXPORT_SYMBOL (AcpiInfo)
-/*
- * The remainder of this module contains internal error functions that may
- * be configured out.
- */
-#if !defined (ACPI_NO_ERROR_MESSAGES) && !defined (ACPI_BIN_APP)
-
/*******************************************************************************
*
- * FUNCTION: AcpiUtPredefinedWarning
+ * FUNCTION: AcpiBiosError
*
- * PARAMETERS: ModuleName - Caller's module name (for error output)
- * LineNumber - Caller's line number (for error output)
- * Pathname - Full pathname to the node
- * NodeFlags - From Namespace node for the method/object
- * Format - Printf format string + additional args
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Format - Printf format string + additional args
*
* RETURN: None
*
- * DESCRIPTION: Warnings for the predefined validation module. Messages are
- * only emitted the first time a problem with a particular
- * method/object is detected. This prevents a flood of error
- * messages for methods that are repeatedly evaluated.
+ * DESCRIPTION: Print "ACPI Firmware Error" message with module/line/version
+ * info
*
******************************************************************************/
void ACPI_INTERNAL_VAR_XFACE
-AcpiUtPredefinedWarning (
+AcpiBiosError (
const char *ModuleName,
UINT32 LineNumber,
- char *Pathname,
- UINT8 NodeFlags,
const char *Format,
...)
{
va_list ArgList;
- /*
- * Warning messages for this method/object will be disabled after the
- * first time a validation fails or an object is successfully repaired.
- */
- if (NodeFlags & ANOBJ_EVALUATED)
- {
- return;
- }
-
- AcpiOsPrintf (ACPI_MSG_WARNING "For %s: ", Pathname);
+ ACPI_MSG_REDIRECT_BEGIN;
+ AcpiOsPrintf (ACPI_MSG_BIOS_ERROR);
va_start (ArgList, Format);
AcpiOsVprintf (Format, ArgList);
ACPI_MSG_SUFFIX;
va_end (ArgList);
+
+ ACPI_MSG_REDIRECT_END;
}
+ACPI_EXPORT_SYMBOL (AcpiBiosError)
+
/*******************************************************************************
*
- * FUNCTION: AcpiUtPredefinedInfo
+ * FUNCTION: AcpiBiosWarning
*
- * PARAMETERS: ModuleName - Caller's module name (for error output)
- * LineNumber - Caller's line number (for error output)
- * Pathname - Full pathname to the node
- * NodeFlags - From Namespace node for the method/object
- * Format - Printf format string + additional args
+ * PARAMETERS: ModuleName - Caller's module name (for error output)
+ * LineNumber - Caller's line number (for error output)
+ * Format - Printf format string + additional args
*
* RETURN: None
*
- * DESCRIPTION: Info messages for the predefined validation module. Messages
- * are only emitted the first time a problem with a particular
- * method/object is detected. This prevents a flood of
- * messages for methods that are repeatedly evaluated.
+ * DESCRIPTION: Print "ACPI Firmware Warning" message with module/line/version
+ * info
*
******************************************************************************/
void ACPI_INTERNAL_VAR_XFACE
-AcpiUtPredefinedInfo (
+AcpiBiosWarning (
const char *ModuleName,
UINT32 LineNumber,
- char *Pathname,
- UINT8 NodeFlags,
const char *Format,
...)
{
va_list ArgList;
- /*
- * Warning messages for this method/object will be disabled after the
- * first time a validation fails or an object is successfully repaired.
- */
- if (NodeFlags & ANOBJ_EVALUATED)
- {
- return;
- }
-
- AcpiOsPrintf (ACPI_MSG_INFO "For %s: ", Pathname);
+ ACPI_MSG_REDIRECT_BEGIN;
+ AcpiOsPrintf (ACPI_MSG_BIOS_WARNING);
va_start (ArgList, Format);
AcpiOsVprintf (Format, ArgList);
ACPI_MSG_SUFFIX;
va_end (ArgList);
-}
-
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtNamespaceError
- *
- * PARAMETERS: ModuleName - Caller's module name (for error output)
- * LineNumber - Caller's line number (for error output)
- * InternalName - Name or path of the namespace node
- * LookupStatus - Exception code from NS lookup
- *
- * RETURN: None
- *
- * DESCRIPTION: Print error message with the full pathname for the NS node.
- *
- ******************************************************************************/
-void
-AcpiUtNamespaceError (
- const char *ModuleName,
- UINT32 LineNumber,
- const char *InternalName,
- ACPI_STATUS LookupStatus)
-{
- ACPI_STATUS Status;
- UINT32 BadName;
- char *Name = NULL;
-
-
- ACPI_MSG_REDIRECT_BEGIN;
- AcpiOsPrintf (ACPI_MSG_ERROR);
-
- if (LookupStatus == AE_BAD_CHARACTER)
- {
- /* There is a non-ascii character in the name */
-
- ACPI_MOVE_32_TO_32 (&BadName, ACPI_CAST_PTR (UINT32, InternalName));
- AcpiOsPrintf ("[0x%4.4X] (NON-ASCII)", BadName);
- }
- else
- {
- /* Convert path to external format */
-
- Status = AcpiNsExternalizeName (ACPI_UINT32_MAX,
- InternalName, NULL, &Name);
-
- /* Print target name */
-
- if (ACPI_SUCCESS (Status))
- {
- AcpiOsPrintf ("[%s]", Name);
- }
- else
- {
- AcpiOsPrintf ("[COULD NOT EXTERNALIZE NAME]");
- }
-
- if (Name)
- {
- ACPI_FREE (Name);
- }
- }
-
- AcpiOsPrintf (" Namespace lookup failure, %s",
- AcpiFormatException (LookupStatus));
-
- ACPI_MSG_SUFFIX;
ACPI_MSG_REDIRECT_END;
}
-
-/*******************************************************************************
- *
- * FUNCTION: AcpiUtMethodError
- *
- * PARAMETERS: ModuleName - Caller's module name (for error output)
- * LineNumber - Caller's line number (for error output)
- * Message - Error message to use on failure
- * PrefixNode - Prefix relative to the path
- * Path - Path to the node (optional)
- * MethodStatus - Execution status
- *
- * RETURN: None
- *
- * DESCRIPTION: Print error message with the full pathname for the method.
- *
- ******************************************************************************/
-
-void
-AcpiUtMethodError (
- const char *ModuleName,
- UINT32 LineNumber,
- const char *Message,
- ACPI_NAMESPACE_NODE *PrefixNode,
- const char *Path,
- ACPI_STATUS MethodStatus)
-{
- ACPI_STATUS Status;
- ACPI_NAMESPACE_NODE *Node = PrefixNode;
-
-
- ACPI_MSG_REDIRECT_BEGIN;
- AcpiOsPrintf (ACPI_MSG_ERROR);
-
- if (Path)
- {
- Status = AcpiNsGetNode (PrefixNode, Path, ACPI_NS_NO_UPSEARCH,
- &Node);
- if (ACPI_FAILURE (Status))
- {
- AcpiOsPrintf ("[Could not get node by pathname]");
- }
- }
-
- AcpiNsPrintNodePathname (Node, Message);
- AcpiOsPrintf (", %s", AcpiFormatException (MethodStatus));
-
- ACPI_MSG_SUFFIX;
- ACPI_MSG_REDIRECT_END;
-}
+ACPI_EXPORT_SYMBOL (AcpiBiosWarning)
#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/usr/src/uts/intel/io/acpica/utilities/utxfinit.c b/usr/src/uts/intel/io/acpica/utilities/utxfinit.c
new file mode 100644
index 0000000000..2a518bc82d
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utxfinit.c
@@ -0,0 +1,340 @@
+/******************************************************************************
+ *
+ * Module Name: utxfinit - External interfaces for ACPICA initialization
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define EXPORT_ACPI_INTERFACES
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acevents.h"
+#include "acnamesp.h"
+#include "acdebug.h"
+#include "actables.h"
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utxfinit")
+
+/* For AcpiExec only */
+void
+AeDoObjectOverrides (
+ void);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiInitializeSubsystem
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initializes all global variables. This is the first function
+ * called, so any early initialization belongs here.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiInitializeSubsystem (
+ void)
+{
+ ACPI_STATUS Status;
+
+
+ ACPI_FUNCTION_TRACE (AcpiInitializeSubsystem);
+
+
+ AcpiGbl_StartupFlags = ACPI_SUBSYSTEM_INITIALIZE;
+ ACPI_DEBUG_EXEC (AcpiUtInitStackPtrTrace ());
+
+ /* Initialize the OS-Dependent layer */
+
+ Status = AcpiOsInitialize ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During OSL initialization"));
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Initialize all globals used by the subsystem */
+
+ Status = AcpiUtInitGlobals ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During initialization of globals"));
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Create the default mutex objects */
+
+ Status = AcpiUtMutexInitialize ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During Global Mutex creation"));
+ return_ACPI_STATUS (Status);
+ }
+
+ /*
+ * Initialize the namespace manager and
+ * the root of the namespace tree
+ */
+ Status = AcpiNsRootInitialize ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During Namespace initialization"));
+ return_ACPI_STATUS (Status);
+ }
+
+ /* Initialize the global OSI interfaces list with the static names */
+
+ Status = AcpiUtInitializeInterfaces ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status, "During OSI interfaces initialization"));
+ return_ACPI_STATUS (Status);
+ }
+
+ return_ACPI_STATUS (AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL_INIT (AcpiInitializeSubsystem)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEnableSubsystem
+ *
+ * PARAMETERS: Flags - Init/enable Options
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Completes the subsystem initialization including hardware.
+ * Puts system into ACPI mode if it isn't already.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEnableSubsystem (
+ UINT32 Flags)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (AcpiEnableSubsystem);
+
+
+ /*
+ * The early initialization phase is complete. The namespace is loaded,
+ * and we can now support address spaces other than Memory, I/O, and
+ * PCI_Config.
+ */
+ AcpiGbl_EarlyInitialization = FALSE;
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /* Enable ACPI mode */
+
+ if (!(Flags & ACPI_NO_ACPI_ENABLE))
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Going into ACPI mode\n"));
+
+ AcpiGbl_OriginalMode = AcpiHwGetMode();
+
+ Status = AcpiEnable ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_WARNING ((AE_INFO, "AcpiEnable failed"));
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ /*
+ * Obtain a permanent mapping for the FACS. This is required for the
+ * Global Lock and the Firmware Waking Vector
+ */
+ if (!(Flags & ACPI_NO_FACS_INIT))
+ {
+ Status = AcpiTbInitializeFacs ();
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_WARNING ((AE_INFO, "Could not map the FACS table"));
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ /*
+ * Initialize ACPI Event handling (Fixed and General Purpose)
+ *
+ * Note1: We must have the hardware and events initialized before we can
+ * execute any control methods safely. Any control method can require
+ * ACPI hardware support, so the hardware must be fully initialized before
+ * any method execution!
+ *
+ * Note2: Fixed events are initialized and enabled here. GPEs are
+ * initialized, but cannot be enabled until after the hardware is
+ * completely initialized (SCI and GlobalLock activated) and the various
+ * initialization control methods are run (_REG, _STA, _INI) on the
+ * entire namespace.
+ */
+ if (!(Flags & ACPI_NO_EVENT_INIT))
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[Init] Initializing ACPI events\n"));
+
+ Status = AcpiEvInitializeEvents ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ /*
+ * Install the SCI handler and Global Lock handler. This completes the
+ * hardware initialization.
+ */
+ if (!(Flags & ACPI_NO_HANDLER_INIT))
+ {
+ ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
+ "[Init] Installing SCI/GL handlers\n"));
+
+ Status = AcpiEvInstallXruptHandlers ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT (AcpiEnableSubsystem)
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiInitializeObjects
+ *
+ * PARAMETERS: Flags - Init/enable Options
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Completes namespace initialization by initializing device
+ * objects and executing AML code for Regions, buffers, etc.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiInitializeObjects (
+ UINT32 Flags)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (AcpiInitializeObjects);
+
+
+#ifdef ACPI_EXEC_APP
+ /*
+ * This call implements the "initialization file" option for AcpiExec.
+ * This is the precise point that we want to perform the overrides.
+ */
+ AeDoObjectOverrides ();
+#endif
+
+ /*
+ * Execute any module-level code that was detected during the table load
+ * phase. Although illegal since ACPI 2.0, there are many machines that
+ * contain this type of code. Each block of detected executable AML code
+ * outside of any control method is wrapped with a temporary control
+ * method object and placed on a global list. The methods on this list
+ * are executed below.
+ *
+ * This case executes the module-level code for all tables only after
+ * all of the tables have been loaded. It is a legacy option and is
+ * not compatible with other ACPI implementations. See AcpiNsLoadTable.
+ */
+ if (AcpiGbl_GroupModuleLevelCode)
+ {
+ AcpiNsExecModuleCodeList ();
+
+ /*
+ * Initialize the objects that remain uninitialized. This
+ * runs the executable AML that may be part of the
+ * declaration of these objects:
+ * OperationRegions, BufferFields, Buffers, and Packages.
+ */
+ if (!(Flags & ACPI_NO_OBJECT_INIT))
+ {
+ Status = AcpiNsInitializeObjects ();
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+ }
+
+ /*
+ * Initialize all device/region objects in the namespace. This runs
+ * the device _STA and _INI methods and region _REG methods.
+ */
+ if (!(Flags & (ACPI_NO_DEVICE_INIT | ACPI_NO_ADDRESS_SPACE_INIT)))
+ {
+ Status = AcpiNsInitializeDevices (Flags);
+ if (ACPI_FAILURE (Status))
+ {
+ return_ACPI_STATUS (Status);
+ }
+ }
+
+ /*
+ * Empty the caches (delete the cached objects) on the assumption that
+ * the table load filled them up more than they will be at runtime --
+ * thus wasting non-paged memory.
+ */
+ Status = AcpiPurgeCachedObjects ();
+
+ AcpiGbl_StartupFlags |= ACPI_INITIALIZED_OK;
+ return_ACPI_STATUS (Status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT (AcpiInitializeObjects)
diff --git a/usr/src/uts/intel/io/acpica/utilities/utxfmutex.c b/usr/src/uts/intel/io/acpica/utilities/utxfmutex.c
new file mode 100644
index 0000000000..f47fbe5546
--- /dev/null
+++ b/usr/src/uts/intel/io/acpica/utilities/utxfmutex.c
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ *
+ * Module Name: utxfmutex - external AML mutex access functions
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpi.h"
+#include "accommon.h"
+#include "acnamesp.h"
+
+
+#define _COMPONENT ACPI_UTILITIES
+ ACPI_MODULE_NAME ("utxfmutex")
+
+
+/* Local prototypes */
+
+static ACPI_STATUS
+AcpiUtGetMutexObject (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname,
+ ACPI_OPERAND_OBJECT **RetObj);
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiUtGetMutexObject
+ *
+ * PARAMETERS: Handle - Mutex or prefix handle (optional)
+ * Pathname - Mutex pathname (optional)
+ * RetObj - Where the mutex object is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get an AML mutex object. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+
+static ACPI_STATUS
+AcpiUtGetMutexObject (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname,
+ ACPI_OPERAND_OBJECT **RetObj)
+{
+ ACPI_NAMESPACE_NODE *MutexNode;
+ ACPI_OPERAND_OBJECT *MutexObj;
+ ACPI_STATUS Status;
+
+
+ /* Parameter validation */
+
+ if (!RetObj || (!Handle && !Pathname))
+ {
+ return (AE_BAD_PARAMETER);
+ }
+
+ /* Get a the namespace node for the mutex */
+
+ MutexNode = Handle;
+ if (Pathname != NULL)
+ {
+ Status = AcpiGetHandle (
+ Handle, Pathname, ACPI_CAST_PTR (ACPI_HANDLE, &MutexNode));
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+ }
+
+ /* Ensure that we actually have a Mutex object */
+
+ if (!MutexNode ||
+ (MutexNode->Type != ACPI_TYPE_MUTEX))
+ {
+ return (AE_TYPE);
+ }
+
+ /* Get the low-level mutex object */
+
+ MutexObj = AcpiNsGetAttachedObject (MutexNode);
+ if (!MutexObj)
+ {
+ return (AE_NULL_OBJECT);
+ }
+
+ *RetObj = MutexObj;
+ return (AE_OK);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiAcquireMutex
+ *
+ * PARAMETERS: Handle - Mutex or prefix handle (optional)
+ * Pathname - Mutex pathname (optional)
+ * Timeout - Max time to wait for the lock (millisec)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Acquire an AML mutex. This is a device driver interface to
+ * AML mutex objects, and allows for transaction locking between
+ * drivers and AML code. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiAcquireMutex (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname,
+ UINT16 Timeout)
+{
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *MutexObj;
+
+
+ /* Get the low-level mutex associated with Handle:Pathname */
+
+ Status = AcpiUtGetMutexObject (Handle, Pathname, &MutexObj);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /* Acquire the OS mutex */
+
+ Status = AcpiOsAcquireMutex (MutexObj->Mutex.OsMutex, Timeout);
+ return (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiReleaseMutex
+ *
+ * PARAMETERS: Handle - Mutex or prefix handle (optional)
+ * Pathname - Mutex pathname (optional)
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Release an AML mutex. This is a device driver interface to
+ * AML mutex objects, and allows for transaction locking between
+ * drivers and AML code. The mutex node is pointed to by
+ * Handle:Pathname. Either Handle or Pathname can be NULL, but
+ * not both.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiReleaseMutex (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname)
+{
+ ACPI_STATUS Status;
+ ACPI_OPERAND_OBJECT *MutexObj;
+
+
+ /* Get the low-level mutex associated with Handle:Pathname */
+
+ Status = AcpiUtGetMutexObject (Handle, Pathname, &MutexObj);
+ if (ACPI_FAILURE (Status))
+ {
+ return (Status);
+ }
+
+ /* Release the OS mutex */
+
+ AcpiOsReleaseMutex (MutexObj->Mutex.OsMutex);
+ return (AE_OK);
+}
diff --git a/usr/src/uts/intel/io/dktp/dcdev/dadk.c b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
index 35f97482b8..f74a0d4137 100644
--- a/usr/src/uts/intel/io/dktp/dcdev/dadk.c
+++ b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
*/
/*
@@ -170,6 +171,8 @@ static int dadk_debug = DGEOM;
#endif /* DADK_DEBUG */
+#define ONE_MIN ((longlong_t)60 * NANOSEC)
+
static int dadk_check_media_time = 3000000; /* 3 Second State Check */
static int dadk_dk_maxphys = 0x80000;
@@ -1376,6 +1379,47 @@ static struct dadkio_derr dadk_errtab[] = {
{COMMAND_DONE_ERROR, GDA_FATAL}, /* 23 DERR_RESV */
};
+/*
+ * A bad disk can result in a large number of errors spewed to the log.
+ * This can in turn lead to /var/adm/messages filling up the file system on
+ * a machine with a small root or /var file system.
+ *
+ * Instead of logging every error, if we're seeing repeated errors on a disk
+ * only log them periodically.
+ */
+static void
+dadk_logerr(struct dadk *dadkp, struct cmpkt *pktp, char *label,
+ int severity, daddr_t blkno, daddr_t err_blkno,
+ char **cmdvec, char **senvec)
+{
+ hrtime_t now;
+
+ now = gethrtime();
+ if ((now - dadkp->dad_last_log) < ONE_MIN) {
+ atomic_add_32(&dadkp->dad_err_cnt, 1);
+ return;
+ }
+
+ if (dadkp->dad_err_cnt > 0) {
+ dev_info_t *dev = dadkp->dad_sd->sd_dev;
+ char name[256], buf[256];
+
+ if (dev)
+ (void) snprintf(name, sizeof (name), "%s (%s%d)",
+ ddi_pathname(dev, buf), label,
+ ddi_get_instance(dev));
+ else
+ (void) strlcpy(name, label, sizeof (name));
+ cmn_err(CE_WARN, "%s: %d additional unlogged errors\n",
+ name, dadkp->dad_err_cnt);
+ }
+
+ gda_errmsg(dadkp->dad_sd, pktp, label, severity, blkno, err_blkno,
+ cmdvec, senvec);
+ dadkp->dad_err_cnt = 0;
+ dadkp->dad_last_log = now;
+}
+
static int
dadk_chkerr(struct cmpkt *pktp)
{
@@ -1462,7 +1506,7 @@ dadk_chkerr(struct cmpkt *pktp)
return (COMMAND_DONE);
}
if (pktp->cp_passthru == NULL) {
- gda_errmsg(dadkp->dad_sd, pktp, dadk_name,
+ dadk_logerr(dadkp, pktp, dadk_name,
dadk_errtab[scb].d_severity, pktp->cp_srtsec,
err_blkno, dadk_cmds, dadk_sense);
}
@@ -1519,7 +1563,7 @@ dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp)
if (rwcmdp->flags & DADKIO_FLAG_SILENT)
return;
- gda_errmsg(dadkp->dad_sd, pktp, dadk_name, dadk_errtab[scb].d_severity,
+ dadk_logerr(dadkp, pktp, dadk_name, dadk_errtab[scb].d_severity,
rwcmdp->blkaddr, rwcmdp->status.failed_blk,
dadk_cmds, dadk_sense);
}
diff --git a/usr/src/uts/intel/io/ipmi/ipmivars.h b/usr/src/uts/intel/io/ipmi/ipmivars.h
index 7fd819cd3d..f547d6f043 100644
--- a/usr/src/uts/intel/io/ipmi/ipmivars.h
+++ b/usr/src/uts/intel/io/ipmi/ipmivars.h
@@ -78,6 +78,7 @@ struct ipmi_request {
#define SMIC_CTL_STS 1
#define SMIC_FLAGS 2
+struct ipmi_softc;
#define IPMI_BUSY 0x1
#define IPMI_CLOSING 0x2
diff --git a/usr/src/uts/intel/io/pci/pci_resource.c b/usr/src/uts/intel/io/pci/pci_resource.c
index 21ca552d91..a088deb456 100644
--- a/usr/src/uts/intel/io/pci/pci_resource.c
+++ b/usr/src/uts/intel/io/pci/pci_resource.c
@@ -22,6 +22,8 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
+ * Copyright 2016 Joyent, Inc.
+ *
* pci_resource.c -- routines to retrieve available bus resources from
* the MP Spec. Table and Hotplug Resource Table
*/
@@ -321,23 +323,23 @@ acpi_wr_cb(ACPI_RESOURCE *rp, void *context)
break;
case ACPI_RESOURCE_TYPE_ADDRESS16:
- if (rp->Data.Address16.AddressLength == 0)
+ if (rp->Data.Address16.Address.AddressLength == 0)
break;
acpi_cb_cnt++;
memlist_insert(rlistpp(rp->Data.Address16.ResourceType,
rp->Data.Address16.Info.TypeSpecific, bus),
- rp->Data.Address16.Minimum,
- rp->Data.Address16.AddressLength);
+ rp->Data.Address16.Address.Minimum,
+ rp->Data.Address16.Address.AddressLength);
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
- if (rp->Data.Address32.AddressLength == 0)
+ if (rp->Data.Address32.Address.AddressLength == 0)
break;
acpi_cb_cnt++;
memlist_insert(rlistpp(rp->Data.Address32.ResourceType,
rp->Data.Address32.Info.TypeSpecific, bus),
- rp->Data.Address32.Minimum,
- rp->Data.Address32.AddressLength);
+ rp->Data.Address32.Address.Minimum,
+ rp->Data.Address32.Address.AddressLength);
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
diff --git a/usr/src/uts/intel/io/vmxnet/buildNumber.h b/usr/src/uts/intel/io/vmxnet/buildNumber.h
new file mode 100644
index 0000000000..97f18a3cbc
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/buildNumber.h
@@ -0,0 +1,12 @@
+#define BUILD_NUMBER \
+ "build-425873"
+#define BUILD_NUMBER_NUMERIC \
+ 425873
+#define BUILD_NUMBER_NUMERIC_STRING \
+ "425873"
+#define PRODUCT_BUILD_NUMBER \
+ "product-build-6261"
+#define PRODUCT_BUILD_NUMBER_NUMERIC \
+ 6261
+#define PRODUCT_BUILD_NUMBER_NUMERIC_STRING \
+ "6261"
diff --git a/usr/src/uts/intel/io/vmxnet/includeCheck.h b/usr/src/uts/intel/io/vmxnet/includeCheck.h
new file mode 100644
index 0000000000..c414d6daf5
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/includeCheck.h
@@ -0,0 +1,159 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no 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 Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of VMware Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission of VMware Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * includeCheck.h --
+ *
+ * Restrict include file use.
+ *
+ * In every .h file, define one or more of these
+ *
+ * INCLUDE_ALLOW_VMX
+ * INCLUDE_ALLOW_USERLEVEL
+ * INCLUDE_ALLOW_VMCORE
+ * INCLUDE_ALLOW_MODULE
+ * INCLUDE_ALLOW_VMKERNEL
+ * INCLUDE_ALLOW_DISTRIBUTE
+ * INCLUDE_ALLOW_VMK_MODULE
+ * INCLUDE_ALLOW_VMKDRIVERS
+ * INCLUDE_ALLOW_VMIROM
+ *
+ * Then include this file.
+ *
+ * Any file that has INCLUDE_ALLOW_DISTRIBUTE defined will potentially
+ * be distributed in source form along with GPLed code. Ensure
+ * that this is acceptable.
+ */
+
+
+/*
+ * Declare a VMCORE-only variable to help classify object
+ * files. The variable goes in the common block and does
+ * not create multiple definition link-time conflicts.
+ */
+
+#if defined VMCORE && defined VMX86_DEVEL && defined VMX86_DEBUG && \
+ defined linux && !defined MODULE && \
+ !defined COMPILED_WITH_VMCORE
+#define COMPILED_WITH_VMCORE compiled_with_vmcore
+#ifdef ASM
+ .comm compiled_with_vmcore, 0
+#else
+ asm(".comm compiled_with_vmcore, 0");
+#endif /* ASM */
+#endif
+
+
+#if defined VMCORE && \
+ !(defined VMX86_VMX || defined VMM || \
+ defined MONITOR_APP || defined VMMON)
+#error "Makefile problem: VMCORE without VMX86_VMX or \
+ VMM or MONITOR_APP or MODULE."
+#endif
+
+#if defined VMCORE && !defined INCLUDE_ALLOW_VMCORE
+#error "The surrounding include file is not allowed in vmcore."
+#endif
+#undef INCLUDE_ALLOW_VMCORE
+
+#if defined VMX86_VMX && !defined VMCORE && \
+ !(defined INCLUDE_ALLOW_VMX || defined INCLUDE_ALLOW_USERLEVEL)
+#error "The surrounding include file is not allowed in the VMX."
+#endif
+#undef INCLUDE_ALLOW_VMX
+
+#if defined USERLEVEL && !defined VMX86_VMX && !defined VMCORE && \
+ !defined INCLUDE_ALLOW_USERLEVEL
+#error "The surrounding include file is not allowed at userlevel."
+#endif
+#undef INCLUDE_ALLOW_USERLEVEL
+
+#if defined MODULE && !defined VMKERNEL_MODULE && \
+ !defined VMMON && !defined INCLUDE_ALLOW_MODULE
+#error "The surrounding include file is not allowed in driver modules."
+#endif
+#undef INCLUDE_ALLOW_MODULE
+
+#if defined VMMON && !defined INCLUDE_ALLOW_VMMON
+#error "The surrounding include file is not allowed in vmmon."
+#endif
+#undef INCLUDE_ALLOW_VMMON
+
+#if defined VMKERNEL && !defined INCLUDE_ALLOW_VMKERNEL
+#error "The surrounding include file is not allowed in the vmkernel."
+#endif
+#undef INCLUDE_ALLOW_VMKERNEL
+
+#if defined GPLED_CODE && !defined INCLUDE_ALLOW_DISTRIBUTE
+#error "The surrounding include file is not allowed in GPL code."
+#endif
+#undef INCLUDE_ALLOW_DISTRIBUTE
+
+#if defined VMKERNEL_MODULE && !defined VMKERNEL && \
+ !defined INCLUDE_ALLOW_VMK_MODULE && !defined INCLUDE_ALLOW_VMKDRIVERS
+#error "The surrounding include file is not allowed in vmkernel modules."
+#endif
+#undef INCLUDE_ALLOW_VMK_MODULE
+#undef INCLUDE_ALLOW_VMKDRIVERS
+
+#if defined VMIROM && ! defined INCLUDE_ALLOW_VMIROM
+#error "The surrounding include file is not allowed in vmirom."
+#endif
+#undef INCLUDE_ALLOW_VMIROM
diff --git a/usr/src/uts/intel/io/vmxnet/net.h b/usr/src/uts/intel/io/vmxnet/net.h
new file mode 100644
index 0000000000..41b6eb1d14
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/net.h
@@ -0,0 +1,220 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/************************************************************
+ *
+ * net.h
+ *
+ * This file should contain all network global defines.
+ * No vlance/vmxnet/vnet/vmknet specific stuff should be
+ * put here only defines used/usable by all network code.
+ * --gustav
+ *
+ ************************************************************/
+
+#ifndef VMWARE_DEVICES_NET_H
+#define VMWARE_DEVICES_NET_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMCORE
+
+#include "includeCheck.h"
+#include "vm_device_version.h"
+
+#ifdef VMCORE
+#include "config.h"
+#include "str.h"
+#include "strutil.h"
+#endif
+
+#define ETHERNET_MTU 1518
+#define ETH_MIN_FRAME_LEN 60
+
+#ifndef ETHER_ADDR_LEN
+#define ETHER_ADDR_LEN 6 /* length of MAC address */
+#endif
+#define ETH_HEADER_LEN 14 /* length of Ethernet header */
+#define IP_ADDR_LEN 4 /* length of IPv4 address */
+#define IP_HEADER_LEN 20 /* minimum length of IPv4 header */
+
+#define ETHER_MAX_QUEUED_PACKET 1600
+
+
+/*
+ * State's that a NIC can be in currently we only use this
+ * in VLance but if we implement/emulate new adapters that
+ * we also want to be able to morph a new corresponding
+ * state should be added.
+ */
+
+#define LANCE_CHIP 0x2934
+#define VMXNET_CHIP 0x4392
+
+/*
+ * Size of reserved IO space needed by the LANCE adapter and
+ * the VMXNET adapter. If you add more ports to Vmxnet than
+ * there is reserved space you must bump VMXNET_CHIP_IO_RESV_SIZE.
+ * The sizes must be powers of 2.
+ */
+
+#define LANCE_CHIP_IO_RESV_SIZE 0x20
+#define VMXNET_CHIP_IO_RESV_SIZE 0x40
+
+#define MORPH_PORT_SIZE 4
+
+#ifdef VMCORE
+typedef struct Net_AdapterCount {
+ uint8 vlance;
+ uint8 vmxnet2;
+ uint8 vmxnet3;
+ uint8 e1000;
+ uint8 e1000e;
+} Net_AdapterCount;
+#endif
+
+#ifdef USERLEVEL
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Net_AddAddrToLADRF --
+ *
+ * Given a MAC address, sets the corresponding bit in the LANCE style
+ * Logical Address Filter 'ladrf'.
+ * The caller should have initialized the ladrf to all 0's, as this
+ * function only ORs on a bit in the array.
+ * 'addr' is presumed to be ETHER_ADDR_LEN in size;
+ * 'ladrf' is presumed to point to a 64-bit vector.
+ *
+ * Derived from a long history of derivations, originally inspired by
+ * sample code from the AMD "Network Products: Ethernet Controllers 1998
+ * Data Book, Book 2", pages 1-53..1-55.
+ *
+ * Returns:
+ * None.
+ *
+ * Side effects:
+ * Updates 'ladrf'.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE void
+Net_AddAddrToLadrf(const uint8 *addr, // IN: pointer to MAC address
+ uint8 *ladrf) // IN/OUT: pointer to ladrf
+{
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+
+ uint16 hashcode;
+ int32 crc = 0xffffffff; /* init CRC for each address */
+ int32 j;
+ int32 bit;
+ int32 byte;
+
+ ASSERT(addr);
+ ASSERT(ladrf);
+
+ for (byte = 0; byte < ETHER_ADDR_LEN; byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addr++, j = 0;
+ j < 8;
+ j++, bit >>= 1) {
+ crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ?
+ CRC_POLYNOMIAL_BE : 0);
+ }
+ }
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j = 0; j < 5; j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc>>=1) & 1);
+ }
+
+ ladrf[hashcode >> 3] |= 1 << (hashcode & 0x07);
+}
+#endif // USERLEVEL
+
+#ifdef VMCORE
+/*
+ *----------------------------------------------------------------------
+ *
+ * Net_GetNumAdapters --
+ *
+ * Returns the number of each type of network adapter configured in this
+ * VM.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE void
+Net_GetNumAdapters(Net_AdapterCount *counts)
+{
+ uint32 i;
+
+ counts->vlance = 0;
+ counts->vmxnet2 = 0;
+ counts->vmxnet3 = 0;
+ counts->e1000 = 0;
+ counts->e1000e = 0;
+
+ for (i = 0; i < MAX_ETHERNET_CARDS; i++) {
+ char* adapterStr;
+
+ if (!Config_GetBool(FALSE, "ethernet%d.present", i)) {
+ continue;
+ }
+ adapterStr = Config_GetString("vlance", "ethernet%d.virtualDev", i);
+ if (Str_Strcasecmp(adapterStr, "vmxnet3") == 0) {
+ counts->vmxnet3++;
+ } else if (Str_Strcasecmp(adapterStr, "vlance") == 0) {
+ counts->vlance++;
+ } else if (Str_Strcasecmp(adapterStr, "vmxnet") == 0) {
+ counts->vmxnet2++;
+ } else if (Str_Strcasecmp(adapterStr, "e1000") == 0) {
+ counts->e1000++;
+ } else if (Str_Strcasecmp(adapterStr, "e1000e") == 0) {
+ counts->e1000e++;
+ } else {
+ LOG_ONCE(("%s: unknown adapter: %s\n", __FUNCTION__, adapterStr));
+ }
+ free(adapterStr);
+ }
+}
+
+#endif // VMCORE
+
+#endif // VMWARE_DEVICES_NET_H
diff --git a/usr/src/uts/intel/io/vmxnet/net_sg.h b/usr/src/uts/intel/io/vmxnet/net_sg.h
new file mode 100644
index 0000000000..f6c30fb2b5
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/net_sg.h
@@ -0,0 +1,84 @@
+/*********************************************************
+ * Copyright (C) 2000 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * net_sg.h --
+ *
+ * Network packet scatter gather structure.
+ */
+
+
+#ifndef _NET_SG_H
+#define _NET_SG_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#define NET_SG_DEFAULT_LENGTH 16
+
+/*
+ * A single scatter-gather element for a network packet.
+ * The address is split into low and high to save space.
+ * If we make it 64 bits then Windows pads things out such that
+ * we lose a lot of space for each scatter gather array.
+ * This adds up when you have embedded scatter-gather
+ * arrays for transmit and receive ring buffers.
+ */
+typedef struct NetSG_Elem {
+ uint32 addrLow;
+ uint16 addrHi;
+ uint16 length;
+} NetSG_Elem;
+
+typedef enum NetSG_AddrType {
+ NET_SG_MACH_ADDR,
+ NET_SG_PHYS_ADDR,
+ NET_SG_VIRT_ADDR,
+} NetSG_AddrType;
+
+typedef struct NetSG_Array {
+ uint16 addrType;
+ uint16 length;
+ NetSG_Elem sg[NET_SG_DEFAULT_LENGTH];
+} NetSG_Array;
+
+#define NET_SG_SIZE(len) (sizeof(NetSG_Array) + (len - NET_SG_DEFAULT_LENGTH) * sizeof(NetSG_Elem))
+
+#define NET_SG_MAKE_PA(elem) (PA)QWORD(elem.addrHi, elem.addrLow)
+#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow)
+
+#endif
diff --git a/usr/src/uts/intel/io/vmxnet/vm_basic_types.h b/usr/src/uts/intel/io/vmxnet/vm_basic_types.h
new file mode 100644
index 0000000000..adeac1b708
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vm_basic_types.h
@@ -0,0 +1,1037 @@
+/*********************************************************
+ * Copyright (C) 1998-2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no 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 Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of VMware Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission of VMware Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ *
+ * vm_basic_types.h --
+ *
+ * basic data types.
+ */
+
+
+#ifndef _VM_BASIC_TYPES_H_
+#define _VM_BASIC_TYPES_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMKDRIVERS
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+/* STRICT ANSI means the Xserver build and X defines Bool differently. */
+#if !defined(_XTYPEDEF_BOOL) && \
+ (!defined(__STRICT_ANSI__) || defined(__FreeBSD__) || defined(__MINGW32__))
+#define _XTYPEDEF_BOOL
+typedef char Bool;
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define IsBool(x) (((x) & ~1) == 0)
+#define IsBool2(x, y) ((((x) | (y)) & ~1) == 0)
+
+/*
+ * Macros __i386__ and __ia64 are intrinsically defined by GCC
+ */
+#if defined _MSC_VER && defined _M_X64
+# define __x86_64__
+#elif defined _MSC_VER && defined _M_IX86
+# define __i386__
+#endif
+
+#ifdef __i386__
+#define VM_I386
+#endif
+
+#ifdef __x86_64__
+#define VM_X86_64
+#define VM_I386
+#define vm_x86_64 (1)
+#else
+#define vm_x86_64 (0)
+#endif
+
+
+#ifdef _MSC_VER
+
+#pragma warning (3 :4505) // unreferenced local function
+#pragma warning (disable :4018) // signed/unsigned mismatch
+#pragma warning (disable :4761) // integral size mismatch in argument; conversion supplied
+#pragma warning (disable :4305) // truncation from 'const int' to 'short'
+#pragma warning (disable :4244) // conversion from 'unsigned short' to 'unsigned char'
+#pragma warning (disable :4267) // truncation of 'size_t'
+#pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned
+#pragma warning (disable :4142) // benign redefinition of type
+
+#endif
+
+#if defined(__APPLE__) || defined(HAVE_STDINT_H)
+
+/*
+ * TODO: This is a C99 standard header. We should be able to test for
+ * #if __STDC_VERSION__ >= 199901L, but that breaks the Netware build
+ * (which doesn't have stdint.h).
+ */
+
+#include <stdint.h>
+
+typedef uint64_t uint64;
+typedef int64_t int64;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint8_t uint8;
+typedef int8_t int8;
+
+/*
+ * Note: C does not specify whether char is signed or unsigned, and
+ * both gcc and msvc implement processor-specific signedness. With
+ * three types:
+ * typeof(char) != typeof(signed char) != typeof(unsigned char)
+ *
+ * Be careful here, because gcc (4.0.1 and others) likes to warn about
+ * conversions between signed char * and char *.
+ */
+
+#else /* !HAVE_STDINT_H */
+
+#ifdef _MSC_VER
+
+typedef unsigned __int64 uint64;
+typedef signed __int64 int64;
+
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+/* The Xserver source compiles with -ansi -pendantic */
+# if !defined(__STRICT_ANSI__) || defined(__FreeBSD__)
+# if defined(VM_X86_64)
+typedef unsigned long uint64;
+typedef long int64;
+# else
+typedef unsigned long long uint64;
+typedef long long int64;
+# endif
+# endif
+#else
+# error - Need compiler define for int64/uint64
+#endif /* _MSC_VER */
+
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+
+typedef int int32;
+typedef short int16;
+typedef signed char int8;
+
+#endif /* HAVE_STDINT_H */
+
+/*
+ * FreeBSD (for the tools build) unconditionally defines these in
+ * sys/inttypes.h so don't redefine them if this file has already
+ * been included. [greg]
+ *
+ * This applies to Solaris as well.
+ */
+
+/*
+ * Before trying to do the includes based on OS defines, see if we can use
+ * feature-based defines to get as much functionality as possible
+ */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_INTTYPES_H
+#include <sys/inttypes.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h> /* For __FreeBSD_version */
+#endif
+
+#if !defined(USING_AUTOCONF)
+# if defined(__FreeBSD__) || defined(sun)
+# ifdef KLD_MODULE
+# include <sys/types.h>
+# else
+# if __FreeBSD_version >= 500043
+# if !defined(VMKERNEL)
+# include <inttypes.h>
+# endif
+# include <sys/types.h>
+# else
+# include <sys/inttypes.h>
+# endif
+# endif
+# elif defined __APPLE__
+# if KERNEL
+# include <sys/unistd.h>
+# include <sys/types.h> /* mostly for size_t */
+# include <stdint.h>
+# else
+# include <unistd.h>
+# include <inttypes.h>
+# include <stdlib.h>
+# include <stdint.h>
+# endif
+# else
+# if !defined(__intptr_t_defined) && !defined(intptr_t)
+# ifdef VM_I386
+# define __intptr_t_defined
+# ifdef VM_X86_64
+typedef int64 intptr_t;
+# else
+typedef int32 intptr_t;
+# endif
+# elif defined(__arm__)
+typedef int32 intptr_t;
+# endif
+# endif
+
+# ifndef _STDINT_H
+# ifdef VM_I386
+# ifdef VM_X86_64
+typedef uint64 uintptr_t;
+# else
+typedef uint32 uintptr_t;
+# endif
+# elif defined(__arm__)
+typedef uint32 uintptr_t;
+# endif
+# endif
+# endif
+#endif
+
+
+/*
+ * Time
+ * XXX These should be cleaned up. -- edward
+ */
+
+typedef int64 VmTimeType; /* Time in microseconds */
+typedef int64 VmTimeRealClock; /* Real clock kept in microseconds */
+typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */
+
+/*
+ * Printf format specifiers for size_t and 64-bit number.
+ * Use them like this:
+ * printf("%"FMT64"d\n", big);
+ *
+ * FMTH is for handles/fds.
+ */
+
+#ifdef _MSC_VER
+ #define FMT64 "I64"
+ #ifdef VM_X86_64
+ #define FMTSZ "I64"
+ #define FMTPD "I64"
+ #define FMTH "I64"
+ #else
+ #define FMTSZ "I"
+ #define FMTPD "I"
+ #define FMTH "I"
+ #endif
+#elif defined __APPLE__
+ /* Mac OS hosts use the same formatters for 32- and 64-bit. */
+ #define FMT64 "ll"
+ #if KERNEL
+ #define FMTSZ "l"
+ #else
+ #define FMTSZ "z"
+ #endif
+ #define FMTPD "l"
+ #define FMTH ""
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+ #define FMTH ""
+ #if defined(N_PLAT_NLM) || defined(sun) || \
+ (defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) < 5))
+ /*
+ * Why (__FreeBSD__ + 0)? See bug 141008.
+ * Yes, we really need to test both (__FreeBSD__ + 0) and
+ * ((__FreeBSD__ + 0) < 5). No, we can't remove "+ 0" from
+ * ((__FreeBSD__ + 0) < 5).
+ */
+ #ifdef VM_X86_64
+ #define FMTSZ "l"
+ #define FMTPD "l"
+ #else
+ #define FMTSZ ""
+ #define FMTPD ""
+ #endif
+ #elif defined(__linux__) \
+ || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) \
+ || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) \
+ || (defined(_POSIX2_VERSION) && _POSIX2_VERSION >= 200112L)
+ /* BSD, Linux */
+ #define FMTSZ "z"
+
+ #if defined(VM_X86_64)
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #else
+ /* Systems with a pre-C99 libc */
+ #define FMTSZ "Z"
+ #ifdef VM_X86_64
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #endif
+ #ifdef VM_X86_64
+ #define FMT64 "l"
+ #elif defined(sun) || defined(__FreeBSD__)
+ #define FMT64 "ll"
+ #else
+ #define FMT64 "L"
+ #endif
+#else
+ #error - Need compiler define for FMT64 and FMTSZ
+#endif
+
+/*
+ * Suffix for 64-bit constants. Use it like this:
+ * CONST64(0x7fffffffffffffff) for signed or
+ * CONST64U(0x7fffffffffffffff) for unsigned.
+ *
+ * 2004.08.30(thutt):
+ * The vmcore/asm64/gen* programs are compiled as 32-bit
+ * applications, but must handle 64 bit constants. If the
+ * 64-bit-constant defining macros are already defined, the
+ * definition will not be overwritten.
+ */
+
+#if !defined(CONST64) || !defined(CONST64U)
+#ifdef _MSC_VER
+#define CONST64(c) c##I64
+#define CONST64U(c) c##uI64
+#elif defined __APPLE__
+#define CONST64(c) c##LL
+#define CONST64U(c) c##uLL
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+#ifdef VM_X86_64
+#define CONST64(c) c##L
+#define CONST64U(c) c##uL
+#else
+#define CONST64(c) c##LL
+#define CONST64U(c) c##uLL
+#endif
+#else
+#error - Need compiler define for CONST64
+#endif
+#endif
+
+/*
+ * Use CONST3264/CONST3264U if you want a constant to be
+ * treated as a 32-bit number on 32-bit compiles and
+ * a 64-bit number on 64-bit compiles. Useful in the case
+ * of shifts, like (CONST3264U(1) << x), where x could be
+ * more than 31 on a 64-bit compile.
+ */
+
+#ifdef VM_X86_64
+ #define CONST3264(a) CONST64(a)
+ #define CONST3264U(a) CONST64U(a)
+#else
+ #define CONST3264(a) (a)
+ #define CONST3264U(a) (a)
+#endif
+
+#define MIN_INT8 ((int8)0x80)
+#define MAX_INT8 ((int8)0x7f)
+
+#define MIN_UINT8 ((uint8)0)
+#define MAX_UINT8 ((uint8)0xff)
+
+#define MIN_INT16 ((int16)0x8000)
+#define MAX_INT16 ((int16)0x7fff)
+
+#define MIN_UINT16 ((uint16)0)
+#define MAX_UINT16 ((uint16)0xffff)
+
+#define MIN_INT32 ((int32)0x80000000)
+#define MAX_INT32 ((int32)0x7fffffff)
+
+#define MIN_UINT32 ((uint32)0)
+#define MAX_UINT32 ((uint32)0xffffffff)
+
+#define MIN_INT64 (CONST64(0x8000000000000000))
+#define MAX_INT64 (CONST64(0x7fffffffffffffff))
+
+#define MIN_UINT64 (CONST64U(0))
+#define MAX_UINT64 (CONST64U(0xffffffffffffffff))
+
+typedef uint8 *TCA; /* Pointer into TC (usually). */
+
+/*
+ * Type big enough to hold an integer between 0..100
+ */
+typedef uint8 Percent;
+#define AsPercent(v) ((Percent)(v))
+#define CHOOSE_PERCENT AsPercent(101)
+
+
+typedef uintptr_t VA;
+typedef uintptr_t VPN;
+
+typedef uint64 PA;
+typedef uint32 PPN;
+
+typedef uint64 PhysMemOff;
+typedef uint64 PhysMemSize;
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 BA;
+#endif
+typedef uint32 BPN;
+typedef uint32 PageNum;
+typedef unsigned MemHandle;
+typedef int32 World_ID;
+
+/* !! do not alter the definition of INVALID_WORLD_ID without ensuring
+ * that the values defined in both bora/public/vm_basic_types.h and
+ * lib/vprobe/vm_basic_types.h are the same. Additionally, the definition
+ * of VMK_INVALID_WORLD_ID in vmkapi_world.h also must be defined with
+ * the same value
+ */
+
+#define INVALID_WORLD_ID ((World_ID)0)
+
+typedef World_ID User_CartelID;
+#define INVALID_CARTEL_ID INVALID_WORLD_ID
+
+typedef User_CartelID User_SessionID;
+#define INVALID_SESSION_ID INVALID_CARTEL_ID
+
+typedef User_CartelID User_CartelGroupID;
+#define INVALID_CARTELGROUP_ID INVALID_CARTEL_ID
+
+typedef uint32 Worldlet_ID;
+#define INVALID_WORLDLET_ID ((Worldlet_ID)-1)
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 MA;
+typedef uint32 MPN;
+#endif
+
+/*
+ * This type should be used for variables that contain sector
+ * position/quantity.
+ */
+typedef uint64 SectorType;
+
+/*
+ * Linear address
+ */
+
+typedef uintptr_t LA;
+typedef uintptr_t LPN;
+#define LA_2_LPN(_la) ((_la) >> PAGE_SHIFT)
+#define LPN_2_LA(_lpn) ((_lpn) << PAGE_SHIFT)
+
+#define LAST_LPN ((((LA) 1) << (8 * sizeof(LA) - PAGE_SHIFT)) - 1)
+#define LAST_LPN32 ((((LA32)1) << (8 * sizeof(LA32) - PAGE_SHIFT)) - 1)
+#define LAST_LPN64 ((((LA64)1) << (8 * sizeof(LA64) - PAGE_SHIFT)) - 1)
+
+/* Valid bits in a LPN. */
+#define LPN_MASK LAST_LPN
+#define LPN_MASK32 LAST_LPN32
+#define LPN_MASK64 LAST_LPN64
+
+/*
+ * On 64 bit platform, address and page number types default
+ * to 64 bit. When we need to represent a 32 bit address, we use
+ * types defined below.
+ *
+ * On 32 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint32 VA32;
+typedef uint32 VPN32;
+typedef uint32 LA32;
+typedef uint32 LPN32;
+typedef uint32 PA32;
+typedef uint32 PPN32;
+typedef uint32 MA32;
+typedef uint32 MPN32;
+
+/*
+ * On 64 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint64 VA64;
+typedef uint64 VPN64;
+typedef uint64 LA64;
+typedef uint64 LPN64;
+typedef uint64 PA64;
+typedef uint64 PPN64;
+typedef uint64 MA64;
+typedef uint64 MPN64;
+
+/*
+ * VA typedefs for user world apps.
+ */
+typedef VA32 UserVA32;
+typedef VA64 UserVA64;
+typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */
+typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */
+typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */
+#ifdef VMKERNEL
+typedef UserVA64 UserVA;
+#else
+typedef void * UserVA;
+#endif
+
+
+/*
+ * Maximal possible PPN value (errors too) that PhysMem can handle.
+ * Must be at least as large as MAX_PPN which is the maximum PPN
+ * for any region other than buserror.
+ */
+#define PHYSMEM_MAX_PPN ((PPN)0xffffffff)
+#define MAX_PPN ((PPN)0x1fffffff) /* Maximal observable PPN value. */
+#define INVALID_PPN ((PPN)0xffffffff)
+
+#define INVALID_BPN ((BPN)0x1fffffff)
+
+#define RESERVED_MPN ((MPN) 0)
+#define INVALID_MPN ((MPN)-1)
+#define MEMREF_MPN ((MPN)-2)
+#define RELEASED_MPN ((MPN)-3)
+#define MAX_MPN ((MPN)0x7fffffff) /* 43 bits of address space. */
+
+#define INVALID_LPN ((LPN)-1)
+#define INVALID_VPN ((VPN)-1)
+#define INVALID_LPN64 ((LPN64)-1)
+#define INVALID_PAGENUM ((PageNum)-1)
+
+
+/*
+ * Format modifier for printing VA, LA, and VPN.
+ * Use them like this: Log("%#"FMTLA"x\n", laddr)
+ */
+
+#if defined(VMM) || defined(FROBOS64) || vm_x86_64 || defined __APPLE__
+# define FMTLA "l"
+# define FMTVA "l"
+# define FMTVPN "l"
+#else
+# define FMTLA ""
+# define FMTVA ""
+# define FMTVPN ""
+#endif
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+#define CONST const
+
+
+#ifndef INLINE
+# ifdef _MSC_VER
+# define INLINE __inline
+# else
+# define INLINE inline
+# endif
+#endif
+
+
+/*
+ * Annotation for data that may be exported into a DLL and used by other
+ * apps that load that DLL and import the data.
+ */
+#if defined(_WIN32) && defined(VMX86_IMPORT_DLLDATA)
+# define VMX86_EXTERN_DATA extern __declspec(dllimport)
+#else // !_WIN32
+# define VMX86_EXTERN_DATA extern
+#endif
+
+#if defined(_WIN32) && !defined(VMX86_NO_THREADS)
+#define THREADSPECIFIC __declspec(thread)
+#else
+#define THREADSPECIFIC
+#endif
+
+/*
+ * Due to the wonderful "registry redirection" feature introduced in
+ * 64-bit Windows, if you access any key under HKLM\Software in 64-bit
+ * code, you need to open/create/delete that key with
+ * VMKEY_WOW64_32KEY if you want a consistent view with 32-bit code.
+ */
+
+#ifdef _WIN32
+#ifdef _WIN64
+#define VMW_KEY_WOW64_32KEY KEY_WOW64_32KEY
+#else
+#define VMW_KEY_WOW64_32KEY 0x0
+#endif
+#endif
+
+
+/*
+ * Consider the following reasons functions are inlined:
+ *
+ * 1) inlined for performance reasons
+ * 2) inlined because it's a single-use function
+ *
+ * Functions which meet only condition 2 should be marked with this
+ * inline macro; It is not critical to be inlined (but there is a
+ * code-space & runtime savings by doing so), so when other callers
+ * are added the inline-ness should be removed.
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+/*
+ * Starting at version 3.3, gcc does not always inline functions marked
+ * 'inline' (it depends on their size). To force gcc to do so, one must use the
+ * extra __always_inline__ attribute.
+ */
+# define INLINE_SINGLE_CALLER INLINE __attribute__((__always_inline__))
+#else
+# define INLINE_SINGLE_CALLER INLINE
+#endif
+
+/*
+ * Used when a hard guaranteed of no inlining is needed. Very few
+ * instances need this since the absence of INLINE is a good hint
+ * that gcc will not do inlining.
+ */
+
+#if defined(__GNUC__) && defined(VMM)
+#define ABSOLUTELY_NOINLINE __attribute__((__noinline__))
+#endif
+
+/*
+ * Attributes placed on function declarations to tell the compiler
+ * that the function never returns.
+ */
+
+#ifdef _MSC_VER
+#define NORETURN __declspec(noreturn)
+#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#endif
+
+/*
+ * GCC 3.2 inline asm needs the + constraint for input/ouput memory operands.
+ * Older GCCs don't know about it --hpreg
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+# define VM_ASM_PLUS 1
+#else
+# define VM_ASM_PLUS 0
+#endif
+
+/*
+ * Branch prediction hints:
+ * LIKELY(exp) - Expression exp is likely TRUE.
+ * UNLIKELY(exp) - Expression exp is likely FALSE.
+ * Usage example:
+ * if (LIKELY(excCode == EXC_NONE)) {
+ * or
+ * if (UNLIKELY(REAL_MODE(vc))) {
+ *
+ * We know how to predict branches on gcc3 and later (hopefully),
+ * all others we don't so we do nothing.
+ */
+
+#if (__GNUC__ >= 3)
+/*
+ * gcc3 uses __builtin_expect() to inform the compiler of an expected value.
+ * We use this to inform the static branch predictor. The '!!' in LIKELY
+ * will convert any !=0 to a 1.
+ */
+#define LIKELY(_exp) __builtin_expect(!!(_exp), 1)
+#define UNLIKELY(_exp) __builtin_expect((_exp), 0)
+#else
+#define LIKELY(_exp) (_exp)
+#define UNLIKELY(_exp) (_exp)
+#endif
+
+/*
+ * GCC's argument checking for printf-like functions
+ * This is conditional until we have replaced all `"%x", void *'
+ * with `"0x%08x", (uint32) void *'. Note that %p prints different things
+ * on different platforms. Argument checking is enabled for the
+ * vmkernel, which has already been cleansed.
+ *
+ * fmtPos is the position of the format string argument, beginning at 1
+ * varPos is the position of the variable argument, beginning at 1
+ */
+
+#if defined(__GNUC__)
+# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos)))
+#else
+# define PRINTF_DECL(fmtPos, varPos)
+#endif
+
+#if defined(__GNUC__)
+# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos)))
+#else
+# define SCANF_DECL(fmtPos, varPos)
+#endif
+
+/*
+ * UNUSED_PARAM should surround the parameter name and type declaration,
+ * e.g. "int MyFunction(int var1, UNUSED_PARAM(int var2))"
+ *
+ */
+
+#ifndef UNUSED_PARAM
+# if defined(__GNUC__)
+# define UNUSED_PARAM(_parm) _parm __attribute__((__unused__))
+# else
+# define UNUSED_PARAM(_parm) _parm
+# endif
+#endif
+
+/*
+ * REGPARM defaults to REGPARM3; i.e., a request that gcc
+ * put the first three arguments in registers. (It is fine
+ * if the function has fewer than three arguments.) Gcc only.
+ * Syntactically, put REGPARM where you'd put INLINE or NORETURN.
+ *
+ * Note that 64-bit code already puts the first six arguments in
+ * registers, so these attributes are only useful for 32-bit code.
+ */
+
+#if defined(__GNUC__)
+# define REGPARM0 __attribute__((regparm(0)))
+# define REGPARM1 __attribute__((regparm(1)))
+# define REGPARM2 __attribute__((regparm(2)))
+# define REGPARM3 __attribute__((regparm(3)))
+# define REGPARM REGPARM3
+#else
+# define REGPARM0
+# define REGPARM1
+# define REGPARM2
+# define REGPARM3
+# define REGPARM
+#endif
+
+/*
+ * ALIGNED specifies minimum alignment in "n" bytes.
+ */
+
+#ifdef __GNUC__
+#define ALIGNED(n) __attribute__((__aligned__(n)))
+#else
+#define ALIGNED(n)
+#endif
+
+/*
+ * __func__ is a stringified function name that is part of the C99 standard. The block
+ * below defines __func__ on older systems where the compiler does not support that
+ * macro.
+ */
+#if defined(__GNUC__) \
+ && ((__GNUC__ == 2 && __GNUC_MINOR < 96) \
+ || (__GNUC__ < 2))
+# define __func__ __FUNCTION__
+#endif
+
+/*
+ * Once upon a time, this was used to silence compiler warnings that
+ * get generated when the compiler thinks that a function returns
+ * when it is marked noreturn. Don't do it. Use NOT_REACHED().
+ */
+
+#define INFINITE_LOOP() do { } while (1)
+
+/*
+ * On FreeBSD (for the tools build), size_t is typedef'd if _BSD_SIZE_T_
+ * is defined. Use the same logic here so we don't define it twice. [greg]
+ */
+#ifdef __FreeBSD__
+# ifdef _BSD_SIZE_T_
+# undef _BSD_SIZE_T_
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+# ifdef _BSD_SSIZE_T_
+# undef _BSD_SSIZE_T_
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+#else
+# ifndef _SIZE_T
+# ifdef VM_I386
+# define _SIZE_T
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# elif defined(__arm__)
+# define _SIZE_T
+ typedef uint32 size_t;
+# endif
+# endif
+
+# if !defined(FROBOS) && !defined(_SSIZE_T) && !defined(_SSIZE_T_) && \
+ !defined(ssize_t) && !defined(__ssize_t_defined) && \
+ !defined(_SSIZE_T_DECLARED)
+# ifdef VM_I386
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# elif defined(__arm__)
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+ typedef int32 ssize_t;
+# endif
+# endif
+
+#endif
+
+/*
+ * Format modifier for printing pid_t. On sun the pid_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The pid is %"FMTPID".\n", pid);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTPID "d"
+# else
+# define FMTPID "lu"
+# endif
+#else
+# define FMTPID "d"
+#endif
+
+/*
+ * Format modifier for printing uid_t. On Solaris 10 and earlier, uid_t
+ * is a ulong, but on other platforms it's an unsigned int.
+ * Use this like this: printf("The uid is %"FMTUID".\n", uid);
+ */
+#if defined(sun) && !defined(SOL11)
+# ifdef VM_X86_64
+# define FMTUID "u"
+# else
+# define FMTUID "lu"
+# endif
+#else
+# define FMTUID "u"
+#endif
+
+/*
+ * Format modifier for printing mode_t. On sun the mode_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The mode is %"FMTMODE".\n", mode);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTMODE "o"
+# else
+# define FMTMODE "lo"
+# endif
+#else
+# define FMTMODE "o"
+#endif
+
+/*
+ * Format modifier for printing time_t. Most platforms define a time_t to be
+ * a long int, but on FreeBSD (as of 5.0, it seems), the time_t is a signed
+ * size quantity. Refer to the definition of FMTSZ to see why we need silly
+ * preprocessor arithmetic.
+ * Use this like this: printf("The mode is %"FMTTIME".\n", time);
+ */
+#if defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) >= 5)
+# define FMTTIME FMTSZ"d"
+#else
+# if defined(_MSC_VER)
+# ifndef _SAFETIME_H_
+# if (_MSC_VER < 1400) || defined(_USE_32BIT_TIME_T)
+# define FMTTIME "ld"
+# else
+# define FMTTIME FMT64"d"
+# endif
+# else
+# ifndef FMTTIME
+# error "safetime.h did not define FMTTIME"
+# endif
+# endif
+# else
+# define FMTTIME "ld"
+# endif
+#endif
+
+#ifdef __APPLE__
+/*
+ * Format specifier for all these annoying types such as {S,U}Int32
+ * which are 'long' in 32-bit builds
+ * and 'int' in 64-bit builds.
+ */
+# ifdef __LP64__
+# define FMTLI ""
+# else
+# define FMTLI "l"
+# endif
+
+/*
+ * Format specifier for all these annoying types such as NS[U]Integer
+ * which are 'int' in 32-bit builds
+ * and 'long' in 64-bit builds.
+ */
+# ifdef __LP64__
+# define FMTIL "l"
+# else
+# define FMTIL ""
+# endif
+#endif
+
+
+/*
+ * Define MXSemaHandle here so both vmmon and vmx see this definition.
+ */
+
+#ifdef _WIN32
+typedef uintptr_t MXSemaHandle;
+#else
+typedef int MXSemaHandle;
+#endif
+
+/*
+ * Define type for poll device handles.
+ */
+
+typedef int64 PollDevHandle;
+
+/*
+ * Define the utf16_t type.
+ */
+
+#if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED)
+typedef wchar_t utf16_t;
+#else
+typedef uint16 utf16_t;
+#endif
+
+/*
+ * Define for point and rectangle types. Defined here so they
+ * can be used by other externally facing headers in bora/public.
+ */
+
+typedef struct VMPoint {
+ int x, y;
+} VMPoint;
+
+#if defined _WIN32 && defined USERLEVEL
+struct tagRECT;
+typedef struct tagRECT VMRect;
+#else
+typedef struct VMRect {
+ int left;
+ int top;
+ int right;
+ int bottom;
+} VMRect;
+#endif
+
+/*
+ * ranked locks "everywhere"
+ */
+
+typedef uint32 MX_Rank;
+
+#endif /* _VM_BASIC_TYPES_H_ */
diff --git a/usr/src/uts/intel/io/vmxnet/vm_device_version.h b/usr/src/uts/intel/io/vmxnet/vm_device_version.h
new file mode 100644
index 0000000000..7046594a6c
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vm_device_version.h
@@ -0,0 +1,246 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no 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 Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef VM_DEVICE_VERSION_H
+#define VM_DEVICE_VERSION_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMCORE
+#include "includeCheck.h"
+
+#ifdef _WIN32
+#ifdef __MINGW32__
+#include "initguid.h"
+#else
+#include "guiddef.h"
+#endif
+#endif
+
+/* LSILogic 53C1030 Parallel SCSI controller
+ * LSILogic SAS1068 SAS controller
+ */
+#define PCI_VENDOR_ID_LSILOGIC 0x1000
+#define PCI_DEVICE_ID_LSI53C1030 0x0030
+#define PCI_DEVICE_ID_LSISAS1068 0x0054
+
+/* Our own PCI IDs
+ * VMware SVGA II (Unified VGA)
+ * VMware SVGA (PCI Accelerator)
+ * VMware vmxnet (Idealized NIC)
+ * VMware vmxscsi (Abortive idealized SCSI controller)
+ * VMware chipset (Subsystem ID for our motherboards)
+ * VMware e1000 (Subsystem ID)
+ * VMware vmxnet3 (Uniform Pass Through NIC)
+ * VMware HD Audio codec
+ * VMware HD Audio controller
+ */
+#define PCI_VENDOR_ID_VMWARE 0x15AD
+#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
+#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710
+#define PCI_DEVICE_ID_VMWARE_NET 0x0720
+#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730
+#define PCI_DEVICE_ID_VMWARE_VMCI 0x0740
+#define PCI_DEVICE_ID_VMWARE_CHIPSET 0x1976
+#define PCI_DEVICE_ID_VMWARE_82545EM 0x0750 /* single port */
+#define PCI_DEVICE_ID_VMWARE_82546EB 0x0760 /* dual port */
+#define PCI_DEVICE_ID_VMWARE_EHCI 0x0770
+#define PCI_DEVICE_ID_VMWARE_UHCI 0x0774
+#define PCI_DEVICE_ID_VMWARE_XHCI 0x0778
+#define PCI_DEVICE_ID_VMWARE_1394 0x0780
+#define PCI_DEVICE_ID_VMWARE_BRIDGE 0x0790
+#define PCI_DEVICE_ID_VMWARE_ROOTPORT 0x07A0
+#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0
+#define PCI_DEVICE_ID_VMWARE_VMXWIFI 0x07B8
+#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
+#define PCI_DEVICE_ID_VMWARE_82574 0x07D0
+#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CODEC 0x1975
+#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CONTROLLER 0x1977
+
+/* The hypervisor device might grow. Please leave room
+ * for 7 more subfunctions.
+ */
+#define PCI_DEVICE_ID_VMWARE_HYPER 0x0800
+#define PCI_DEVICE_ID_VMWARE_VMI 0x0801
+
+#define PCI_DEVICE_VMI_CLASS 0x05
+#define PCI_DEVICE_VMI_SUBCLASS 0x80
+#define PCI_DEVICE_VMI_INTERFACE 0x00
+#define PCI_DEVICE_VMI_REVISION 0x01
+
+/* From linux/pci_ids.h:
+ * AMD Lance Ethernet controller
+ * BusLogic SCSI controller
+ * Ensoniq ES1371 sound controller
+ */
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_VLANCE 0x2000
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+
+/* From linux/pci_ids.h:
+ * Intel 82439TX (430 HX North Bridge)
+ * Intel 82371AB (PIIX4 South Bridge)
+ * Intel 82443BX (440 BX North Bridge and AGP Bridge)
+ * Intel 82545EM (e1000, server adapter, single port)
+ * Intel 82546EB (e1000, server adapter, dual port)
+ * Intel HECI (as embedded in ich9m)
+ */
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82439TX 0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82443BX 0x7190
+#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191
+#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 /* Used when no AGP support */
+#define PCI_DEVICE_ID_INTEL_82545EM 0x100f
+#define PCI_DEVICE_ID_INTEL_82546EB 0x1010
+#define PCI_DEVICE_ID_INTEL_82574 0x10d3
+#define PCI_DEVICE_ID_INTEL_82574_APPLE 0x10f6
+#define PCI_DEVICE_ID_INTEL_HECI 0x2a74
+
+#define E1000E_PCI_DEVICE_ID_CONFIG_STR "e1000e.pci.deviceID"
+#define E1000E_PCI_SUB_VENDOR_ID_CONFIG_STR "e1000e.pci.subVendorID"
+#define E1000E_PCI_SUB_DEVICE_ID_CONFIG_STR "e1000e.pci.subDeviceID"
+
+/*
+ * Intel HD Audio controller and Realtek ALC885 codec.
+ */
+#define PCI_DEVICE_ID_INTEL_631XESB_632XESB 0x269a
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_ALC885 0x0885
+
+
+/*
+ * Fresco Logic xHCI (USB 3.0) Controller
+ */
+#define PCI_VENDOR_ID_FRESCO 0x1B73
+#define PCI_DEVICE_ID_FRESCO_FL1000 0x1000 // Original 1-port chip
+#define PCI_DEVICE_ID_FRESCO_FL1009 0x1009 // New 2-port chip (Driver 3.0.98+)
+#define PCI_DEVICE_ID_FRESCO_FL1400 0x1400 // Unknown (4-port? Dev hardware?)
+
+/*
+ * NEC/Renesas xHCI (USB 3.0) Controller
+ */
+#define PCI_VENDOR_ID_NEC 0x1033
+#define PCI_DEVICE_ID_NEC_UPD720200 0x0194
+#define PCI_REVISION_NEC_UPD720200 0x03
+#define PCI_FIRMWARE_NEC_UPD720200 0x3015
+
+
+/************* Strings for IDE Identity Fields **************************/
+#define VIDE_ID_SERIAL_STR "00000000000000000001" /* Must be 20 Bytes */
+#define VIDE_ID_FIRMWARE_STR "00000001" /* Must be 8 Bytes */
+
+/* No longer than 40 Bytes */
+#define VIDE_ATA_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE Hard Drive"
+#define VIDE_ATAPI_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE CDROM Drive"
+
+#define ATAPI_VENDOR_ID "NECVMWar" /* Must be 8 Bytes */
+#define ATAPI_PRODUCT_ID PRODUCT_GENERIC_NAME " IDE CDROM" /* Must be 16 Bytes */
+#define ATAPI_REV_LEVEL "1.00" /* Must be 4 Bytes */
+
+#define IDE_NUM_INTERFACES 2 /* support for two interfaces */
+#define IDE_DRIVES_PER_IF 2
+
+/************* Strings for SCSI Identity Fields **************************/
+#define SCSI_DISK_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI Hard Drive"
+#define SCSI_DISK_VENDOR_NAME COMPANY_NAME
+#define SCSI_DISK_REV_LEVEL "1.0"
+#define SCSI_CDROM_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI CDROM Drive"
+#define SCSI_CDROM_VENDOR_NAME COMPANY_NAME
+#define SCSI_CDROM_REV_LEVEL "1.0"
+
+/************* SCSI implementation limits ********************************/
+#define SCSI_MAX_CONTROLLERS 4 // Need more than 1 for MSCS clustering
+#define SCSI_MAX_DEVICES 16 // BT-958 emulates only 16
+#define PVSCSI_MAX_DEVICES 255 // 255 (including the controller)
+/*
+ * VSCSI_BV_INTS is the number of uint32's needed for a bit vector
+ * to cover all scsi devices per target.
+ */
+#define VSCSI_BV_INTS CEILING(PVSCSI_MAX_DEVICES, 8 * sizeof (uint32))
+#define SCSI_IDE_CHANNEL SCSI_MAX_CONTROLLERS
+#define SCSI_IDE_HOSTED_CHANNEL (SCSI_MAX_CONTROLLERS + 1)
+#define SCSI_MAX_CHANNELS (SCSI_MAX_CONTROLLERS + 2)
+
+/************* Strings for the VESA BIOS Identity Fields *****************/
+#define VBE_OEM_STRING COMPANY_NAME " SVGA"
+#define VBE_VENDOR_NAME COMPANY_NAME
+#define VBE_PRODUCT_NAME PRODUCT_GENERIC_NAME
+
+/************* PCI implementation limits ********************************/
+#define PCI_MAX_BRIDGES 15
+
+/************* Ethernet implementation limits ***************************/
+#define MAX_ETHERNET_CARDS 10
+
+/********************** Floppy limits ***********************************/
+#define MAX_FLOPPY_DRIVES 2
+
+/************* PCI Passthrough implementation limits ********************/
+#define MAX_PCI_PASSTHRU_DEVICES 6
+
+/************* USB implementation limits ********************************/
+#define MAX_USB_DEVICES_PER_HOST_CONTROLLER 127
+
+/************* Strings for Host USB Driver *******************************/
+
+#ifdef _WIN32
+
+/*
+ * Globally unique ID for the VMware device interface. Define INITGUID before including
+ * this header file to instantiate the variable.
+ */
+DEFINE_GUID(GUID_DEVICE_INTERFACE_VMWARE_USB_DEVICES,
+0x2da1fe75, 0xaab3, 0x4d2c, 0xac, 0xdf, 0x39, 0x8, 0x8c, 0xad, 0xa6, 0x65);
+
+/*
+ * Globally unique ID for the VMware device setup class.
+ */
+DEFINE_GUID(GUID_CLASS_VMWARE_USB_DEVICES,
+0x3b3e62a5, 0x3556, 0x4d7e, 0xad, 0xad, 0xf5, 0xfa, 0x3a, 0x71, 0x2b, 0x56);
+
+/*
+ * This string defines the device ID string of a VMware USB device.
+ * The format is USB\Vid_XXXX&Pid_YYYY, where XXXX and YYYY are the
+ * hexadecimal representations of the vendor and product ids, respectively.
+ *
+ * The official vendor ID for VMware, Inc. is 0x0E0F.
+ * The product id for USB generic devices is 0x0001.
+ */
+#define USB_VMWARE_DEVICE_ID_WIDE L"USB\\Vid_0E0F&Pid_0001"
+#define USB_DEVICE_ID_LENGTH (sizeof(USB_VMWARE_DEVICE_ID_WIDE) / sizeof(WCHAR))
+
+#ifdef UNICODE
+#define USB_PNP_SETUP_CLASS_NAME L"VMwareUSBDevices"
+#define USB_PNP_DRIVER_NAME L"vmusb"
+#else
+#define USB_PNP_SETUP_CLASS_NAME "VMwareUSBDevices"
+#define USB_PNP_DRIVER_NAME "vmusb"
+#endif
+#endif
+
+#endif /* VM_DEVICE_VERSION_H */
diff --git a/usr/src/uts/intel/io/vmxnet/vmnet_def.h b/usr/src/uts/intel/io/vmxnet/vmnet_def.h
new file mode 100644
index 0000000000..6e44aea2bb
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmnet_def.h
@@ -0,0 +1,91 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vmnet_def.h
+ *
+ * - definitions which are (mostly) not vmxnet or vlance specific
+ */
+
+#ifndef _VMNET_DEF_H_
+#define _VMNET_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMCORE
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#define VMNET_NAME_BUFFER_LEN 128 /* Increased for i18n. */
+#define VMNET_COAL_SCHEME_NAME_LEN 16
+
+
+/*
+ * capabilities - not all of these are implemented in the virtual HW
+ * (eg VLAN support is in the virtual switch) so even vlance
+ * can use them
+ */
+#define VMNET_CAP_SG 0x0001 /* Can do scatter-gather transmits. */
+#define VMNET_CAP_IP4_CSUM 0x0002 /* Can checksum only TCP/UDP over IPv4. */
+#define VMNET_CAP_HW_CSUM 0x0004 /* Can checksum all packets. */
+#define VMNET_CAP_HIGH_DMA 0x0008 /* Can DMA to high memory. */
+#define VMNET_CAP_TOE 0x0010 /* Supports TCP/IP offload. */
+#define VMNET_CAP_TSO 0x0020 /* Supports TCP Segmentation offload */
+#define VMNET_CAP_SW_TSO 0x0040 /* Supports SW TCP Segmentation */
+#define VMNET_CAP_VMXNET_APROM 0x0080 /* Vmxnet APROM support */
+#define VMNET_CAP_HW_TX_VLAN 0x0100 /* Can we do VLAN tagging in HW */
+#define VMNET_CAP_HW_RX_VLAN 0x0200 /* Can we do VLAN untagging in HW */
+#define VMNET_CAP_SW_VLAN 0x0400 /* Can we do VLAN tagging/untagging in SW */
+#define VMNET_CAP_WAKE_PCKT_RCV 0x0800 /* Can wake on network packet recv? */
+#define VMNET_CAP_ENABLE_INT_INLINE 0x1000 /* Enable Interrupt Inline */
+#define VMNET_CAP_ENABLE_HEADER_COPY 0x2000 /* copy header for vmkernel */
+#define VMNET_CAP_TX_CHAIN 0x4000 /* Guest can use multiple tx entries for a pkt */
+#define VMNET_CAP_RX_CHAIN 0x8000 /* a pkt can span multiple rx entries */
+#define VMNET_CAP_LPD 0x10000 /* large pkt delivery */
+#define VMNET_CAP_BPF 0x20000 /* BPF Support in VMXNET Virtual Hardware */
+#define VMNET_CAP_SG_SPAN_PAGES 0x40000 /* Can do scatter-gather span multiple pages transmits. */
+#define VMNET_CAP_IP6_CSUM 0x80000 /* Can do IPv6 csum offload. */
+#define VMNET_CAP_TSO6 0x100000 /* Can do TSO segmentation offload for IPv6 pkts. */
+#define VMNET_CAP_TSO256k 0x200000 /* Can do TSO segmentation offload for pkts up to 256kB. */
+#define VMNET_CAP_UPT 0x400000 /* Support UPT */
+#define VMNET_CAP_RDONLY_INETHDRS 0x800000 /* Modifies inet headers for TSO/CSUm */
+#define VMNET_CAP_NPA 0x1000000 /* Support NPA */
+#define VMNET_CAP_DCB 0x2000000 /* Support DCB */
+#define VMNET_CAP_OFFLOAD_8OFFSET 0x4000000 /* supports 8bit parameterized offsets */
+#define VMNET_CAP_OFFLOAD_16OFFSET 0x8000000 /* supports 16bit parameterized offsets */
+#define VMNET_CAP_IP6_CSUM_EXT_HDRS 0x10000000 /* support csum of ip6 ext hdrs */
+#define VMNET_CAP_TSO6_EXT_HDRS 0x20000000 /* support TSO for ip6 ext hdrs */
+#define VMNET_CAP_SCHED 0x40000000 /* compliant with network scheduling */
+#endif // _VMNET_DEF_H_
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet.c b/usr/src/uts/intel/io/vmxnet/vmxnet.c
new file mode 100644
index 0000000000..e0046d8deb
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet.c
@@ -0,0 +1,2438 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/kmem.h>
+#include <sys/stat.h>
+#include <sys/kstat.h>
+#include <sys/vtrace.h>
+#include <sys/dlpi.h>
+#include <sys/strsun.h>
+#include <sys/ethernet.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/gld.h>
+#include <sys/pci.h>
+#include <sys/strsubr.h>
+
+/*
+ * This used to be defined in sys/gld.h, but was flagged as private,
+ * and we used it anyway. Now it no longer exists, and we're stuck
+ * with it for the time being.
+ */
+#ifndef GLD_MAX_MULTICAST
+#define GLD_MAX_MULTICAST 64
+#endif
+
+#define __intptr_t_defined
+#define _STDINT_H
+#include "vm_basic_types.h"
+#include "vmxnet2_def.h"
+#include "vm_device_version.h"
+#include "net.h"
+#include "buildNumber.h"
+
+#define SOLVMXNET_SUCCESS 1
+#define SOLVMXNET_FAILURE 0
+
+#ifdef SOLVMXNET_DEBUG_LEVEL
+static int vxn_debug = SOLVMXNET_DEBUG_LEVEL;
+#define DPRINTF(n, args) if (vxn_debug>(n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+static char ident[] = "VMware Ethernet Adapter b" BUILD_NUMBER_NUMERIC_STRING;
+char _depends_on[] = {"misc/gld"};
+
+#define MAX_NUM_RECV_BUFFERS 128
+#define DEFAULT_NUM_RECV_BUFFERS 100
+#define MAX_NUM_XMIT_BUFFERS 128
+#define DEFAULT_NUM_XMIT_BUFFERS 100
+#define CRC_POLYNOMIAL_LE 0xedb88320UL
+#define SOLVMXNET_MAXNAME 20
+#define MAX_TX_WAIT_ON_STOP 2000
+
+#define ETHERALIGN 2
+#define SLACKBYTES 4
+#define MAXPKTBUF (14 + ETHERALIGN + ETHERMTU + SLACKBYTES)
+
+
+#define QHIWATER (MAX_NUM_RECV_BUFFERS*ETHERMTU)
+
+#define OUTB(dp, p, v) \
+ ddi_put8((dp)->vxnIOHdl, \
+ (uint8_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define OUTW(dp, p, v) \
+ ddi_put16((dp)->vxnIOHdl, \
+ (uint16_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define OUTL(dp, p, v) \
+ ddi_put32((dp)->vxnIOHdl, \
+ (uint32_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define INB(dp, p) \
+ ddi_get8((dp)->vxnIOHdl, \
+ (uint8_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+#define INW(dp, p) \
+ ddi_get16((dp)->vxnIOHdl, \
+ (uint16_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+#define INL(dp, p) \
+ ddi_get32((dp)->vxnIOHdl, \
+ (uint32_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+
+#define VMXNET_INC(val, max) \
+ val++; \
+ if (UNLIKELY(val == max)) { \
+ val = 0; \
+ }
+
+#define TX_RINGBUF_MBLK(dp, idx) (dp->txRingBuf[idx].mblk)
+#define TX_RINGBUF_DMAMEM(dp, idx) (dp->txRingBuf[idx].dmaMem)
+
+typedef struct {
+ caddr_t buf; /* Virtual address */
+ uint32_t phyBuf; /* Physical address */
+ size_t bufLen; /* Buffer length */
+ ddi_dma_cookie_t cookie; /* Dma cookie */
+ uint_t cookieCount; /* Cookie count */
+ ddi_dma_handle_t dmaHdl; /* Dma handle */
+ ddi_acc_handle_t dataAccHdl; /* Dada access handle */
+} dma_buf_t;
+
+typedef struct rx_dma_buf {
+ dma_buf_t dmaDesc; /* Dma descriptor */
+ mblk_t *mblk; /* Streams message block */
+ frtn_t freeCB; /* Free callback */
+ struct vxn_softc *softc; /* Back pointer to softc */
+ struct rx_dma_buf *next; /* Next one in list */
+} rx_dma_buf_t;
+
+typedef struct vxn_stats {
+ uint32_t errxmt; /* Transmit errors */
+ uint32_t errrcv; /* Receive errors */
+ uint32_t runt; /* Runt packets */
+ uint32_t norcvbuf; /* Buffer alloc errors */
+ uint32_t interrupts; /* Interrupts */
+ uint32_t defer; /* Deferred transmits */
+} vxn_stats_t;
+
+typedef struct tx_ring_buf {
+ mblk_t *mblk;
+ dma_buf_t dmaMem;
+} tx_ring_buf_t;
+
+typedef struct vxn_softc {
+ char drvName[SOLVMXNET_MAXNAME]; /* Driver name string */
+ int unit; /* Driver instance */
+ vxn_stats_t stats; /* Stats */
+
+ dev_info_t *dip; /* Info pointer */
+ ddi_iblock_cookie_t iblockCookie; /* Interrupt block cookie */
+ gld_mac_info_t *macInfo; /* GLD mac info */
+ ddi_acc_handle_t confHdl; /* Configuration space handle */
+ ddi_acc_handle_t vxnIOHdl; /* I/O space handle */
+ caddr_t vxnIOp; /* I/O space pointer */
+ boolean_t morphed; /* Adapter morphed ? */
+
+ kmutex_t intrlock; /* Interrupt lock */
+ kmutex_t xmitlock; /* Transmit lock */
+ kmutex_t rxlistlock; /* Rx free pool lock */
+
+ boolean_t nicActive; /* NIC active flag */
+ boolean_t inIntr; /* Interrupt processing flag */
+
+ struct ether_addr devAddr; /* MAC address */
+
+ uint32_t vxnNumRxBufs; /* Number of reveice buffers */
+ uint32_t vxnNumTxBufs; /* Number of transmit buffers */
+
+ dma_buf_t driverDataDmaMem; /* Driver Data (dma handle) */
+ Vmxnet2_DriverData *driverData; /* Driver Data */
+ void *driverDataPhy; /* Driver Data busaddr pointer */
+ Vmxnet2_RxRingEntry *rxRing; /* Receive ring */
+ Vmxnet2_TxRingEntry *txRing; /* Transmit ring */
+ ddi_dma_handle_t txDmaHdl; /* Tx buffers dma handle */
+ rx_dma_buf_t *rxRingBuffPtr[MAX_NUM_RECV_BUFFERS];
+ /* DMA buffers associated with rxRing */
+ tx_ring_buf_t txRingBuf[MAX_NUM_XMIT_BUFFERS]; /* tx Ring buffers */
+
+ rx_dma_buf_t *rxFreeBufList;
+ uint32_t rxNumFreeBufs; /* current # of buffers in pool */
+ uint32_t rxMaxFreeBufs; /* max # of buffers in pool */
+
+ uint32_t txPending; /* Pending transmits */
+ uint32_t maxTxFrags; /* Max Tx fragments */
+
+ int multiCount; /* Multicast address count */
+ struct ether_addr multicastList[GLD_MAX_MULTICAST]; /* Multicast list */
+
+ struct vxn_softc *next; /* Circular list of instances */
+ struct vxn_softc *prev;
+} vxn_softc_t;
+
+/* used for rx buffers or buffers allocated by ddi_dma_mem_alloc() */
+static ddi_dma_attr_t vxn_dma_attrs = {
+ DMA_ATTR_V0, /* dma_attr version */
+ 0, /* dma_attr_addr_lo */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
+ 0x7FFFFFFF, /* dma_attr_count_max */
+ 4, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0, /* dma_attr_flags */
+};
+
+/* used for tx buffers */
+static ddi_dma_attr_t vxn_dma_attrs_tx = {
+ DMA_ATTR_V0, /* dma_attr version */
+ 0, /* dma_attr_addr_lo */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
+ 0x7FFFFFFF, /* dma_attr_count_max */
+ 1, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0, /* dma_attr_flags */
+};
+
+
+static struct ether_addr etherbroadcastaddr = {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+};
+
+static struct ddi_device_acc_attr vxn_buf_attrs = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static struct ddi_device_acc_attr dev_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static vxn_softc_t vxnList; /* for debugging */
+static kmutex_t vxnListLock;
+
+static void *Vxn_Memset(void *s, int c, size_t n);
+static int Vxn_Reset(gld_mac_info_t *macInfo);
+static int Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag);
+static int Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs);
+static void Vxn_ApplyAddressFilter(vxn_softc_t *dp);
+static int Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag);
+static int Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac);
+static int Vxn_Start(gld_mac_info_t *macInfo);
+static int Vxn_Stop(gld_mac_info_t *macInfo);
+static void Vxn_FreeTxBuf(vxn_softc_t *dp, int idx);
+static int Vxn_EncapTxBuf(vxn_softc_t *dp, mblk_t *mp, Vmxnet2_TxRingEntry *xre,
+ tx_ring_buf_t *txBuf);
+static int Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp);
+static boolean_t Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp);
+static boolean_t Vxn_Receive(vxn_softc_t *dp);
+static u_int Vxn_Interrupt(gld_mac_info_t *macInfo);
+static void Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc);
+static void Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc);
+static rx_dma_buf_t *Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep);
+static void Vxn_FreeInitBuffers(vxn_softc_t *dp);
+static int Vxn_AllocInitBuffers(vxn_softc_t *dp);
+static void Vxn_FreeDmaMem(dma_buf_t *dma);
+static int Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma);
+static void Vxn_FreeDriverData(vxn_softc_t *dp);
+static int Vxn_AllocDriverData(vxn_softc_t *dp);
+static int Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int Vxn_AllocRxBufPool(vxn_softc_t *dp);
+static void Vxn_FreeRxBufPool(vxn_softc_t *dp);
+static rx_dma_buf_t * Vxn_AllocRxBufFromPool(vxn_softc_t *dp);
+static void Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc);
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Memset --
+ * memset() (Because bzero does not get resolved by module loader)
+ *
+ * Results:
+ * pointer to the memory area s
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void *
+Vxn_Memset(void *s, int c, size_t n)
+{
+ while (n--) {
+ ((uint8_t *)s)[n] = c;
+ }
+
+ return s;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Reset --
+ * Stub routine to reset hardware. Presently does nothing. Start/Stop should
+ * take care of resets.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Reset(gld_mac_info_t *macInfo)
+{
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetPromiscuous --
+ * Set/Reset NIC to/from promiscuous mode
+ *
+ * Results:
+ * GLD_SUCCESS
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag)
+{
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+
+ mutex_enter(&dp->intrlock);
+ if (flag == GLD_MAC_PROMISC_PHYS) {
+ dd->ifflags |= VMXNET_IFF_PROMISC;
+ } else if (flag == GLD_MAC_PROMISC_MULTI) {
+ /*
+ * This should really set VMXNET_IFF_ALLMULTI,
+ * but unfortunately it doesn't exist. The next
+ * best thing would be to set the LADRFs to all
+ * 0xFFs and set VMXNET_IFF_MULTICAST, but that
+ * opens up a whole new set of potential pitfalls,
+ * so this is a reasonable temporary solution.
+ */
+ dd->ifflags |= VMXNET_IFF_PROMISC;
+ } else if (flag == GLD_MAC_PROMISC_NONE) {
+ dd->ifflags &= ~VMXNET_IFF_PROMISC;
+ } else {
+ /* This could be GLD_MAC_PROMISC_NOOP? */
+ mutex_exit(&dp->intrlock);
+ cmn_err(CE_WARN, "%s%d: Vxn_SetPromiscuous: Unexpected mode flag: 0x%x",
+ dp->drvName, dp->unit, flag);
+
+ return GLD_FAILURE;
+ }
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ mutex_exit(&dp->intrlock);
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_GetStats --
+ * Get driver specific stats
+ *
+ * Results:
+ * GLD_SUCCESS
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs)
+{
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ gs->glds_errxmt = dp->stats.errxmt;
+ gs->glds_errrcv = dp->stats.errrcv;
+ gs->glds_short = dp->stats.runt;
+ gs->glds_norcvbuf = dp->stats.norcvbuf;
+ gs->glds_intr = dp->stats.interrupts;
+ gs->glds_defer = dp->stats.defer;
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_ApplyAddressFilter --
+ * Go over multicast list and compute/apply address filter
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_ApplyAddressFilter(vxn_softc_t *dp)
+{
+ uint8_t *ep;
+ int i, j, bit, byte;
+ uint32_t crc, poly = CRC_POLYNOMIAL_LE;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ volatile uint16_t *mcastTable = (uint16_t *)dd->LADRF;
+
+ ASSERT(MUTEX_HELD(&dp->intrlock));
+
+ /* clear the multicast filter */
+ dd->LADRF[0] = 0;
+ dd->LADRF[1] = 0;
+
+ for (i = 0; i < dp->multiCount; i++) {
+ crc = 0xffffffff;
+ ep = (uint8_t *)&dp->multicastList[i].ether_addr_octet;
+
+ for (byte = 0; byte < 6; byte++) {
+ for (bit = *ep++, j = 0; j < 8; j++, bit >>= 1) {
+ int test;
+
+ test = ((bit ^ crc) & 0x01);
+ crc >>= 1;
+
+ if (test) {
+ crc = crc ^ poly;
+ }
+ }
+ }
+
+ crc = crc >> 26;
+ mcastTable[crc >> 4] |= 1 << (crc & 0xf);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetMulticast --
+ * Add delete entry from multicast list
+ *
+ * Results:
+ * GLD_FAILURE on failure
+ * GLD_SUCCESS on success
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag)
+{
+ int i;
+ int copyLen;
+ vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+
+ if (flag == GLD_MULTI_ENABLE) {
+ /*
+ * Exceeded multicast address limit
+ */
+ if (dp->multiCount >= GLD_MAX_MULTICAST) {
+ return GLD_FAILURE;
+ }
+
+ /*
+ * Add mac address to multicast list
+ */
+ bcopy(ep, dp->multicastList[dp->multiCount].ether_addr_octet,
+ ETHERADDRL);
+ dp->multiCount++;
+ }
+ else {
+ for (i=0; i<dp->multiCount; i++) {
+ if (bcmp(ep, dp->multicastList[i].ether_addr_octet, ETHERADDRL) == 0) {
+ goto found;
+ }
+ }
+ return GLD_FAILURE;
+
+ found:
+ /*
+ * Delete mac address from multicast list
+ */
+ copyLen = (dp->multiCount - (i+1)) * sizeof(struct ether_addr);
+ if (copyLen > 0) {
+ bcopy(&dp->multicastList[i+1], &dp->multicastList[i], copyLen);
+ }
+ dp->multiCount--;
+ }
+
+ /*
+ * Compute address filter from list of addressed and apply it
+ */
+ mutex_enter(&dp->intrlock);
+ Vxn_ApplyAddressFilter(dp);
+
+ if (dp->multiCount) {
+ ASSERT(dd->LADRF[0] || dd->LADRF[1]);
+ dd->ifflags |= VMXNET_IFF_MULTICAST;
+ } else {
+ ASSERT(!(dd->LADRF[0] || dd->LADRF[1]));
+ dd->ifflags &= ~VMXNET_IFF_MULTICAST;
+ }
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);
+ mutex_exit(&dp->intrlock);
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetMacAddress --
+ * Change device MAC address
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac)
+{
+ int i;
+ int err = GLD_SUCCESS;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ /*
+ * Don't change MAC address on a running NIC
+ */
+ if (dp->nicActive) {
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Save new MAC address
+ */
+ for (i = 0; i < 6; i++) {
+ dp->devAddr.ether_addr_octet[i] = mac[i];
+ }
+
+ /*
+ * Push new MAC address down into hardware
+ */
+ for (i = 0; i < 6; i++) {
+ OUTB(dp, VMXNET_MAC_ADDR + i, mac[i]);
+ }
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Start --
+ * Device start routine. Called on "ifconfig plumb"
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Start(gld_mac_info_t *macInfo)
+{
+ int err = GLD_SUCCESS;
+ uint32_t r, capabilities, features;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ if (!dp->nicActive) {
+ /*
+ * Register ring structure with hardware
+ *
+ * This downcast is OK because we requested a 32-bit physical address
+ */
+ OUTL(dp, VMXNET_INIT_ADDR, (uint32_t)(uintptr_t)dp->driverDataPhy);
+ OUTL(dp, VMXNET_INIT_LENGTH, dp->driverData->length);
+
+ /*
+ * Make sure registeration succeded
+ */
+ r = INL(dp, VMXNET_INIT_LENGTH);
+ if (!r) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to register ring",
+ dp->drvName, dp->unit);
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Get maximum tx fragments supported
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_CAPABILITIES);
+ capabilities = INL(dp, VMXNET_COMMAND_ADDR);
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_FEATURES);
+ features = INL(dp, VMXNET_COMMAND_ADDR);
+
+ DPRINTF(3, (CE_CONT, "%s%d: chip capabilities=0x%x features=0x%x\n",
+ dp->drvName, dp->unit, capabilities, features));
+
+ if ((capabilities & VMNET_CAP_SG) &&
+ (features & VMXNET_FEATURE_ZERO_COPY_TX)) {
+ dp->maxTxFrags = VMXNET2_SG_DEFAULT_LENGTH;
+ } else {
+ dp->maxTxFrags = 1;
+ }
+ ASSERT(dp->maxTxFrags >= 1);
+
+ /*
+ * Alloc Tx DMA handle
+ */
+ vxn_dma_attrs_tx.dma_attr_sgllen = dp->maxTxFrags;
+ if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs_tx, DDI_DMA_SLEEP,
+ NULL, &dp->txDmaHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to alloc tx dma handle",
+ dp->drvName, dp->unit);
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Enable interrupts on the card
+ */
+ dp->driverData->ifflags |= VMXNET_IFF_BROADCAST | VMXNET_IFF_DIRECTED;
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ENABLE);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);
+
+ dp->nicActive = TRUE;
+ }
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Stop --
+ * Device stop routine. Called on "ifconfig unplumb"
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Stop(gld_mac_info_t *macInfo)
+{
+ int i;
+ int err = GLD_SUCCESS;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+ boolean_t resched;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ if (!dp->nicActive) {
+ goto out;
+ }
+
+ /*
+ * Disable interrupts
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_DISABLE);
+
+ /*
+ * Wait for pending transmits
+ */
+ if (dp->txPending) {
+ for (i=0; i < MAX_TX_WAIT_ON_STOP && dp->txPending; i++) {
+ delay(drv_usectohz(1000));
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_CHECK_TX_DONE);
+ (void) Vxn_TxComplete(dp, &resched);
+ /*
+ * Don't worry about rescheduling transmits - GLD handles
+ * this automatically.
+ */
+ }
+ }
+ if (dp->txPending) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Stop: giving up on %d pending transmits",
+ dp->drvName, dp->unit, dp->txPending);
+ }
+
+ OUTL(dp, VMXNET_INIT_ADDR, 0);
+ dp->nicActive = FALSE;
+
+ /*
+ * Free Tx DMA handle
+ *
+ * The ddi_dma_free_handle() man page says that ddi_dma_unbind_handle() must be called
+ * prior to calling ddi_dma_free_handle().
+ * However, call to ddi_dma_unbind_handle() is not required here, because
+ * ddi_dma_addr_bind_handle() and matching ddi_dma_unbind_handle() are called from
+ * Vxn_EncapTxBuf().
+ * xmitlock is held in Vxn_EncapTxBuf() as well as acquired above in Vxn_Stop().
+ */
+ ddi_dma_free_handle(&dp->txDmaHdl);
+ dp->txDmaHdl = NULL;
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeTxBuf --
+ * Free transmit buffer
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeTxBuf(vxn_softc_t *dp, int idx)
+{
+ mblk_t **txMblkp = &TX_RINGBUF_MBLK(dp, idx);
+ dma_buf_t *dmaMem = &TX_RINGBUF_DMAMEM(dp, idx);
+
+ if (*txMblkp) {
+ freemsg(*txMblkp);
+ *txMblkp = NULL;
+ }
+
+ if (dmaMem->buf) {
+ Vxn_FreeDmaMem(dmaMem);
+ ASSERT(dmaMem->buf == NULL);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_EncapTxBuf --
+ * Go over dma mappings of Tx buffers and drop buffer physical address
+ * into ring entry
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *---------------- -------------------------------------------------------------
+ */
+static int
+Vxn_EncapTxBuf(vxn_softc_t *dp,
+ mblk_t *mp,
+ Vmxnet2_TxRingEntry *xre,
+ tx_ring_buf_t *txBuf)
+{
+ int frag;
+ int fragcount;
+ int rval;
+ mblk_t *tp;
+ mblk_t *mblk;
+ boolean_t needPullup = FALSE;
+ boolean_t dmaMemAlloced = FALSE;
+
+ ASSERT(txBuf);
+ ASSERT(txBuf->mblk == NULL);
+ ASSERT(MUTEX_HELD(&dp->xmitlock));
+
+ xre->sg.length = 0;
+ xre->flags = 0;
+
+ fragcount = 0;
+ for (tp = mp; tp != NULL; tp = tp->b_cont) {
+ fragcount++;
+ }
+ if (fragcount > dp->maxTxFrags) {
+ needPullup = TRUE;
+ }
+
+pullup:
+ frag = 0;
+ if (needPullup) {
+ if (!(mblk = msgpullup(mp, -1))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: msgpullup failed",
+ dp->drvName, dp->unit);
+ goto err;
+ }
+ } else {
+ mblk = mp;
+ }
+
+ /*
+ * Go through message chain and drop packet pointers into ring
+ * scatter/gather array
+ */
+ for (tp = mblk; tp != NULL; tp = tp->b_cont) {
+
+ uint_t nCookies;
+ ddi_dma_cookie_t dmaCookie;
+ int len = tp->b_wptr - tp->b_rptr;
+
+ if (len) {
+ /*
+ * Associate tx buffer with dma handle
+ */
+ ASSERT(dp->txDmaHdl);
+ if ((rval = ddi_dma_addr_bind_handle(dp->txDmaHdl, NULL, (caddr_t)tp->b_rptr,
+ len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
+ DDI_DMA_DONTWAIT, NULL,
+ &dmaCookie, &nCookies))
+ != DDI_DMA_MAPPED) {
+
+ /*
+ * Try to handle bind failure caused by a page boundary spill
+ * by allocating a private dma buffer and copying data into it
+ */
+ if ((rval == DDI_DMA_TOOBIG) && !dmaMemAlloced ) {
+ /*
+ * Force pullup
+ */
+ if (!needPullup && (dp->maxTxFrags > 1)) {
+ needPullup = TRUE;
+ goto pullup;
+ }
+
+ if (Vxn_AllocDmaMem(dp, len, FALSE, &txBuf->dmaMem)
+ != SOLVMXNET_SUCCESS) {
+ goto err;
+ }
+
+ dmaMemAlloced = TRUE;
+
+ /*
+ * Copy data into DMA capable buffer
+ */
+ bcopy(tp->b_rptr, txBuf->dmaMem.buf, len);
+
+ /*
+ * Stick buffer physical addr in the ring
+ */
+ xre->sg.sg[frag].addrLow = txBuf->dmaMem.phyBuf;
+ xre->sg.sg[frag].length = len;
+ frag++;
+
+ continue;
+
+ } else {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: failed (%d) to bind dma "
+ "handle for len %d. [dmaMemAlloced=%d]",
+ dp->drvName, dp->unit, rval, len, dmaMemAlloced);
+ goto err;
+ }
+ }
+
+ /*
+ * Extract tx buffer physical addresses from cookie
+ */
+ while (nCookies) {
+ if (UNLIKELY(frag == dp->maxTxFrags)) {
+ (void)ddi_dma_unbind_handle(dp->txDmaHdl);
+
+ if (!needPullup) {
+ ASSERT(!dmaMemAlloced);
+ needPullup = TRUE;
+ goto pullup;
+ } else {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: "
+ "exceeded max (%d) fragments in message",
+ dp->drvName, dp->unit, dp->maxTxFrags);
+ goto err;
+ }
+ }
+
+ /*
+ * Stick it in the ring
+ */
+ xre->sg.sg[frag].addrLow = dmaCookie.dmac_address;
+ xre->sg.sg[frag].length = dmaCookie.dmac_size;
+ frag++;
+
+ if (--nCookies) {
+ ddi_dma_nextcookie(dp->txDmaHdl, &dmaCookie);
+ }
+ }
+
+ (void)ddi_dma_unbind_handle(dp->txDmaHdl);
+ }
+ }
+
+ if (frag > 0) {
+ xre->sg.length = frag;
+
+ /* Give ownership to NIC */
+ xre->sg.addrType = NET_SG_PHYS_ADDR;
+ xre->ownership = VMXNET2_OWNERSHIP_NIC;
+ xre->flags |= VMXNET2_TX_CAN_KEEP;
+ txBuf->mblk = mblk;
+
+ /*
+ * If we called msgpullup to concatenate fragments, free
+ * original mblk now since we're going to return success.
+ */
+ if (mblk != mp) {
+ freemsg(mp);
+ }
+
+ return SOLVMXNET_SUCCESS;
+ }
+
+err:
+ if (mblk != NULL && mblk != mp) {
+ /*
+ * Free mblk allocated by msgpullup.
+ */
+ freemsg(mblk);
+ }
+
+ if (dmaMemAlloced) {
+ ASSERT(txBuf->dmaMem.buf);
+ Vxn_FreeDmaMem(&txBuf->dmaMem);
+ }
+
+ return SOLVMXNET_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Send --
+ * GLD Transmit routine. Starts packet hard tx.
+ *
+ * Results:
+ * GLD_SUCCESS on success
+ * GLD_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp)
+{
+ Vmxnet2_TxRingEntry *xre;
+ int err = GLD_SUCCESS;
+ vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ boolean_t resched = FALSE;
+
+ mutex_enter(&dp->xmitlock);
+
+ /*
+ * Check if ring entry at drop pointer is available
+ */
+ if (TX_RINGBUF_MBLK(dp, dd->txDriverNext) != NULL) {
+ DPRINTF(3, (CE_NOTE, "%s%d: Vxn_Send: tx ring full",
+ dp->drvName, dp->unit));
+ err = GLD_NORESOURCES;
+ dd->txStopped = TRUE;
+ dp->stats.defer++;
+ goto out;
+ }
+
+ xre = &dp->txRing[dd->txDriverNext];
+
+ /*
+ * Drop packet into ring entry
+ */
+ if (Vxn_EncapTxBuf(dp, mp, xre, &dp->txRingBuf[dd->txDriverNext])
+ != SOLVMXNET_SUCCESS) {
+ err = GLD_FAILURE;
+ dp->stats.errxmt++;
+ goto out;
+ }
+
+ /*
+ * Increment drop pointer
+ */
+ VMXNET_INC(dd->txDriverNext, dd->txRingLength);
+ dd->txNumDeferred++;
+ dp->txPending++;
+
+ /*
+ * Transmit, if number of pending packets > tx cluster length
+ */
+ if (dd->txNumDeferred >= dd->txClusterLength) {
+ dd->txNumDeferred = 0;
+
+ /*
+ * Call hardware transmit
+ */
+ INL(dp, VMXNET_TX_ADDR);
+ }
+
+ /*
+ * Clean up transmit ring. TX completion interrupts are not guaranteed
+ */
+ (void) Vxn_TxComplete(dp, &resched);
+
+out:
+ mutex_exit(&dp->xmitlock);
+ if (resched) {
+ /* Tell GLD to retry any deferred packets */
+ gld_sched(dp->macInfo);
+ }
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_TxComplete --
+ * Scan Tx ring for completed transmits. Reclaim Tx buffers.
+ *
+ * Results:
+ * Returns TRUE if it found a completed transmit, FALSE otherwise.
+ * Also sets *reschedp to TRUE if the caller should call gld_sched
+ * to reschedule transmits (once all locks are dropped).
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static boolean_t
+Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp)
+{
+ Vmxnet2_DriverData *dd = dp->driverData;
+ boolean_t found = FALSE;
+ boolean_t needresched = FALSE;
+
+ ASSERT(MUTEX_HELD(&dp->xmitlock));
+
+ while (1) {
+ Vmxnet2_TxRingEntry *xre = &dp->txRing[dd->txDriverCur];
+
+ if (xre->ownership != VMXNET2_OWNERSHIP_DRIVER ||
+ (TX_RINGBUF_MBLK(dp, dd->txDriverCur) == NULL)) {
+ break;
+ }
+
+ found = TRUE;
+ Vxn_FreeTxBuf(dp, dd->txDriverCur);
+
+ dp->txPending--;
+ VMXNET_INC(dd->txDriverCur, dd->txRingLength);
+ if (dd->txStopped) {
+ needresched = TRUE;
+ dd->txStopped = FALSE;
+ }
+ }
+
+ *reschedp = needresched;
+ return found;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Receive --
+ * Rx handler. First assembles the packets into a chain of mblks,
+ * then drops locks and passes them up the stack to GLD.
+ *
+ * Results:
+ * Returns TRUE if it find a packet ready for processing, FALSE
+ * otherwise.
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static boolean_t
+Vxn_Receive(vxn_softc_t *dp)
+{
+ int ringnext;
+ short pktlen;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ rx_dma_buf_t *rxDesc;
+ rx_dma_buf_t *newRxDesc;
+ mblk_t *mblk;
+ mblk_t *head = NULL;
+ mblk_t **tail = &head;
+ mblk_t *next;
+ boolean_t found = FALSE; /* Did we find at least one packet? */
+
+ ASSERT(MUTEX_HELD(&dp->intrlock));
+
+ /*
+ * Walk receive ring looking for entries with ownership
+ * reverted back to driver
+ */
+ while (1) {
+ Vmxnet2_RxRingEntry *rre;
+ rx_dma_buf_t **rbuf;
+
+ ringnext = dd->rxDriverNext;
+ rre = &dp->rxRing[ringnext];
+ rbuf = &dp->rxRingBuffPtr[ringnext];
+
+ if (rre->ownership != VMXNET2_OWNERSHIP_DRIVER) {
+ break;
+ }
+
+ found = TRUE;
+
+ pktlen = rre->actualLength;
+
+ if (pktlen < (60 - 4)) {
+ /*
+ * Ethernet header vlan tags are 4 bytes. Some vendors generate
+ * 60byte frames including vlan tags. When vlan tag
+ * is stripped, such frames become 60 - 4. (PR106153)
+ */
+ dp->stats.errrcv++;
+ if (pktlen != 0) {
+ DPRINTF(3, (CE_CONT, "%s%d: runt packet\n", dp->drvName, dp->unit));
+ dp->stats.runt++;
+ }
+ } else {
+ /*
+ * Alloc new Rx buffer to replace current one
+ */
+ newRxDesc = Vxn_AllocRxBufFromPool(dp);
+
+ if (newRxDesc) {
+ rxDesc = *rbuf;
+ mblk = rxDesc->mblk;
+
+ *rbuf = newRxDesc;
+ rre->paddr = newRxDesc->dmaDesc.phyBuf + ETHERALIGN;
+ rre->bufferLength = MAXPKTBUF - ETHERALIGN;
+ rre->actualLength = 0;
+
+ /*
+ * Advance write pointer past packet length
+ */
+ mblk->b_wptr = mblk->b_rptr + pktlen;
+
+ /*
+ * Add to end of chain.
+ */
+ mblk->b_next = NULL;
+ *tail = mblk;
+ tail = &mblk->b_next;
+ } else {
+ dp->stats.errrcv++;
+ dp->stats.norcvbuf++;
+ }
+ }
+
+ /* Give the descriptor back to NIC */
+ rre->ownership = VMXNET2_OWNERSHIP_NIC;
+ VMXNET_INC(dd->rxDriverNext, dd->rxRingLength);
+ }
+
+ /*
+ * Walk chain and pass mblks up to gld_recv one by one.
+ */
+ mutex_exit(&dp->intrlock);
+ for (mblk = head; mblk != NULL; mblk = next) {
+ next = mblk->b_next;
+ mblk->b_next = NULL;
+ gld_recv(dp->macInfo, mblk);
+ }
+ mutex_enter(&dp->intrlock);
+
+ return (found);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Interrupt --
+ * GLD interrupt handler. Scan: Rx ring for received packets, Tx ring for
+ * completed transmits
+ *
+ * Results:
+ * - DDI_INTR_CLAIMED (if we found something to do)
+ * - DDI_INTR_UNCLAIMED (if not)
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static u_int
+Vxn_Interrupt(gld_mac_info_t *macInfo)
+{
+ u_int ret = DDI_INTR_UNCLAIMED;
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+ boolean_t foundRx, foundTx;
+ boolean_t resched = FALSE;
+
+ mutex_enter(&dp->intrlock);
+ dp->inIntr = TRUE;
+
+ if (!dp->nicActive) {
+ goto out;
+ }
+
+ /*
+ * Ack interrupt
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ACK);
+
+ foundRx = Vxn_Receive(dp);
+
+ mutex_enter(&dp->xmitlock);
+ foundTx = Vxn_TxComplete(dp, &resched);
+ mutex_exit(&dp->xmitlock);
+
+ if (foundRx || foundTx) {
+ ret = DDI_INTR_CLAIMED;
+ dp->stats.interrupts++;
+ }
+
+out:
+ dp->inIntr = FALSE;
+ mutex_exit(&dp->intrlock);
+
+ if (resched) {
+ gld_sched(dp->macInfo);
+ }
+
+ return ret;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_ReclaimRxBuf --
+ * Callback handler invoked by freemsg(). Frees Rx buffer memory and mappings
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc)
+{
+ Vxn_FreeRxBufToPool(rxDesc);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBuf --
+ * Free allocated Rx buffer
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc)
+{
+ ASSERT(rxDesc);
+
+ if (rxDesc->mblk) {
+ freemsg(rxDesc->mblk);
+ } else {
+ Vxn_FreeDmaMem(&rxDesc->dmaDesc);
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBuf --
+ * Allocate Rx buffer
+ *
+ * Results:
+ * Pointer to Rx buffer descriptor - on success
+ * NULL - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static rx_dma_buf_t *
+Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep)
+{
+ rx_dma_buf_t *rxDesc;
+
+ rxDesc = (rx_dma_buf_t *)kmem_zalloc(sizeof(rx_dma_buf_t),
+ cansleep ? KM_SLEEP : KM_NOSLEEP);
+ if (!rxDesc) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBuf: kmem_zalloc failed",
+ dp->drvName, dp->unit);
+ return NULL;
+ }
+
+ rxDesc->softc = dp;
+
+ /*
+ * Alloc dma-able packet memory
+ */
+ if (Vxn_AllocDmaMem(dp, MAXPKTBUF, cansleep, &rxDesc->dmaDesc)
+ != SOLVMXNET_SUCCESS) {
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ return NULL;
+ }
+
+ /*
+ * Fill in free callback; fired by freemsg()
+ */
+ rxDesc->freeCB.free_func = &Vxn_ReclaimRxBuf;
+ rxDesc->freeCB.free_arg = (caddr_t) rxDesc;
+
+ rxDesc->mblk = NULL;
+ return rxDesc;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeInitBuffers --
+ * Free allocated Tx and Rx buffers
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeInitBuffers(vxn_softc_t *dp)
+{
+ int i;
+
+ for (i=0; i<dp->vxnNumRxBufs; i++) {
+ if (dp->rxRingBuffPtr[i]) {
+ Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
+ dp->rxRingBuffPtr[i] = NULL;
+ }
+ }
+
+ for (i=0; i<dp->vxnNumTxBufs; i++) {
+ if (TX_RINGBUF_MBLK(dp, i)) {
+ Vxn_FreeTxBuf(dp, i);
+ }
+ }
+
+ /*
+ * Rx pool must get freed last. Rx buffers above will
+ * show up on the pool when freemsg callback fires.
+ */
+ Vxn_FreeRxBufPool(dp);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBufPool --
+ * Allocate pool of rx buffers - 3 * configured Rx buffers
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS/SOLVMXNET_FAILURE
+ *
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocRxBufPool(vxn_softc_t *dp)
+{
+ int i;
+
+ dp->rxFreeBufList = NULL;
+
+ // Allow list to double in size if needed. Any additional buffers
+ // that are allocated on the fly will be freed back to main memory.
+ dp->rxMaxFreeBufs = dp->vxnNumRxBufs * 6;
+
+ for (i = 0; i < dp->vxnNumRxBufs * 3; i++) {
+ rx_dma_buf_t *rxDesc;
+
+ /*
+ * Alloc rx buffer
+ */
+ if (!(rxDesc = Vxn_AllocRxBuf(dp, TRUE))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufPool: failed to allocate memory",
+ dp->drvName, dp->unit);
+ dp->rxNumFreeBufs = i;
+ return SOLVMXNET_FAILURE;
+ }
+ /*
+ * Add to free list
+ */
+ rxDesc->next = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc;
+ }
+
+ dp->rxNumFreeBufs = i;
+ return SOLVMXNET_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBufPool --
+ * Free rx buffers pool
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBufPool(vxn_softc_t *dp)
+{
+ while (dp->rxFreeBufList) {
+ rx_dma_buf_t *rxDesc = dp->rxFreeBufList;
+
+ /* unlink */
+ dp->rxFreeBufList = rxDesc->next;
+
+ ASSERT(rxDesc->mblk == NULL);
+ Vxn_FreeDmaMem(&rxDesc->dmaDesc);
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ }
+ dp->rxNumFreeBufs = 0;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBufFromPool --
+ * Allocate Rx buffer from free pool
+ *
+ * Results:
+ * Pointer to Rx buffer descriptor - on success
+ * NULL - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static rx_dma_buf_t *
+Vxn_AllocRxBufFromPool(vxn_softc_t *dp)
+{
+ rx_dma_buf_t *rxDesc = NULL;
+
+ mutex_enter(&dp->rxlistlock);
+ if (dp->rxFreeBufList) {
+ rxDesc = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc->next;
+ ASSERT(dp->rxNumFreeBufs >= 1);
+ dp->rxNumFreeBufs--;
+ }
+ mutex_exit(&dp->rxlistlock);
+
+ if (!rxDesc) {
+ /*
+ * Try to allocate new descriptor from memory. Can't block here
+ * since we could be being called from interrupt context.
+ */
+ DPRINTF(5, (CE_NOTE, "%s%d: allocating rx buf from memory",
+ dp->drvName, dp->unit));
+ if (!(rxDesc = Vxn_AllocRxBuf(dp, FALSE))) {
+ cmn_err(CE_WARN,
+ "%s%d: Vxn_AllocRxBufFromPool : pool rx alloc failed",
+ dp->drvName, dp->unit);
+ return NULL;
+ }
+ }
+
+ /*
+ * Allocate new message block for this buffer
+ */
+ rxDesc->mblk = desballoc((uchar_t *)rxDesc->dmaDesc.buf + ETHERALIGN,
+ rxDesc->dmaDesc.bufLen - ETHERALIGN,
+ BPRI_MED, &rxDesc->freeCB);
+ if (!rxDesc->mblk) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufFromPool : desballoc failed",
+ dp->drvName, dp->unit);
+
+ /* put back on free list */
+ Vxn_FreeRxBufToPool(rxDesc);
+ return NULL;
+ }
+
+ return rxDesc;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBufToPool --
+ * Return rx buffer to free pool
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc)
+{
+ vxn_softc_t *dp = rxDesc->softc;
+
+ rxDesc->mblk = NULL;
+
+ /*
+ * Insert on free list, or free if the list is full
+ */
+ mutex_enter(&dp->rxlistlock);
+ if (dp->rxNumFreeBufs >= dp->rxMaxFreeBufs) {
+ DPRINTF(5, (CE_NOTE, "%s%d: freeing rx buf to memory",
+ dp->drvName, dp->unit));
+ Vxn_FreeRxBuf(rxDesc);
+ } else {
+ rxDesc->next = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc;
+ dp->rxNumFreeBufs++;
+ }
+ mutex_exit(&dp->rxlistlock);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocInitBuffers --
+ * Allocated Rx buffers and init ring entries
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS - on success
+ * SOLVMXNET_FAILURE - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocInitBuffers(vxn_softc_t *dp)
+{
+ Vmxnet2_DriverData *dd;
+ uint32_t i, offset;
+
+ dd = dp->driverData;
+ offset = sizeof(*dd);
+
+ /*
+ * Init shared structures
+ */
+ dd->rxRingLength = dp->vxnNumRxBufs;
+ dd->rxRingOffset = offset;
+ dp->rxRing = (Vmxnet2_RxRingEntry *)((uintptr_t)dd + offset);
+ offset += dp->vxnNumRxBufs * sizeof(Vmxnet2_RxRingEntry);
+
+ dd->rxRingLength2 = 1;
+ dd->rxRingOffset2 = offset;
+ offset += sizeof(Vmxnet2_RxRingEntry);
+
+ dd->txRingLength = dp->vxnNumTxBufs;
+ dd->txRingOffset = offset;
+ dp->txRing = (Vmxnet2_TxRingEntry *)((uintptr_t)dd + offset);
+ offset += dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);
+
+ /*
+ * Alloc Rx buffers pool
+ */
+ if ( Vxn_AllocRxBufPool(dp) != SOLVMXNET_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: failed to alloc buf pool",
+ dp->drvName, dp->unit);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Allocate receive buffers
+ */
+ for (i = 0; i < dp->vxnNumRxBufs; i++) {
+ rx_dma_buf_t *rxDesc;
+ Vmxnet2_RxRingEntry *rre = &dp->rxRing[i];
+
+ if (!(rxDesc = Vxn_AllocRxBufFromPool(dp))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: "
+ "failed to alloc buf from pool", dp->drvName, dp->unit);
+ goto err;
+ }
+
+ /*
+ * Init ring entries
+ */
+ rre->paddr = rxDesc->dmaDesc.phyBuf + ETHERALIGN;
+ rre->bufferLength = MAXPKTBUF - ETHERALIGN;
+ rre->actualLength = 0;
+ dp->rxRingBuffPtr[i] = rxDesc;
+ rre->ownership = VMXNET2_OWNERSHIP_NIC;
+ }
+
+ dp->txDmaHdl = NULL;
+
+ /*
+ * Dummy recvRing2 tacked on to the end, with a single unusable entry
+ */
+ dp->rxRing[i].paddr = 0;
+ dp->rxRing[i].bufferLength = 0;
+ dp->rxRing[i].actualLength = 0;
+ dp->rxRingBuffPtr[i] = NULL;
+ dp->rxRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
+
+ dd->rxDriverNext = 0;
+
+ /*
+ * Give xmit ring ownership to DRIVER
+ */
+ for (i = 0; i < dp->vxnNumTxBufs; i++) {
+ dp->txRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
+ dp->txRingBuf[i].mblk = NULL;
+ dp->txRingBuf[i].dmaMem.buf = NULL;
+ dp->txRing[i].sg.sg[0].addrHi = 0;
+ }
+
+ dd->txDriverCur = dd->txDriverNext = 0;
+ dd->txStopped = FALSE;
+
+ return SOLVMXNET_SUCCESS;
+
+err:
+ for (i=0; i<dp->vxnNumRxBufs; i++) {
+ if (dp->rxRingBuffPtr[i]) {
+ Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
+ dp->rxRingBuffPtr[i] = NULL;
+ }
+ }
+
+ Vxn_FreeRxBufPool(dp);
+ return SOLVMXNET_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeDmaMem --
+ * Free allocated dma memory
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeDmaMem(dma_buf_t *dma)
+{
+ ddi_dma_unbind_handle(dma->dmaHdl);
+ ddi_dma_mem_free(&dma->dataAccHdl);
+ ddi_dma_free_handle(&dma->dmaHdl);
+
+ dma->buf = NULL;
+ dma->phyBuf = NULL;
+ dma->bufLen = 0;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocDmaMem --
+ * Allocate dma-able memory and fill passed in dma descriptor pointer
+ * if successful
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma)
+{
+ /*
+ * Allocate handle
+ */
+ if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
+ NULL, &dma->dmaHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to allocate handle",
+ dp->drvName, dp->unit);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Allocate memory
+ */
+ if (ddi_dma_mem_alloc(dma->dmaHdl, size, &vxn_buf_attrs, DDI_DMA_CONSISTENT,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, NULL,
+ &dma->buf, &dma->bufLen, &dma->dataAccHdl)
+ != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: "
+ "ddi_dma_mem_alloc %d bytes failed",
+ dp->drvName, dp->unit, size);
+ ddi_dma_free_handle(&dma->dmaHdl);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Mapin memory
+ */
+ if (ddi_dma_addr_bind_handle(dma->dmaHdl, NULL, dma->buf, dma->bufLen,
+ DDI_DMA_RDWR | DDI_DMA_STREAMING,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
+ NULL, &dma->cookie, &dma->cookieCount)
+ != DDI_DMA_MAPPED) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to bind handle",
+ dp->drvName, dp->unit);
+ ddi_dma_mem_free(&dma->dataAccHdl);
+ ddi_dma_free_handle(&dma->dmaHdl);
+ return SOLVMXNET_FAILURE;
+ }
+
+ if (dma->cookieCount != 1) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: too many DMA cookies",
+ dp->drvName, dp->unit);
+ Vxn_FreeDmaMem(dma);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Save physical address (for easy use)
+ */
+ dma->phyBuf = dma->cookie.dmac_address;
+
+ return SOLVMXNET_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeDriverData --
+ * Free driver data structures and Tx Rx buffers
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeDriverData(vxn_softc_t *dp)
+{
+ Vxn_FreeInitBuffers(dp);
+ Vxn_FreeDmaMem(&dp->driverDataDmaMem);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocDriverData --
+ * Allocate driver data structures and Tx Rx buffers on init
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocDriverData(vxn_softc_t *dp)
+{
+ uint32_t r, driverDataSize;
+
+ /*
+ * Get configured receive buffers
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_RX_BUFFERS);
+ r = INL(dp, VMXNET_COMMAND_ADDR);
+ if (r == 0 || r > MAX_NUM_RECV_BUFFERS) {
+ r = DEFAULT_NUM_RECV_BUFFERS;
+ }
+ dp->vxnNumRxBufs = r;
+
+ /*
+ * Get configured transmit buffers
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_TX_BUFFERS);
+ r = INL(dp, VMXNET_COMMAND_ADDR);
+ if (r == 0 || r > MAX_NUM_XMIT_BUFFERS) {
+ r = DEFAULT_NUM_XMIT_BUFFERS;
+ }
+ dp->vxnNumTxBufs = r;
+
+ /*
+ * Calculate shared data size and allocate memory for it
+ */
+ driverDataSize =
+ sizeof(Vmxnet2_DriverData) +
+ /* numRecvBuffers + 1 for the dummy recvRing2 (used only by Windows) */
+ (dp->vxnNumRxBufs + 1) * sizeof(Vmxnet2_RxRingEntry) +
+ dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);
+
+ if (Vxn_AllocDmaMem(dp, driverDataSize, TRUE, &dp->driverDataDmaMem)
+ != SOLVMXNET_SUCCESS) {
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Clear memory (bzero isn't resolved by module loader for some reason)
+ */
+ ASSERT(dp->driverDataDmaMem.buf && dp->driverDataDmaMem.bufLen);
+ Vxn_Memset(dp->driverDataDmaMem.buf, 0, dp->driverDataDmaMem.bufLen);
+
+ dp->driverData = (Vmxnet2_DriverData *)dp->driverDataDmaMem.buf;
+ dp->driverDataPhy = (void *)(uintptr_t)dp->driverDataDmaMem.phyBuf;
+
+ /* So that the vmkernel can check it is compatible */
+ dp->driverData->magic = VMXNET2_MAGIC;
+ dp->driverData->length = driverDataSize;
+
+ /*
+ * Alloc rx/tx buffers, init ring, register with hardware etc.
+ */
+ if (Vxn_AllocInitBuffers(dp) != SOLVMXNET_SUCCESS) {
+ Vxn_FreeDmaMem(&dp->driverDataDmaMem);
+ return SOLVMXNET_FAILURE;
+ }
+
+ DPRINTF(3, (CE_CONT, "%s%d: numRxBufs=(%d*%"FMT64"d) numTxBufs=(%d*%"FMT64"d)"
+ " driverDataSize=%d driverDataPhy=0x%p\n",
+ dp->drvName, dp->unit,
+ dp->vxnNumRxBufs, (uint64_t)sizeof(Vmxnet2_RxRingEntry),
+ dp->vxnNumTxBufs, (uint64_t)sizeof(Vmxnet2_TxRingEntry),
+ driverDataSize, dp->driverDataPhy));
+
+ return SOLVMXNET_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Attach --
+ * Probe and attach driver to stack
+ *
+ * Results:
+ * DDI_SUCCESS
+ * DDI_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i, ret, len, unit;
+ const char *drvName;
+ ddi_acc_handle_t confHdl;
+ uint16_t vid, did;
+ uint8_t revid;
+ struct pci_phys_spec *regs;
+ caddr_t vxnIOp;
+ ddi_acc_handle_t vxnIOHdl;
+ uint32_t vLow, vHigh;
+ gld_mac_info_t *macInfo;
+ vxn_softc_t *dp;
+ boolean_t morphed = FALSE;
+ uint_t regSpaceSize;
+ uint_t chip;
+ uint_t vxnIOSize;
+
+ if (cmd != DDI_ATTACH) {
+ return DDI_FAILURE;
+ }
+
+ unit = ddi_get_instance(dip);
+ drvName = ddi_driver_name(dip);
+
+ /*
+ * Check if chip is supported.
+ */
+ if (pci_config_setup(dip, &confHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: pci_config_setup() failed", drvName, unit);
+ return DDI_FAILURE;
+ }
+
+ vid = pci_config_get16(confHdl, PCI_CONF_VENID);
+ did = pci_config_get16(confHdl, PCI_CONF_DEVID);
+ revid = pci_config_get8(confHdl, PCI_CONF_REVID);
+
+ if (vid == PCI_VENDOR_ID_VMWARE && did == PCI_DEVICE_ID_VMWARE_NET) {
+ /* Found vmxnet */
+ chip = VMXNET_CHIP;
+ }
+ else if (vid == PCI_VENDOR_ID_AMD && did == PCI_DEVICE_ID_AMD_VLANCE) {
+ /* Found vlance (maybe a vmxnet disguise) */
+ chip = LANCE_CHIP;
+ }
+ else {
+ /* Not Found */
+ DPRINTF(3, (CE_WARN, "%s: Vxn_Attach: wrong PCI venid/devid (0x%x, 0x%x)",
+ drvName, vid, did));
+ goto err;
+ }
+
+ DPRINTF(3, (CE_CONT, "%s%d: (vid: 0x%04x, did: 0x%04x, revid: 0x%02x)\n",
+ drvName, unit, vid, did, revid));
+
+ /*
+ * Get device properties
+ */
+ regs = NULL;
+ len = 0;
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (caddr_t)&regs, &len) != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to get reg property",
+ drvName, unit);
+ goto err;
+ }
+
+ ASSERT(regs != NULL && len > 0);
+
+ /*
+ * Search device properties for IO-space
+ */
+ for (i = 0; i <len / sizeof(struct pci_phys_spec); i++) {
+ if ((regs[i].pci_phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_IO) {
+ regSpaceSize = regs[i].pci_size_low;
+ DPRINTF(5, (CE_CONT, "%s%d: Vxn_Attach: regSpaceSize=%d\n",
+ drvName, unit, regSpaceSize));
+ kmem_free(regs, len);
+ goto map_space_found;
+ }
+ }
+
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to find IO space", drvName, unit);
+ kmem_free(regs, len);
+ goto err;
+
+map_space_found:
+
+ /*
+ * Ensure we can access registers through IO space.
+ */
+ ret = pci_config_get16(confHdl, PCI_CONF_COMM);
+ ret |= PCI_COMM_IO | PCI_COMM_ME;
+ pci_config_put16(confHdl, PCI_CONF_COMM, ret);
+
+ if (ddi_regs_map_setup(dip, i, (caddr_t *)&vxnIOp, 0, 0, &dev_attr,
+ &vxnIOHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_regs_map_setup failed",
+ drvName, unit);
+ goto err;
+ }
+
+ if (chip == VMXNET_CHIP) {
+ vxnIOSize = VMXNET_CHIP_IO_RESV_SIZE;
+ }
+ else {
+ /*
+ * Since this is a vlance adapter we can only use it if
+ * its I/0 space is big enough for the adapter to be
+ * capable of morphing. This is the first requirement
+ * for this adapter to potentially be morphable. The
+ * layout of a morphable LANCE adapter is
+ *
+ * I/O space:
+ *
+ * |------------------|
+ * | LANCE IO PORTS |
+ * |------------------|
+ * | MORPH PORT |
+ * |------------------|
+ * | VMXNET IO PORTS |
+ * |------------------|
+ *
+ * VLance has 8 ports of size 4 bytes, the morph port is 4 bytes, and
+ * Vmxnet has 10 ports of size 4 bytes.
+ *
+ * We shift up the ioaddr with the size of the LANCE I/O space since
+ * we want to access the vmxnet ports. We also shift the ioaddr up by
+ * the MORPH_PORT_SIZE so other port access can be independent of
+ * whether we are Vmxnet or a morphed VLance. This means that when
+ * we want to access the MORPH port we need to subtract the size
+ * from ioaddr to get to it.
+ */
+ vxnIOp += LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE;
+ vxnIOSize = LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE +
+ VMXNET_CHIP_IO_RESV_SIZE;
+ }
+
+ /*
+ * Do not attempt to morph non-morphable AMD PCnet
+ */
+ if (vxnIOSize > regSpaceSize) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: "
+ "vlance device is not supported by this driver", drvName, unit);
+ goto err_free_regs_map;
+ }
+
+ /*
+ * Morph, if we found a vlance adapter
+ */
+ if (chip == LANCE_CHIP) {
+ uint16_t magic;
+
+ /* Read morph port to verify that we can morph the adapter */
+ magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
+ if (magic != LANCE_CHIP && magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: Invalid magic, read: 0x%08X",
+ drvName, unit, magic);
+ goto err_free_regs_map;
+ }
+
+ /* Morph */
+ ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), VMXNET_CHIP);
+ morphed = TRUE;
+
+ /* Verify that we morphed correctly */
+ magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
+ if (magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: Couldn't morph adapter."
+ " Invalid magic, read:: 0x%08X", drvName, unit, magic);
+ goto err_morph_back;
+ }
+ }
+
+ /*
+ * Check the version number of the device implementation
+ */
+ vLow = (uint32_t)ddi_get32(vxnIOHdl,
+ (uint32_t *)(vxnIOp+VMXNET_LOW_VERSION));
+ vHigh = (uint32_t)ddi_get32(vxnIOHdl,
+ (uint32_t *)(vxnIOp+VMXNET_HIGH_VERSION));
+
+ if ((vLow & 0xffff0000) != (VMXNET2_MAGIC & 0xffff0000) ||
+ ((VMXNET2_MAGIC < vLow) || (VMXNET2_MAGIC > vHigh))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: driver version 0x%08X doesn't "
+ "match device 0x%08X:0x%08X",
+ drvName, unit, VMXNET2_MAGIC, vLow, vHigh);
+ goto err_version_mismatch;
+ }
+
+ /*
+ * Alloc soft state
+ */
+ macInfo = gld_mac_alloc(dip);
+ if (!macInfo) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: gld_mac_alloc failed",
+ drvName, unit);
+ goto err_gld_mac_alloc;
+ }
+
+ dp = (vxn_softc_t *) kmem_zalloc(sizeof(vxn_softc_t), KM_SLEEP);
+ ASSERT(dp);
+
+ /*
+ * Get interrupt cookie
+ */
+ if (ddi_get_iblock_cookie(dip, 0, &dp->iblockCookie) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_get_iblock_cookie failed",
+ drvName, unit);
+ goto err_get_iblock_cookie;
+ }
+
+ strncpy(dp->drvName, drvName, SOLVMXNET_MAXNAME);
+ dp->unit = unit;
+ dp->dip = dip;
+ dp->macInfo = macInfo;
+ dp->confHdl = confHdl;
+ dp->vxnIOHdl = vxnIOHdl;
+ dp->vxnIOp = vxnIOp;
+ dp->morphed = morphed;
+ dp->nicActive = FALSE;
+ dp->txPending = 0;
+ dp->maxTxFrags = 1;
+
+ /*
+ * Initialize mutexes
+ */
+ mutex_init(&dp->intrlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+ mutex_init(&dp->xmitlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+ mutex_init(&dp->rxlistlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+
+ /*
+ * Allocate and initialize our private and shared data structures
+ */
+ if (Vxn_AllocDriverData(dp) != SOLVMXNET_SUCCESS) {
+ goto err_alloc_driverdata;
+ }
+
+ /*
+ * Read the MAC address from the device
+ */
+ for (i = 0; i < 6; i++) {
+ dp->devAddr.ether_addr_octet[i] =
+ (uint8_t)ddi_get8(vxnIOHdl, (uint8_t *)(vxnIOp + VMXNET_MAC_ADDR + i));
+ }
+ macInfo->gldm_vendor_addr = dp->devAddr.ether_addr_octet;
+ macInfo->gldm_broadcast_addr = etherbroadcastaddr.ether_addr_octet;
+
+ DPRINTF(3, (CE_CONT,
+ "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dp->devAddr.ether_addr_octet[0],
+ dp->devAddr.ether_addr_octet[1],
+ dp->devAddr.ether_addr_octet[2],
+ dp->devAddr.ether_addr_octet[3],
+ dp->devAddr.ether_addr_octet[4],
+ dp->devAddr.ether_addr_octet[5]));
+
+ /*
+ * Configure GLD entry points
+ */
+ macInfo->gldm_devinfo = dip;
+ macInfo->gldm_private = (caddr_t)dp;
+ macInfo->gldm_cookie = dp->iblockCookie;
+ macInfo->gldm_reset = Vxn_Reset;
+ macInfo->gldm_start = Vxn_Start;
+ macInfo->gldm_stop = Vxn_Stop;
+ macInfo->gldm_set_mac_addr = Vxn_SetMacAddress;
+ macInfo->gldm_send = Vxn_Send;
+ macInfo->gldm_set_promiscuous = Vxn_SetPromiscuous;
+ macInfo->gldm_get_stats = Vxn_GetStats;
+ macInfo->gldm_ioctl = NULL;
+ macInfo->gldm_set_multicast= Vxn_SetMulticast;
+ macInfo->gldm_intr = Vxn_Interrupt;
+ macInfo->gldm_mctl = NULL;
+
+ macInfo->gldm_ident = (char *)ddi_driver_name(dip);
+ macInfo->gldm_type = DL_ETHER;
+ macInfo->gldm_minpkt = 0;
+ macInfo->gldm_maxpkt = ETHERMTU;
+ macInfo->gldm_addrlen = ETHERADDRL;
+ macInfo->gldm_saplen = -2;
+ macInfo->gldm_ppa = unit;
+
+ /*
+ * Register with GLD (Generic Lan Driver) framework
+ */
+ if (gld_register(dip,
+ (char *)ddi_driver_name(dip), macInfo) != DDI_SUCCESS) {
+ goto err_gld_register;
+ }
+
+ /*
+ * Add interrupt to system.
+ */
+ if (ddi_add_intr(dip, 0, NULL, NULL, gld_intr,
+ (caddr_t)macInfo) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: ddi_add_intr failed", drvName, unit);
+ goto err_ddi_add_intr;
+ }
+
+ /*
+ * Add to list of interfaces.
+ */
+ mutex_enter(&vxnListLock);
+ dp->next = &vxnList;
+ dp->prev = vxnList.prev;
+ vxnList.prev->next = dp;
+ vxnList.prev = dp;
+ mutex_exit(&vxnListLock);
+
+ /*
+ * Success
+ */
+ return DDI_SUCCESS;
+
+err_ddi_add_intr:
+ gld_unregister(macInfo);
+
+err_gld_register:
+ Vxn_FreeDriverData(dp);
+
+err_alloc_driverdata:
+ mutex_destroy(&dp->intrlock);
+ mutex_destroy(&dp->xmitlock);
+
+err_get_iblock_cookie:
+ kmem_free(dp, sizeof(*dp));
+ gld_mac_free(macInfo);
+
+err_gld_mac_alloc:
+err_version_mismatch:
+err_morph_back:
+ if (morphed) {
+ ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);
+ }
+
+err_free_regs_map:
+ ddi_regs_map_free(&vxnIOHdl);
+
+err:
+ pci_config_teardown(&confHdl);
+ return DDI_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Detach --
+ * Called on module unload
+ *
+ * Results:
+ * DDI_SUCCESS
+ * DDI_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ gld_mac_info_t *macInfo;
+ vxn_softc_t *dp;
+
+ macInfo = (gld_mac_info_t *)ddi_get_driver_private(dip);
+ dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ if (cmd == DDI_DETACH) {
+ /*
+ * Tear down interrupt
+ */
+ ddi_remove_intr(dip, 0, macInfo->gldm_cookie);
+ gld_unregister(macInfo);
+
+ /*
+ * Quiesce hardware
+ */
+ Vxn_Stop(macInfo);
+
+ /*
+ * Free driver-data, tx/rx buffers etc
+ */
+ Vxn_FreeDriverData(dp);
+
+ /*
+ * Destroy locks
+ */
+ mutex_destroy(&dp->intrlock);
+ mutex_destroy(&dp->xmitlock);
+
+ /*
+ * Unmorph
+ */
+ if (dp->morphed) {
+ uint16_t magic;
+
+ /* Verify that we had morphed earlier */
+ magic = ddi_get16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
+ if (magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Detach: Adapter not morphed"
+ " magic=0x%08X", dp->drvName, dp->unit, magic);
+ }
+ else {
+ /* Unmorph */
+ ddi_put16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);
+
+ /* Verify */
+ magic = ddi_get16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
+ if (magic != LANCE_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Detach: Unable to unmorph adapter"
+ " magic=0x%08X", dp->drvName, dp->unit, magic);
+ }
+ }
+ }
+
+ /*
+ * Release resister mappings
+ */
+ ddi_regs_map_free(&dp->vxnIOHdl);
+ pci_config_teardown(&dp->confHdl);
+
+ /*
+ * Remove from list of interfaces.
+ */
+ mutex_enter(&vxnListLock);
+ ASSERT(dp != &vxnList);
+ dp->prev->next = dp->next;
+ dp->next->prev = dp->prev;
+ mutex_exit(&vxnListLock);
+
+ /*
+ * Release memory
+ */
+ kmem_free(dp, sizeof(*dp));
+ gld_mac_free(macInfo);
+
+ return DDI_SUCCESS;
+ }
+ else {
+ return DDI_FAILURE;
+ }
+}
+
+static struct module_info vxnminfo = {
+ 0, /* mi_idnum */
+ "vmxnet", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ QHIWATER, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit vxnrinit = {
+ NULL, /* qi_putp */
+ gld_rsrv, /* qi_srvp */
+ gld_open, /* qi_qopen */
+ gld_close, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &vxnminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit vxnwinit = {
+ gld_wput, /* qi_putp */
+ gld_wsrv, /* qi_srvp */
+ NULL, /* qi_qopen */
+ NULL, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &vxnminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab vxn_info = {
+ &vxnrinit, /* st_rdinit */
+ &vxnwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_vxn_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &vxn_info, /* cb_stream */
+ D_NEW|D_MP /* cb_flag */
+};
+
+static struct dev_ops vxn_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ gld_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ Vxn_Attach, /* devo_attach */
+ Vxn_Detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_vxn_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ ddi_power /* devo_power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ ident,
+ &vxn_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, {&modldrv, NULL,}
+};
+
+
+/*
+ * Module load entry point
+ */
+int
+_init(void)
+{
+ int err;
+
+ DPRINTF(5, (CE_CONT, "vxn: _init:\n"));
+ /* Initialize interface list */
+ vxnList.next = vxnList.prev = &vxnList;
+ mutex_init(&vxnListLock, NULL, MUTEX_DRIVER, NULL);
+ if ((err = mod_install(&modlinkage)) != 0) {
+ mutex_destroy(&vxnListLock);
+ }
+ return err;
+}
+
+/*
+ * Module unload entry point
+ */
+int
+_fini(void)
+{
+ int err;
+
+ DPRINTF(5, (CE_CONT, "vxn: _fini:\n"));
+ if ((err = mod_remove(&modlinkage)) == 0) {
+ mutex_destroy(&vxnListLock);
+ }
+ return err;
+}
+
+/*
+ * Module info entry point
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet.conf b/usr/src/uts/intel/io/vmxnet/vmxnet.conf
new file mode 100644
index 0000000000..eb3b160412
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet.conf
@@ -0,0 +1,24 @@
+#
+# 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) 2012, Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h b/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h
new file mode 100644
index 0000000000..5ea437df72
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h
@@ -0,0 +1,436 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef _VMXNET2_DEF_H_
+#define _VMXNET2_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "net_sg.h"
+#include "vmxnet_def.h"
+
+
+/*
+ * Magic number that identifies this version of the vmxnet protocol.
+ */
+#define VMXNET2_MAGIC 0xbabe864f
+
+/* size of the rx ring */
+#define VMXNET2_MAX_NUM_RX_BUFFERS 128
+#define VMXNET2_DEFAULT_NUM_RX_BUFFERS 100
+
+
+/* size of the rx ring when enhanced vmxnet is used */
+#define ENHANCED_VMXNET2_MAX_NUM_RX_BUFFERS 512
+#define ENHANCED_VMXNET2_DEFAULT_NUM_RX_BUFFERS 150
+
+/* size of the 2nd rx ring */
+#define VMXNET2_MAX_NUM_RX_BUFFERS2 2048
+#define VMXNET2_DEFAULT_NUM_RX_BUFFERS2 512
+
+/* size of the tx ring */
+#define VMXNET2_MAX_NUM_TX_BUFFERS 128
+#define VMXNET2_DEFAULT_NUM_TX_BUFFERS 100
+
+/* size of the tx ring when tso/jf is used */
+#define VMXNET2_MAX_NUM_TX_BUFFERS_TSO 512
+#define VMXNET2_DEFAULT_NUM_TX_BUFFERS_TSO 256
+
+enum {
+ VMXNET2_OWNERSHIP_DRIVER,
+ VMXNET2_OWNERSHIP_DRIVER_PENDING,
+ VMXNET2_OWNERSHIP_NIC,
+ VMXNET2_OWNERSHIP_NIC_PENDING,
+ VMXNET2_OWNERSHIP_NIC_FRAG,
+ VMXNET2_OWNERSHIP_DRIVER_FRAG,
+};
+
+#define VMXNET2_SG_DEFAULT_LENGTH 6
+
+typedef struct Vmxnet2_SG_Array {
+ uint16 addrType;
+ uint16 length;
+ NetSG_Elem sg[VMXNET2_SG_DEFAULT_LENGTH];
+} Vmxnet2_SG_Array;
+
+typedef struct Vmxnet2_RxRingEntry {
+ uint64 paddr; /* Physical address of the packet data. */
+ uint32 bufferLength; /* The length of the data at paddr. */
+ uint32 actualLength; /* The actual length of the received data. */
+ uint16 ownership; /* Who owns the packet. */
+ uint16 flags; /* Flags as defined below. */
+ uint32 index; /*
+ * Currently:
+ *
+ * This is being used as an packet index to
+ * rx buffers.
+ *
+ * Originally:
+ *
+ * was void* driverData ("Driver specific data.")
+ * which was used for sk_buf**s in Linux and
+ * VmxnetRxBuff*s in Windows. It could not be
+ * here because the structure needs to be the
+ * same size between architectures, and it was
+ * not used on the device side, anyway. Look
+ * for its replacement in
+ * Vmxnet_Private.rxRingBuffPtr on Linux and
+ * VmxnetAdapter.rxRingBuffPtr on Windows.
+ */
+} Vmxnet2_RxRingEntry;
+
+/*
+ * Vmxnet2_RxRingEntry flags:
+ *
+ * VMXNET2_RX_HW_XSUM_OK The hardware verified the TCP/UDP checksum.
+ * VMXNET2_RX_WITH_FRAG More data is in the 2nd ring
+ * VMXNET2_RX_FRAG_EOP This is the last frag, the only valid flag for
+ * 2nd ring entry
+ *
+ */
+#define VMXNET2_RX_HW_XSUM_OK 0x01
+#define VMXNET2_RX_WITH_FRAG 0x02
+#define VMXNET2_RX_FRAG_EOP 0x04
+
+typedef struct Vmxnet2_TxRingEntry {
+ uint16 flags; /* Flags as defined below. */
+ uint16 ownership; /* Who owns this packet. */
+ uint32 extra; /*
+ * was void* driverData ("Driver specific data.")
+ * which was used for sk_buf*s in Linux and
+ * VmxnetTxInfo*s in Windows. It could not be
+ * here because the structure needs to be the
+ * same size between architectures, and it was
+ * not used on the device side, anyway. Look
+ * for its replacement in
+ * Vmxnet_Private.txRingBuffPtr on Linux and
+ * VmxnetAdapter.txRingBuffPtr on Windows.
+ */
+ uint32 tsoMss; /* TSO pkt MSS */
+ Vmxnet2_SG_Array sg; /* Packet data. */
+} Vmxnet2_TxRingEntry;
+
+/*
+ * Vmxnet2_TxRingEntry flags:
+ *
+ * VMXNET2_TX_CAN_KEEP The implementation can return the tx ring entry
+ * to the driver when it is ready as opposed to
+ * before the transmit call from the driver completes.
+ * VMXNET2_TX_RING_LOW The driver's transmit ring buffer is low on free
+ * slots.
+ * VMXNET2_TX_HW_XSUM The hardware should perform the TCP/UDP checksum
+ * VMXNET2_TX_TSO The hardware should do TCP segmentation.
+ * VMXNET2_TX_PINNED_BUFFER The driver used one of the preallocated vmkernel
+ * buffers *and* it has been pinned with Net_PinTxBuffers.
+ * VMXNET2_TX_MORE This is *not* the last tx entry for the pkt.
+ * All flags except VMXNET2_TX_MORE are ignored
+ * for the subsequent tx entries.
+ */
+#define VMXNET2_TX_CAN_KEEP 0x0001
+#define VMXNET2_TX_RING_LOW 0x0002
+#define VMXNET2_TX_HW_XSUM 0x0004
+#define VMXNET2_TX_TSO 0x0008
+#define VMXNET2_TX_PINNED_BUFFER 0x0010
+#define VMXNET2_TX_MORE 0x0020
+
+/*
+ * Structure used by implementations. This structure allows the inline
+ * functions below to be used.
+ */
+typedef struct Vmxnet2_RxRingInfo {
+ Vmxnet2_RxRingEntry *base; /* starting addr of the ring */
+ uint32 nicNext; /* next entry to use in the ring */
+ uint32 ringLength; /* # of entries in the ring */
+ PA startPA; /* PA of the starting addr of the ring */
+#ifdef VMX86_DEBUG
+ const char *name;
+#endif
+} Vmxnet2_RxRingInfo;
+
+typedef struct Vmxnet2_TxRingInfo {
+ Vmxnet2_TxRingEntry *base; /* starting addr of the ring */
+ uint32 nicNext; /* next entry to use in the ring */
+ uint32 ringLength; /* # of entries in the ring */
+ PA startPA; /* PA of the starting addr of the ring */
+#ifdef VMX86_DEBUG
+ const char *name;
+#endif
+} Vmxnet2_TxRingInfo;
+
+typedef struct Vmxnet2_ImplData {
+ Vmxnet2_RxRingInfo rxRing;
+ Vmxnet2_RxRingInfo rxRing2;
+ Vmxnet2_TxRingInfo txRing;
+
+ struct PhysMem_Token *ddPhysMemToken;
+} Vmxnet2_ImplData;
+
+/*
+ * Used internally for performance studies. By default this will be off so there
+ * should be no compatibilty or other interferences.
+ */
+
+/* #define ENABLE_VMXNET2_PROFILING */
+
+
+#ifdef ENABLE_VMXNET2_PROFILING
+typedef struct Vmxnet2_VmmStats {
+ uint64 vIntTSC; /* the time that virtual int was posted */
+ uint64 actionsCount; /* Number of actions received */
+ uint64 numWasteActions; /* Number of non-productive actions */
+} Vmxnet2_VmmStats;
+#endif
+
+typedef struct Vmxnet2_DriverStats {
+ uint32 transmits; /* # of times that the drivers transmit function */
+ /* is called. The driver could transmit more */
+ /* than one packet per call. */
+ uint32 pktsTransmitted; /* # of packets transmitted. */
+ uint32 noCopyTransmits; /* # of packets that are transmitted without */
+ /* copying any data. */
+ uint32 copyTransmits; /* # of packets that are transmittted by copying */
+ /* the data into a buffer. */
+ uint32 maxTxsPending; /* Max # of transmits outstanding. */
+ uint32 txStopped; /* # of times that transmits got stopped because */
+ /* the tx ring was full. */
+ uint32 txRingOverflow; /* # of times that transmits got deferred bc */
+ /* the tx ring was full. This must be >= */
+ /* txStopped since there will be one */
+ /* txStopped when the ring fills up and then */
+ /* one txsRingOverflow for each packet that */
+ /* that gets deferred until there is space. */
+ uint32 interrupts; /* # of times interrupted. */
+ uint32 pktsReceived; /* # of packets received. */
+ uint32 rxBuffersLow; /* # of times that the driver was low on */
+ /* receive buffers. */
+#ifdef ENABLE_VMXNET2_PROFILING
+ Vmxnet2_VmmStats vmmStats; /* vmm related stats for perf study */
+#endif
+} Vmxnet2_DriverStats;
+
+/*
+ * Shared data structure between the vm, the vmm, and the vmkernel.
+ * This structure was originally arranged to try to group common data
+ * on 32-byte cache lines, but bit rot and the fact that we no longer
+ * run on many CPUs with that cacheline size killed that optimization.
+ * vmxnet3 should target 128 byte sizes and alignments to optimize for
+ * the 64 byte cacheline pairs on P4.
+ */
+typedef struct Vmxnet2_DriverData {
+ /*
+ * Magic must be first.
+ */
+ Vmxnet_DDMagic magic;
+
+ /*
+ * Receive fields.
+ */
+ uint32 rxRingLength; /* Length of the receive ring. */
+ uint32 rxDriverNext; /* Index of the next packet that will */
+ /* be filled in by the impl */
+
+ uint32 rxRingLength2; /* Length of the 2nd receive ring. */
+ uint32 rxDriverNext2; /* Index of the next packet that will */
+ /* be filled in by the impl */
+
+ uint32 notUsed1; /* was "irq" */
+
+ /*
+ * Interface flags and multicast filter.
+ */
+ uint32 ifflags;
+ uint32 LADRF[VMXNET_MAX_LADRF];
+
+ /*
+ * Transmit fields
+ */
+ uint32 txDontClusterSize; /* All packets <= this will be transmitted */
+ /* immediately, regardless of clustering */
+ /* settings [was fill[1]] */
+ uint32 txRingLength; /* Length of the transmit ring. */
+ uint32 txDriverCur; /* Index of the next packet to be */
+ /* returned by the implementation.*/
+ uint32 txDriverNext; /* Index of the entry in the ring */
+ /* buffer to use for the next packet.*/
+ uint32 txStopped; /* The driver has stopped transmitting */
+ /* because its ring buffer is full.*/
+ uint32 txClusterLength; /* Maximum number of packets to */
+ /* put in the ring buffer before */
+ /* asking the implementation to */
+ /* transmit the packets in the buffer.*/
+ uint32 txNumDeferred; /* Number of packets that have been */
+ /* queued in the ring buffer since */
+ /* the last time the implementation */
+ /* was asked to transmit. */
+ uint32 notUsed3; /* This field is deprecated but still used */
+ /* as minXmitPhysLength on the escher branch. */
+ /* It cannot be used for other purposes */
+ /* until escher vms no longer are allowed */
+ /* to install this driver. */
+
+ uint32 totalRxBuffers; /* used by esx for max rx buffers */
+ uint64 rxBufferPhysStart; /* used by esx for pinng rx buffers */
+ /*
+ * Extra fields for future expansion.
+ */
+ uint32 extra[2];
+
+ uint16 maxFrags; /* # of frags the driver can handle */
+ uint16 featureCtl; /* for driver to enable some feature */
+
+ /*
+ * The following fields are used to save the nicNext indexes part
+ * of implData in the vmkernel when disconnecting the adapter, we
+ * need them when we reconnect. This mechanism is used for
+ * checkpointing as well.
+ */
+ uint32 savedRxNICNext;
+ uint32 savedRxNICNext2;
+ uint32 savedTxNICNext;
+
+ /*
+ * Fields used during initialization or debugging.
+ */
+ uint32 length;
+ uint32 rxRingOffset;
+ uint32 rxRingOffset2;
+ uint32 txRingOffset;
+ uint32 debugLevel;
+ uint32 txBufferPhysStart;
+ uint32 txBufferPhysLength;
+ uint32 txPktMaxSize;
+
+ /*
+ * Driver statistics.
+ */
+ Vmxnet2_DriverStats stats;
+} Vmxnet2_DriverData;
+
+/*
+ * Shared between VMM and Vmkernel part of vmxnet2 to optimize action posting
+ * VMM writes 1 (don't post) or 0 (okay to post) and vmk reads this.
+ */
+typedef struct VmxnetVMKShared {
+ uint32 dontPostActions;
+} VmxnetVMKShared;
+
+#if defined VMX86_VMX || defined VMKERNEL
+
+/*
+ * Inline functions used to assist the implementation of the vmxnet interface.
+ */
+
+/*
+ * Get the next empty packet out of the receive ring and move to
+ * the next packet.
+ */
+static INLINE Vmxnet2_RxRingEntry *
+Vmxnet2_GetNextRx(Vmxnet2_RxRingInfo *ri, uint16 ownership)
+{
+ Vmxnet2_RxRingEntry *rre = ri->base + ri->nicNext;
+ if (rre->ownership == ownership) {
+ VMXNET_INC(ri->nicNext, ri->ringLength);
+ } else {
+ rre = NULL;
+ }
+
+ return rre;
+}
+
+/*
+ * Return ownership of a packet in the receive ring to the driver.
+ */
+static INLINE void
+Vmxnet2_PutRx(Vmxnet2_RxRingEntry *rre, uint32 pktLength, uint16 ownership)
+{
+ rre->actualLength = pktLength;
+ COMPILER_MEM_BARRIER();
+ rre->ownership = ownership;
+}
+
+/*
+ * Get the next pending packet out of the transmit ring.
+ */
+static INLINE Vmxnet2_TxRingEntry *
+Vmxnet2_GetNextTx(Vmxnet2_TxRingInfo *ri)
+{
+ Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;
+ if (txre->ownership == VMXNET2_OWNERSHIP_NIC) {
+ return txre;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Move to the next entry in the transmit ring.
+ */
+static INLINE unsigned int
+Vmxnet2_IncNextTx(Vmxnet2_TxRingInfo *ri)
+{
+ unsigned int prev = ri->nicNext;
+ Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;
+
+ txre->ownership = VMXNET2_OWNERSHIP_NIC_PENDING;
+
+ VMXNET_INC(ri->nicNext, ri->ringLength);
+ return prev;
+}
+
+/*
+ * Get the indicated entry from transmit ring.
+ */
+static INLINE Vmxnet2_TxRingEntry *
+Vmxnet2_GetTxEntry(Vmxnet2_TxRingInfo *ri, unsigned int idx)
+{
+ return ri->base + idx;
+}
+
+/*
+ * Get the indicated entry from the given rx ring
+ */
+static INLINE Vmxnet2_RxRingEntry *
+Vmxnet2_GetRxEntry(Vmxnet2_RxRingInfo *ri, unsigned int idx)
+{
+ return ri->base + idx;
+}
+
+#endif /* defined VMX86_VMX || defined VMKERNEL */
+
+#endif
+
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet_def.h b/usr/src/uts/intel/io/vmxnet/vmxnet_def.h
new file mode 100644
index 0000000000..703466c995
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet_def.h
@@ -0,0 +1,184 @@
+/*********************************************************
+ * Copyright (C) 1999 VMware, Inc. All rights reserved.
+ *
+ * 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 version 2 and no 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef _VMXNET_DEF_H_
+#define _VMXNET_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "net_sg.h"
+#include "vmnet_def.h"
+
+
+/*
+ * Vmxnet I/O ports, used by both the vmxnet driver and
+ * the device emulation code.
+ */
+
+#define VMXNET_INIT_ADDR 0x00
+#define VMXNET_INIT_LENGTH 0x04
+#define VMXNET_TX_ADDR 0x08
+#define VMXNET_COMMAND_ADDR 0x0c
+#define VMXNET_MAC_ADDR 0x10
+#define VMXNET_LOW_VERSION 0x18
+#define VMXNET_HIGH_VERSION 0x1c
+#define VMXNET_STATUS_ADDR 0x20
+#define VMXNET_TOE_INIT_ADDR 0x24
+#define VMXNET_APROM_ADDR 0x28
+#define VMXNET_INT_ENABLE_ADDR 0x30
+#define VMXNET_WAKE_PKT_PATTERNS 0x34
+
+/*
+ * Vmxnet command register values.
+ */
+#define VMXNET_CMD_INTR_ACK 0x0001
+#define VMXNET_CMD_UPDATE_LADRF 0x0002
+#define VMXNET_CMD_UPDATE_IFF 0x0004
+#define VMXNET_CMD_UNUSED 1 0x0008
+#define VMXNET_CMD_UNUSED_2 0x0010
+#define VMXNET_CMD_INTR_DISABLE 0x0020
+#define VMXNET_CMD_INTR_ENABLE 0x0040
+#define VMXNET_CMD_UNUSED_3 0x0080
+#define VMXNET_CMD_CHECK_TX_DONE 0x0100
+#define VMXNET_CMD_GET_NUM_RX_BUFFERS 0x0200
+#define VMXNET_CMD_GET_NUM_TX_BUFFERS 0x0400
+#define VMXNET_CMD_PIN_TX_BUFFERS 0x0800
+#define VMXNET_CMD_GET_CAPABILITIES 0x1000
+#define VMXNET_CMD_GET_FEATURES 0x2000
+#define VMXNET_CMD_SET_POWER_FULL 0x4000
+#define VMXNET_CMD_SET_POWER_LOW 0x8000
+
+/*
+ * Vmxnet status register values.
+ */
+#define VMXNET_STATUS_CONNECTED 0x0001
+#define VMXNET_STATUS_ENABLED 0x0002
+#define VMXNET_STATUS_TX_PINNED 0x0004
+
+/*
+ * Values for the interface flags.
+ */
+#define VMXNET_IFF_PROMISC 0x01
+#define VMXNET_IFF_BROADCAST 0x02
+#define VMXNET_IFF_MULTICAST 0x04
+#define VMXNET_IFF_DIRECTED 0x08
+
+/*
+ * Length of the multicast address filter.
+ */
+#define VMXNET_MAX_LADRF 2
+
+/*
+ * Size of Vmxnet APROM.
+ */
+#define VMXNET_APROM_SIZE 6
+
+/*
+ * An invalid ring index.
+ */
+#define VMXNET_INVALID_RING_INDEX (-1)
+
+/*
+ * Features that are implemented by the driver. These are driver
+ * specific so not all features will be listed here. In addition not all
+ * drivers have to pay attention to these feature flags.
+ *
+ * VMXNET_FEATURE_ZERO_COPY_TX The driver won't do any copies as long as
+ * the packet length is >
+ * Vmxnet_DriverData.minTxPhysLength.
+ *
+ * VMXNET_FEATURE_TSO The driver will use the TSO capabilities
+ * of the underlying hardware if available
+ * and enabled.
+ *
+ * VMXNET_FEATURE_JUMBO_FRAME The driver can send/rcv jumbo frame
+ *
+ * VMXNET_FEATURE_LPD The backend can deliver large pkts
+ */
+#define VMXNET_FEATURE_ZERO_COPY_TX 0x01
+#define VMXNET_FEATURE_TSO 0x02
+#define VMXNET_FEATURE_JUMBO_FRAME 0x04
+#define VMXNET_FEATURE_LPD 0x08
+
+/*
+ * Define the set of capabilities required by each feature above
+ */
+#define VMXNET_FEATURE_ZERO_COPY_TX_CAPS VMXNET_CAP_SG
+#define VMXNET_FEATURE_TSO_CAPS VMXNET_CAP_TSO
+#define VMXNET_HIGHEST_FEATURE_BIT VMXNET_FEATURE_TSO
+
+#define VMXNET_INC(val, max) \
+ val++; \
+ if (UNLIKELY(val == max)) { \
+ val = 0; \
+ }
+
+/*
+ * code that just wants to switch on the different versions of the
+ * guest<->implementation protocol can cast driver data to this.
+ */
+typedef uint32 Vmxnet_DDMagic;
+
+/*
+ * Wake packet pattern commands sent through VMXNET_WAKE_PKT_PATTERNS port
+ */
+
+#define VMXNET_PM_OPCODE_START 3 /* args: cnt of wake packet patterns */
+#define VMXNET_PM_OPCODE_LEN 2 /* args: index of wake packet pattern */
+ /* number of pattern byte values */
+#define VMXNET_PM_OPCODE_DATA 1 /* args: index of wake packet pattern */
+ /* offset in pattern byte values list */
+ /* packet byte offset */
+ /* packet byte value */
+#define VMXNET_PM_OPCODE_END 0 /* args: <none> */
+
+typedef union Vmxnet_WakePktCmd {
+ uint32 pktData : 32;
+ struct {
+ unsigned cmd : 2; /* wake packet pattern cmd [from list above] */
+ unsigned cnt : 3; /* cnt wk pkt pttrns 1..MAX_NUM_FILTER_PTTRNS */
+ unsigned ind : 3; /* ind wk pkt pttrn 0..MAX_NUM_FILTER_PTTRNS-1 */
+ unsigned lenOff : 8; /* num pttrn byte vals 1..MAX_PKT_FILTER_SIZE */
+ /* OR offset in pattern byte values list */
+ /* 0..MAX_PKT_FILTER_SIZE-1 */
+ unsigned byteOff : 8; /* pkt byte offset 0..MAX_PKT_FILTER_SIZE-1 */
+ unsigned byteVal : 8; /* packet byte value 0..255 */
+ } pktPttrn;
+} Vmxnet_WakePktCmd;
+
+#endif /* _VMXNET_DEF_H_ */
diff --git a/usr/src/uts/intel/ipf/Makefile b/usr/src/uts/intel/ipf/Makefile
index 046a6c223d..e18158a43d 100644
--- a/usr/src/uts/intel/ipf/Makefile
+++ b/usr/src/uts/intel/ipf/Makefile
@@ -58,7 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
CPPFLAGS += -DIPFILTER_LKM -DIPFILTER_LOG -DIPFILTER_LOOKUP -DUSE_INET6
CPPFLAGS += -DSUNDDI -DSOLARIS2=$(RELEASE_MINOR) -DIRE_ILL_CN
-LDFLAGS += -dy -Ndrv/ip -Nmisc/md5 -Nmisc/neti -Nmisc/hook -Nmisc/kcf
+LDFLAGS += -dy -Ndrv/ip -Nmisc/md5 -Nmisc/neti -Nmisc/hook -Nmisc/kcf -Ndrv/vnd
INC_PATH += -I$(UTSBASE)/common/inet/ipf
diff --git a/usr/src/uts/intel/ipf/ipf.global-objs.debug64 b/usr/src/uts/intel/ipf/ipf.global-objs.debug64
index 663613cee3..0af6da41fa 100644
--- a/usr/src/uts/intel/ipf/ipf.global-objs.debug64
+++ b/usr/src/uts/intel/ipf/ipf.global-objs.debug64
@@ -39,6 +39,10 @@ hook4_nicevents
hook4_nicevents_gz
hook4_out
hook4_out_gz
+hook4_vnd_in
+hook4_vnd_in_gz
+hook4_vnd_out
+hook4_vnd_out_gz
hook6_in
hook6_in_gz
hook6_loop_in
@@ -49,6 +53,10 @@ hook6_nicevents
hook6_nicevents_gz
hook6_out
hook6_out_gz
+hook6_vnd_in
+hook6_vnd_in_gz
+hook6_vnd_out
+hook6_vnd_out_gz
icmpreplytype4
icmpreplytype6
icmptoicmp6types
diff --git a/usr/src/uts/intel/iptun/Makefile b/usr/src/uts/intel/iptun/Makefile
index 24fb7f9fe8..c71c39f911 100644
--- a/usr/src/uts/intel/iptun/Makefile
+++ b/usr/src/uts/intel/iptun/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -Ndrv/dld -Nmisc/dls -Nmisc/mac -Ndrv/ip
-INC_PATH += -I$(UTSBASE)/common/io/bpf
LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
diff --git a/usr/src/uts/intel/ixgbe/Makefile b/usr/src/uts/intel/ixgbe/Makefile
index c0408d0667..26d0e670f2 100644
--- a/usr/src/uts/intel/ixgbe/Makefile
+++ b/usr/src/uts/intel/ixgbe/Makefile
@@ -71,6 +71,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
# Driver depends on MAC
#
LDFLAGS += -dy -N misc/mac
+MAPFILES += ddi mac random kernel
#
# Default build targets.
@@ -96,4 +97,5 @@ install: $(INSTALL_DEPS)
#
# Include common targets.
#
+include $(UTSBASE)/Makefile.mapfile
include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/kdi/kdi_idt.c b/usr/src/uts/intel/kdi/kdi_idt.c
index 4bd6ca73e1..73d9628e62 100644
--- a/usr/src/uts/intel/kdi/kdi_idt.c
+++ b/usr/src/uts/intel/kdi/kdi_idt.c
@@ -217,7 +217,8 @@ kdi_idt_patch(caddr_t code, size_t sz)
continue; /* uses kernel's handler */
gd = &kdi_idt[i];
- patch = (uchar_t *)GATESEG_GETOFFSET(gd) + KDI_MSR_PATCHOFF;
+ patch = ((uchar_t *)(uintptr_t)GATESEG_GETOFFSET(gd)) +
+ KDI_MSR_PATCHOFF;
/*
* We can't ASSERT that there's a nop here, because this may be
diff --git a/usr/src/uts/intel/lx_brand/Makefile b/usr/src/uts/intel/lx_brand/Makefile
new file mode 100644
index 0000000000..daa16fa868
--- /dev/null
+++ b/usr/src/uts/intel/lx_brand/Makefile
@@ -0,0 +1,111 @@
+#
+# 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 2016 Joyent, Inc.
+#
+# This makefile drives the production of the kernel component of
+# the lx brand
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Path to where brand common sources live
+#
+LX_CMN = $(SRC)/common/brand/lx
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_brand
+OBJECTS = $(LX_BRAND_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_BRAND_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_BRAND_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN) -I$(SRC)/common
+INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
+INC_PATH += -I$(UTSBASE)/common/fs/sockfs
+INC_PATH += -I$(UTSBASE)/common/fs/zfs
+AS_INC_PATH += -I$(UTSBASE)/i86pc/genassym/$(OBJS_DIR)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nexec/elfexec -Nfs/fifofs -Nfs/sockfs -Ndrv/ip -Nfs/zfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_brand/Makefile.rules
diff --git a/usr/src/uts/intel/lx_brand/Makefile.rules b/usr/src/uts/intel/lx_brand/Makefile.rules
new file mode 100644
index 0000000000..1f641cea05
--- /dev/null
+++ b/usr/src/uts/intel/lx_brand/Makefile.rules
@@ -0,0 +1,100 @@
+#
+# 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 2016 Joyent, Inc.
+#
+#
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR_OBJ64)/%.o: $(LX_CMN)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(LX_CMN)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+#
+# Section 1b: Lint `object' build rules.
+#
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/os/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/syscall/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(LX_CMN)/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/brand/lx/%.s
+ @($(LHEAD) $(LINT.s) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/brand/lx/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_cgroup/Makefile b/usr/src/uts/intel/lx_cgroup/Makefile
new file mode 100644
index 0000000000..6f9116f32a
--- /dev/null
+++ b/usr/src/uts/intel/lx_cgroup/Makefile
@@ -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 2015 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_cgroup
+OBJECTS = $(LX_CGROUP_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_CGROUP_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nbrand/lx_brand
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_cgroup/Makefile.rules
diff --git a/usr/src/uts/intel/lx_cgroup/Makefile.rules b/usr/src/uts/intel/lx_cgroup/Makefile.rules
new file mode 100644
index 0000000000..6d4c7c4060
--- /dev/null
+++ b/usr/src/uts/intel/lx_cgroup/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/cgroups/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/cgroups/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_devfs/Makefile b/usr/src/uts/intel/lx_devfs/Makefile
new file mode 100644
index 0000000000..1254f596eb
--- /dev/null
+++ b/usr/src/uts/intel/lx_devfs/Makefile
@@ -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 2015 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_devfs
+OBJECTS = $(LX_DEVFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_DEVFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nbrand/lx_brand
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_devfs/Makefile.rules
diff --git a/usr/src/uts/intel/lx_devfs/Makefile.rules b/usr/src/uts/intel/lx_devfs/Makefile.rules
new file mode 100644
index 0000000000..4b9748314c
--- /dev/null
+++ b/usr/src/uts/intel/lx_devfs/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/devfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/devfs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_netlink/Makefile b/usr/src/uts/intel/lx_netlink/Makefile
new file mode 100644
index 0000000000..2ada8e28a6
--- /dev/null
+++ b/usr/src/uts/intel/lx_netlink/Makefile
@@ -0,0 +1,77 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_netlink
+OBJECTS = $(LX_NETLINK_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_NETLINK_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+LDFLAGS += -dy -Ndrv/ip -Nfs/sockfs -Nbrand/lx_brand
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/io/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/io/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_proc/Makefile b/usr/src/uts/intel/lx_proc/Makefile
new file mode 100644
index 0000000000..6e065b7899
--- /dev/null
+++ b/usr/src/uts/intel/lx_proc/Makefile
@@ -0,0 +1,117 @@
+#
+# 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
+#
+#
+# uts/intel/lx_proc/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2015 Joyent, Inc.
+#
+# This makefile drives the production of the lxproc file system
+# kernel module.
+#
+# i86 architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Path to where brand common sources live
+#
+LX_CMN = $(SRC)/common/brand/lx
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_proc
+OBJECTS = $(LX_PROC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_PROC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+INC_PATH += -I$(UTSBASE)/common/fs/zfs
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Overrides.
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Depends on procfs and lx_brand
+#
+LDFLAGS += -dy -Nfs/procfs -Nbrand/lx_brand -Ndrv/inotify -Ndrv/ip
+LDFLAGS += -Nfs/sockfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_proc/Makefile.rules
diff --git a/usr/src/uts/intel/lx_proc/Makefile.rules b/usr/src/uts/intel/lx_proc/Makefile.rules
new file mode 100644
index 0000000000..b8592d2fdd
--- /dev/null
+++ b/usr/src/uts/intel/lx_proc/Makefile.rules
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/procfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+#
+# Section 1b: Lint `object' build rules.
+#
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/procfs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_ptm/Makefile b/usr/src/uts/intel/lx_ptm/Makefile
new file mode 100644
index 0000000000..dcead27da7
--- /dev/null
+++ b/usr/src/uts/intel/lx_ptm/Makefile
@@ -0,0 +1,91 @@
+#
+# 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
+#
+#
+# uts/intel/lx_ptm/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This makefile drives the production of the lx_ptm driver
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_ptm
+OBJECTS = $(LX_PTM_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_PTM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/io/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/io/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_sysfs/Makefile b/usr/src/uts/intel/lx_sysfs/Makefile
new file mode 100644
index 0000000000..14e0603533
--- /dev/null
+++ b/usr/src/uts/intel/lx_sysfs/Makefile
@@ -0,0 +1,66 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_sysfs
+OBJECTS = $(LX_SYS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_SYS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nbrand/lx_brand -Ndrv/ip
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+# XXX JJ
+# LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+# LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_sysfs/Makefile.rules
diff --git a/usr/src/uts/intel/lx_sysfs/Makefile.rules b/usr/src/uts/intel/lx_sysfs/Makefile.rules
new file mode 100644
index 0000000000..c9d4c28f85
--- /dev/null
+++ b/usr/src/uts/intel/lx_sysfs/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/sysfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/sysfs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lx_systrace/Makefile b/usr/src/uts/intel/lx_systrace/Makefile
new file mode 100644
index 0000000000..20c4a6a3a3
--- /dev/null
+++ b/usr/src/uts/intel/lx_systrace/Makefile
@@ -0,0 +1,80 @@
+#
+# 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.
+#
+
+UTSBASE = ../..
+
+MODULE = lx_systrace
+OBJECTS = $(LX_SYSTRACE_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_SYSTRACE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK = $(USR_DTRACE_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/dtrace
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
+LDFLAGS += -dy -Ndrv/dtrace -Nbrand/lx_brand
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_STATIC_UNUSED
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(USR_DTRACE_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/dtrace/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/dtrace/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lxautofs/Makefile b/usr/src/uts/intel/lxautofs/Makefile
new file mode 100644
index 0000000000..5de66af48f
--- /dev/null
+++ b/usr/src/uts/intel/lxautofs/Makefile
@@ -0,0 +1,114 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# This makefile drives the production of the lxautofs file system
+# kernel module.
+#
+# i86 architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+# Note that the name of the actual filesystem is lxautofs and
+# not lx_autofs. This is becase filesystem names are stupidly
+# limited to 8 characters.
+#
+MODULE = lxautofs
+OBJECTS = $(LX_AUTOFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LX_AUTOFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK = $(USR_FS_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/autofs
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+#
+# Overrides.
+#
+CFLAGS += $(CCVERBOSE)
+LDFLAGS += -dy -Nfs/nfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(ROOT_FS_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lxautofs/Makefile.rules
diff --git a/usr/src/uts/intel/lxautofs/Makefile.rules b/usr/src/uts/intel/lxautofs/Makefile.rules
new file mode 100644
index 0000000000..474743ce9d
--- /dev/null
+++ b/usr/src/uts/intel/lxautofs/Makefile.rules
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/autofs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+#
+# Section 1b: Lint `object' build rules.
+#
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/brand/lx/autofs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/intel/lxprocfs/Makefile b/usr/src/uts/intel/lxprocfs/Makefile
new file mode 100644
index 0000000000..c6ffec0199
--- /dev/null
+++ b/usr/src/uts/intel/lxprocfs/Makefile
@@ -0,0 +1,88 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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
+#
+#
+# uts/intel/lxprocfs/Makefile
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This makefile drives the production of the lxprocfs file system
+# kernel module.
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lxprocfs
+OBJECTS = $(LXPROC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(LXPROC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_FS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Depends on procfs
+#
+LDFLAGS += -dy -Nfs/procfs
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/mac/Makefile b/usr/src/uts/intel/mac/Makefile
index 8346daa30b..bc9f412051 100644
--- a/usr/src/uts/intel/mac/Makefile
+++ b/usr/src/uts/intel/mac/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy
-INC_PATH += -I$(UTSBASE)/common/io/bpf
LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
diff --git a/usr/src/uts/intel/mac_ether/Makefile b/usr/src/uts/intel/mac_ether/Makefile
index 889f7a73de..c58051a7b1 100644
--- a/usr/src/uts/intel/mac_ether/Makefile
+++ b/usr/src/uts/intel/mac_ether/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/mac_ib/Makefile b/usr/src/uts/intel/mac_ib/Makefile
index 5045d1bbbf..ebcef78eaa 100644
--- a/usr/src/uts/intel/mac_ib/Makefile
+++ b/usr/src/uts/intel/mac_ib/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/mac_wifi/Makefile b/usr/src/uts/intel/mac_wifi/Makefile
index 11f22cfca7..b05cb5d627 100644
--- a/usr/src/uts/intel/mac_wifi/Makefile
+++ b/usr/src/uts/intel/mac_wifi/Makefile
@@ -58,7 +58,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -Nmisc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/nfp/Makefile b/usr/src/uts/intel/nfp/Makefile
new file mode 100644
index 0000000000..3acbc6a725
--- /dev/null
+++ b/usr/src/uts/intel/nfp/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 (c) 2014, Joyent, Inc
+#
+
+#
+# uts/intel/nfp/Makefile
+#
+# This makefile drives the production of the nfp
+# driver kernel module.
+#
+# intel architecture dependent
+#
+
+#
+# Paths to the base of the uts directory trees
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = nfp
+OBJECTS = $(NFP_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(NFP_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Driver-specific flags
+#
+CPPFLAGS += -DCH_KERNELVER=270
+LDFLAGS += -dy
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-function
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/opteron_pcbe/Makefile b/usr/src/uts/intel/opteron_pcbe/Makefile
index aea8e89fbe..7431f50329 100644
--- a/usr/src/uts/intel/opteron_pcbe/Makefile
+++ b/usr/src/uts/intel/opteron_pcbe/Makefile
@@ -34,7 +34,7 @@ UTSBASE = ../..
MODULE = pcbe.AuthenticAMD
OBJECTS = $(OPTERON_PCBE_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(OPTERON_PCBE_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
#
# Include common rules.
diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases
index 3116819932..1cea287121 100644
--- a/usr/src/uts/intel/os/driver_aliases
+++ b/usr/src/uts/intel/os/driver_aliases
@@ -1 +1,2 @@
asy "pci11c1,480"
+vmxnet "pci15ad,720"
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index c5ad4c9bf0..1fb86f9a50 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -2,3 +2,4 @@ md 85
devinfo 88
asy 106
did 239
+vmxnet 270
diff --git a/usr/src/uts/intel/overlay/Makefile b/usr/src/uts/intel/overlay/Makefile
new file mode 100644
index 0000000000..645b888cbc
--- /dev/null
+++ b/usr/src/uts/intel/overlay/Makefile
@@ -0,0 +1,54 @@
+#
+# 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 2015 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = overlay
+OBJECTS = $(OVERLAY_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(OVERLAY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+CONF_SRCDIR = $(UTSBASE)/common/io/overlay
+MAPFILE = $(UTSBASE)/common/io/overlay/overlay.mapfile
+
+LDFLAGS += -dy -Nmisc/mac -Ndrv/dld -Nmisc/dls -Nmisc/ksocket
+
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/p123_pcbe/Makefile b/usr/src/uts/intel/p123_pcbe/Makefile
index 71b7d9bcec..d591d52e62 100644
--- a/usr/src/uts/intel/p123_pcbe/Makefile
+++ b/usr/src/uts/intel/p123_pcbe/Makefile
@@ -35,8 +35,8 @@ UTSBASE = ../..
MODULE = pcbe.GenuineIntel.6
OBJECTS = $(P123_PCBE_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(P123_PCBE_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
-ROOTLINK = $(USR_PCBE_DIR)/pcbe.GenuineIntel.5
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
+ROOTLINK = $(ROOT_PSM_PCBE_DIR)/pcbe.GenuineIntel.5
#
# Include common rules.
diff --git a/usr/src/uts/intel/p4_pcbe/Makefile b/usr/src/uts/intel/p4_pcbe/Makefile
index 07689646c1..42dc040eee 100644
--- a/usr/src/uts/intel/p4_pcbe/Makefile
+++ b/usr/src/uts/intel/p4_pcbe/Makefile
@@ -35,7 +35,7 @@ UTSBASE = ../..
MODULE = pcbe.GenuineIntel.15
OBJECTS = $(P4_PCBE_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(P4_PCBE_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
#
# Include common rules.
diff --git a/usr/src/uts/intel/pci_autoconfig/Makefile b/usr/src/uts/intel/pci_autoconfig/Makefile
index 94fdc98af0..afbf30e155 100644
--- a/usr/src/uts/intel/pci_autoconfig/Makefile
+++ b/usr/src/uts/intel/pci_autoconfig/Makefile
@@ -23,6 +23,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
# This makefile drives the production of the PCI autoconfiguration
# kernel module.
#
@@ -69,6 +71,8 @@ LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
CERRWARN += -_gcc=-Wno-parentheses
+$(OBJS_DIR)/pci_boot.o := CERRWARN += -_gcc=-Wno-unused-function
+$(OBJS_DIR)/pci_resource.o := CERRWARN += -_gcc=-Wno-unused-function
#
# Default build targets.
diff --git a/usr/src/uts/intel/power/Makefile b/usr/src/uts/intel/power/Makefile
index 363cf2407e..7765878b09 100644
--- a/usr/src/uts/intel/power/Makefile
+++ b/usr/src/uts/intel/power/Makefile
@@ -23,6 +23,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2016 Joyent, Inc.
+#
#
# This makefile drives the production of the power driver
@@ -67,6 +69,8 @@ CFLAGS += $(CCVERBOSE)
CFLAGS += -DACPI_POWER_BUTTON
LINTFLAGS += -DACPI_POWER_BUTTON
+CERRWARN += -_gcc=-Wno-unused-function
+
LDFLAGS += -dy -N misc/acpica
#
diff --git a/usr/src/uts/intel/promif/prom_emul.c b/usr/src/uts/intel/promif/prom_emul.c
index 5497d9eab8..cdf190ec6a 100644
--- a/usr/src/uts/intel/promif/prom_emul.c
+++ b/usr/src/uts/intel/promif/prom_emul.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
#include <sys/promif.h>
#include <sys/promimpl.h>
@@ -46,7 +48,7 @@ promif_create_prop(prom_node_t *pnp, char *name, void *val, int len, int flags)
q = kmem_zalloc(sizeof (*q), KM_SLEEP);
q->pp_name = kmem_zalloc(strlen(name) + 1, KM_SLEEP);
(void) strcpy(q->pp_name, name);
- q->pp_val = kmem_alloc(len, KM_SLEEP);
+ q->pp_val = len > 0 ? kmem_alloc(len, KM_SLEEP) : NULL;
q->pp_len = len;
switch (flags) {
case DDI_PROP_TYPE_INT:
diff --git a/usr/src/uts/intel/smrt/Makefile b/usr/src/uts/intel/smrt/Makefile
new file mode 100644
index 0000000000..40b4a52002
--- /dev/null
+++ b/usr/src/uts/intel/smrt/Makefile
@@ -0,0 +1,72 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# Path to the base of the uts directory tree
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = smrt
+OBJECTS = $(SMRT_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SMRT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io/scsi/adapters/smrt
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Kernel Module Dependencies
+#
+LDFLAGS += -dy -Nmisc/scsi
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/sockpfp/Makefile b/usr/src/uts/intel/sockpfp/Makefile
index ab2f16a0b3..ac5ca9209d 100644
--- a/usr/src/uts/intel/sockpfp/Makefile
+++ b/usr/src/uts/intel/sockpfp/Makefile
@@ -58,7 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/bpf -Ndrv/ip
+LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/ip
INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
#
diff --git a/usr/src/uts/intel/spdsock/Makefile b/usr/src/uts/intel/spdsock/Makefile
index 8517e4882a..5f980df27f 100644
--- a/usr/src/uts/intel/spdsock/Makefile
+++ b/usr/src/uts/intel/spdsock/Makefile
@@ -60,8 +60,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
LDFLAGS += -dy -Ndrv/ip
-INC_PATH += -I$(UTSBASE)/common/io/bpf
-
#
# For now, disable these lint checks; maintainers should endeavor
# to investigate and remove these for maximum lint coverage.
diff --git a/usr/src/uts/intel/sys/acpi/acapps.h b/usr/src/uts/intel/sys/acpi/acapps.h
index 2d3f84186e..87284f20b0 100644
--- a/usr/src/uts/intel/sys/acpi/acapps.h
+++ b/usr/src/uts/intel/sys/acpi/acapps.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,7 @@
#ifndef _ACAPPS
#define _ACAPPS
+#include <stdio.h>
#ifdef _MSC_VER /* disable some level-4 warnings */
#pragma warning(disable:4100) /* warning C4100: unreferenced formal parameter */
@@ -52,7 +53,7 @@
/* Common info for tool signons */
#define ACPICA_NAME "Intel ACPI Component Architecture"
-#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2011 Intel Corporation"
+#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2016 Intel Corporation"
#if ACPI_MACHINE_WIDTH == 64
#define ACPI_WIDTH "-64"
@@ -69,20 +70,70 @@
/* Macros for signons and file headers */
#define ACPI_COMMON_SIGNON(UtilityName) \
- "\n%s\n%s version %8.8X%s [%s]\n%s\n\n", \
+ "\n%s\n%s version %8.8X%s\n%s\n\n", \
ACPICA_NAME, \
- UtilityName, ((UINT32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+ UtilityName, ((UINT32) ACPI_CA_VERSION), ACPI_WIDTH, \
ACPICA_COPYRIGHT
#define ACPI_COMMON_HEADER(UtilityName, Prefix) \
- "%s%s\n%s%s version %8.8X%s [%s]\n%s%s\n%s\n", \
+ "%s%s\n%s%s version %8.8X%s\n%s%s\n%s\n", \
Prefix, ACPICA_NAME, \
- Prefix, UtilityName, ((UINT32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+ Prefix, UtilityName, ((UINT32) ACPI_CA_VERSION), ACPI_WIDTH, \
Prefix, ACPICA_COPYRIGHT, \
Prefix
+/* Macros for usage messages */
+
+#define ACPI_USAGE_HEADER(Usage) \
+ AcpiOsPrintf ("Usage: %s\nOptions:\n", Usage);
+
+#define ACPI_USAGE_TEXT(Description) \
+ AcpiOsPrintf (Description);
+
+#define ACPI_OPTION(Name, Description) \
+ AcpiOsPrintf (" %-20s%s\n", Name, Description);
+
+
+/* Check for unexpected exceptions */
+
+#define ACPI_CHECK_STATUS(Name, Status, Expected) \
+ if (Status != Expected) \
+ { \
+ AcpiOsPrintf ("Unexpected %s from %s (%s-%d)\n", \
+ AcpiFormatException (Status), #Name, _AcpiModuleName, __LINE__); \
+ }
+
+/* Check for unexpected non-AE_OK errors */
+
+
+#define ACPI_CHECK_OK(Name, Status) ACPI_CHECK_STATUS (Name, Status, AE_OK);
+
#define FILE_SUFFIX_DISASSEMBLY "dsl"
-#define ACPI_TABLE_FILE_SUFFIX ".dat"
+#define FILE_SUFFIX_BINARY_TABLE ".dat" /* Needs the dot */
+
+
+/* acfileio */
+
+ACPI_STATUS
+AcGetAllTablesFromFile (
+ char *Filename,
+ UINT8 GetOnlyAmlTables,
+ ACPI_NEW_TABLE_DESC **ReturnListHead);
+
+BOOLEAN
+AcIsFileBinary (
+ FILE *File);
+
+ACPI_STATUS
+AcValidateTableHeader (
+ FILE *File,
+ long TableOffset);
+
+
+/* Values for GetOnlyAmlTables */
+
+#define ACPI_GET_ONLY_AML_TABLES TRUE
+#define ACPI_GET_ALL_TABLES FALSE
/*
@@ -94,55 +145,23 @@ AcpiGetopt(
char **argv,
char *opts);
+int
+AcpiGetoptArgument (
+ int argc,
+ char **argv);
+
extern int AcpiGbl_Optind;
extern int AcpiGbl_Opterr;
+extern int AcpiGbl_SubOptChar;
extern char *AcpiGbl_Optarg;
/*
- * adisasm
+ * cmfsize - Common get file size function
*/
-ACPI_STATUS
-AdAmlDisassemble (
- BOOLEAN OutToFile,
- char *Filename,
- char *Prefix,
- char **OutFilename,
- BOOLEAN GetAllTables);
-
-void
-AdPrintStatistics (
- void);
-
-ACPI_STATUS
-AdFindDsdt(
- UINT8 **DsdtPtr,
- UINT32 *DsdtLength);
-
-void
-AdDumpTables (
- void);
-
-ACPI_STATUS
-AdGetLocalTables (
- char *Filename,
- BOOLEAN GetAllTables);
-
-ACPI_STATUS
-AdParseTable (
- ACPI_TABLE_HEADER *Table,
- ACPI_OWNER_ID *OwnerId,
- BOOLEAN LoadTable,
- BOOLEAN External);
-
-ACPI_STATUS
-AdDisplayTables (
- char *Filename,
- ACPI_TABLE_HEADER *Table);
-
-ACPI_STATUS
-AdDisplayStatistics (
- void);
+UINT32
+CmGetFileSize (
+ ACPI_FILE File);
/*
@@ -205,4 +224,3 @@ AdWriteTable (
char *OemTableId);
#endif /* _ACAPPS */
-
diff --git a/usr/src/uts/intel/sys/acpi/acbuffer.h b/usr/src/uts/intel/sys/acpi/acbuffer.h
new file mode 100644
index 0000000000..78f6db4a9e
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/acbuffer.h
@@ -0,0 +1,255 @@
+/******************************************************************************
+ *
+ * Name: acbuffer.h - Support for buffers returned by ACPI predefined names
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACBUFFER_H__
+#define __ACBUFFER_H__
+
+/*
+ * Contains buffer structures for these predefined names:
+ * _FDE, _GRT, _GTM, _PLD, _SRT
+ */
+
+/*
+ * Note: C bitfields are not used for this reason:
+ *
+ * "Bitfields are great and easy to read, but unfortunately the C language
+ * does not specify the layout of bitfields in memory, which means they are
+ * essentially useless for dealing with packed data in on-disk formats or
+ * binary wire protocols." (Or ACPI tables and buffers.) "If you ask me,
+ * this decision was a design error in C. Ritchie could have picked an order
+ * and stuck with it." Norman Ramsey.
+ * See http://stackoverflow.com/a/1053662/41661
+ */
+
+
+/* _FDE return value */
+
+typedef struct acpi_fde_info
+{
+ UINT32 Floppy0;
+ UINT32 Floppy1;
+ UINT32 Floppy2;
+ UINT32 Floppy3;
+ UINT32 Tape;
+
+} ACPI_FDE_INFO;
+
+/*
+ * _GRT return value
+ * _SRT input value
+ */
+typedef struct acpi_grt_info
+{
+ UINT16 Year;
+ UINT8 Month;
+ UINT8 Day;
+ UINT8 Hour;
+ UINT8 Minute;
+ UINT8 Second;
+ UINT8 Valid;
+ UINT16 Milliseconds;
+ UINT16 Timezone;
+ UINT8 Daylight;
+ UINT8 Reserved[3];
+
+} ACPI_GRT_INFO;
+
+/* _GTM return value */
+
+typedef struct acpi_gtm_info
+{
+ UINT32 PioSpeed0;
+ UINT32 DmaSpeed0;
+ UINT32 PioSpeed1;
+ UINT32 DmaSpeed1;
+ UINT32 Flags;
+
+} ACPI_GTM_INFO;
+
+/*
+ * Formatted _PLD return value. The minimum size is a package containing
+ * one buffer.
+ * Revision 1: Buffer is 16 bytes (128 bits)
+ * Revision 2: Buffer is 20 bytes (160 bits)
+ *
+ * Note: This structure is returned from the AcpiDecodePldBuffer
+ * interface.
+ */
+typedef struct acpi_pld_info
+{
+ UINT8 Revision;
+ UINT8 IgnoreColor;
+ UINT8 Red;
+ UINT8 Green;
+ UINT8 Blue;
+ UINT16 Width;
+ UINT16 Height;
+ UINT8 UserVisible;
+ UINT8 Dock;
+ UINT8 Lid;
+ UINT8 Panel;
+ UINT8 VerticalPosition;
+ UINT8 HorizontalPosition;
+ UINT8 Shape;
+ UINT8 GroupOrientation;
+ UINT8 GroupToken;
+ UINT8 GroupPosition;
+ UINT8 Bay;
+ UINT8 Ejectable;
+ UINT8 OspmEjectRequired;
+ UINT8 CabinetNumber;
+ UINT8 CardCageNumber;
+ UINT8 Reference;
+ UINT8 Rotation;
+ UINT8 Order;
+ UINT8 Reserved;
+ UINT16 VerticalOffset;
+ UINT16 HorizontalOffset;
+
+} ACPI_PLD_INFO;
+
+
+/*
+ * Macros to:
+ * 1) Convert a _PLD buffer to internal ACPI_PLD_INFO format - ACPI_PLD_GET*
+ * (Used by AcpiDecodePldBuffer)
+ * 2) Construct a _PLD buffer - ACPI_PLD_SET*
+ * (Intended for BIOS use only)
+ */
+#define ACPI_PLD_REV1_BUFFER_SIZE 16 /* For Revision 1 of the buffer (From ACPI spec) */
+#define ACPI_PLD_REV2_BUFFER_SIZE 20 /* For Revision 2 of the buffer (From ACPI spec) */
+#define ACPI_PLD_BUFFER_SIZE 20 /* For Revision 2 of the buffer (From ACPI spec) */
+
+/* First 32-bit dword, bits 0:32 */
+
+#define ACPI_PLD_GET_REVISION(dword) ACPI_GET_BITS (dword, 0, ACPI_7BIT_MASK)
+#define ACPI_PLD_SET_REVISION(dword,value) ACPI_SET_BITS (dword, 0, ACPI_7BIT_MASK, value) /* Offset 0, Len 7 */
+
+#define ACPI_PLD_GET_IGNORE_COLOR(dword) ACPI_GET_BITS (dword, 7, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_IGNORE_COLOR(dword,value) ACPI_SET_BITS (dword, 7, ACPI_1BIT_MASK, value) /* Offset 7, Len 1 */
+
+#define ACPI_PLD_GET_RED(dword) ACPI_GET_BITS (dword, 8, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_RED(dword,value) ACPI_SET_BITS (dword, 8, ACPI_8BIT_MASK, value) /* Offset 8, Len 8 */
+
+#define ACPI_PLD_GET_GREEN(dword) ACPI_GET_BITS (dword, 16, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_GREEN(dword,value) ACPI_SET_BITS (dword, 16, ACPI_8BIT_MASK, value) /* Offset 16, Len 8 */
+
+#define ACPI_PLD_GET_BLUE(dword) ACPI_GET_BITS (dword, 24, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_BLUE(dword,value) ACPI_SET_BITS (dword, 24, ACPI_8BIT_MASK, value) /* Offset 24, Len 8 */
+
+/* Second 32-bit dword, bits 33:63 */
+
+#define ACPI_PLD_GET_WIDTH(dword) ACPI_GET_BITS (dword, 0, ACPI_16BIT_MASK)
+#define ACPI_PLD_SET_WIDTH(dword,value) ACPI_SET_BITS (dword, 0, ACPI_16BIT_MASK, value) /* Offset 32+0=32, Len 16 */
+
+#define ACPI_PLD_GET_HEIGHT(dword) ACPI_GET_BITS (dword, 16, ACPI_16BIT_MASK)
+#define ACPI_PLD_SET_HEIGHT(dword,value) ACPI_SET_BITS (dword, 16, ACPI_16BIT_MASK, value) /* Offset 32+16=48, Len 16 */
+
+/* Third 32-bit dword, bits 64:95 */
+
+#define ACPI_PLD_GET_USER_VISIBLE(dword) ACPI_GET_BITS (dword, 0, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_USER_VISIBLE(dword,value) ACPI_SET_BITS (dword, 0, ACPI_1BIT_MASK, value) /* Offset 64+0=64, Len 1 */
+
+#define ACPI_PLD_GET_DOCK(dword) ACPI_GET_BITS (dword, 1, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_DOCK(dword,value) ACPI_SET_BITS (dword, 1, ACPI_1BIT_MASK, value) /* Offset 64+1=65, Len 1 */
+
+#define ACPI_PLD_GET_LID(dword) ACPI_GET_BITS (dword, 2, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_LID(dword,value) ACPI_SET_BITS (dword, 2, ACPI_1BIT_MASK, value) /* Offset 64+2=66, Len 1 */
+
+#define ACPI_PLD_GET_PANEL(dword) ACPI_GET_BITS (dword, 3, ACPI_3BIT_MASK)
+#define ACPI_PLD_SET_PANEL(dword,value) ACPI_SET_BITS (dword, 3, ACPI_3BIT_MASK, value) /* Offset 64+3=67, Len 3 */
+
+#define ACPI_PLD_GET_VERTICAL(dword) ACPI_GET_BITS (dword, 6, ACPI_2BIT_MASK)
+#define ACPI_PLD_SET_VERTICAL(dword,value) ACPI_SET_BITS (dword, 6, ACPI_2BIT_MASK, value) /* Offset 64+6=70, Len 2 */
+
+#define ACPI_PLD_GET_HORIZONTAL(dword) ACPI_GET_BITS (dword, 8, ACPI_2BIT_MASK)
+#define ACPI_PLD_SET_HORIZONTAL(dword,value) ACPI_SET_BITS (dword, 8, ACPI_2BIT_MASK, value) /* Offset 64+8=72, Len 2 */
+
+#define ACPI_PLD_GET_SHAPE(dword) ACPI_GET_BITS (dword, 10, ACPI_4BIT_MASK)
+#define ACPI_PLD_SET_SHAPE(dword,value) ACPI_SET_BITS (dword, 10, ACPI_4BIT_MASK, value) /* Offset 64+10=74, Len 4 */
+
+#define ACPI_PLD_GET_ORIENTATION(dword) ACPI_GET_BITS (dword, 14, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_ORIENTATION(dword,value) ACPI_SET_BITS (dword, 14, ACPI_1BIT_MASK, value) /* Offset 64+14=78, Len 1 */
+
+#define ACPI_PLD_GET_TOKEN(dword) ACPI_GET_BITS (dword, 15, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_TOKEN(dword,value) ACPI_SET_BITS (dword, 15, ACPI_8BIT_MASK, value) /* Offset 64+15=79, Len 8 */
+
+#define ACPI_PLD_GET_POSITION(dword) ACPI_GET_BITS (dword, 23, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_POSITION(dword,value) ACPI_SET_BITS (dword, 23, ACPI_8BIT_MASK, value) /* Offset 64+23=87, Len 8 */
+
+#define ACPI_PLD_GET_BAY(dword) ACPI_GET_BITS (dword, 31, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_BAY(dword,value) ACPI_SET_BITS (dword, 31, ACPI_1BIT_MASK, value) /* Offset 64+31=95, Len 1 */
+
+/* Fourth 32-bit dword, bits 96:127 */
+
+#define ACPI_PLD_GET_EJECTABLE(dword) ACPI_GET_BITS (dword, 0, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_EJECTABLE(dword,value) ACPI_SET_BITS (dword, 0, ACPI_1BIT_MASK, value) /* Offset 96+0=96, Len 1 */
+
+#define ACPI_PLD_GET_OSPM_EJECT(dword) ACPI_GET_BITS (dword, 1, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_OSPM_EJECT(dword,value) ACPI_SET_BITS (dword, 1, ACPI_1BIT_MASK, value) /* Offset 96+1=97, Len 1 */
+
+#define ACPI_PLD_GET_CABINET(dword) ACPI_GET_BITS (dword, 2, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_CABINET(dword,value) ACPI_SET_BITS (dword, 2, ACPI_8BIT_MASK, value) /* Offset 96+2=98, Len 8 */
+
+#define ACPI_PLD_GET_CARD_CAGE(dword) ACPI_GET_BITS (dword, 10, ACPI_8BIT_MASK)
+#define ACPI_PLD_SET_CARD_CAGE(dword,value) ACPI_SET_BITS (dword, 10, ACPI_8BIT_MASK, value) /* Offset 96+10=106, Len 8 */
+
+#define ACPI_PLD_GET_REFERENCE(dword) ACPI_GET_BITS (dword, 18, ACPI_1BIT_MASK)
+#define ACPI_PLD_SET_REFERENCE(dword,value) ACPI_SET_BITS (dword, 18, ACPI_1BIT_MASK, value) /* Offset 96+18=114, Len 1 */
+
+#define ACPI_PLD_GET_ROTATION(dword) ACPI_GET_BITS (dword, 19, ACPI_4BIT_MASK)
+#define ACPI_PLD_SET_ROTATION(dword,value) ACPI_SET_BITS (dword, 19, ACPI_4BIT_MASK, value) /* Offset 96+19=115, Len 4 */
+
+#define ACPI_PLD_GET_ORDER(dword) ACPI_GET_BITS (dword, 23, ACPI_5BIT_MASK)
+#define ACPI_PLD_SET_ORDER(dword,value) ACPI_SET_BITS (dword, 23, ACPI_5BIT_MASK, value) /* Offset 96+23=119, Len 5 */
+
+/* Fifth 32-bit dword, bits 128:159 (Revision 2 of _PLD only) */
+
+#define ACPI_PLD_GET_VERT_OFFSET(dword) ACPI_GET_BITS (dword, 0, ACPI_16BIT_MASK)
+#define ACPI_PLD_SET_VERT_OFFSET(dword,value) ACPI_SET_BITS (dword, 0, ACPI_16BIT_MASK, value) /* Offset 128+0=128, Len 16 */
+
+#define ACPI_PLD_GET_HORIZ_OFFSET(dword) ACPI_GET_BITS (dword, 16, ACPI_16BIT_MASK)
+#define ACPI_PLD_SET_HORIZ_OFFSET(dword,value) ACPI_SET_BITS (dword, 16, ACPI_16BIT_MASK, value) /* Offset 128+16=144, Len 16 */
+
+
+#endif /* ACBUFFER_H */
diff --git a/usr/src/uts/intel/sys/acpi/acclib.h b/usr/src/uts/intel/sys/acpi/acclib.h
new file mode 100644
index 0000000000..56a30723ae
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/acclib.h
@@ -0,0 +1,167 @@
+/******************************************************************************
+ *
+ * Name: acclib.h -- C library support. Prototypes for the (optional) local
+ * implementations of required C library functions.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef _ACCLIB_H
+#define _ACCLIB_H
+
+
+/*
+ * Prototypes and macros for local implementations of C library functions
+ */
+
+/* is* functions. The AcpiGbl_Ctypes array is defined in utclib.c */
+
+extern const UINT8 AcpiGbl_Ctypes[];
+
+#define _ACPI_XA 0x00 /* extra alphabetic - not supported */
+#define _ACPI_XS 0x40 /* extra space */
+#define _ACPI_BB 0x00 /* BEL, BS, etc. - not supported */
+#define _ACPI_CN 0x20 /* CR, FF, HT, NL, VT */
+#define _ACPI_DI 0x04 /* '0'-'9' */
+#define _ACPI_LO 0x02 /* 'a'-'z' */
+#define _ACPI_PU 0x10 /* punctuation */
+#define _ACPI_SP 0x08 /* space, tab, CR, LF, VT, FF */
+#define _ACPI_UP 0x01 /* 'A'-'Z' */
+#define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */
+
+#define isdigit(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_DI))
+#define isspace(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_SP))
+#define isxdigit(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_XD))
+#define isupper(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_UP))
+#define islower(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_LO))
+#define isprint(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP | _ACPI_DI | _ACPI_XS | _ACPI_PU))
+#define isalpha(c) (AcpiGbl_Ctypes[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP))
+
+
+/* Strings */
+
+char *
+strcat (
+ char *DstString,
+ const char *SrcString);
+
+char *
+strchr (
+ const char *String,
+ int ch);
+
+char *
+strcpy (
+ char *DstString,
+ const char *SrcString);
+
+int
+strcmp (
+ const char *String1,
+ const char *String2);
+
+ACPI_SIZE
+strlen (
+ const char *String);
+
+char *
+strncat (
+ char *DstString,
+ const char *SrcString,
+ ACPI_SIZE Count);
+
+int
+strncmp (
+ const char *String1,
+ const char *String2,
+ ACPI_SIZE Count);
+
+char *
+strncpy (
+ char *DstString,
+ const char *SrcString,
+ ACPI_SIZE Count);
+
+char *
+strstr (
+ char *String1,
+ char *String2);
+
+
+/* Conversion */
+
+UINT32
+strtoul (
+ const char *String,
+ char **Terminator,
+ UINT32 Base);
+
+
+/* Memory */
+
+int
+memcmp (
+ void *Buffer1,
+ void *Buffer2,
+ ACPI_SIZE Count);
+
+void *
+memcpy (
+ void *Dest,
+ const void *Src,
+ ACPI_SIZE Count);
+
+void *
+memset (
+ void *Dest,
+ int Value,
+ ACPI_SIZE Count);
+
+
+/* upper/lower case */
+
+int
+tolower (
+ int c);
+
+int
+toupper (
+ int c);
+
+#endif /* _ACCLIB_H */
diff --git a/usr/src/uts/intel/sys/acpi/accommon.h b/usr/src/uts/intel/sys/acpi/accommon.h
index 58bb83ddb6..02e6f51396 100644
--- a/usr/src/uts/intel/sys/acpi/accommon.h
+++ b/usr/src/uts/intel/sys/acpi/accommon.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -59,6 +59,9 @@
#include "acglobal.h" /* All global variables */
#include "achware.h" /* Hardware defines and interfaces */
#include "acutils.h" /* Utility interfaces */
+#ifndef ACPI_USE_SYSTEM_CLIBRARY
+#include "acclib.h" /* C library interfaces */
+#endif /* !ACPI_USE_SYSTEM_CLIBRARY */
#endif /* __ACCOMMON_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acconfig.h b/usr/src/uts/intel/sys/acpi/acconfig.h
index 7a2107aa76..a626f87591 100644
--- a/usr/src/uts/intel/sys/acpi/acconfig.h
+++ b/usr/src/uts/intel/sys/acpi/acconfig.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -53,8 +53,8 @@
/*
* ACPI_DEBUG_OUTPUT - This switch enables all the debug facilities of the
- * ACPI subsystem. This includes the DEBUG_PRINT output
- * statements. When disabled, all DEBUG_PRINT
+ * ACPI subsystem. This includes the DEBUG_PRINT output
+ * statements. When disabled, all DEBUG_PRINT
* statements are compiled out.
*
* ACPI_APPLICATION - Use this switch if the subsystem is going to be run
@@ -63,12 +63,12 @@
*/
/*
- * OS name, used for the _OS object. The _OS object is essentially obsolete,
+ * OS name, used for the _OS object. The _OS object is essentially obsolete,
* but there is a large base of ASL/AML code in existing machines that check
- * for the string below. The use of this string usually guarantees that
- * the ASL will execute down the most tested code path. Also, there is some
+ * for the string below. The use of this string usually guarantees that
+ * the ASL will execute down the most tested code path. Also, there is some
* code that will not execute the _OSI method unless _OS matches the string
- * below. Therefore, change this string at your own risk.
+ * below. Therefore, change this string at your own risk.
*/
#define ACPI_OS_NAME "Microsoft Windows NT"
@@ -84,7 +84,28 @@
* Should the subsystem abort the loading of an ACPI table if the
* table checksum is incorrect?
*/
+#ifndef ACPI_CHECKSUM_ABORT
#define ACPI_CHECKSUM_ABORT FALSE
+#endif
+
+/*
+ * Generate a version of ACPICA that only supports "reduced hardware"
+ * platforms (as defined in ACPI 5.0). Set to TRUE to generate a specialized
+ * version of ACPICA that ONLY supports the ACPI 5.0 "reduced hardware"
+ * model. In other words, no ACPI hardware is supported.
+ *
+ * If TRUE, this means no support for the following:
+ * PM Event and Control registers
+ * SCI interrupt (and handler)
+ * Fixed Events
+ * General Purpose Events (GPEs)
+ * Global Lock
+ * ACPI PM timer
+ * FACS table (Waking vectors and Global Lock)
+ */
+#ifndef ACPI_REDUCED_HARDWARE
+#define ACPI_REDUCED_HARDWARE FALSE
+#endif
/******************************************************************************
@@ -95,7 +116,7 @@
/* Version of ACPI supported */
-#define ACPI_CA_SUPPORT_LEVEL 3
+#define ACPI_CA_SUPPORT_LEVEL 5
/* Maximum count for a semaphore object */
@@ -117,13 +138,13 @@
#define ACPI_ROOT_TABLE_SIZE_INCREMENT 4
-/* Maximum number of While() loop iterations before forced abort */
+/* Maximum sleep allowed via Sleep() operator */
-#define ACPI_MAX_LOOP_ITERATIONS 0xFFFF
+#define ACPI_MAX_SLEEP 2000 /* 2000 millisec == two seconds */
-/* Maximum sleep allowed via Sleep() operator */
+/* Address Range lists are per-SpaceId (Memory and I/O only) */
-#define ACPI_MAX_SLEEP 20000 /* Two seconds */
+#define ACPI_ADDRESS_RANGE_MAX 2
/******************************************************************************
@@ -173,8 +194,9 @@
/* Maximum SpaceIds for Operation Regions */
#define ACPI_MAX_ADDRESS_SPACE 255
+#define ACPI_NUM_DEFAULT_SPACES 4
-/* Array sizes. Used for range checking also */
+/* Array sizes. Used for range checking also */
#define ACPI_MAX_MATCH_OPCODE 5
@@ -183,9 +205,10 @@
#define ACPI_RSDP_CHECKSUM_LENGTH 20
#define ACPI_RSDP_XCHECKSUM_LENGTH 36
-/* SMBus and IPMI bidirectional buffer size */
+/* SMBus, GSBus and IPMI bidirectional buffer size */
#define ACPI_SMBUS_BUFFER_SIZE 34
+#define ACPI_GSBUS_BUFFER_SIZE 34
#define ACPI_IPMI_BUFFER_SIZE 66
/* _SxD and _SxW control methods */
@@ -196,11 +219,30 @@
/******************************************************************************
*
+ * Miscellaneous constants
+ *
+ *****************************************************************************/
+
+/* UUID constants */
+
+#define UUID_BUFFER_LENGTH 16 /* Length of UUID in memory */
+#define UUID_STRING_LENGTH 36 /* Total length of a UUID string */
+
+/* Positions for required hyphens (dashes) in UUID strings */
+
+#define UUID_HYPHEN1_OFFSET 8
+#define UUID_HYPHEN2_OFFSET 13
+#define UUID_HYPHEN3_OFFSET 18
+#define UUID_HYPHEN4_OFFSET 23
+
+
+/******************************************************************************
+ *
* ACPI AML Debugger
*
*****************************************************************************/
-#define ACPI_DEBUGGER_MAX_ARGS ACPI_METHOD_NUM_ARGS + 2 /* Max command line arguments */
+#define ACPI_DEBUGGER_MAX_ARGS ACPI_METHOD_NUM_ARGS + 4 /* Max command line arguments */
#define ACPI_DB_LINE_BUFFER_SIZE 512
#define ACPI_DEBUGGER_COMMAND_PROMPT '-'
@@ -208,4 +250,3 @@
#endif /* _ACCONFIG_H */
-
diff --git a/usr/src/uts/intel/sys/acpi/acdebug.h b/usr/src/uts/intel/sys/acpi/acdebug.h
index 2c1fe73429..8f3f47175b 100644
--- a/usr/src/uts/intel/sys/acpi/acdebug.h
+++ b/usr/src/uts/intel/sys/acpi/acdebug.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,34 +44,45 @@
#ifndef __ACDEBUG_H__
#define __ACDEBUG_H__
+/* The debugger is used in conjunction with the disassembler most of time */
+
+#ifdef ACPI_DISASSEMBLER
+#include "acdisasm.h"
+#endif
+
#define ACPI_DEBUG_BUFFER_SIZE 0x4000 /* 16K buffer for return objects */
-typedef struct CommandInfo
+typedef struct acpi_db_command_info
{
- char *Name; /* Command Name */
+ const char *Name; /* Command Name */
UINT8 MinArgs; /* Minimum arguments required */
-} COMMAND_INFO;
+} ACPI_DB_COMMAND_INFO;
-typedef struct ArgumentInfo
+typedef struct acpi_db_command_help
{
- char *Name; /* Argument Name */
+ UINT8 LineCount; /* Number of help lines */
+ char *Invocation; /* Command Invocation */
+ char *Description; /* Command Description */
-} ARGUMENT_INFO;
+} ACPI_DB_COMMAND_HELP;
-typedef struct acpi_execute_walk
+typedef struct acpi_db_argument_info
+{
+ const char *Name; /* Argument Name */
+
+} ACPI_DB_ARGUMENT_INFO;
+
+typedef struct acpi_db_execute_walk
{
UINT32 Count;
UINT32 MaxCount;
-} ACPI_EXECUTE_WALK;
+} ACPI_DB_EXECUTE_WALK;
#define PARAM_LIST(pl) pl
-#define DBTEST_OUTPUT_LEVEL(lvl) if (AcpiGbl_DbOpt_verbose)
-#define VERBOSE_PRINT(fp) DBTEST_OUTPUT_LEVEL(lvl) {\
- AcpiOsPrintf PARAM_LIST(fp);}
#define EX_NO_SINGLE_STEP 1
#define EX_SINGLE_STEP 2
@@ -80,19 +91,17 @@ typedef struct acpi_execute_walk
/*
* dbxface - external debugger interfaces
*/
-ACPI_STATUS
-AcpiDbInitialize (
- void);
-
-void
-AcpiDbTerminate (
- void);
-
+ACPI_DBR_DEPENDENT_RETURN_OK (
ACPI_STATUS
AcpiDbSingleStep (
ACPI_WALK_STATE *WalkState,
ACPI_PARSE_OBJECT *Op,
- UINT32 OpType);
+ UINT32 OpType))
+
+ACPI_DBR_DEPENDENT_RETURN_VOID (
+void
+AcpiDbSignalBreakPoint (
+ ACPI_WALK_STATE *WalkState))
/*
@@ -107,9 +116,12 @@ AcpiDbDisplayTableInfo (
char *TableArg);
void
+AcpiDbDisplayTemplate (
+ char *BufferArg);
+
+void
AcpiDbUnloadAcpiTable (
- char *TableArg,
- char *InstanceArg);
+ char *Name);
void
AcpiDbSendNotify (
@@ -126,6 +138,12 @@ AcpiDbSleep (
char *ObjectArg);
void
+AcpiDbTrace (
+ char *EnableArg,
+ char *MethodArg,
+ char *OnceArg);
+
+void
AcpiDbDisplayLocks (
void);
@@ -133,18 +151,57 @@ void
AcpiDbDisplayResources (
char *ObjectArg);
+ACPI_HW_DEPENDENT_RETURN_VOID (
void
AcpiDbDisplayGpes (
- void);
+ void))
void
AcpiDbDisplayHandlers (
void);
+ACPI_HW_DEPENDENT_RETURN_VOID (
void
AcpiDbGenerateGpe (
char *GpeArg,
- char *BlockArg);
+ char *BlockArg))
+
+ACPI_HW_DEPENDENT_RETURN_VOID (
+void
+AcpiDbGenerateSci (
+ void))
+
+void
+AcpiDbExecuteTest (
+ char *TypeArg);
+
+
+/*
+ * dbconvert - miscellaneous conversion routines
+ */
+ACPI_STATUS
+AcpiDbHexCharToValue (
+ int HexChar,
+ UINT8 *ReturnValue);
+
+ACPI_STATUS
+AcpiDbConvertToPackage (
+ char *String,
+ ACPI_OBJECT *Object);
+
+ACPI_STATUS
+AcpiDbConvertToObject (
+ ACPI_OBJECT_TYPE Type,
+ char *String,
+ ACPI_OBJECT *Object);
+
+UINT8 *
+AcpiDbEncodePldBuffer (
+ ACPI_PLD_INFO *PldInfo);
+
+void
+AcpiDbDumpPldBuffer (
+ ACPI_OBJECT *ObjDesc);
/*
@@ -193,6 +250,10 @@ AcpiDbDumpNamespace (
char *DepthArg);
void
+AcpiDbDumpNamespacePaths (
+ void);
+
+void
AcpiDbDumpNamespaceByOwner (
char *OwnerArg,
char *DepthArg);
@@ -235,10 +296,11 @@ AcpiDbDecodeAndDisplayObject (
char *Target,
char *OutputType);
+ACPI_DBR_DEPENDENT_RETURN_VOID (
void
AcpiDbDisplayResultObject (
ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState);
+ ACPI_WALK_STATE *WalkState))
ACPI_STATUS
AcpiDbDisplayAllMethods (
@@ -264,10 +326,11 @@ void
AcpiDbDisplayObjectType (
char *ObjectArg);
+ACPI_DBR_DEPENDENT_RETURN_VOID (
void
AcpiDbDisplayArgumentObject (
ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState);
+ ACPI_WALK_STATE *WalkState))
/*
@@ -286,6 +349,11 @@ AcpiDbCreateExecutionThreads (
char *NumLoopsArg,
char *MethodNameArg);
+void
+AcpiDbDeleteObjects (
+ UINT32 Count,
+ ACPI_OBJECT *Objects);
+
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
UINT32
AcpiDbGetCacheInfo (
@@ -299,7 +367,7 @@ AcpiDbGetCacheInfo (
ACPI_OBJECT_TYPE
AcpiDbMatchArgument (
char *UserArgument,
- ARGUMENT_INFO *Arguments);
+ ACPI_DB_ARGUMENT_INFO *Arguments);
void
AcpiDbCloseDebugFile (
@@ -314,14 +382,8 @@ AcpiDbLoadAcpiTable (
char *Filename);
ACPI_STATUS
-AcpiDbGetTableFromFile (
- char *Filename,
- ACPI_TABLE_HEADER **Table);
-
-ACPI_STATUS
-AcpiDbReadTableFromFile (
- char *Filename,
- ACPI_TABLE_HEADER **Table);
+AcpiDbLoadTables (
+ ACPI_NEW_TABLE_DESC *ListHead);
/*
@@ -339,6 +401,10 @@ char *
AcpiDbGetFromHistory (
char *CommandNumArg);
+char *
+AcpiDbGetHistoryByIndex (
+ UINT32 CommanddNum);
+
/*
* dbinput - user front-end to the AML debugger
@@ -366,6 +432,32 @@ AcpiDbGetNextToken (
/*
+ * dbobject
+ */
+void
+AcpiDbDecodeInternalObject (
+ ACPI_OPERAND_OBJECT *ObjDesc);
+
+void
+AcpiDbDisplayInternalObject (
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiDbDecodeArguments (
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiDbDecodeLocals (
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiDbDumpMethodInfo (
+ ACPI_STATUS Status,
+ ACPI_WALK_STATE *WalkState);
+
+
+/*
* dbstats - Generation and display of ACPI table statistics
*/
void
@@ -399,7 +491,7 @@ AcpiDbLocalNsLookup (
char *Name);
void
-AcpiDbUInt32ToHexString (
+AcpiDbUint32ToHexString (
UINT32 Value,
char *Buffer);
diff --git a/usr/src/uts/intel/sys/acpi/acdisasm.h b/usr/src/uts/intel/sys/acpi/acdisasm.h
index aaaac11d86..545622d4ec 100644
--- a/usr/src/uts/intel/sys/acpi/acdisasm.h
+++ b/usr/src/uts/intel/sys/acpi/acdisasm.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -60,77 +60,110 @@
#define ACPI_RAW_TABLE_DATA_HEADER "Raw Table Data"
-typedef const struct acpi_dmtable_info
+typedef struct acpi_dmtable_info
{
UINT8 Opcode;
- UINT8 Offset;
+ UINT16 Offset;
char *Name;
UINT8 Flags;
} ACPI_DMTABLE_INFO;
+/* Values for Flags field above */
+
#define DT_LENGTH 0x01 /* Field is a subtable length */
#define DT_FLAG 0x02 /* Field is a flag value */
#define DT_NON_ZERO 0x04 /* Field must be non-zero */
-
-/* TBD: Not used at this time */
-
-#define DT_OPTIONAL 0x08
-#define DT_COUNT 0x10
+#define DT_OPTIONAL 0x08 /* Field is optional */
+#define DT_DESCRIBES_OPTIONAL 0x10 /* Field describes an optional field (length, etc.) */
+#define DT_COUNT 0x20 /* Currently not used */
/*
* Values for Opcode above.
- * Note: 0-7 must not change, used as a flag shift value
+ * Note: 0-7 must not change, they are used as a flag shift value. Other
+ * than those, new values can be added wherever appropriate.
*/
-#define ACPI_DMT_FLAG0 0
-#define ACPI_DMT_FLAG1 1
-#define ACPI_DMT_FLAG2 2
-#define ACPI_DMT_FLAG3 3
-#define ACPI_DMT_FLAG4 4
-#define ACPI_DMT_FLAG5 5
-#define ACPI_DMT_FLAG6 6
-#define ACPI_DMT_FLAG7 7
-#define ACPI_DMT_FLAGS0 8
-#define ACPI_DMT_FLAGS2 9
-#define ACPI_DMT_UINT8 10
-#define ACPI_DMT_UINT16 11
-#define ACPI_DMT_UINT24 12
-#define ACPI_DMT_UINT32 13
-#define ACPI_DMT_UINT56 14
-#define ACPI_DMT_UINT64 15
-#define ACPI_DMT_STRING 16
-#define ACPI_DMT_NAME4 17
-#define ACPI_DMT_NAME6 18
-#define ACPI_DMT_NAME8 19
-#define ACPI_DMT_CHKSUM 20
-#define ACPI_DMT_SPACEID 21
-#define ACPI_DMT_GAS 22
-#define ACPI_DMT_ASF 23
-#define ACPI_DMT_DMAR 24
-#define ACPI_DMT_HEST 25
-#define ACPI_DMT_HESTNTFY 26
-#define ACPI_DMT_HESTNTYP 27
-#define ACPI_DMT_MADT 28
-#define ACPI_DMT_SRAT 29
-#define ACPI_DMT_EXIT 30
-#define ACPI_DMT_SIG 31
-#define ACPI_DMT_FADTPM 32
-#define ACPI_DMT_BUF16 33
-#define ACPI_DMT_IVRS 34
-#define ACPI_DMT_BUFFER 35
-#define ACPI_DMT_PCI_PATH 36
-#define ACPI_DMT_EINJACT 37
-#define ACPI_DMT_EINJINST 38
-#define ACPI_DMT_ERSTACT 39
-#define ACPI_DMT_ERSTINST 40
-#define ACPI_DMT_ACCWIDTH 41
-#define ACPI_DMT_UNICODE 42
-#define ACPI_DMT_UUID 43
-#define ACPI_DMT_DEVICE_PATH 44
-#define ACPI_DMT_LABEL 45
-#define ACPI_DMT_BUF7 46
-#define ACPI_DMT_BUF128 47
-#define ACPI_DMT_SLIC 48
+typedef enum
+{
+ /* Simple Data Types */
+
+ ACPI_DMT_FLAG0 = 0,
+ ACPI_DMT_FLAG1 = 1,
+ ACPI_DMT_FLAG2 = 2,
+ ACPI_DMT_FLAG3 = 3,
+ ACPI_DMT_FLAG4 = 4,
+ ACPI_DMT_FLAG5 = 5,
+ ACPI_DMT_FLAG6 = 6,
+ ACPI_DMT_FLAG7 = 7,
+ ACPI_DMT_FLAGS0,
+ ACPI_DMT_FLAGS1,
+ ACPI_DMT_FLAGS2,
+ ACPI_DMT_FLAGS4,
+ ACPI_DMT_UINT8,
+ ACPI_DMT_UINT16,
+ ACPI_DMT_UINT24,
+ ACPI_DMT_UINT32,
+ ACPI_DMT_UINT40,
+ ACPI_DMT_UINT48,
+ ACPI_DMT_UINT56,
+ ACPI_DMT_UINT64,
+ ACPI_DMT_BUF7,
+ ACPI_DMT_BUF10,
+ ACPI_DMT_BUF16,
+ ACPI_DMT_BUF128,
+ ACPI_DMT_SIG,
+ ACPI_DMT_STRING,
+ ACPI_DMT_NAME4,
+ ACPI_DMT_NAME6,
+ ACPI_DMT_NAME8,
+
+ /* Types that are decoded to strings and miscellaneous */
+
+ ACPI_DMT_ACCWIDTH,
+ ACPI_DMT_CHKSUM,
+ ACPI_DMT_GAS,
+ ACPI_DMT_SPACEID,
+ ACPI_DMT_UNICODE,
+ ACPI_DMT_UUID,
+
+ /* Types used only for the Data Table Compiler */
+
+ ACPI_DMT_BUFFER,
+ ACPI_DMT_RAW_BUFFER, /* Large, multiple line buffer */
+ ACPI_DMT_DEVICE_PATH,
+ ACPI_DMT_LABEL,
+ ACPI_DMT_PCI_PATH,
+
+ /* Types that are specific to particular ACPI tables */
+
+ ACPI_DMT_ASF,
+ ACPI_DMT_DMAR,
+ ACPI_DMT_DMAR_SCOPE,
+ ACPI_DMT_EINJACT,
+ ACPI_DMT_EINJINST,
+ ACPI_DMT_ERSTACT,
+ ACPI_DMT_ERSTINST,
+ ACPI_DMT_FADTPM,
+ ACPI_DMT_GTDT,
+ ACPI_DMT_HEST,
+ ACPI_DMT_HESTNTFY,
+ ACPI_DMT_HESTNTYP,
+ ACPI_DMT_IORTMEM,
+ ACPI_DMT_IVRS,
+ ACPI_DMT_LPIT,
+ ACPI_DMT_MADT,
+ ACPI_DMT_NFIT,
+ ACPI_DMT_PCCT,
+ ACPI_DMT_PMTT,
+ ACPI_DMT_SLIC,
+ ACPI_DMT_SRAT,
+
+ /* Special opcodes */
+
+ ACPI_DMT_EXTRA_TEXT,
+ ACPI_DMT_EXIT
+
+} ACPI_ENTRY_TYPES;
typedef
void (*ACPI_DMTABLE_HANDLER) (
@@ -147,19 +180,22 @@ typedef struct acpi_dmtable_data
ACPI_DMTABLE_HANDLER TableHandler;
ACPI_CMTABLE_HANDLER CmTableHandler;
const unsigned char *Template;
- char *Name;
} ACPI_DMTABLE_DATA;
typedef struct acpi_op_walk_info
{
+ ACPI_WALK_STATE *WalkState;
+ ACPI_PARSE_OBJECT *MappingOp;
+ UINT8 *PreviousAml;
+ UINT8 *StartAml;
UINT32 Level;
UINT32 LastLevel;
UINT32 Count;
UINT32 BitOffset;
UINT32 Flags;
- ACPI_WALK_STATE *WalkState;
+ UINT32 AmlOffset;
} ACPI_OP_WALK_INFO;
@@ -175,6 +211,12 @@ ACPI_STATUS (*ASL_WALK_CALLBACK) (
#define ASL_WALK_CALLBACK_DEFINED
#endif
+typedef
+void (*ACPI_RESOURCE_HANDLER) (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
typedef struct acpi_resource_tag
{
@@ -202,8 +244,19 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoAsf4[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoAsfHdr[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoBoot[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoBert[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoBgrt[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoCpep[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoCpep0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoCsrt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoCsrt1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoCsrt2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoCsrt2a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2Device[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2Addr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2Size[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2Name[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbg2OemData[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDbgp[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmarHdr[];
@@ -212,6 +265,13 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar3[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDmar4[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm0a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm1a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoDrtm2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoEcdt[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoEinj[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoEinj0[];
@@ -221,7 +281,18 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoFacs[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoFadt1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoFadt2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoFadt3[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFadt5[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFadt6[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFpdt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFpdtHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFpdt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoFpdt1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoGas[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoGtdt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoGtdtHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoGtdt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoGtdt0a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoGtdt1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHeader[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest0[];
@@ -231,9 +302,28 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest6[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest7[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest8[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest9[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoHest10[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHestNotify[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHestBank[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoHpet[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoLpitHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoLpit0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoLpit1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort0a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort1a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort3[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort3a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort3b[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort3c[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIort4[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIortAcc[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIortHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIortMap[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoIortPad[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoIvrs[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoIvrs0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoIvrs1[];
@@ -254,18 +344,56 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt7[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt8[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt9[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt10[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt11[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt12[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt13[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt14[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadt15[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMadtHdr[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMcfg[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMcfg0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMchi[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst0A[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst0B[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMpst2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMsct[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoMsct0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMtmr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoMtmr0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfitHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit2a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit3[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit3a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit4[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit5[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit6[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoNfit6a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmtt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmtt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmtt1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmtt1a[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmtt2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPmttHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPcct[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPcctHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPcct0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPcct1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoPcct2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoRsdp1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoRsdp2[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoS3pt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoS3ptHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoS3pt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoS3pt1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSbst[];
-extern ACPI_DMTABLE_INFO AcpiDmTableInfoSlicHdr[];
-extern ACPI_DMTABLE_INFO AcpiDmTableInfoSlic0[];
-extern ACPI_DMTABLE_INFO AcpiDmTableInfoSlic1[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoSlic[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSlit[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSpcr[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSpmi[];
@@ -274,21 +402,32 @@ extern ACPI_DMTABLE_INFO AcpiDmTableInfoSratHdr[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSrat0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSrat1[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoSrat2[];
-extern ACPI_DMTABLE_INFO AcpiDmTableInfoTcpa[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoSrat3[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoStao[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoStaoStr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoTcpaHdr[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoTcpaClient[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoTcpaServer[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoTpm2[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoUefi[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoVrtc[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoVrtc0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoWaet[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoWdat[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoWdat0[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoWddt[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoWdrt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoWpbt[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoWpbt0[];
+extern ACPI_DMTABLE_INFO AcpiDmTableInfoXenv[];
extern ACPI_DMTABLE_INFO AcpiDmTableInfoGeneric[][2];
-
/*
- * dmtable
+ * dmtable and ahtable
*/
-extern ACPI_DMTABLE_DATA AcpiDmTableData[];
+extern const ACPI_DMTABLE_DATA AcpiDmTableData[];
+extern const AH_TABLE AcpiSupportedTables[];
UINT8
AcpiDmGenerateChecksum (
@@ -296,7 +435,7 @@ AcpiDmGenerateChecksum (
UINT32 Length,
UINT8 OriginalChecksum);
-ACPI_DMTABLE_DATA *
+const ACPI_DMTABLE_DATA *
AcpiDmGetTableData (
char *Signature);
@@ -330,6 +469,20 @@ AcpiDmLineHeader2 (
* dmtbdump
*/
void
+AcpiDmDumpBuffer (
+ void *Table,
+ UINT32 BufferOffset,
+ UINT32 Length,
+ UINT32 AbsoluteOffset,
+ char *Header);
+
+void
+AcpiDmDumpUnicode (
+ void *Table,
+ UINT32 BufferOffset,
+ UINT32 ByteLength);
+
+void
AcpiDmDumpAsf (
ACPI_TABLE_HEADER *Table);
@@ -338,10 +491,22 @@ AcpiDmDumpCpep (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpCsrt (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpDbg2 (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpDmar (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpDrtm (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpEinj (
ACPI_TABLE_HEADER *Table);
@@ -354,15 +519,27 @@ AcpiDmDumpFadt (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpFpdt (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpGtdt (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpHest (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpIort (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpIvrs (
ACPI_TABLE_HEADER *Table);
void
-AcpiDmDumpMcfg (
+AcpiDmDumpLpit (
ACPI_TABLE_HEADER *Table);
void
@@ -370,9 +547,33 @@ AcpiDmDumpMadt (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpMcfg (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpMpst (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpMsct (
ACPI_TABLE_HEADER *Table);
+void
+AcpiDmDumpMtmr (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpNfit (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpPcct (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpPmtt (
+ ACPI_TABLE_HEADER *Table);
+
UINT32
AcpiDmDumpRsdp (
ACPI_TABLE_HEADER *Table);
@@ -381,6 +582,10 @@ void
AcpiDmDumpRsdt (
ACPI_TABLE_HEADER *Table);
+UINT32
+AcpiDmDumpS3pt (
+ ACPI_TABLE_HEADER *Table);
+
void
AcpiDmDumpSlic (
ACPI_TABLE_HEADER *Table);
@@ -394,10 +599,26 @@ AcpiDmDumpSrat (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpStao (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpTcpa (
+ ACPI_TABLE_HEADER *Table);
+
+void
+AcpiDmDumpVrtc (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpWdat (
ACPI_TABLE_HEADER *Table);
void
+AcpiDmDumpWpbt (
+ ACPI_TABLE_HEADER *Table);
+
+void
AcpiDmDumpXsdt (
ACPI_TABLE_HEADER *Table);
@@ -428,10 +649,6 @@ AcpiDmDisassembleOneOp (
ACPI_OP_WALK_INFO *Info,
ACPI_PARSE_OBJECT *Op);
-void
-AcpiDmDecodeInternalObject (
- ACPI_OPERAND_OBJECT *ObjDesc);
-
UINT32
AcpiDmListType (
ACPI_PARSE_OBJECT *Op);
@@ -441,6 +658,22 @@ AcpiDmMethodFlags (
ACPI_PARSE_OBJECT *Op);
void
+AcpiDmDisplayTargetPathname (
+ ACPI_PARSE_OBJECT *Op);
+
+void
+AcpiDmNotifyDescription (
+ ACPI_PARSE_OBJECT *Op);
+
+void
+AcpiDmPredefinedDescription (
+ ACPI_PARSE_OBJECT *Op);
+
+void
+AcpiDmFieldPredefinedDescription (
+ ACPI_PARSE_OBJECT *Op);
+
+void
AcpiDmFieldFlags (
ACPI_PARSE_OBJECT *Op);
@@ -475,29 +708,6 @@ AcpiDmNamestring (
/*
- * dmobject
- */
-void
-AcpiDmDisplayInternalObject (
- ACPI_OPERAND_OBJECT *ObjDesc,
- ACPI_WALK_STATE *WalkState);
-
-void
-AcpiDmDisplayArguments (
- ACPI_WALK_STATE *WalkState);
-
-void
-AcpiDmDisplayLocals (
- ACPI_WALK_STATE *WalkState);
-
-void
-AcpiDmDumpMethodInfo (
- ACPI_STATUS Status,
- ACPI_WALK_STATE *WalkState,
- ACPI_PARSE_OBJECT *Op);
-
-
-/*
* dmbuffer
*/
void
@@ -512,14 +722,18 @@ AcpiDmByteList (
ACPI_PARSE_OBJECT *Op);
void
-AcpiDmIsEisaId (
+AcpiDmCheckForHardwareId (
ACPI_PARSE_OBJECT *Op);
void
-AcpiDmEisaId (
+AcpiDmDecompressEisaId (
UINT32 EncodedId);
BOOLEAN
+AcpiDmIsUuidBuffer (
+ ACPI_PARSE_OBJECT *Op);
+
+BOOLEAN
AcpiDmIsUnicodeBuffer (
ACPI_PARSE_OBJECT *Op);
@@ -527,11 +741,22 @@ BOOLEAN
AcpiDmIsStringBuffer (
ACPI_PARSE_OBJECT *Op);
+BOOLEAN
+AcpiDmIsPldBuffer (
+ ACPI_PARSE_OBJECT *Op);
+
/*
- * dmextern
+ * dmdeferred
*/
+ACPI_STATUS
+AcpiDmParseDeferredOps (
+ ACPI_PARSE_OBJECT *Root);
+
+/*
+ * dmextern
+ */
ACPI_STATUS
AcpiDmAddToExternalFileList (
char *PathList);
@@ -541,11 +766,19 @@ AcpiDmClearExternalFileList (
void);
void
-AcpiDmAddToExternalList (
+AcpiDmAddOpToExternalList (
ACPI_PARSE_OBJECT *Op,
char *Path,
UINT8 Type,
- UINT32 Value);
+ UINT32 Value,
+ UINT16 Flags);
+
+void
+AcpiDmAddNodeToExternalList (
+ ACPI_NAMESPACE_NODE *Node,
+ UINT8 Type,
+ UINT32 Value,
+ UINT16 Flags);
void
AcpiDmAddExternalsToNamespace (
@@ -563,6 +796,13 @@ void
AcpiDmEmitExternals (
void);
+void
+AcpiDmUnresolvedWarning (
+ UINT8 Type);
+
+void
+AcpiDmGetExternalsFromFile (
+ void);
/*
* dmresrc
@@ -570,22 +810,22 @@ AcpiDmEmitExternals (
void
AcpiDmDumpInteger8 (
UINT8 Value,
- char *Name);
+ const char *Name);
void
AcpiDmDumpInteger16 (
UINT16 Value,
- char *Name);
+ const char *Name);
void
AcpiDmDumpInteger32 (
UINT32 Value,
- char *Name);
+ const char *Name);
void
AcpiDmDumpInteger64 (
UINT64 Value,
- char *Name);
+ const char *Name);
void
AcpiDmResourceTemplate (
@@ -596,6 +836,7 @@ AcpiDmResourceTemplate (
ACPI_STATUS
AcpiDmIsResourceTemplate (
+ ACPI_WALK_STATE *WalkState,
ACPI_PARSE_OBJECT *Op);
void
@@ -612,67 +853,91 @@ AcpiDmDescriptorName (
*/
void
AcpiDmWordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmDwordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmExtendedDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmQwordDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmMemory24Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmMemory32Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmFixedMemory32Descriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmGenericRegisterDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmInterruptDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmVendorLargeDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+void
+AcpiDmGpioDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+void
+AcpiDmSerialBusDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmVendorCommon (
- char *Name,
+ const char *Name,
UINT8 *ByteData,
UINT32 Length,
UINT32 Level);
@@ -683,42 +948,56 @@ AcpiDmVendorCommon (
*/
void
AcpiDmIrqDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmDmaDescriptor (
+ ACPI_OP_WALK_INFO *Info,
+ AML_RESOURCE *Resource,
+ UINT32 Length,
+ UINT32 Level);
+
+void
+AcpiDmFixedDmaDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmIoDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmFixedIoDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmStartDependentDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmEndDependentDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
void
AcpiDmVendorSmallDescriptor (
+ ACPI_OP_WALK_INFO *Info,
AML_RESOURCE *Resource,
UINT32 Length,
UINT32 Level);
@@ -758,11 +1037,58 @@ AcpiDmCheckResourceReference (
/*
- * acdisasm
+ * dmcstyle
+ */
+BOOLEAN
+AcpiDmCheckForSymbolicOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_OP_WALK_INFO *Info);
+
+void
+AcpiDmCloseOperator (
+ ACPI_PARSE_OBJECT *Op);
+
+
+/*
+ * dmtables
*/
void
AdDisassemblerHeader (
- char *Filename);
+ char *Filename,
+ UINT8 TableType);
+
+#define ACPI_IS_AML_TABLE 0
+#define ACPI_IS_DATA_TABLE 1
+
+/*
+ * adisasm
+ */
+ACPI_STATUS
+AdAmlDisassemble (
+ BOOLEAN OutToFile,
+ char *Filename,
+ char *Prefix,
+ char **OutFilename);
+
+ACPI_STATUS
+AdGetLocalTables (
+ void);
+
+ACPI_STATUS
+AdParseTable (
+ ACPI_TABLE_HEADER *Table,
+ ACPI_OWNER_ID *OwnerId,
+ BOOLEAN LoadTable,
+ BOOLEAN External);
+
+ACPI_STATUS
+AdDisplayTables (
+ char *Filename,
+ ACPI_TABLE_HEADER *Table);
+
+ACPI_STATUS
+AdDisplayStatistics (
+ void);
#endif /* __ACDISASM_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acdispat.h b/usr/src/uts/intel/sys/acpi/acdispat.h
index ae8dd93154..1a6f39e2aa 100644
--- a/usr/src/uts/intel/sys/acpi/acdispat.h
+++ b/usr/src/uts/intel/sys/acpi/acdispat.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#ifndef _ACDISPAT_H_
#define _ACDISPAT_H_
@@ -173,13 +172,15 @@ AcpiDsInitFieldObjects (
/*
- * dsload - Parser/Interpreter interface, pass 1 namespace load callbacks
+ * dsload - Parser/Interpreter interface
*/
ACPI_STATUS
AcpiDsInitCallbacks (
ACPI_WALK_STATE *WalkState,
UINT32 PassNumber);
+/* dsload - pass 1 namespace load callbacks */
+
ACPI_STATUS
AcpiDsLoad1BeginOp (
ACPI_WALK_STATE *WalkState,
@@ -190,9 +191,8 @@ AcpiDsLoad1EndOp (
ACPI_WALK_STATE *WalkState);
-/*
- * dsload - Parser/Interpreter interface, pass 2 namespace load callbacks
- */
+/* dsload - pass 2 namespace load callbacks */
+
ACPI_STATUS
AcpiDsLoad2BeginOp (
ACPI_WALK_STATE *WalkState,
@@ -257,8 +257,9 @@ AcpiDsMethodDataInit (
* dsmethod - Parser/Interpreter interface - control method parsing
*/
ACPI_STATUS
-AcpiDsParseMethod (
- ACPI_NAMESPACE_NODE *Node);
+AcpiDsAutoSerializeMethod (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_OPERAND_OBJECT *ObjDesc);
ACPI_STATUS
AcpiDsCallControlMethod (
@@ -460,4 +461,14 @@ AcpiDsResultPush (
ACPI_OPERAND_OBJECT *Object,
ACPI_WALK_STATE *WalkState);
+
+/*
+ * dsdebug - parser debugging routines
+ */
+void
+AcpiDsDumpMethodStack (
+ ACPI_STATUS Status,
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT *Op);
+
#endif /* _ACDISPAT_H_ */
diff --git a/usr/src/uts/intel/sys/acpi/acevents.h b/usr/src/uts/intel/sys/acpi/acevents.h
index 8681ed5f38..a14dbe57c1 100644
--- a/usr/src/uts/intel/sys/acpi/acevents.h
+++ b/usr/src/uts/intel/sys/acpi/acevents.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -85,13 +85,15 @@ ACPI_STATUS
AcpiEvInitGlobalLockHandler (
void);
+ACPI_HW_DEPENDENT_RETURN_OK (
ACPI_STATUS
AcpiEvAcquireGlobalLock(
- UINT16 Timeout);
+ UINT16 Timeout))
+ACPI_HW_DEPENDENT_RETURN_OK (
ACPI_STATUS
AcpiEvReleaseGlobalLock(
- void);
+ void))
ACPI_STATUS
AcpiEvRemoveGlobalLockHandler (
@@ -142,9 +144,10 @@ AcpiEvFinishGpe (
ACPI_STATUS
AcpiEvCreateGpeBlock (
ACPI_NAMESPACE_NODE *GpeDevice,
- ACPI_GENERIC_ADDRESS *GpeBlockAddress,
+ UINT64 Address,
+ UINT8 SpaceId,
UINT32 RegisterCount,
- UINT8 GpeBlockBaseNumber,
+ UINT16 GpeBlockBaseNumber,
UINT32 InterruptNumber,
ACPI_GPE_BLOCK_INFO **ReturnGpeBlock);
@@ -154,9 +157,10 @@ AcpiEvInitializeGpeBlock (
ACPI_GPE_BLOCK_INFO *GpeBlock,
void *Context);
+ACPI_HW_DEPENDENT_RETURN_OK (
ACPI_STATUS
AcpiEvDeleteGpeBlock (
- ACPI_GPE_BLOCK_INFO *GpeBlock);
+ ACPI_GPE_BLOCK_INFO *GpeBlock))
UINT32
AcpiEvGpeDispatch (
@@ -164,6 +168,7 @@ AcpiEvGpeDispatch (
ACPI_GPE_EVENT_INFO *GpeEventInfo,
UINT32 GpeNumber);
+
/*
* evgpeinit - GPE initialization and update
*/
@@ -171,9 +176,10 @@ ACPI_STATUS
AcpiEvGpeInitialize (
void);
+ACPI_HW_DEPENDENT_RETURN_VOID (
void
AcpiEvUpdateGpes (
- ACPI_OWNER_ID TableOwnerId);
+ ACPI_OWNER_ID TableOwnerId))
ACPI_STATUS
AcpiEvMatchGpeMethod (
@@ -182,6 +188,7 @@ AcpiEvMatchGpeMethod (
void *Context,
void **ReturnValue);
+
/*
* evgpeutil - GPE utilities
*/
@@ -190,19 +197,16 @@ AcpiEvWalkGpeList (
ACPI_GPE_CALLBACK GpeWalkCallback,
void *Context);
-BOOLEAN
-AcpiEvValidGpeEvent (
- ACPI_GPE_EVENT_INFO *GpeEventInfo);
-
ACPI_STATUS
AcpiEvGetGpeDevice (
ACPI_GPE_XRUPT_INFO *GpeXruptInfo,
ACPI_GPE_BLOCK_INFO *GpeBlock,
void *Context);
-ACPI_GPE_XRUPT_INFO *
+ACPI_STATUS
AcpiEvGetGpeXruptBlock (
- UINT32 InterruptNumber);
+ UINT32 InterruptNumber,
+ ACPI_GPE_XRUPT_INFO **GpeXruptBlock);
ACPI_STATUS
AcpiEvDeleteGpeXrupt (
@@ -216,19 +220,42 @@ AcpiEvDeleteGpeHandlers (
/*
- * evregion - Address Space handling
+ * evhandler - Address space handling
*/
+ACPI_OPERAND_OBJECT *
+AcpiEvFindRegionHandler (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_OPERAND_OBJECT *HandlerObj);
+
+BOOLEAN
+AcpiEvHasDefaultHandler (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_ADR_SPACE_TYPE SpaceId);
+
ACPI_STATUS
AcpiEvInstallRegionHandlers (
void);
ACPI_STATUS
+AcpiEvInstallSpaceHandler (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_ADR_SPACE_HANDLER Handler,
+ ACPI_ADR_SPACE_SETUP Setup,
+ void *Context);
+
+
+/*
+ * evregion - Operation region support
+ */
+ACPI_STATUS
AcpiEvInitializeOpRegions (
void);
ACPI_STATUS
AcpiEvAddressSpaceDispatch (
- ACPI_OPERAND_OBJECT *RegionObj,
+ ACPI_OPERAND_OBJECT *RegionObj,
+ ACPI_OPERAND_OBJECT *FieldObj,
UINT32 Function,
UINT32 RegionOffset,
UINT32 BitWidth,
@@ -242,25 +269,18 @@ AcpiEvAttachRegion (
void
AcpiEvDetachRegion (
- ACPI_OPERAND_OBJECT *RegionObj,
+ ACPI_OPERAND_OBJECT *RegionObj,
BOOLEAN AcpiNsIsLocked);
-ACPI_STATUS
-AcpiEvInstallSpaceHandler (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId,
- ACPI_ADR_SPACE_HANDLER Handler,
- ACPI_ADR_SPACE_SETUP Setup,
- void *Context);
-
-ACPI_STATUS
+void
AcpiEvExecuteRegMethods (
ACPI_NAMESPACE_NODE *Node,
- ACPI_ADR_SPACE_TYPE SpaceId);
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ UINT32 Function);
ACPI_STATUS
AcpiEvExecuteRegMethod (
- ACPI_OPERAND_OBJECT *RegionObj,
+ ACPI_OPERAND_OBJECT *RegionObj,
UINT32 Function);
@@ -323,20 +343,20 @@ AcpiEvGpeXruptHandler (
void *Context);
UINT32
+AcpiEvSciDispatch (
+ void);
+
+UINT32
AcpiEvInstallSciHandler (
void);
ACPI_STATUS
-AcpiEvRemoveSciHandler (
+AcpiEvRemoveAllSciHandlers (
void);
-UINT32
-AcpiEvInitializeSCI (
- UINT32 ProgramSCI);
-
+ACPI_HW_DEPENDENT_RETURN_VOID (
void
AcpiEvTerminate (
- void);
-
+ void))
#endif /* __ACEVENTS_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acexcep.h b/usr/src/uts/intel/sys/acpi/acexcep.h
index 7d426d0f26..0f9a6aa569 100644
--- a/usr/src/uts/intel/sys/acpi/acexcep.h
+++ b/usr/src/uts/intel/sys/acpi/acexcep.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,69 +45,106 @@
#define __ACEXCEP_H__
+/* This module contains all possible exception codes for ACPI_STATUS */
+
/*
- * Exceptions returned by external ACPI interfaces
+ * Exception code classes
*/
-#define AE_CODE_ENVIRONMENTAL 0x0000
-#define AE_CODE_PROGRAMMER 0x1000
-#define AE_CODE_ACPI_TABLES 0x2000
-#define AE_CODE_AML 0x3000
-#define AE_CODE_CONTROL 0x4000
+#define AE_CODE_ENVIRONMENTAL 0x0000 /* General ACPICA environment */
+#define AE_CODE_PROGRAMMER 0x1000 /* External ACPICA interface caller */
+#define AE_CODE_ACPI_TABLES 0x2000 /* ACPI tables */
+#define AE_CODE_AML 0x3000 /* From executing AML code */
+#define AE_CODE_CONTROL 0x4000 /* Internal control codes */
+
+#define AE_CODE_MAX 0x4000
#define AE_CODE_MASK 0xF000
+/*
+ * Macros to insert the exception code classes
+ */
+#define EXCEP_ENV(code) ((ACPI_STATUS) (code | AE_CODE_ENVIRONMENTAL))
+#define EXCEP_PGM(code) ((ACPI_STATUS) (code | AE_CODE_PROGRAMMER))
+#define EXCEP_TBL(code) ((ACPI_STATUS) (code | AE_CODE_ACPI_TABLES))
+#define EXCEP_AML(code) ((ACPI_STATUS) (code | AE_CODE_AML))
+#define EXCEP_CTL(code) ((ACPI_STATUS) (code | AE_CODE_CONTROL))
+
+/*
+ * Exception info table. The "Description" field is used only by the
+ * ACPICA help application (acpihelp).
+ */
+typedef struct acpi_exception_info
+{
+ char *Name;
+
+#ifdef ACPI_HELP_APP
+ char *Description;
+#endif
+} ACPI_EXCEPTION_INFO;
+
+#ifdef ACPI_HELP_APP
+#define EXCEP_TXT(Name,Description) {Name, Description}
+#else
+#define EXCEP_TXT(Name,Description) {Name}
+#endif
+
+/*
+ * Success is always zero, failure is non-zero
+ */
#define ACPI_SUCCESS(a) (!(a))
#define ACPI_FAILURE(a) (a)
-
#define AE_OK (ACPI_STATUS) 0x0000
/*
* Environmental exceptions
*/
-#define AE_ERROR (ACPI_STATUS) (0x0001 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_ACPI_TABLES (ACPI_STATUS) (0x0002 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_NAMESPACE (ACPI_STATUS) (0x0003 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_MEMORY (ACPI_STATUS) (0x0004 | AE_CODE_ENVIRONMENTAL)
-#define AE_NOT_FOUND (ACPI_STATUS) (0x0005 | AE_CODE_ENVIRONMENTAL)
-#define AE_NOT_EXIST (ACPI_STATUS) (0x0006 | AE_CODE_ENVIRONMENTAL)
-#define AE_ALREADY_EXISTS (ACPI_STATUS) (0x0007 | AE_CODE_ENVIRONMENTAL)
-#define AE_TYPE (ACPI_STATUS) (0x0008 | AE_CODE_ENVIRONMENTAL)
-#define AE_NULL_OBJECT (ACPI_STATUS) (0x0009 | AE_CODE_ENVIRONMENTAL)
-#define AE_NULL_ENTRY (ACPI_STATUS) (0x000A | AE_CODE_ENVIRONMENTAL)
-#define AE_BUFFER_OVERFLOW (ACPI_STATUS) (0x000B | AE_CODE_ENVIRONMENTAL)
-#define AE_STACK_OVERFLOW (ACPI_STATUS) (0x000C | AE_CODE_ENVIRONMENTAL)
-#define AE_STACK_UNDERFLOW (ACPI_STATUS) (0x000D | AE_CODE_ENVIRONMENTAL)
-#define AE_NOT_IMPLEMENTED (ACPI_STATUS) (0x000E | AE_CODE_ENVIRONMENTAL)
-#define AE_SUPPORT (ACPI_STATUS) (0x000F | AE_CODE_ENVIRONMENTAL)
-#define AE_LIMIT (ACPI_STATUS) (0x0010 | AE_CODE_ENVIRONMENTAL)
-#define AE_TIME (ACPI_STATUS) (0x0011 | AE_CODE_ENVIRONMENTAL)
-#define AE_ACQUIRE_DEADLOCK (ACPI_STATUS) (0x0012 | AE_CODE_ENVIRONMENTAL)
-#define AE_RELEASE_DEADLOCK (ACPI_STATUS) (0x0013 | AE_CODE_ENVIRONMENTAL)
-#define AE_NOT_ACQUIRED (ACPI_STATUS) (0x0014 | AE_CODE_ENVIRONMENTAL)
-#define AE_ALREADY_ACQUIRED (ACPI_STATUS) (0x0015 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_HARDWARE_RESPONSE (ACPI_STATUS) (0x0016 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_GLOBAL_LOCK (ACPI_STATUS) (0x0017 | AE_CODE_ENVIRONMENTAL)
-#define AE_ABORT_METHOD (ACPI_STATUS) (0x0018 | AE_CODE_ENVIRONMENTAL)
-#define AE_SAME_HANDLER (ACPI_STATUS) (0x0019 | AE_CODE_ENVIRONMENTAL)
-#define AE_NO_HANDLER (ACPI_STATUS) (0x001A | AE_CODE_ENVIRONMENTAL)
-#define AE_OWNER_ID_LIMIT (ACPI_STATUS) (0x001B | AE_CODE_ENVIRONMENTAL)
-
-#define AE_CODE_ENV_MAX 0x001B
+#define AE_ERROR EXCEP_ENV (0x0001)
+#define AE_NO_ACPI_TABLES EXCEP_ENV (0x0002)
+#define AE_NO_NAMESPACE EXCEP_ENV (0x0003)
+#define AE_NO_MEMORY EXCEP_ENV (0x0004)
+#define AE_NOT_FOUND EXCEP_ENV (0x0005)
+#define AE_NOT_EXIST EXCEP_ENV (0x0006)
+#define AE_ALREADY_EXISTS EXCEP_ENV (0x0007)
+#define AE_TYPE EXCEP_ENV (0x0008)
+#define AE_NULL_OBJECT EXCEP_ENV (0x0009)
+#define AE_NULL_ENTRY EXCEP_ENV (0x000A)
+#define AE_BUFFER_OVERFLOW EXCEP_ENV (0x000B)
+#define AE_STACK_OVERFLOW EXCEP_ENV (0x000C)
+#define AE_STACK_UNDERFLOW EXCEP_ENV (0x000D)
+#define AE_NOT_IMPLEMENTED EXCEP_ENV (0x000E)
+#define AE_SUPPORT EXCEP_ENV (0x000F)
+#define AE_LIMIT EXCEP_ENV (0x0010)
+#define AE_TIME EXCEP_ENV (0x0011)
+#define AE_ACQUIRE_DEADLOCK EXCEP_ENV (0x0012)
+#define AE_RELEASE_DEADLOCK EXCEP_ENV (0x0013)
+#define AE_NOT_ACQUIRED EXCEP_ENV (0x0014)
+#define AE_ALREADY_ACQUIRED EXCEP_ENV (0x0015)
+#define AE_NO_HARDWARE_RESPONSE EXCEP_ENV (0x0016)
+#define AE_NO_GLOBAL_LOCK EXCEP_ENV (0x0017)
+#define AE_ABORT_METHOD EXCEP_ENV (0x0018)
+#define AE_SAME_HANDLER EXCEP_ENV (0x0019)
+#define AE_NO_HANDLER EXCEP_ENV (0x001A)
+#define AE_OWNER_ID_LIMIT EXCEP_ENV (0x001B)
+#define AE_NOT_CONFIGURED EXCEP_ENV (0x001C)
+#define AE_ACCESS EXCEP_ENV (0x001D)
+#define AE_IO_ERROR EXCEP_ENV (0x001E)
+
+#define AE_CODE_ENV_MAX 0x001E
/*
* Programmer exceptions
*/
-#define AE_BAD_PARAMETER (ACPI_STATUS) (0x0001 | AE_CODE_PROGRAMMER)
-#define AE_BAD_CHARACTER (ACPI_STATUS) (0x0002 | AE_CODE_PROGRAMMER)
-#define AE_BAD_PATHNAME (ACPI_STATUS) (0x0003 | AE_CODE_PROGRAMMER)
-#define AE_BAD_DATA (ACPI_STATUS) (0x0004 | AE_CODE_PROGRAMMER)
-#define AE_BAD_HEX_CONSTANT (ACPI_STATUS) (0x0005 | AE_CODE_PROGRAMMER)
-#define AE_BAD_OCTAL_CONSTANT (ACPI_STATUS) (0x0006 | AE_CODE_PROGRAMMER)
-#define AE_BAD_DECIMAL_CONSTANT (ACPI_STATUS) (0x0007 | AE_CODE_PROGRAMMER)
-#define AE_MISSING_ARGUMENTS (ACPI_STATUS) (0x0008 | AE_CODE_PROGRAMMER)
-#define AE_BAD_ADDRESS (ACPI_STATUS) (0x0009 | AE_CODE_PROGRAMMER)
+#define AE_BAD_PARAMETER EXCEP_PGM (0x0001)
+#define AE_BAD_CHARACTER EXCEP_PGM (0x0002)
+#define AE_BAD_PATHNAME EXCEP_PGM (0x0003)
+#define AE_BAD_DATA EXCEP_PGM (0x0004)
+#define AE_BAD_HEX_CONSTANT EXCEP_PGM (0x0005)
+#define AE_BAD_OCTAL_CONSTANT EXCEP_PGM (0x0006)
+#define AE_BAD_DECIMAL_CONSTANT EXCEP_PGM (0x0007)
+#define AE_MISSING_ARGUMENTS EXCEP_PGM (0x0008)
+#define AE_BAD_ADDRESS EXCEP_PGM (0x0009)
#define AE_CODE_PGM_MAX 0x0009
@@ -115,196 +152,203 @@
/*
* Acpi table exceptions
*/
-#define AE_BAD_SIGNATURE (ACPI_STATUS) (0x0001 | AE_CODE_ACPI_TABLES)
-#define AE_BAD_HEADER (ACPI_STATUS) (0x0002 | AE_CODE_ACPI_TABLES)
-#define AE_BAD_CHECKSUM (ACPI_STATUS) (0x0003 | AE_CODE_ACPI_TABLES)
-#define AE_BAD_VALUE (ACPI_STATUS) (0x0004 | AE_CODE_ACPI_TABLES)
-#define AE_INVALID_TABLE_LENGTH (ACPI_STATUS) (0x0005 | AE_CODE_ACPI_TABLES)
+#define AE_BAD_SIGNATURE EXCEP_TBL (0x0001)
+#define AE_BAD_HEADER EXCEP_TBL (0x0002)
+#define AE_BAD_CHECKSUM EXCEP_TBL (0x0003)
+#define AE_BAD_VALUE EXCEP_TBL (0x0004)
+#define AE_INVALID_TABLE_LENGTH EXCEP_TBL (0x0005)
#define AE_CODE_TBL_MAX 0x0005
/*
- * AML exceptions. These are caused by problems with
+ * AML exceptions. These are caused by problems with
* the actual AML byte stream
*/
-#define AE_AML_BAD_OPCODE (ACPI_STATUS) (0x0001 | AE_CODE_AML)
-#define AE_AML_NO_OPERAND (ACPI_STATUS) (0x0002 | AE_CODE_AML)
-#define AE_AML_OPERAND_TYPE (ACPI_STATUS) (0x0003 | AE_CODE_AML)
-#define AE_AML_OPERAND_VALUE (ACPI_STATUS) (0x0004 | AE_CODE_AML)
-#define AE_AML_UNINITIALIZED_LOCAL (ACPI_STATUS) (0x0005 | AE_CODE_AML)
-#define AE_AML_UNINITIALIZED_ARG (ACPI_STATUS) (0x0006 | AE_CODE_AML)
-#define AE_AML_UNINITIALIZED_ELEMENT (ACPI_STATUS) (0x0007 | AE_CODE_AML)
-#define AE_AML_NUMERIC_OVERFLOW (ACPI_STATUS) (0x0008 | AE_CODE_AML)
-#define AE_AML_REGION_LIMIT (ACPI_STATUS) (0x0009 | AE_CODE_AML)
-#define AE_AML_BUFFER_LIMIT (ACPI_STATUS) (0x000A | AE_CODE_AML)
-#define AE_AML_PACKAGE_LIMIT (ACPI_STATUS) (0x000B | AE_CODE_AML)
-#define AE_AML_DIVIDE_BY_ZERO (ACPI_STATUS) (0x000C | AE_CODE_AML)
-#define AE_AML_BAD_NAME (ACPI_STATUS) (0x000D | AE_CODE_AML)
-#define AE_AML_NAME_NOT_FOUND (ACPI_STATUS) (0x000E | AE_CODE_AML)
-#define AE_AML_INTERNAL (ACPI_STATUS) (0x000F | AE_CODE_AML)
-#define AE_AML_INVALID_SPACE_ID (ACPI_STATUS) (0x0010 | AE_CODE_AML)
-#define AE_AML_STRING_LIMIT (ACPI_STATUS) (0x0011 | AE_CODE_AML)
-#define AE_AML_NO_RETURN_VALUE (ACPI_STATUS) (0x0012 | AE_CODE_AML)
-#define AE_AML_METHOD_LIMIT (ACPI_STATUS) (0x0013 | AE_CODE_AML)
-#define AE_AML_NOT_OWNER (ACPI_STATUS) (0x0014 | AE_CODE_AML)
-#define AE_AML_MUTEX_ORDER (ACPI_STATUS) (0x0015 | AE_CODE_AML)
-#define AE_AML_MUTEX_NOT_ACQUIRED (ACPI_STATUS) (0x0016 | AE_CODE_AML)
-#define AE_AML_INVALID_RESOURCE_TYPE (ACPI_STATUS) (0x0017 | AE_CODE_AML)
-#define AE_AML_INVALID_INDEX (ACPI_STATUS) (0x0018 | AE_CODE_AML)
-#define AE_AML_REGISTER_LIMIT (ACPI_STATUS) (0x0019 | AE_CODE_AML)
-#define AE_AML_NO_WHILE (ACPI_STATUS) (0x001A | AE_CODE_AML)
-#define AE_AML_ALIGNMENT (ACPI_STATUS) (0x001B | AE_CODE_AML)
-#define AE_AML_NO_RESOURCE_END_TAG (ACPI_STATUS) (0x001C | AE_CODE_AML)
-#define AE_AML_BAD_RESOURCE_VALUE (ACPI_STATUS) (0x001D | AE_CODE_AML)
-#define AE_AML_CIRCULAR_REFERENCE (ACPI_STATUS) (0x001E | AE_CODE_AML)
-#define AE_AML_BAD_RESOURCE_LENGTH (ACPI_STATUS) (0x001F | AE_CODE_AML)
-#define AE_AML_ILLEGAL_ADDRESS (ACPI_STATUS) (0x0020 | AE_CODE_AML)
-#define AE_AML_INFINITE_LOOP (ACPI_STATUS) (0x0021 | AE_CODE_AML)
-
-#define AE_CODE_AML_MAX 0x0021
+#define AE_AML_BAD_OPCODE EXCEP_AML (0x0001)
+#define AE_AML_NO_OPERAND EXCEP_AML (0x0002)
+#define AE_AML_OPERAND_TYPE EXCEP_AML (0x0003)
+#define AE_AML_OPERAND_VALUE EXCEP_AML (0x0004)
+#define AE_AML_UNINITIALIZED_LOCAL EXCEP_AML (0x0005)
+#define AE_AML_UNINITIALIZED_ARG EXCEP_AML (0x0006)
+#define AE_AML_UNINITIALIZED_ELEMENT EXCEP_AML (0x0007)
+#define AE_AML_NUMERIC_OVERFLOW EXCEP_AML (0x0008)
+#define AE_AML_REGION_LIMIT EXCEP_AML (0x0009)
+#define AE_AML_BUFFER_LIMIT EXCEP_AML (0x000A)
+#define AE_AML_PACKAGE_LIMIT EXCEP_AML (0x000B)
+#define AE_AML_DIVIDE_BY_ZERO EXCEP_AML (0x000C)
+#define AE_AML_BAD_NAME EXCEP_AML (0x000D)
+#define AE_AML_NAME_NOT_FOUND EXCEP_AML (0x000E)
+#define AE_AML_INTERNAL EXCEP_AML (0x000F)
+#define AE_AML_INVALID_SPACE_ID EXCEP_AML (0x0010)
+#define AE_AML_STRING_LIMIT EXCEP_AML (0x0011)
+#define AE_AML_NO_RETURN_VALUE EXCEP_AML (0x0012)
+#define AE_AML_METHOD_LIMIT EXCEP_AML (0x0013)
+#define AE_AML_NOT_OWNER EXCEP_AML (0x0014)
+#define AE_AML_MUTEX_ORDER EXCEP_AML (0x0015)
+#define AE_AML_MUTEX_NOT_ACQUIRED EXCEP_AML (0x0016)
+#define AE_AML_INVALID_RESOURCE_TYPE EXCEP_AML (0x0017)
+#define AE_AML_INVALID_INDEX EXCEP_AML (0x0018)
+#define AE_AML_REGISTER_LIMIT EXCEP_AML (0x0019)
+#define AE_AML_NO_WHILE EXCEP_AML (0x001A)
+#define AE_AML_ALIGNMENT EXCEP_AML (0x001B)
+#define AE_AML_NO_RESOURCE_END_TAG EXCEP_AML (0x001C)
+#define AE_AML_BAD_RESOURCE_VALUE EXCEP_AML (0x001D)
+#define AE_AML_CIRCULAR_REFERENCE EXCEP_AML (0x001E)
+#define AE_AML_BAD_RESOURCE_LENGTH EXCEP_AML (0x001F)
+#define AE_AML_ILLEGAL_ADDRESS EXCEP_AML (0x0020)
+#define AE_AML_INFINITE_LOOP EXCEP_AML (0x0021)
+#define AE_AML_UNINITIALIZED_NODE EXCEP_AML (0x0022)
+#define AE_AML_TARGET_TYPE EXCEP_AML (0x0023)
+
+#define AE_CODE_AML_MAX 0x0023
/*
* Internal exceptions used for control
*/
-#define AE_CTRL_RETURN_VALUE (ACPI_STATUS) (0x0001 | AE_CODE_CONTROL)
-#define AE_CTRL_PENDING (ACPI_STATUS) (0x0002 | AE_CODE_CONTROL)
-#define AE_CTRL_TERMINATE (ACPI_STATUS) (0x0003 | AE_CODE_CONTROL)
-#define AE_CTRL_TRUE (ACPI_STATUS) (0x0004 | AE_CODE_CONTROL)
-#define AE_CTRL_FALSE (ACPI_STATUS) (0x0005 | AE_CODE_CONTROL)
-#define AE_CTRL_DEPTH (ACPI_STATUS) (0x0006 | AE_CODE_CONTROL)
-#define AE_CTRL_END (ACPI_STATUS) (0x0007 | AE_CODE_CONTROL)
-#define AE_CTRL_TRANSFER (ACPI_STATUS) (0x0008 | AE_CODE_CONTROL)
-#define AE_CTRL_BREAK (ACPI_STATUS) (0x0009 | AE_CODE_CONTROL)
-#define AE_CTRL_CONTINUE (ACPI_STATUS) (0x000A | AE_CODE_CONTROL)
-#define AE_CTRL_SKIP (ACPI_STATUS) (0x000B | AE_CODE_CONTROL)
-#define AE_CTRL_PARSE_CONTINUE (ACPI_STATUS) (0x000C | AE_CODE_CONTROL)
-#define AE_CTRL_PARSE_PENDING (ACPI_STATUS) (0x000D | AE_CODE_CONTROL)
+#define AE_CTRL_RETURN_VALUE EXCEP_CTL (0x0001)
+#define AE_CTRL_PENDING EXCEP_CTL (0x0002)
+#define AE_CTRL_TERMINATE EXCEP_CTL (0x0003)
+#define AE_CTRL_TRUE EXCEP_CTL (0x0004)
+#define AE_CTRL_FALSE EXCEP_CTL (0x0005)
+#define AE_CTRL_DEPTH EXCEP_CTL (0x0006)
+#define AE_CTRL_END EXCEP_CTL (0x0007)
+#define AE_CTRL_TRANSFER EXCEP_CTL (0x0008)
+#define AE_CTRL_BREAK EXCEP_CTL (0x0009)
+#define AE_CTRL_CONTINUE EXCEP_CTL (0x000A)
+#define AE_CTRL_SKIP EXCEP_CTL (0x000B)
+#define AE_CTRL_PARSE_CONTINUE EXCEP_CTL (0x000C)
+#define AE_CTRL_PARSE_PENDING EXCEP_CTL (0x000D)
#define AE_CODE_CTRL_MAX 0x000D
/* Exception strings for AcpiFormatException */
-#ifdef DEFINE_ACPI_GLOBALS
+#ifdef ACPI_DEFINE_EXCEPTION_TABLE
/*
* String versions of the exception codes above
* These strings must match the corresponding defines exactly
*/
-char const *AcpiGbl_ExceptionNames_Env[] =
+static const ACPI_EXCEPTION_INFO AcpiGbl_ExceptionNames_Env[] =
{
- "AE_OK",
- "AE_ERROR",
- "AE_NO_ACPI_TABLES",
- "AE_NO_NAMESPACE",
- "AE_NO_MEMORY",
- "AE_NOT_FOUND",
- "AE_NOT_EXIST",
- "AE_ALREADY_EXISTS",
- "AE_TYPE",
- "AE_NULL_OBJECT",
- "AE_NULL_ENTRY",
- "AE_BUFFER_OVERFLOW",
- "AE_STACK_OVERFLOW",
- "AE_STACK_UNDERFLOW",
- "AE_NOT_IMPLEMENTED",
- "AE_SUPPORT",
- "AE_LIMIT",
- "AE_TIME",
- "AE_ACQUIRE_DEADLOCK",
- "AE_RELEASE_DEADLOCK",
- "AE_NOT_ACQUIRED",
- "AE_ALREADY_ACQUIRED",
- "AE_NO_HARDWARE_RESPONSE",
- "AE_NO_GLOBAL_LOCK",
- "AE_ABORT_METHOD",
- "AE_SAME_HANDLER",
- "AE_NO_HANDLER",
- "AE_OWNER_ID_LIMIT"
+ EXCEP_TXT ("AE_OK", "No error"),
+ EXCEP_TXT ("AE_ERROR", "Unspecified error"),
+ EXCEP_TXT ("AE_NO_ACPI_TABLES", "ACPI tables could not be found"),
+ EXCEP_TXT ("AE_NO_NAMESPACE", "A namespace has not been loaded"),
+ EXCEP_TXT ("AE_NO_MEMORY", "Insufficient dynamic memory"),
+ EXCEP_TXT ("AE_NOT_FOUND", "A requested entity is not found"),
+ EXCEP_TXT ("AE_NOT_EXIST", "A required entity does not exist"),
+ EXCEP_TXT ("AE_ALREADY_EXISTS", "An entity already exists"),
+ EXCEP_TXT ("AE_TYPE", "The object type is incorrect"),
+ EXCEP_TXT ("AE_NULL_OBJECT", "A required object was missing"),
+ EXCEP_TXT ("AE_NULL_ENTRY", "The requested object does not exist"),
+ EXCEP_TXT ("AE_BUFFER_OVERFLOW", "The buffer provided is too small"),
+ EXCEP_TXT ("AE_STACK_OVERFLOW", "An internal stack overflowed"),
+ EXCEP_TXT ("AE_STACK_UNDERFLOW", "An internal stack underflowed"),
+ EXCEP_TXT ("AE_NOT_IMPLEMENTED", "The feature is not implemented"),
+ EXCEP_TXT ("AE_SUPPORT", "The feature is not supported"),
+ EXCEP_TXT ("AE_LIMIT", "A predefined limit was exceeded"),
+ EXCEP_TXT ("AE_TIME", "A time limit or timeout expired"),
+ EXCEP_TXT ("AE_ACQUIRE_DEADLOCK", "Internal error, attempt was made to acquire a mutex in improper order"),
+ EXCEP_TXT ("AE_RELEASE_DEADLOCK", "Internal error, attempt was made to release a mutex in improper order"),
+ EXCEP_TXT ("AE_NOT_ACQUIRED", "An attempt to release a mutex or Global Lock without a previous acquire"),
+ EXCEP_TXT ("AE_ALREADY_ACQUIRED", "Internal error, attempt was made to acquire a mutex twice"),
+ EXCEP_TXT ("AE_NO_HARDWARE_RESPONSE", "Hardware did not respond after an I/O operation"),
+ EXCEP_TXT ("AE_NO_GLOBAL_LOCK", "There is no FACS Global Lock"),
+ EXCEP_TXT ("AE_ABORT_METHOD", "A control method was aborted"),
+ EXCEP_TXT ("AE_SAME_HANDLER", "Attempt was made to install the same handler that is already installed"),
+ EXCEP_TXT ("AE_NO_HANDLER", "A handler for the operation is not installed"),
+ EXCEP_TXT ("AE_OWNER_ID_LIMIT", "There are no more Owner IDs available for ACPI tables or control methods"),
+ EXCEP_TXT ("AE_NOT_CONFIGURED", "The interface is not part of the current subsystem configuration"),
+ EXCEP_TXT ("AE_ACCESS", "Permission denied for the requested operation"),
+ EXCEP_TXT ("AE_IO_ERROR", "An I/O error occurred")
};
-char const *AcpiGbl_ExceptionNames_Pgm[] =
+static const ACPI_EXCEPTION_INFO AcpiGbl_ExceptionNames_Pgm[] =
{
- NULL,
- "AE_BAD_PARAMETER",
- "AE_BAD_CHARACTER",
- "AE_BAD_PATHNAME",
- "AE_BAD_DATA",
- "AE_BAD_HEX_CONSTANT",
- "AE_BAD_OCTAL_CONSTANT",
- "AE_BAD_DECIMAL_CONSTANT",
- "AE_MISSING_ARGUMENTS",
- "AE_BAD_ADDRESS"
+ EXCEP_TXT (NULL, NULL),
+ EXCEP_TXT ("AE_BAD_PARAMETER", "A parameter is out of range or invalid"),
+ EXCEP_TXT ("AE_BAD_CHARACTER", "An invalid character was found in a name"),
+ EXCEP_TXT ("AE_BAD_PATHNAME", "An invalid character was found in a pathname"),
+ EXCEP_TXT ("AE_BAD_DATA", "A package or buffer contained incorrect data"),
+ EXCEP_TXT ("AE_BAD_HEX_CONSTANT", "Invalid character in a Hex constant"),
+ EXCEP_TXT ("AE_BAD_OCTAL_CONSTANT", "Invalid character in an Octal constant"),
+ EXCEP_TXT ("AE_BAD_DECIMAL_CONSTANT", "Invalid character in a Decimal constant"),
+ EXCEP_TXT ("AE_MISSING_ARGUMENTS", "Too few arguments were passed to a control method"),
+ EXCEP_TXT ("AE_BAD_ADDRESS", "An illegal null I/O address")
};
-char const *AcpiGbl_ExceptionNames_Tbl[] =
+static const ACPI_EXCEPTION_INFO AcpiGbl_ExceptionNames_Tbl[] =
{
- NULL,
- "AE_BAD_SIGNATURE",
- "AE_BAD_HEADER",
- "AE_BAD_CHECKSUM",
- "AE_BAD_VALUE",
- "AE_INVALID_TABLE_LENGTH"
+ EXCEP_TXT (NULL, NULL),
+ EXCEP_TXT ("AE_BAD_SIGNATURE", "An ACPI table has an invalid signature"),
+ EXCEP_TXT ("AE_BAD_HEADER", "Invalid field in an ACPI table header"),
+ EXCEP_TXT ("AE_BAD_CHECKSUM", "An ACPI table checksum is not correct"),
+ EXCEP_TXT ("AE_BAD_VALUE", "An invalid value was found in a table"),
+ EXCEP_TXT ("AE_INVALID_TABLE_LENGTH", "The FADT or FACS has improper length")
};
-char const *AcpiGbl_ExceptionNames_Aml[] =
+static const ACPI_EXCEPTION_INFO AcpiGbl_ExceptionNames_Aml[] =
{
- NULL,
- "AE_AML_BAD_OPCODE",
- "AE_AML_NO_OPERAND",
- "AE_AML_OPERAND_TYPE",
- "AE_AML_OPERAND_VALUE",
- "AE_AML_UNINITIALIZED_LOCAL",
- "AE_AML_UNINITIALIZED_ARG",
- "AE_AML_UNINITIALIZED_ELEMENT",
- "AE_AML_NUMERIC_OVERFLOW",
- "AE_AML_REGION_LIMIT",
- "AE_AML_BUFFER_LIMIT",
- "AE_AML_PACKAGE_LIMIT",
- "AE_AML_DIVIDE_BY_ZERO",
- "AE_AML_BAD_NAME",
- "AE_AML_NAME_NOT_FOUND",
- "AE_AML_INTERNAL",
- "AE_AML_INVALID_SPACE_ID",
- "AE_AML_STRING_LIMIT",
- "AE_AML_NO_RETURN_VALUE",
- "AE_AML_METHOD_LIMIT",
- "AE_AML_NOT_OWNER",
- "AE_AML_MUTEX_ORDER",
- "AE_AML_MUTEX_NOT_ACQUIRED",
- "AE_AML_INVALID_RESOURCE_TYPE",
- "AE_AML_INVALID_INDEX",
- "AE_AML_REGISTER_LIMIT",
- "AE_AML_NO_WHILE",
- "AE_AML_ALIGNMENT",
- "AE_AML_NO_RESOURCE_END_TAG",
- "AE_AML_BAD_RESOURCE_VALUE",
- "AE_AML_CIRCULAR_REFERENCE",
- "AE_AML_BAD_RESOURCE_LENGTH",
- "AE_AML_ILLEGAL_ADDRESS",
- "AE_AML_INFINITE_LOOP"
+ EXCEP_TXT (NULL, NULL),
+ EXCEP_TXT ("AE_AML_BAD_OPCODE", "Invalid AML opcode encountered"),
+ EXCEP_TXT ("AE_AML_NO_OPERAND", "A required operand is missing"),
+ EXCEP_TXT ("AE_AML_OPERAND_TYPE", "An operand of an incorrect type was encountered"),
+ EXCEP_TXT ("AE_AML_OPERAND_VALUE", "The operand had an inappropriate or invalid value"),
+ EXCEP_TXT ("AE_AML_UNINITIALIZED_LOCAL", "Method tried to use an uninitialized local variable"),
+ EXCEP_TXT ("AE_AML_UNINITIALIZED_ARG", "Method tried to use an uninitialized argument"),
+ EXCEP_TXT ("AE_AML_UNINITIALIZED_ELEMENT", "Method tried to use an empty package element"),
+ EXCEP_TXT ("AE_AML_NUMERIC_OVERFLOW", "Overflow during BCD conversion or other"),
+ EXCEP_TXT ("AE_AML_REGION_LIMIT", "Tried to access beyond the end of an Operation Region"),
+ EXCEP_TXT ("AE_AML_BUFFER_LIMIT", "Tried to access beyond the end of a buffer"),
+ EXCEP_TXT ("AE_AML_PACKAGE_LIMIT", "Tried to access beyond the end of a package"),
+ EXCEP_TXT ("AE_AML_DIVIDE_BY_ZERO", "During execution of AML Divide operator"),
+ EXCEP_TXT ("AE_AML_BAD_NAME", "An ACPI name contains invalid character(s)"),
+ EXCEP_TXT ("AE_AML_NAME_NOT_FOUND", "Could not resolve a named reference"),
+ EXCEP_TXT ("AE_AML_INTERNAL", "An internal error within the interprete"),
+ EXCEP_TXT ("AE_AML_INVALID_SPACE_ID", "An Operation Region SpaceID is invalid"),
+ EXCEP_TXT ("AE_AML_STRING_LIMIT", "String is longer than 200 characters"),
+ EXCEP_TXT ("AE_AML_NO_RETURN_VALUE", "A method did not return a required value"),
+ EXCEP_TXT ("AE_AML_METHOD_LIMIT", "A control method reached the maximum reentrancy limit of 255"),
+ EXCEP_TXT ("AE_AML_NOT_OWNER", "A thread tried to release a mutex that it does not own"),
+ EXCEP_TXT ("AE_AML_MUTEX_ORDER", "Mutex SyncLevel release mismatch"),
+ EXCEP_TXT ("AE_AML_MUTEX_NOT_ACQUIRED", "Attempt to release a mutex that was not previously acquired"),
+ EXCEP_TXT ("AE_AML_INVALID_RESOURCE_TYPE", "Invalid resource type in resource list"),
+ EXCEP_TXT ("AE_AML_INVALID_INDEX", "Invalid Argx or Localx (x too large)"),
+ EXCEP_TXT ("AE_AML_REGISTER_LIMIT", "Bank value or Index value beyond range of register"),
+ EXCEP_TXT ("AE_AML_NO_WHILE", "Break or Continue without a While"),
+ EXCEP_TXT ("AE_AML_ALIGNMENT", "Non-aligned memory transfer on platform that does not support this"),
+ EXCEP_TXT ("AE_AML_NO_RESOURCE_END_TAG", "No End Tag in a resource list"),
+ EXCEP_TXT ("AE_AML_BAD_RESOURCE_VALUE", "Invalid value of a resource element"),
+ EXCEP_TXT ("AE_AML_CIRCULAR_REFERENCE", "Two references refer to each other"),
+ EXCEP_TXT ("AE_AML_BAD_RESOURCE_LENGTH", "The length of a Resource Descriptor in the AML is incorrect"),
+ EXCEP_TXT ("AE_AML_ILLEGAL_ADDRESS", "A memory, I/O, or PCI configuration address is invalid"),
+ EXCEP_TXT ("AE_AML_INFINITE_LOOP", "An apparent infinite AML While loop, method was aborted"),
+ EXCEP_TXT ("AE_AML_UNINITIALIZED_NODE", "A namespace node is uninitialized or unresolved"),
+ EXCEP_TXT ("AE_AML_TARGET_TYPE", "A target operand of an incorrect type was encountered")
};
-char const *AcpiGbl_ExceptionNames_Ctrl[] =
+static const ACPI_EXCEPTION_INFO AcpiGbl_ExceptionNames_Ctrl[] =
{
- NULL,
- "AE_CTRL_RETURN_VALUE",
- "AE_CTRL_PENDING",
- "AE_CTRL_TERMINATE",
- "AE_CTRL_TRUE",
- "AE_CTRL_FALSE",
- "AE_CTRL_DEPTH",
- "AE_CTRL_END",
- "AE_CTRL_TRANSFER",
- "AE_CTRL_BREAK",
- "AE_CTRL_CONTINUE",
- "AE_CTRL_SKIP",
- "AE_CTRL_PARSE_CONTINUE",
- "AE_CTRL_PARSE_PENDING"
+ EXCEP_TXT (NULL, NULL),
+ EXCEP_TXT ("AE_CTRL_RETURN_VALUE", "A Method returned a value"),
+ EXCEP_TXT ("AE_CTRL_PENDING", "Method is calling another method"),
+ EXCEP_TXT ("AE_CTRL_TERMINATE", "Terminate the executing method"),
+ EXCEP_TXT ("AE_CTRL_TRUE", "An If or While predicate result"),
+ EXCEP_TXT ("AE_CTRL_FALSE", "An If or While predicate result"),
+ EXCEP_TXT ("AE_CTRL_DEPTH", "Maximum search depth has been reached"),
+ EXCEP_TXT ("AE_CTRL_END", "An If or While predicate is false"),
+ EXCEP_TXT ("AE_CTRL_TRANSFER", "Transfer control to called method"),
+ EXCEP_TXT ("AE_CTRL_BREAK", "A Break has been executed"),
+ EXCEP_TXT ("AE_CTRL_CONTINUE", "A Continue has been executed"),
+ EXCEP_TXT ("AE_CTRL_SKIP", "Not currently used"),
+ EXCEP_TXT ("AE_CTRL_PARSE_CONTINUE", "Used to skip over bad opcodes"),
+ EXCEP_TXT ("AE_CTRL_PARSE_PENDING", "Used to implement AML While loops")
};
-#endif /* ACPI GLOBALS */
+#endif /* EXCEPTION_TABLE */
#endif /* __ACEXCEP_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acglobal.h b/usr/src/uts/intel/sys/acpi/acglobal.h
index ebbae0f595..a883ac4921 100644
--- a/usr/src/uts/intel/sys/acpi/acglobal.h
+++ b/usr/src/uts/intel/sys/acpi/acglobal.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,139 +45,51 @@
#define __ACGLOBAL_H__
-/*
- * Ensure that the globals are actually defined and initialized only once.
- *
- * The use of these macros allows a single list of globals (here) in order
- * to simplify maintenance of the code.
- */
-#ifdef DEFINE_ACPI_GLOBALS
-#define ACPI_EXTERN
-#define ACPI_INIT_GLOBAL(a,b) a=b
-#else
-#define ACPI_EXTERN extern
-#define ACPI_INIT_GLOBAL(a,b) a
-#endif
-
-
-#ifdef DEFINE_ACPI_GLOBALS
-
-/* Public globals, available from outside ACPICA subsystem */
-
/*****************************************************************************
*
- * Runtime configuration (static defaults that can be overriden at runtime)
+ * Globals related to the ACPI tables
*
****************************************************************************/
-/*
- * Enable "slack" in the AML interpreter? Default is FALSE, and the
- * interpreter strictly follows the ACPI specification. Setting to TRUE
- * allows the interpreter to ignore certain errors and/or bad AML constructs.
- *
- * Currently, these features are enabled by this flag:
- *
- * 1) Allow "implicit return" of last value in a control method
- * 2) Allow access beyond the end of an operation region
- * 3) Allow access to uninitialized locals/args (auto-init to integer 0)
- * 4) Allow ANY object type to be a source operand for the Store() operator
- * 5) Allow unresolved references (invalid target name) in package objects
- * 6) Enable warning messages for behavior that is not ACPI spec compliant
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_EnableInterpreterSlack, FALSE);
-
-/*
- * Automatically serialize ALL control methods? Default is FALSE, meaning
- * to use the Serialized/NotSerialized method flags on a per method basis.
- * Only change this if the ASL code is poorly written and cannot handle
- * reentrancy even though methods are marked "NotSerialized".
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_AllMethodsSerialized, FALSE);
+/* Master list of all ACPI tables that were found in the RSDT/XSDT */
-/*
- * Create the predefined _OSI method in the namespace? Default is TRUE
- * because ACPI CA is fully compatible with other ACPI implementations.
- * Changing this will revert ACPI CA (and machine ASL) to pre-OSI behavior.
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_CreateOsiMethod, TRUE);
+ACPI_GLOBAL (ACPI_TABLE_LIST, AcpiGbl_RootTableList);
-/*
- * Optionally use default values for the ACPI register widths. Set this to
- * TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_UseDefaultRegisterWidths, TRUE);
-
-/*
- * Optionally enable output from the AML Debug Object.
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_EnableAmlDebugObject, FALSE);
-
-/*
- * Optionally copy the entire DSDT to local memory (instead of simply
- * mapping it.) There are some BIOSs that corrupt or replace the original
- * DSDT, creating the need for this option. Default is FALSE, do not copy
- * the DSDT.
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_CopyDsdtLocally, FALSE);
-
-/*
- * Optionally truncate I/O addresses to 16 bits. Provides compatibility
- * with other ACPI implementations. NOTE: During ACPICA initialization,
- * this value is set to TRUE if any Windows OSI strings have been
- * requested by the BIOS.
- */
-UINT8 ACPI_INIT_GLOBAL (AcpiGbl_TruncateIoAddresses, FALSE);
+/* DSDT information. Used to check for DSDT corruption */
+ACPI_GLOBAL (ACPI_TABLE_HEADER *, AcpiGbl_DSDT);
+ACPI_GLOBAL (ACPI_TABLE_HEADER, AcpiGbl_OriginalDsdtHeader);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_DsdtIndex, ACPI_INVALID_TABLE_INDEX);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_FacsIndex, ACPI_INVALID_TABLE_INDEX);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_XFacsIndex, ACPI_INVALID_TABLE_INDEX);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_FadtIndex, ACPI_INVALID_TABLE_INDEX);
-/* AcpiGbl_FADT is a local copy of the FADT, converted to a common format. */
+#if (!ACPI_REDUCED_HARDWARE)
+ACPI_GLOBAL (ACPI_TABLE_FACS *, AcpiGbl_FACS);
-ACPI_TABLE_FADT AcpiGbl_FADT;
-UINT32 AcpiCurrentGpeCount;
-UINT32 AcpiGbl_TraceFlags;
-ACPI_NAME AcpiGbl_TraceMethodName;
-BOOLEAN AcpiGbl_SystemAwakeAndRunning;
-
-#endif
-
-/*****************************************************************************
- *
- * ACPI Table globals
- *
- ****************************************************************************/
-
-/*
- * AcpiGbl_RootTableList is the master list of ACPI tables that were
- * found in the RSDT/XSDT.
- */
-ACPI_EXTERN ACPI_TABLE_LIST AcpiGbl_RootTableList;
-ACPI_EXTERN ACPI_TABLE_FACS *AcpiGbl_FACS;
+#endif /* !ACPI_REDUCED_HARDWARE */
/* These addresses are calculated from the FADT Event Block addresses */
-ACPI_EXTERN ACPI_GENERIC_ADDRESS AcpiGbl_XPm1aStatus;
-ACPI_EXTERN ACPI_GENERIC_ADDRESS AcpiGbl_XPm1aEnable;
-
-ACPI_EXTERN ACPI_GENERIC_ADDRESS AcpiGbl_XPm1bStatus;
-ACPI_EXTERN ACPI_GENERIC_ADDRESS AcpiGbl_XPm1bEnable;
-
-/* DSDT information. Used to check for DSDT corruption */
+ACPI_GLOBAL (ACPI_GENERIC_ADDRESS, AcpiGbl_XPm1aStatus);
+ACPI_GLOBAL (ACPI_GENERIC_ADDRESS, AcpiGbl_XPm1aEnable);
-ACPI_EXTERN ACPI_TABLE_HEADER *AcpiGbl_DSDT;
-ACPI_EXTERN ACPI_TABLE_HEADER AcpiGbl_OriginalDsdtHeader;
+ACPI_GLOBAL (ACPI_GENERIC_ADDRESS, AcpiGbl_XPm1bStatus);
+ACPI_GLOBAL (ACPI_GENERIC_ADDRESS, AcpiGbl_XPm1bEnable);
/*
- * Handle both ACPI 1.0 and ACPI 2.0 Integer widths. The integer width is
+ * Handle both ACPI 1.0 and ACPI 2.0+ Integer widths. The integer width is
* determined by the revision of the DSDT: If the DSDT revision is less than
* 2, use only the lower 32 bits of the internal 64-bit Integer.
*/
-ACPI_EXTERN UINT8 AcpiGbl_IntegerBitWidth;
-ACPI_EXTERN UINT8 AcpiGbl_IntegerByteWidth;
-ACPI_EXTERN UINT8 AcpiGbl_IntegerNybbleWidth;
+ACPI_GLOBAL (UINT8, AcpiGbl_IntegerBitWidth);
+ACPI_GLOBAL (UINT8, AcpiGbl_IntegerByteWidth);
+ACPI_GLOBAL (UINT8, AcpiGbl_IntegerNybbleWidth);
/*****************************************************************************
*
- * Mutual exlusion within ACPICA subsystem
+ * Mutual exclusion within ACPICA subsystem
*
****************************************************************************/
@@ -186,35 +98,36 @@ ACPI_EXTERN UINT8 AcpiGbl_IntegerNybbleWidth;
* actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs.
* (The table maps local handles to the real OS handles)
*/
-ACPI_EXTERN ACPI_MUTEX_INFO AcpiGbl_MutexInfo[ACPI_NUM_MUTEX];
+ACPI_GLOBAL (ACPI_MUTEX_INFO, AcpiGbl_MutexInfo[ACPI_NUM_MUTEX]);
/*
* Global lock mutex is an actual AML mutex object
* Global lock semaphore works in conjunction with the actual global lock
* Global lock spinlock is used for "pending" handshake
*/
-ACPI_EXTERN ACPI_OPERAND_OBJECT *AcpiGbl_GlobalLockMutex;
-ACPI_EXTERN ACPI_SEMAPHORE AcpiGbl_GlobalLockSemaphore;
-ACPI_EXTERN ACPI_SPINLOCK AcpiGbl_GlobalLockPendingLock;
-ACPI_EXTERN UINT16 AcpiGbl_GlobalLockHandle;
-ACPI_EXTERN BOOLEAN AcpiGbl_GlobalLockAcquired;
-ACPI_EXTERN BOOLEAN AcpiGbl_GlobalLockPresent;
-ACPI_EXTERN BOOLEAN AcpiGbl_GlobalLockPending;
+ACPI_GLOBAL (ACPI_OPERAND_OBJECT *, AcpiGbl_GlobalLockMutex);
+ACPI_GLOBAL (ACPI_SEMAPHORE, AcpiGbl_GlobalLockSemaphore);
+ACPI_GLOBAL (ACPI_SPINLOCK, AcpiGbl_GlobalLockPendingLock);
+ACPI_GLOBAL (UINT16, AcpiGbl_GlobalLockHandle);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_GlobalLockAcquired);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_GlobalLockPresent);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_GlobalLockPending);
/*
* Spinlocks are used for interfaces that can be possibly called at
* interrupt level
*/
-ACPI_EXTERN ACPI_SPINLOCK AcpiGbl_GpeLock; /* For GPE data structs and registers */
-ACPI_EXTERN ACPI_SPINLOCK AcpiGbl_HardwareLock; /* For ACPI H/W except GPE registers */
+ACPI_GLOBAL (ACPI_SPINLOCK, AcpiGbl_GpeLock); /* For GPE data structs and registers */
+ACPI_GLOBAL (ACPI_SPINLOCK, AcpiGbl_HardwareLock); /* For ACPI H/W except GPE registers */
+ACPI_GLOBAL (ACPI_SPINLOCK, AcpiGbl_ReferenceCountLock);
/* Mutex for _OSI support */
-ACPI_EXTERN ACPI_MUTEX AcpiGbl_OsiMutex;
+ACPI_GLOBAL (ACPI_MUTEX, AcpiGbl_OsiMutex);
/* Reader/Writer lock is used for namespace walk and dynamic table unload */
-ACPI_EXTERN ACPI_RW_LOCK AcpiGbl_NamespaceRwLock;
+ACPI_GLOBAL (ACPI_RW_LOCK, AcpiGbl_NamespaceRwLock);
/*****************************************************************************
@@ -225,78 +138,70 @@ ACPI_EXTERN ACPI_RW_LOCK AcpiGbl_NamespaceRwLock;
/* Object caches */
-ACPI_EXTERN ACPI_CACHE_T *AcpiGbl_NamespaceCache;
-ACPI_EXTERN ACPI_CACHE_T *AcpiGbl_StateCache;
-ACPI_EXTERN ACPI_CACHE_T *AcpiGbl_PsNodeCache;
-ACPI_EXTERN ACPI_CACHE_T *AcpiGbl_PsNodeExtCache;
-ACPI_EXTERN ACPI_CACHE_T *AcpiGbl_OperandCache;
+ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_NamespaceCache);
+ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_StateCache);
+ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_PsNodeCache);
+ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_PsNodeExtCache);
+ACPI_GLOBAL (ACPI_CACHE_T *, AcpiGbl_OperandCache);
+
+/* System */
+
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_StartupFlags, 0);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_Shutdown, TRUE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_EarlyInitialization, TRUE);
/* Global handlers */
-ACPI_EXTERN ACPI_OBJECT_NOTIFY_HANDLER AcpiGbl_DeviceNotify;
-ACPI_EXTERN ACPI_OBJECT_NOTIFY_HANDLER AcpiGbl_SystemNotify;
-ACPI_EXTERN ACPI_EXCEPTION_HANDLER AcpiGbl_ExceptionHandler;
-ACPI_EXTERN ACPI_INIT_HANDLER AcpiGbl_InitHandler;
-ACPI_EXTERN ACPI_TABLE_HANDLER AcpiGbl_TableHandler;
-ACPI_EXTERN void *AcpiGbl_TableHandlerContext;
-ACPI_EXTERN ACPI_WALK_STATE *AcpiGbl_BreakpointWalk;
-ACPI_EXTERN ACPI_INTERFACE_HANDLER AcpiGbl_InterfaceHandler;
+ACPI_GLOBAL (ACPI_GLOBAL_NOTIFY_HANDLER,AcpiGbl_GlobalNotify[2]);
+ACPI_GLOBAL (ACPI_EXCEPTION_HANDLER, AcpiGbl_ExceptionHandler);
+ACPI_GLOBAL (ACPI_INIT_HANDLER, AcpiGbl_InitHandler);
+ACPI_GLOBAL (ACPI_TABLE_HANDLER, AcpiGbl_TableHandler);
+ACPI_GLOBAL (void *, AcpiGbl_TableHandlerContext);
+ACPI_GLOBAL (ACPI_INTERFACE_HANDLER, AcpiGbl_InterfaceHandler);
+ACPI_GLOBAL (ACPI_SCI_HANDLER_INFO *, AcpiGbl_SciHandlerList);
/* Owner ID support */
-ACPI_EXTERN UINT32 AcpiGbl_OwnerIdMask[ACPI_NUM_OWNERID_MASKS];
-ACPI_EXTERN UINT8 AcpiGbl_LastOwnerIdIndex;
-ACPI_EXTERN UINT8 AcpiGbl_NextOwnerIdOffset;
+ACPI_GLOBAL (UINT32, AcpiGbl_OwnerIdMask[ACPI_NUM_OWNERID_MASKS]);
+ACPI_GLOBAL (UINT8, AcpiGbl_LastOwnerIdIndex);
+ACPI_GLOBAL (UINT8, AcpiGbl_NextOwnerIdOffset);
/* Initialization sequencing */
-ACPI_EXTERN BOOLEAN AcpiGbl_RegMethodsExecuted;
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_NamespaceInitialized, FALSE);
/* Misc */
-ACPI_EXTERN UINT32 AcpiGbl_OriginalMode;
-ACPI_EXTERN UINT32 AcpiGbl_RsdpOriginalLocation;
-ACPI_EXTERN UINT32 AcpiGbl_NsLookupCount;
-ACPI_EXTERN UINT32 AcpiGbl_PsFindCount;
-ACPI_EXTERN UINT16 AcpiGbl_Pm1EnableRegisterSave;
-ACPI_EXTERN UINT8 AcpiGbl_DebuggerConfiguration;
-ACPI_EXTERN BOOLEAN AcpiGbl_StepToNextCall;
-ACPI_EXTERN BOOLEAN AcpiGbl_AcpiHardwarePresent;
-ACPI_EXTERN BOOLEAN AcpiGbl_EventsInitialized;
-ACPI_EXTERN UINT8 AcpiGbl_OsiData;
-ACPI_EXTERN ACPI_INTERFACE_INFO *AcpiGbl_SupportedInterfaces;
-
-
-#ifndef DEFINE_ACPI_GLOBALS
-
-/* Exception codes */
-
-extern char const *AcpiGbl_ExceptionNames_Env[];
-extern char const *AcpiGbl_ExceptionNames_Pgm[];
-extern char const *AcpiGbl_ExceptionNames_Tbl[];
-extern char const *AcpiGbl_ExceptionNames_Aml[];
-extern char const *AcpiGbl_ExceptionNames_Ctrl[];
+ACPI_GLOBAL (UINT32, AcpiGbl_OriginalMode);
+ACPI_GLOBAL (UINT32, AcpiGbl_NsLookupCount);
+ACPI_GLOBAL (UINT32, AcpiGbl_PsFindCount);
+ACPI_GLOBAL (UINT16, AcpiGbl_Pm1EnableRegisterSave);
+ACPI_GLOBAL (UINT8, AcpiGbl_DebuggerConfiguration);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_StepToNextCall);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_AcpiHardwarePresent);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_EventsInitialized);
+ACPI_GLOBAL (ACPI_INTERFACE_INFO *, AcpiGbl_SupportedInterfaces);
+ACPI_GLOBAL (ACPI_ADDRESS_RANGE *, AcpiGbl_AddressRangeList[ACPI_ADDRESS_RANGE_MAX]);
-/* Other miscellaneous */
+/* Other miscellaneous, declared and initialized in utglobal */
-extern BOOLEAN AcpiGbl_Shutdown;
-extern UINT32 AcpiGbl_StartupFlags;
extern const char *AcpiGbl_SleepStateNames[ACPI_S_STATE_COUNT];
extern const char *AcpiGbl_LowestDstateNames[ACPI_NUM_SxW_METHODS];
extern const char *AcpiGbl_HighestDstateNames[ACPI_NUM_SxD_METHODS];
-extern const ACPI_OPCODE_INFO AcpiGbl_AmlOpInfo[AML_NUM_OPCODES];
extern const char *AcpiGbl_RegionTypes[ACPI_NUM_PREDEFINED_REGIONS];
-#endif
+extern const char AcpiGbl_LowerHexDigits[];
+extern const char AcpiGbl_UpperHexDigits[];
+extern const ACPI_OPCODE_INFO AcpiGbl_AmlOpInfo[AML_NUM_OPCODES];
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
-/* Lists for tracking memory allocations */
+/* Lists for tracking memory allocations (debug only) */
-ACPI_EXTERN ACPI_MEMORY_LIST *AcpiGbl_GlobalList;
-ACPI_EXTERN ACPI_MEMORY_LIST *AcpiGbl_NsNodeList;
-ACPI_EXTERN BOOLEAN AcpiGbl_DisplayFinalMemStats;
-ACPI_EXTERN BOOLEAN AcpiGbl_DisableMemTracking;
+ACPI_GLOBAL (ACPI_MEMORY_LIST *, AcpiGbl_GlobalList);
+ACPI_GLOBAL (ACPI_MEMORY_LIST *, AcpiGbl_NsNodeList);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DisplayFinalMemStats);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DisableMemTracking);
#endif
@@ -312,22 +217,23 @@ ACPI_EXTERN BOOLEAN AcpiGbl_DisableMemTracking;
#define NUM_PREDEFINED_NAMES 9
#endif
-ACPI_EXTERN ACPI_NAMESPACE_NODE AcpiGbl_RootNodeStruct;
-ACPI_EXTERN ACPI_NAMESPACE_NODE *AcpiGbl_RootNode;
-ACPI_EXTERN ACPI_NAMESPACE_NODE *AcpiGbl_FadtGpeDevice;
-ACPI_EXTERN ACPI_OPERAND_OBJECT *AcpiGbl_ModuleCodeList;
+ACPI_GLOBAL (ACPI_NAMESPACE_NODE, AcpiGbl_RootNodeStruct);
+ACPI_GLOBAL (ACPI_NAMESPACE_NODE *, AcpiGbl_RootNode);
+ACPI_GLOBAL (ACPI_NAMESPACE_NODE *, AcpiGbl_FadtGpeDevice);
+ACPI_GLOBAL (ACPI_OPERAND_OBJECT *, AcpiGbl_ModuleCodeList);
extern const UINT8 AcpiGbl_NsProperties [ACPI_NUM_NS_TYPES];
extern const ACPI_PREDEFINED_NAMES AcpiGbl_PreDefinedNames [NUM_PREDEFINED_NAMES];
#ifdef ACPI_DEBUG_OUTPUT
-ACPI_EXTERN UINT32 AcpiGbl_CurrentNodeCount;
-ACPI_EXTERN UINT32 AcpiGbl_CurrentNodeSize;
-ACPI_EXTERN UINT32 AcpiGbl_MaxConcurrentNodeCount;
-ACPI_EXTERN ACPI_SIZE *AcpiGbl_EntryStackPointer;
-ACPI_EXTERN ACPI_SIZE *AcpiGbl_LowestStackPointer;
-ACPI_EXTERN UINT32 AcpiGbl_DeepestNesting;
+ACPI_GLOBAL (UINT32, AcpiGbl_CurrentNodeCount);
+ACPI_GLOBAL (UINT32, AcpiGbl_CurrentNodeSize);
+ACPI_GLOBAL (UINT32, AcpiGbl_MaxConcurrentNodeCount);
+ACPI_GLOBAL (ACPI_SIZE *, AcpiGbl_EntryStackPointer);
+ACPI_GLOBAL (ACPI_SIZE *, AcpiGbl_LowestStackPointer);
+ACPI_GLOBAL (UINT32, AcpiGbl_DeepestNesting);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_NestingLevel, 0);
#endif
@@ -337,12 +243,15 @@ ACPI_EXTERN UINT32 AcpiGbl_DeepestNesting;
*
****************************************************************************/
+ACPI_GLOBAL (ACPI_THREAD_STATE *, AcpiGbl_CurrentWalkList);
-ACPI_EXTERN ACPI_THREAD_STATE *AcpiGbl_CurrentWalkList;
+/* Maximum number of While() loop iterations before forced abort */
+
+ACPI_GLOBAL (UINT16, AcpiGbl_MaxLoopIterations);
/* Control method single step flag */
-ACPI_EXTERN UINT8 AcpiGbl_CmSingleStep;
+ACPI_GLOBAL (UINT8, AcpiGbl_CmSingleStep);
/*****************************************************************************
@@ -351,9 +260,10 @@ ACPI_EXTERN UINT8 AcpiGbl_CmSingleStep;
*
****************************************************************************/
-extern ACPI_BIT_REGISTER_INFO AcpiGbl_BitRegisterInfo[ACPI_NUM_BITREG];
-ACPI_EXTERN UINT8 AcpiGbl_SleepTypeA;
-ACPI_EXTERN UINT8 AcpiGbl_SleepTypeB;
+extern ACPI_BIT_REGISTER_INFO AcpiGbl_BitRegisterInfo[ACPI_NUM_BITREG];
+
+ACPI_GLOBAL (UINT8, AcpiGbl_SleepTypeA);
+ACPI_GLOBAL (UINT8, AcpiGbl_SleepTypeB);
/*****************************************************************************
@@ -362,14 +272,18 @@ ACPI_EXTERN UINT8 AcpiGbl_SleepTypeB;
*
****************************************************************************/
-ACPI_EXTERN UINT8 AcpiGbl_AllGpesInitialized;
-ACPI_EXTERN ACPI_GPE_XRUPT_INFO *AcpiGbl_GpeXruptListHead;
-ACPI_EXTERN ACPI_GPE_BLOCK_INFO *AcpiGbl_GpeFadtBlocks[ACPI_MAX_GPE_BLOCKS];
-ACPI_EXTERN ACPI_GBL_EVENT_HANDLER AcpiGbl_GlobalEventHandler;
-ACPI_EXTERN void *AcpiGbl_GlobalEventHandlerContext;
-ACPI_EXTERN ACPI_FIXED_EVENT_HANDLER AcpiGbl_FixedEventHandlers[ACPI_NUM_FIXED_EVENTS];
-extern ACPI_FIXED_EVENT_INFO AcpiGbl_FixedEventInfo[ACPI_NUM_FIXED_EVENTS];
+#if (!ACPI_REDUCED_HARDWARE)
+ACPI_GLOBAL (UINT8, AcpiGbl_AllGpesInitialized);
+ACPI_GLOBAL (ACPI_GPE_XRUPT_INFO *, AcpiGbl_GpeXruptListHead);
+ACPI_GLOBAL (ACPI_GPE_BLOCK_INFO *, AcpiGbl_GpeFadtBlocks[ACPI_MAX_GPE_BLOCKS]);
+ACPI_GLOBAL (ACPI_GBL_EVENT_HANDLER, AcpiGbl_GlobalEventHandler);
+ACPI_GLOBAL (void *, AcpiGbl_GlobalEventHandlerContext);
+ACPI_GLOBAL (ACPI_FIXED_EVENT_HANDLER, AcpiGbl_FixedEventHandlers[ACPI_NUM_FIXED_EVENTS]);
+
+extern ACPI_FIXED_EVENT_INFO AcpiGbl_FixedEventInfo[ACPI_NUM_FIXED_EVENTS];
+
+#endif /* !ACPI_REDUCED_HARDWARE */
/*****************************************************************************
*
@@ -377,82 +291,124 @@ extern ACPI_FIXED_EVENT_INFO AcpiGbl_FixedEventInfo[ACPI_NUM_FIXED_EV
*
****************************************************************************/
-/* Procedure nesting level for debug output */
-
-extern UINT32 AcpiGbl_NestingLevel;
-
/* Event counters */
-ACPI_EXTERN UINT32 AcpiMethodCount;
-ACPI_EXTERN UINT32 AcpiGpeCount;
-ACPI_EXTERN UINT32 AcpiSciCount;
-ACPI_EXTERN UINT32 AcpiFixedEventCount[ACPI_NUM_FIXED_EVENTS];
+ACPI_GLOBAL (UINT32, AcpiMethodCount);
+ACPI_GLOBAL (UINT32, AcpiGpeCount);
+ACPI_GLOBAL (UINT32, AcpiSciCount);
+ACPI_GLOBAL (UINT32, AcpiFixedEventCount[ACPI_NUM_FIXED_EVENTS]);
/* Support for dynamic control method tracing mechanism */
-ACPI_EXTERN UINT32 AcpiGbl_OriginalDbgLevel;
-ACPI_EXTERN UINT32 AcpiGbl_OriginalDbgLayer;
-ACPI_EXTERN UINT32 AcpiGbl_TraceDbgLevel;
-ACPI_EXTERN UINT32 AcpiGbl_TraceDbgLayer;
+ACPI_GLOBAL (UINT32, AcpiGbl_OriginalDbgLevel);
+ACPI_GLOBAL (UINT32, AcpiGbl_OriginalDbgLayer);
/*****************************************************************************
*
- * Debugger globals
+ * Debugger and Disassembler globals
*
****************************************************************************/
-ACPI_EXTERN UINT8 AcpiGbl_DbOutputFlags;
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DbOutputFlags, ACPI_DB_CONSOLE_OUTPUT);
#ifdef ACPI_DISASSEMBLER
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_disasm;
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_verbose;
-ACPI_EXTERN ACPI_EXTERNAL_LIST *AcpiGbl_ExternalList;
-ACPI_EXTERN ACPI_EXTERNAL_FILE *AcpiGbl_ExternalFileList;
+/* Do not disassemble buffers to resource descriptors */
+
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_NoResourceDisassembly, FALSE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_IgnoreNoopOperator, FALSE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_CstyleDisassembly, TRUE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_ForceAmlDisassembly, FALSE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_DmOpt_Verbose, TRUE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_DmEmitExternalOpcodes, FALSE);
+
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DmOpt_Disasm);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DmOpt_Listing);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_NumExternalMethods);
+ACPI_GLOBAL (UINT32, AcpiGbl_ResolvedExternalMethods);
+ACPI_GLOBAL (ACPI_EXTERNAL_LIST *, AcpiGbl_ExternalList);
+ACPI_GLOBAL (ACPI_EXTERNAL_FILE *, AcpiGbl_ExternalFileList);
#endif
-
#ifdef ACPI_DEBUGGER
-extern BOOLEAN AcpiGbl_MethodExecuting;
-extern BOOLEAN AcpiGbl_AbortMethod;
-extern BOOLEAN AcpiGbl_DbTerminateThreads;
-
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_tables;
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_stats;
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_ini_methods;
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOpt_NoRegionSupport;
-
-ACPI_EXTERN char *AcpiGbl_DbArgs[ACPI_DEBUGGER_MAX_ARGS];
-ACPI_EXTERN ACPI_OBJECT_TYPE AcpiGbl_DbArgTypes[ACPI_DEBUGGER_MAX_ARGS];
-ACPI_EXTERN char AcpiGbl_DbLineBuf[ACPI_DB_LINE_BUFFER_SIZE];
-ACPI_EXTERN char AcpiGbl_DbParsedBuf[ACPI_DB_LINE_BUFFER_SIZE];
-ACPI_EXTERN char AcpiGbl_DbScopeBuf[80];
-ACPI_EXTERN char AcpiGbl_DbDebugFilename[80];
-ACPI_EXTERN BOOLEAN AcpiGbl_DbOutputToFile;
-ACPI_EXTERN char *AcpiGbl_DbBuffer;
-ACPI_EXTERN char *AcpiGbl_DbFilename;
-ACPI_EXTERN UINT32 AcpiGbl_DbDebugLevel;
-ACPI_EXTERN UINT32 AcpiGbl_DbConsoleDebugLevel;
-ACPI_EXTERN ACPI_NAMESPACE_NODE *AcpiGbl_DbScopeNode;
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_AbortMethod, FALSE);
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_MethodExecuting, FALSE);
+ACPI_INIT_GLOBAL (ACPI_THREAD_ID, AcpiGbl_DbThreadId, ACPI_INVALID_THREAD_ID);
+
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DbOpt_NoIniMethods);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DbOpt_NoRegionSupport);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DbOutputToFile);
+ACPI_GLOBAL (char *, AcpiGbl_DbBuffer);
+ACPI_GLOBAL (char *, AcpiGbl_DbFilename);
+ACPI_GLOBAL (UINT32, AcpiGbl_DbDebugLevel);
+ACPI_GLOBAL (UINT32, AcpiGbl_DbConsoleDebugLevel);
+ACPI_GLOBAL (ACPI_NAMESPACE_NODE *, AcpiGbl_DbScopeNode);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DbTerminateLoop);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_DbThreadsTerminated);
+
+ACPI_GLOBAL (char *, AcpiGbl_DbArgs[ACPI_DEBUGGER_MAX_ARGS]);
+ACPI_GLOBAL (ACPI_OBJECT_TYPE, AcpiGbl_DbArgTypes[ACPI_DEBUGGER_MAX_ARGS]);
+
+/* These buffers should all be the same size */
+
+ACPI_GLOBAL (char, AcpiGbl_DbLineBuf[ACPI_DB_LINE_BUFFER_SIZE]);
+ACPI_GLOBAL (char, AcpiGbl_DbParsedBuf[ACPI_DB_LINE_BUFFER_SIZE]);
+ACPI_GLOBAL (char, AcpiGbl_DbScopeBuf[ACPI_DB_LINE_BUFFER_SIZE]);
+ACPI_GLOBAL (char, AcpiGbl_DbDebugFilename[ACPI_DB_LINE_BUFFER_SIZE]);
/*
* Statistic globals
*/
-ACPI_EXTERN UINT16 AcpiGbl_ObjTypeCount[ACPI_TYPE_NS_NODE_MAX+1];
-ACPI_EXTERN UINT16 AcpiGbl_NodeTypeCount[ACPI_TYPE_NS_NODE_MAX+1];
-ACPI_EXTERN UINT16 AcpiGbl_ObjTypeCountMisc;
-ACPI_EXTERN UINT16 AcpiGbl_NodeTypeCountMisc;
-ACPI_EXTERN UINT32 AcpiGbl_NumNodes;
-ACPI_EXTERN UINT32 AcpiGbl_NumObjects;
+ACPI_GLOBAL (UINT16, AcpiGbl_ObjTypeCount[ACPI_TOTAL_TYPES]);
+ACPI_GLOBAL (UINT16, AcpiGbl_NodeTypeCount[ACPI_TOTAL_TYPES]);
+ACPI_GLOBAL (UINT16, AcpiGbl_ObjTypeCountMisc);
+ACPI_GLOBAL (UINT16, AcpiGbl_NodeTypeCountMisc);
+ACPI_GLOBAL (UINT32, AcpiGbl_NumNodes);
+ACPI_GLOBAL (UINT32, AcpiGbl_NumObjects);
-
-ACPI_EXTERN UINT32 AcpiGbl_SizeOfParseTree;
-ACPI_EXTERN UINT32 AcpiGbl_SizeOfMethodTrees;
-ACPI_EXTERN UINT32 AcpiGbl_SizeOfNodeEntries;
-ACPI_EXTERN UINT32 AcpiGbl_SizeOfAcpiObjects;
+ACPI_GLOBAL (ACPI_MUTEX, AcpiGbl_DbCommandReady);
+ACPI_GLOBAL (ACPI_MUTEX, AcpiGbl_DbCommandComplete);
#endif /* ACPI_DEBUGGER */
+#if defined (ACPI_DISASSEMBLER) || defined (ACPI_ASL_COMPILER)
+
+ACPI_GLOBAL (const char, *AcpiGbl_PldPanelList[]);
+ACPI_GLOBAL (const char, *AcpiGbl_PldVerticalPositionList[]);
+ACPI_GLOBAL (const char, *AcpiGbl_PldHorizontalPositionList[]);
+ACPI_GLOBAL (const char, *AcpiGbl_PldShapeList[]);
+
+#endif
+
+/*****************************************************************************
+ *
+ * Application globals
+ *
+ ****************************************************************************/
+
+#ifdef ACPI_APPLICATION
+
+ACPI_INIT_GLOBAL (ACPI_FILE, AcpiGbl_DebugFile, NULL);
+ACPI_INIT_GLOBAL (ACPI_FILE, AcpiGbl_OutputFile, NULL);
+
+/* Print buffer */
+
+ACPI_GLOBAL (ACPI_SPINLOCK, AcpiGbl_PrintLock); /* For print buffer */
+ACPI_GLOBAL (char, AcpiGbl_PrintBuffer[1024]);
+
+#endif /* ACPI_APPLICATION */
+
+
+/*****************************************************************************
+ *
+ * Info/help support
+ *
+ ****************************************************************************/
+
+extern const AH_PREDEFINED_NAME AslPredefinedInfo[];
+extern const AH_DEVICE_ID AslDeviceIds[];
+
+
#endif /* __ACGLOBAL_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/achware.h b/usr/src/uts/intel/sys/acpi/achware.h
index ae4a77a9f2..b7a5f20fec 100644
--- a/usr/src/uts/intel/sys/acpi/achware.h
+++ b/usr/src/uts/intel/sys/acpi/achware.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -110,6 +110,43 @@ AcpiHwClearAcpiStatus (
/*
+ * hwsleep - sleep/wake support (Legacy sleep registers)
+ */
+ACPI_STATUS
+AcpiHwLegacySleep (
+ UINT8 SleepState);
+
+ACPI_STATUS
+AcpiHwLegacyWakePrep (
+ UINT8 SleepState);
+
+ACPI_STATUS
+AcpiHwLegacyWake (
+ UINT8 SleepState);
+
+
+/*
+ * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
+ */
+void
+AcpiHwExecuteSleepMethod (
+ char *MethodName,
+ UINT32 IntegerArgument);
+
+ACPI_STATUS
+AcpiHwExtendedSleep (
+ UINT8 SleepState);
+
+ACPI_STATUS
+AcpiHwExtendedWakePrep (
+ UINT8 SleepState);
+
+ACPI_STATUS
+AcpiHwExtendedWake (
+ UINT8 SleepState);
+
+
+/*
* hwvalid - Port I/O with validation
*/
ACPI_STATUS
@@ -130,8 +167,7 @@ AcpiHwWritePort (
*/
UINT32
AcpiHwGetGpeRegisterBit (
- ACPI_GPE_EVENT_INFO *GpeEventInfo,
- ACPI_GPE_REGISTER_INFO *GpeRegisterInfo);
+ ACPI_GPE_EVENT_INFO *GpeEventInfo);
ACPI_STATUS
AcpiHwLowSetGpe (
@@ -188,22 +224,4 @@ AcpiHwDerivePciId (
ACPI_HANDLE PciRegion);
-/*
- * hwtimer - ACPI Timer prototypes
- */
-ACPI_STATUS
-AcpiGetTimerResolution (
- UINT32 *Resolution);
-
-ACPI_STATUS
-AcpiGetTimer (
- UINT32 *Ticks);
-
-ACPI_STATUS
-AcpiGetTimerDuration (
- UINT32 StartTicks,
- UINT32 EndTicks,
- UINT32 *TimeElapsed);
-
-
#endif /* __ACHWARE_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acinterp.h b/usr/src/uts/intel/sys/acpi/acinterp.h
index 8ac828c59c..148e8d0caf 100644
--- a/usr/src/uts/intel/sys/acpi/acinterp.h
+++ b/usr/src/uts/intel/sys/acpi/acinterp.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,7 +69,7 @@ typedef const struct acpi_exdump_info
{
UINT8 Opcode;
UINT8 Offset;
- char *Name;
+ const char *Name;
} ACPI_EXDUMP_INFO;
@@ -89,6 +89,10 @@ typedef const struct acpi_exdump_info
#define ACPI_EXD_PACKAGE 11
#define ACPI_EXD_FIELD 12
#define ACPI_EXD_REFERENCE 13
+#define ACPI_EXD_LIST 14 /* Operand object list */
+#define ACPI_EXD_HDLR_LIST 15 /* Address Handler list */
+#define ACPI_EXD_RGN_LIST 16 /* Region list */
+#define ACPI_EXD_NODE 17 /* Namespace Node */
/* restore default alignment */
@@ -139,6 +143,35 @@ AcpiExDoDebugObject (
UINT32 Level,
UINT32 Index);
+void
+AcpiExStartTraceMethod (
+ ACPI_NAMESPACE_NODE *MethodNode,
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiExStopTraceMethod (
+ ACPI_NAMESPACE_NODE *MethodNode,
+ ACPI_OPERAND_OBJECT *ObjDesc,
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiExStartTraceOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiExStopTraceOpcode (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_WALK_STATE *WalkState);
+
+void
+AcpiExTracePoint (
+ ACPI_TRACE_EVENT_TYPE Type,
+ BOOLEAN Begin,
+ UINT8 *Aml,
+ char *Pathname);
+
/*
* exfield - ACPI AML (p-code) execution - field manipulation
@@ -612,15 +645,7 @@ void
AcpiExExitInterpreter (
void);
-void
-AcpiExReacquireInterpreter (
- void);
-
-void
-AcpiExRelinquishInterpreter (
- void);
-
-void
+BOOLEAN
AcpiExTruncateFor32bitTable (
ACPI_OPERAND_OBJECT *ObjDesc);
@@ -642,6 +667,15 @@ AcpiExIntegerToString (
char *Dest,
UINT64 Value);
+void
+AcpiExPciClsToString (
+ char *Dest,
+ UINT8 ClassCode[3]);
+
+BOOLEAN
+AcpiIsValidSpaceId (
+ UINT8 SpaceId);
+
/*
* exregion - default OpRegion handlers
diff --git a/usr/src/uts/intel/sys/acpi/aclocal.h b/usr/src/uts/intel/sys/acpi/aclocal.h
index 2eb6fc21d4..e7a0d37d14 100644
--- a/usr/src/uts/intel/sys/acpi/aclocal.h
+++ b/usr/src/uts/intel/sys/acpi/aclocal.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,7 +54,7 @@ typedef UINT32 ACPI_MUTEX_HANDLE;
/* Total number of aml opcodes defined */
-#define AML_NUM_OPCODES 0x7F
+#define AML_NUM_OPCODES 0x82
/* Forward declarations */
@@ -87,11 +87,9 @@ union acpi_parse_object;
#define ACPI_MTX_EVENTS 3 /* Data for ACPI events */
#define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */
#define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */
-#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */
-#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */
-#define ACPI_MAX_MUTEX 7
-#define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1
+#define ACPI_MAX_MUTEX 5
+#define ACPI_NUM_MUTEX (ACPI_MAX_MUTEX+1)
/* Lock structure for reader/writer interfaces */
@@ -113,12 +111,20 @@ typedef struct acpi_rw_lock
#define ACPI_LOCK_HARDWARE 1
#define ACPI_MAX_LOCK 1
-#define ACPI_NUM_LOCK ACPI_MAX_LOCK+1
+#define ACPI_NUM_LOCK (ACPI_MAX_LOCK+1)
/* This Thread ID means that the mutex is not in use (unlocked) */
-#define ACPI_MUTEX_NOT_ACQUIRED (ACPI_THREAD_ID) -1
+#define ACPI_MUTEX_NOT_ACQUIRED ((ACPI_THREAD_ID) -1)
+
+/* This Thread ID means an invalid thread ID */
+
+#ifdef ACPI_OS_INVALID_THREAD_ID
+#define ACPI_INVALID_THREAD_ID ACPI_OS_INVALID_THREAD_ID
+#else
+#define ACPI_INVALID_THREAD_ID ((ACPI_THREAD_ID) 0xFFFFFFFF)
+#endif
/* Table for the global mutexes */
@@ -193,8 +199,12 @@ typedef struct acpi_namespace_node
*/
#ifdef ACPI_LARGE_NAMESPACE_NODE
union acpi_parse_object *Op;
+ void *MethodLocals;
+ void *MethodArgs;
UINT32 Value;
UINT32 Length;
+ UINT8 ArgCount;
+
#endif
} ACPI_NAMESPACE_NODE;
@@ -213,7 +223,6 @@ typedef struct acpi_namespace_node
#define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */
#define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */
#define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* iASL only: Method has at least one return value */
-#define ANOBJ_IS_BIT_OFFSET 0x40 /* iASL only: Reference is a bit offset */
#define ANOBJ_IS_REFERENCED 0x80 /* iASL only: Object was referenced */
@@ -235,10 +244,19 @@ typedef struct acpi_table_list
#define ACPI_ROOT_ALLOW_RESIZE (2)
-/* Predefined (fixed) table indexes */
+/* List to manage incoming ACPI tables */
+
+typedef struct acpi_new_table_desc
+{
+ ACPI_TABLE_HEADER *Table;
+ struct acpi_new_table_desc *Next;
+
+} ACPI_NEW_TABLE_DESC;
+
+
+/* Predefined table indexes */
-#define ACPI_TABLE_INDEX_DSDT (0)
-#define ACPI_TABLE_INDEX_FACS (1)
+#define ACPI_INVALID_TABLE_INDEX (0xFFFFFFFF)
typedef struct acpi_find_context
@@ -286,12 +304,17 @@ typedef struct acpi_create_field_info
ACPI_NAMESPACE_NODE *FieldNode;
ACPI_NAMESPACE_NODE *RegisterNode;
ACPI_NAMESPACE_NODE *DataRegisterNode;
+ ACPI_NAMESPACE_NODE *ConnectionNode;
+ UINT8 *ResourceBuffer;
UINT32 BankValue;
UINT32 FieldBitPosition;
UINT32 FieldBitLength;
+ UINT16 ResourceLength;
+ UINT16 PinNumberIndex;
UINT8 FieldFlags;
UINT8 Attribute;
UINT8 FieldType;
+ UINT8 AccessLength;
} ACPI_CREATE_FIELD_INFO;
@@ -302,7 +325,7 @@ ACPI_STATUS (*ACPI_INTERNAL_METHOD) (
/*
- * Bitmapped ACPI types. Used internally only
+ * Bitmapped ACPI types. Used internally only
*/
#define ACPI_BTYPE_ANY 0x00000000
#define ACPI_BTYPE_INTEGER 0x00000001
@@ -321,17 +344,22 @@ ACPI_STATUS (*ACPI_INTERNAL_METHOD) (
#define ACPI_BTYPE_BUFFER_FIELD 0x00002000
#define ACPI_BTYPE_DDB_HANDLE 0x00004000
#define ACPI_BTYPE_DEBUG_OBJECT 0x00008000
-#define ACPI_BTYPE_REFERENCE 0x00010000
+#define ACPI_BTYPE_REFERENCE_OBJECT 0x00010000 /* From Index(), RefOf(), etc (Type6Opcodes) */
#define ACPI_BTYPE_RESOURCE 0x00020000
+#define ACPI_BTYPE_NAMED_REFERENCE 0x00040000 /* Generic unresolved Name or Namepath */
#define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER)
#define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE)
-#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE | ACPI_BTYPE_DDB_HANDLE)
+
+ /* Used by Copy, DeRefOf, Store, Printf, Fprintf */
+
+#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE_OBJECT | ACPI_BTYPE_DDB_HANDLE)
#define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR)
#define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */
#define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF
+#pragma pack(1)
/*
* Information structure for ACPI predefined names.
@@ -344,7 +372,7 @@ ACPI_STATUS (*ACPI_INTERNAL_METHOD) (
typedef struct acpi_name_info
{
char Name[ACPI_NAME_SIZE];
- UINT8 ParamCount;
+ UINT16 ArgumentList;
UINT8 ExpectedBtypes;
} ACPI_NAME_INFO;
@@ -359,7 +387,8 @@ typedef struct acpi_name_info
/*
* Used for ACPI_PTYPE1_FIXED, ACPI_PTYPE1_VAR, ACPI_PTYPE2,
- * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT
+ * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT,
+ * ACPI_PTYPE2_FIX_VAR
*/
typedef struct acpi_package_info
{
@@ -368,7 +397,7 @@ typedef struct acpi_package_info
UINT8 Count1;
UINT8 ObjectType2;
UINT8 Count2;
- UINT8 Reserved;
+ UINT16 Reserved;
} ACPI_PACKAGE_INFO;
@@ -379,6 +408,7 @@ typedef struct acpi_package_info2
UINT8 Type;
UINT8 Count;
UINT8 ObjectType[4];
+ UINT8 Reserved;
} ACPI_PACKAGE_INFO2;
@@ -390,35 +420,51 @@ typedef struct acpi_package_info3
UINT8 Count;
UINT8 ObjectType[2];
UINT8 TailObjectType;
- UINT8 Reserved;
+ UINT16 Reserved;
} ACPI_PACKAGE_INFO3;
+typedef struct acpi_package_info4
+{
+ UINT8 Type;
+ UINT8 ObjectType1;
+ UINT8 Count1;
+ UINT8 SubObjectTypes;
+ UINT8 PkgCount;
+ UINT16 Reserved;
+
+} ACPI_PACKAGE_INFO4;
+
typedef union acpi_predefined_info
{
ACPI_NAME_INFO Info;
ACPI_PACKAGE_INFO RetInfo;
ACPI_PACKAGE_INFO2 RetInfo2;
ACPI_PACKAGE_INFO3 RetInfo3;
+ ACPI_PACKAGE_INFO4 RetInfo4;
} ACPI_PREDEFINED_INFO;
+/* Reset to default packing */
-/* Data block used during object validation */
+#pragma pack()
-typedef struct acpi_predefined_data
-{
- char *Pathname;
- const ACPI_PREDEFINED_INFO *Predefined;
- union acpi_operand_object *ParentPackage;
- UINT32 Flags;
- UINT8 NodeFlags;
-} ACPI_PREDEFINED_DATA;
+/* Return object auto-repair info */
+
+typedef ACPI_STATUS (*ACPI_OBJECT_CONVERTER) (
+ struct acpi_namespace_node *Scope,
+ union acpi_operand_object *OriginalObject,
+ union acpi_operand_object **ConvertedObject);
-/* Defines for Flags field above */
+typedef struct acpi_simple_repair_info
+{
+ char Name[ACPI_NAME_SIZE];
+ UINT32 UnexpectedBtypes;
+ UINT32 PackageIndex;
+ ACPI_OBJECT_CONVERTER ObjectConverter;
-#define ACPI_OBJECT_REPAIRED 1
+} ACPI_SIMPLE_REPAIR_INFO;
/*
@@ -438,12 +484,33 @@ typedef struct acpi_predefined_data
#define ACPI_NUM_RTYPES 5 /* Number of actual object types */
+/* Info for running the _REG methods */
+
+typedef struct acpi_reg_walk_info
+{
+ ACPI_ADR_SPACE_TYPE SpaceId;
+ UINT32 Function;
+ UINT32 RegRunCount;
+
+} ACPI_REG_WALK_INFO;
+
+
/*****************************************************************************
*
* Event typedefs and structs
*
****************************************************************************/
+/* Dispatch info for each host-installed SCI handler */
+
+typedef struct acpi_sci_handler_info
+{
+ struct acpi_sci_handler_info *Next;
+ ACPI_SCI_HANDLER Address; /* Address of handler */
+ void *Context; /* Context to be passed to handler */
+
+} ACPI_SCI_HANDLER_INFO;
+
/* Dispatch info for each GPE -- either a method or handler, cannot be both */
typedef struct acpi_gpe_handler_info
@@ -456,6 +523,15 @@ typedef struct acpi_gpe_handler_info
} ACPI_GPE_HANDLER_INFO;
+/* Notify info for implicit notify, multiple device objects */
+
+typedef struct acpi_gpe_notify_info
+{
+ ACPI_NAMESPACE_NODE *DeviceNode; /* Device to be notified */
+ struct acpi_gpe_notify_info *Next;
+
+} ACPI_GPE_NOTIFY_INFO;
+
/*
* GPE dispatch info. At any time, the GPE can have at most one type
* of dispatch - Method, Handler, or Implicit Notify.
@@ -463,8 +539,8 @@ typedef struct acpi_gpe_handler_info
typedef union acpi_gpe_dispatch_info
{
ACPI_NAMESPACE_NODE *MethodNode; /* Method node for this GPE level */
- struct acpi_gpe_handler_info *Handler; /* Installed GPE handler */
- ACPI_NAMESPACE_NODE *DeviceNode; /* Parent _PRW device for implicit notify */
+ ACPI_GPE_HANDLER_INFO *Handler; /* Installed GPE handler */
+ ACPI_GPE_NOTIFY_INFO *NotifyList; /* List of _PRW devices for implicit notifies */
} ACPI_GPE_DISPATCH_INFO;
@@ -474,7 +550,7 @@ typedef union acpi_gpe_dispatch_info
*/
typedef struct acpi_gpe_event_info
{
- union acpi_gpe_dispatch_info Dispatch; /* Either Method or Handler */
+ union acpi_gpe_dispatch_info Dispatch; /* Either Method, Handler, or NotifyList */
struct acpi_gpe_register_info *RegisterInfo; /* Backpointer to register info */
UINT8 Flags; /* Misc info about this GPE */
UINT8 GpeNumber; /* This GPE */
@@ -488,9 +564,10 @@ typedef struct acpi_gpe_register_info
{
ACPI_GENERIC_ADDRESS StatusAddress; /* Address of status reg */
ACPI_GENERIC_ADDRESS EnableAddress; /* Address of enable reg */
+ UINT16 BaseGpeNumber; /* Base GPE number for this register */
UINT8 EnableForWake; /* GPEs to keep enabled when sleeping */
UINT8 EnableForRun; /* GPEs to keep enabled when running */
- UINT8 BaseGpeNumber; /* Base GPE number for this register */
+ UINT8 EnableMask; /* Current mask of enabled GPEs */
} ACPI_GPE_REGISTER_INFO;
@@ -506,10 +583,11 @@ typedef struct acpi_gpe_block_info
struct acpi_gpe_xrupt_info *XruptBlock; /* Backpointer to interrupt block */
ACPI_GPE_REGISTER_INFO *RegisterInfo; /* One per GPE register pair */
ACPI_GPE_EVENT_INFO *EventInfo; /* One for each GPE */
- ACPI_GENERIC_ADDRESS BlockAddress; /* Base address of the block */
+ UINT64 Address; /* Base address of the block */
UINT32 RegisterCount; /* Number of register pairs in block */
UINT16 GpeCount; /* Number of individual GPEs in block */
- UINT8 BlockBaseNumber;/* Base GPE number for this block */
+ UINT16 BlockBaseNumber;/* Base GPE number for this block */
+ UINT8 SpaceId;
BOOLEAN Initialized; /* TRUE if this block is initialized */
} ACPI_GPE_BLOCK_INFO;
@@ -674,7 +752,7 @@ typedef struct acpi_pscope_state
/*
- * Thread state - one per thread across multiple walk states. Multiple walk
+ * Thread state - one per thread across multiple walk states. Multiple walk
* states are created when there are nested control methods executing.
*/
typedef struct acpi_thread_state
@@ -710,6 +788,15 @@ ACPI_STATUS (*ACPI_PARSE_UPWARDS) (
struct acpi_walk_state *WalkState);
+/* Global handlers for AML Notifies */
+
+typedef struct acpi_global_notify_handler
+{
+ ACPI_NOTIFY_HANDLER Handler;
+ void *Context;
+
+} ACPI_GLOBAL_NOTIFY_HANDLER;
+
/*
* Notify info - used to pass info to the deferred notify
* handler/dispatcher.
@@ -717,8 +804,10 @@ ACPI_STATUS (*ACPI_PARSE_UPWARDS) (
typedef struct acpi_notify_info
{
ACPI_STATE_COMMON
+ UINT8 HandlerListId;
ACPI_NAMESPACE_NODE *Node;
- union acpi_operand_object *HandlerObj;
+ union acpi_operand_object *HandlerListHead;
+ ACPI_GLOBAL_NOTIFY_HANDLER *Global;
} ACPI_NOTIFY_INFO;
@@ -750,6 +839,17 @@ typedef
ACPI_STATUS (*ACPI_EXECUTE_OP) (
struct acpi_walk_state *WalkState);
+/* Address Range info block */
+
+typedef struct acpi_address_range
+{
+ struct acpi_address_range *Next;
+ ACPI_NAMESPACE_NODE *RegionNode;
+ ACPI_PHYSICAL_ADDRESS StartAddress;
+ ACPI_PHYSICAL_ADDRESS EndAddress;
+
+} ACPI_ADDRESS_RANGE;
+
/*****************************************************************************
*
@@ -774,6 +874,17 @@ typedef struct acpi_opcode_info
} ACPI_OPCODE_INFO;
+/* Structure for Resource Tag information */
+
+typedef struct acpi_tag_info
+{
+ UINT32 BitOffset;
+ UINT32 BitLength;
+
+} ACPI_TAG_INFO;
+
+/* Value associated with the parse object */
+
typedef union acpi_parse_value
{
UINT64 Integer; /* Integer constant (Up to 64 bits) */
@@ -782,11 +893,12 @@ typedef union acpi_parse_value
UINT8 *Buffer; /* buffer or string */
char *Name; /* NULL terminated string */
union acpi_parse_object *Arg; /* arguments and contained ops */
+ ACPI_TAG_INFO Tag; /* Resource descriptor tag info */
} ACPI_PARSE_VALUE;
-#ifdef ACPI_DISASSEMBLER
+#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT)
#define ACPI_DISASM_ONLY_MEMBERS(a) a;
#else
#define ACPI_DISASM_ONLY_MEMBERS(a)
@@ -797,7 +909,7 @@ typedef union acpi_parse_value
UINT8 DescriptorType; /* To differentiate various internal objs */\
UINT8 Flags; /* Type of Op */\
UINT16 AmlOpcode; /* AML opcode */\
- UINT32 AmlOffset; /* Offset of declaration in AML */\
+ UINT8 *Aml; /* Address of declaration in AML */\
union acpi_parse_object *Next; /* Next op */\
ACPI_NAMESPACE_NODE *Node; /* For use by interpreter */\
ACPI_PARSE_VALUE Value; /* Value or args associated with the opcode */\
@@ -805,18 +917,24 @@ typedef union acpi_parse_value
ACPI_DISASM_ONLY_MEMBERS (\
UINT8 DisasmFlags; /* Used during AML disassembly */\
UINT8 DisasmOpcode; /* Subtype used for disassembly */\
+ char *OperatorSymbol;/* Used for C-style operator name strings */\
char AmlOpName[16]) /* Op name (debug only) */
-#define ACPI_DASM_BUFFER 0x00
-#define ACPI_DASM_RESOURCE 0x01
-#define ACPI_DASM_STRING 0x02
-#define ACPI_DASM_UNICODE 0x03
-#define ACPI_DASM_EISAID 0x04
-#define ACPI_DASM_MATCHOP 0x05
-#define ACPI_DASM_LNOT_PREFIX 0x06
-#define ACPI_DASM_LNOT_SUFFIX 0x07
-#define ACPI_DASM_IGNORE 0x08
+/* Flags for DisasmFlags field above */
+
+#define ACPI_DASM_BUFFER 0x00 /* Buffer is a simple data buffer */
+#define ACPI_DASM_RESOURCE 0x01 /* Buffer is a Resource Descriptor */
+#define ACPI_DASM_STRING 0x02 /* Buffer is a ASCII string */
+#define ACPI_DASM_UNICODE 0x03 /* Buffer is a Unicode string */
+#define ACPI_DASM_PLD_METHOD 0x04 /* Buffer is a _PLD method bit-packed buffer */
+#define ACPI_DASM_UUID 0x05 /* Buffer is a UUID/GUID */
+#define ACPI_DASM_EISAID 0x06 /* Integer is an EISAID */
+#define ACPI_DASM_MATCHOP 0x07 /* Parent opcode is a Match() operator */
+#define ACPI_DASM_LNOT_PREFIX 0x08 /* Start of a LNotEqual (etc.) pair of opcodes */
+#define ACPI_DASM_LNOT_SUFFIX 0x09 /* End of a LNotEqual (etc.) pair of opcodes */
+#define ACPI_DASM_HID_STRING 0x0A /* String is a _HID or _CID */
+#define ACPI_DASM_IGNORE 0x0B /* Not used at this time */
/*
* Generic operation (for example: If, While, Store)
@@ -907,20 +1025,24 @@ typedef struct acpi_parse_state
/* Parse object flags */
-#define ACPI_PARSEOP_GENERIC 0x01
-#define ACPI_PARSEOP_NAMED 0x02
-#define ACPI_PARSEOP_DEFERRED 0x04
-#define ACPI_PARSEOP_BYTELIST 0x08
-#define ACPI_PARSEOP_IN_STACK 0x10
-#define ACPI_PARSEOP_TARGET 0x20
-#define ACPI_PARSEOP_IN_CACHE 0x80
+#define ACPI_PARSEOP_GENERIC 0x01
+#define ACPI_PARSEOP_NAMED_OBJECT 0x02
+#define ACPI_PARSEOP_DEFERRED 0x04
+#define ACPI_PARSEOP_BYTELIST 0x08
+#define ACPI_PARSEOP_IN_STACK 0x10
+#define ACPI_PARSEOP_TARGET 0x20
+#define ACPI_PARSEOP_IN_CACHE 0x80
/* Parse object DisasmFlags */
-#define ACPI_PARSEOP_IGNORE 0x01
-#define ACPI_PARSEOP_PARAMLIST 0x02
-#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04
-#define ACPI_PARSEOP_SPECIAL 0x10
+#define ACPI_PARSEOP_IGNORE 0x01
+#define ACPI_PARSEOP_PARAMETER_LIST 0x02
+#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04
+#define ACPI_PARSEOP_PREDEFINED_CHECKED 0x08
+#define ACPI_PARSEOP_CLOSING_PAREN 0x10
+#define ACPI_PARSEOP_COMPOUND_ASSIGNMENT 0x20
+#define ACPI_PARSEOP_ASSIGNMENT 0x40
+#define ACPI_PARSEOP_ELSEIF 0x80
/*****************************************************************************
@@ -1044,18 +1166,6 @@ typedef struct acpi_bit_register_info
/* Structs and definitions for _OSI support and I/O port validation */
-#define ACPI_OSI_WIN_2000 0x01
-#define ACPI_OSI_WIN_XP 0x02
-#define ACPI_OSI_WIN_XP_SP1 0x03
-#define ACPI_OSI_WINSRV_2003 0x04
-#define ACPI_OSI_WIN_XP_SP2 0x05
-#define ACPI_OSI_WINSRV_2003_SP1 0x06
-#define ACPI_OSI_WIN_VISTA 0x07
-#define ACPI_OSI_WINSRV_2008 0x08
-#define ACPI_OSI_WIN_VISTA_SP1 0x09
-#define ACPI_OSI_WIN_VISTA_SP2 0x0A
-#define ACPI_OSI_WIN_7 0x0B
-
#define ACPI_ALWAYS_ILLEGAL 0x00
typedef struct acpi_interface_info
@@ -1069,6 +1179,9 @@ typedef struct acpi_interface_info
#define ACPI_OSI_INVALID 0x01
#define ACPI_OSI_DYNAMIC 0x02
+#define ACPI_OSI_FEATURE 0x04
+#define ACPI_OSI_DEFAULT_INVALID 0x08
+#define ACPI_OSI_OPTIONAL_FEATURE (ACPI_OSI_FEATURE | ACPI_OSI_DEFAULT_INVALID | ACPI_OSI_INVALID)
typedef struct acpi_port_info
{
@@ -1112,7 +1225,7 @@ typedef struct acpi_port_info
#define ACPI_RESOURCE_NAME_END_DEPENDENT 0x38
#define ACPI_RESOURCE_NAME_IO 0x40
#define ACPI_RESOURCE_NAME_FIXED_IO 0x48
-#define ACPI_RESOURCE_NAME_RESERVED_S1 0x50
+#define ACPI_RESOURCE_NAME_FIXED_DMA 0x50
#define ACPI_RESOURCE_NAME_RESERVED_S2 0x58
#define ACPI_RESOURCE_NAME_RESERVED_S3 0x60
#define ACPI_RESOURCE_NAME_RESERVED_S4 0x68
@@ -1134,7 +1247,9 @@ typedef struct acpi_port_info
#define ACPI_RESOURCE_NAME_EXTENDED_IRQ 0x89
#define ACPI_RESOURCE_NAME_ADDRESS64 0x8A
#define ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 0x8B
-#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8B
+#define ACPI_RESOURCE_NAME_GPIO 0x8C
+#define ACPI_RESOURCE_NAME_SERIAL_BUS 0x8E
+#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8E
/*****************************************************************************
@@ -1159,14 +1274,18 @@ typedef struct acpi_external_list
struct acpi_external_list *Next;
UINT32 Value;
UINT16 Length;
+ UINT16 Flags;
UINT8 Type;
- UINT8 Flags;
} ACPI_EXTERNAL_LIST;
/* Values for Flags field above */
-#define ACPI_IPATH_ALLOCATED 0x01
+#define ACPI_EXT_RESOLVED_REFERENCE 0x01 /* Object was resolved during cross ref */
+#define ACPI_EXT_ORIGIN_FROM_FILE 0x02 /* External came from a file */
+#define ACPI_EXT_INTERNAL_PATH_ALLOCATED 0x04 /* Deallocate internal path on completion */
+#define ACPI_EXT_EXTERNAL_EMITTED 0x08 /* External() statement has been emitted */
+#define ACPI_EXT_ORIGIN_FROM_OPCODE 0x10 /* External came from a External() opcode */
typedef struct acpi_external_file
@@ -1185,6 +1304,7 @@ typedef struct acpi_external_file
typedef struct acpi_db_method_info
{
+ ACPI_HANDLE Method;
ACPI_HANDLE MainThreadGate;
ACPI_HANDLE ThreadCompleteGate;
ACPI_HANDLE InfoGate;
@@ -1196,7 +1316,7 @@ typedef struct acpi_db_method_info
char *Name;
UINT32 Flags;
UINT32 NumLoops;
- char Pathname[128];
+ char Pathname[ACPI_DB_LINE_BUFFER_SIZE];
char **Args;
ACPI_OBJECT_TYPE *Types;
@@ -1207,7 +1327,9 @@ typedef struct acpi_db_method_info
* Index of current thread inside all them created.
*/
char InitArgs;
+#ifdef ACPI_DEBUGGER
ACPI_OBJECT_TYPE ArgTypes[4];
+#endif
char *Arguments[4];
char NumThreadsStr[11];
char IdOfThreadStr[11];
@@ -1223,11 +1345,19 @@ typedef struct acpi_integrity_info
} ACPI_INTEGRITY_INFO;
+#define ACPI_DB_DISABLE_OUTPUT 0x00
#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01
#define ACPI_DB_CONSOLE_OUTPUT 0x02
#define ACPI_DB_DUPLICATE_OUTPUT 0x03
+typedef struct acpi_object_info
+{
+ UINT32 Types[ACPI_TOTAL_TYPES];
+
+} ACPI_OBJECT_INFO;
+
+
/*****************************************************************************
*
* Debug
@@ -1269,4 +1399,41 @@ typedef struct acpi_debug_mem_block
#define ACPI_NUM_MEM_LISTS 2
+/*****************************************************************************
+ *
+ * Info/help support
+ *
+ ****************************************************************************/
+
+typedef struct ah_predefined_name
+{
+ char *Name;
+ char *Description;
+#ifndef ACPI_ASL_COMPILER
+ char *Action;
+#endif
+
+} AH_PREDEFINED_NAME;
+
+typedef struct ah_device_id
+{
+ char *Name;
+ char *Description;
+
+} AH_DEVICE_ID;
+
+typedef struct ah_uuid
+{
+ char *Description;
+ char *String;
+
+} AH_UUID;
+
+typedef struct ah_table
+{
+ char *Signature;
+ char *Description;
+
+} AH_TABLE;
+
#endif /* __ACLOCAL_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acmacros.h b/usr/src/uts/intel/sys/acpi/acmacros.h
index 6581d111c9..21256cbba7 100644
--- a/usr/src/uts/intel/sys/acpi/acmacros.h
+++ b/usr/src/uts/intel/sys/acpi/acmacros.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,29 +50,26 @@
* get into potential aligment issues -- see the STORE macros below.
* Use with care.
*/
-#define ACPI_GET8(ptr) *ACPI_CAST_PTR (UINT8, ptr)
-#define ACPI_GET16(ptr) *ACPI_CAST_PTR (UINT16, ptr)
-#define ACPI_GET32(ptr) *ACPI_CAST_PTR (UINT32, ptr)
-#define ACPI_GET64(ptr) *ACPI_CAST_PTR (UINT64, ptr)
-#define ACPI_SET8(ptr) *ACPI_CAST_PTR (UINT8, ptr)
-#define ACPI_SET16(ptr) *ACPI_CAST_PTR (UINT16, ptr)
-#define ACPI_SET32(ptr) *ACPI_CAST_PTR (UINT32, ptr)
-#define ACPI_SET64(ptr) *ACPI_CAST_PTR (UINT64, ptr)
+#define ACPI_CAST8(ptr) ACPI_CAST_PTR (UINT8, (ptr))
+#define ACPI_CAST16(ptr) ACPI_CAST_PTR (UINT16, (ptr))
+#define ACPI_CAST32(ptr) ACPI_CAST_PTR (UINT32, (ptr))
+#define ACPI_CAST64(ptr) ACPI_CAST_PTR (UINT64, (ptr))
+#define ACPI_GET8(ptr) (*ACPI_CAST8 (ptr))
+#define ACPI_GET16(ptr) (*ACPI_CAST16 (ptr))
+#define ACPI_GET32(ptr) (*ACPI_CAST32 (ptr))
+#define ACPI_GET64(ptr) (*ACPI_CAST64 (ptr))
+#define ACPI_SET8(ptr, val) (*ACPI_CAST8 (ptr) = (UINT8) (val))
+#define ACPI_SET16(ptr, val) (*ACPI_CAST16 (ptr) = (UINT16) (val))
+#define ACPI_SET32(ptr, val) (*ACPI_CAST32 (ptr) = (UINT32) (val))
+#define ACPI_SET64(ptr, val) (*ACPI_CAST64 (ptr) = (UINT64) (val))
/*
- * printf() format helpers
+ * printf() format helper. This macros is a workaround for the difficulties
+ * with emitting 64-bit integers and 64-bit pointers with the same code
+ * for both 32-bit and 64-bit hosts.
*/
-
-/* Split 64-bit integer into two 32-bit values. Use with %8.8X%8.8X */
-
#define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i)
-#if ACPI_MACHINE_WIDTH == 64
-#define ACPI_FORMAT_NATIVE_UINT(i) ACPI_FORMAT_UINT64(i)
-#else
-#define ACPI_FORMAT_NATIVE_UINT(i) 0, (i)
-#endif
-
/*
* Macros for moving data around to/from buffers that are possibly unaligned.
@@ -226,6 +223,16 @@
#define ACPI_MUL_32(a) _ACPI_MUL(a, 5)
#define ACPI_MOD_32(a) _ACPI_MOD(a, 32)
+/* Test for ASCII character */
+
+#define ACPI_IS_ASCII(c) ((c) < 0x80)
+
+/* Signed integers */
+
+#define ACPI_SIGN_POSITIVE 0
+#define ACPI_SIGN_NEGATIVE 1
+
+
/*
* Rounding macros (Power of two boundaries only)
*/
@@ -268,20 +275,65 @@
/* Bitfields within ACPI registers */
-#define ACPI_REGISTER_PREPARE_BITS(Val, Pos, Mask) ((Val << Pos) & Mask)
-#define ACPI_REGISTER_INSERT_VALUE(Reg, Pos, Mask, Val) Reg = (Reg & (~(Mask))) | ACPI_REGISTER_PREPARE_BITS(Val, Pos, Mask)
+#define ACPI_REGISTER_PREPARE_BITS(Val, Pos, Mask) \
+ ((Val << Pos) & Mask)
+
+#define ACPI_REGISTER_INSERT_VALUE(Reg, Pos, Mask, Val) \
+ Reg = (Reg & (~(Mask))) | ACPI_REGISTER_PREPARE_BITS(Val, Pos, Mask)
+
+#define ACPI_INSERT_BITS(Target, Mask, Source) \
+ Target = ((Target & (~(Mask))) | (Source & Mask))
+
+/* Generic bitfield macros and masks */
+
+#define ACPI_GET_BITS(SourcePtr, Position, Mask) \
+ ((*(SourcePtr) >> (Position)) & (Mask))
-#define ACPI_INSERT_BITS(Target, Mask, Source) Target = ((Target & (~(Mask))) | (Source & Mask))
+#define ACPI_SET_BITS(TargetPtr, Position, Mask, Value) \
+ (*(TargetPtr) |= (((Value) & (Mask)) << (Position)))
+
+#define ACPI_1BIT_MASK 0x00000001
+#define ACPI_2BIT_MASK 0x00000003
+#define ACPI_3BIT_MASK 0x00000007
+#define ACPI_4BIT_MASK 0x0000000F
+#define ACPI_5BIT_MASK 0x0000001F
+#define ACPI_6BIT_MASK 0x0000003F
+#define ACPI_7BIT_MASK 0x0000007F
+#define ACPI_8BIT_MASK 0x000000FF
+#define ACPI_16BIT_MASK 0x0000FFFF
+#define ACPI_24BIT_MASK 0x00FFFFFF
+
+/* Macros to extract flag bits from position zero */
+
+#define ACPI_GET_1BIT_FLAG(Value) ((Value) & ACPI_1BIT_MASK)
+#define ACPI_GET_2BIT_FLAG(Value) ((Value) & ACPI_2BIT_MASK)
+#define ACPI_GET_3BIT_FLAG(Value) ((Value) & ACPI_3BIT_MASK)
+#define ACPI_GET_4BIT_FLAG(Value) ((Value) & ACPI_4BIT_MASK)
+
+/* Macros to extract flag bits from position one and above */
+
+#define ACPI_EXTRACT_1BIT_FLAG(Field, Position) (ACPI_GET_1BIT_FLAG ((Field) >> Position))
+#define ACPI_EXTRACT_2BIT_FLAG(Field, Position) (ACPI_GET_2BIT_FLAG ((Field) >> Position))
+#define ACPI_EXTRACT_3BIT_FLAG(Field, Position) (ACPI_GET_3BIT_FLAG ((Field) >> Position))
+#define ACPI_EXTRACT_4BIT_FLAG(Field, Position) (ACPI_GET_4BIT_FLAG ((Field) >> Position))
+
+/* ACPI Pathname helpers */
+
+#define ACPI_IS_ROOT_PREFIX(c) ((c) == (UINT8) 0x5C) /* Backslash */
+#define ACPI_IS_PARENT_PREFIX(c) ((c) == (UINT8) 0x5E) /* Carat */
+#define ACPI_IS_PATH_SEPARATOR(c) ((c) == (UINT8) 0x2E) /* Period (dot) */
/*
- * An ACPI_NAMESPACE_NODE can appear in some contexts
- * where a pointer to an ACPI_OPERAND_OBJECT can also
+ * An object of type ACPI_NAMESPACE_NODE can appear in some contexts
+ * where a pointer to an object of type ACPI_OPERAND_OBJECT can also
* appear. This macro is used to distinguish them.
*
- * The "Descriptor" field is the first field in both structures.
+ * The "DescriptorType" field is the second field in both structures.
*/
+#define ACPI_GET_DESCRIPTOR_PTR(d) (((ACPI_DESCRIPTOR *)(void *)(d))->Common.CommonPointer)
+#define ACPI_SET_DESCRIPTOR_PTR(d, p) (((ACPI_DESCRIPTOR *)(void *)(d))->Common.CommonPointer = (p))
#define ACPI_GET_DESCRIPTOR_TYPE(d) (((ACPI_DESCRIPTOR *)(void *)(d))->Common.DescriptorType)
-#define ACPI_SET_DESCRIPTOR_TYPE(d, t) (((ACPI_DESCRIPTOR *)(void *)(d))->Common.DescriptorType = t)
+#define ACPI_SET_DESCRIPTOR_TYPE(d, t) (((ACPI_DESCRIPTOR *)(void *)(d))->Common.DescriptorType = (t))
/*
* Macros for the master AML opcode table
@@ -328,10 +380,11 @@
* the plist contains a set of parens to allow variable-length lists.
* These macros are used for both the debug and non-debug versions of the code.
*/
-#define ACPI_ERROR_NAMESPACE(s, e) AcpiUtNamespaceError (AE_INFO, s, e);
-#define ACPI_ERROR_METHOD(s, n, p, e) AcpiUtMethodError (AE_INFO, s, n, p, e);
-#define ACPI_WARN_PREDEFINED(plist) AcpiUtPredefinedWarning plist
-#define ACPI_INFO_PREDEFINED(plist) AcpiUtPredefinedInfo plist
+#define ACPI_ERROR_NAMESPACE(s, e) AcpiUtNamespaceError (AE_INFO, s, e);
+#define ACPI_ERROR_METHOD(s, n, p, e) AcpiUtMethodError (AE_INFO, s, n, p, e);
+#define ACPI_WARN_PREDEFINED(plist) AcpiUtPredefinedWarning plist
+#define ACPI_INFO_PREDEFINED(plist) AcpiUtPredefinedInfo plist
+#define ACPI_BIOS_ERROR_PREDEFINED(plist) AcpiUtPredefinedBiosError plist
#else
@@ -341,179 +394,16 @@
#define ACPI_ERROR_METHOD(s, n, p, e)
#define ACPI_WARN_PREDEFINED(plist)
#define ACPI_INFO_PREDEFINED(plist)
+#define ACPI_BIOS_ERROR_PREDEFINED(plist)
#endif /* ACPI_NO_ERROR_MESSAGES */
-/*
- * Debug macros that are conditionally compiled
- */
-#ifdef ACPI_DEBUG_OUTPUT
-/*
- * Function entry tracing
- */
-#define ACPI_FUNCTION_TRACE(a) ACPI_FUNCTION_NAME(a) \
- AcpiUtTrace(ACPI_DEBUG_PARAMETERS)
-#define ACPI_FUNCTION_TRACE_PTR(a, b) ACPI_FUNCTION_NAME(a) \
- AcpiUtTracePtr(ACPI_DEBUG_PARAMETERS, (void *)b)
-#define ACPI_FUNCTION_TRACE_U32(a, b) ACPI_FUNCTION_NAME(a) \
- AcpiUtTraceU32(ACPI_DEBUG_PARAMETERS, (UINT32)b)
-#define ACPI_FUNCTION_TRACE_STR(a, b) ACPI_FUNCTION_NAME(a) \
- AcpiUtTraceStr(ACPI_DEBUG_PARAMETERS, (char *)b)
-
-#define ACPI_FUNCTION_ENTRY() AcpiUtTrackStackPtr()
-
-/*
- * Function exit tracing.
- * WARNING: These macros include a return statement. This is usually considered
- * bad form, but having a separate exit macro is very ugly and difficult to maintain.
- * One of the FUNCTION_TRACE macros above must be used in conjunction with these macros
- * so that "_AcpiFunctionName" is defined.
- *
- * Note: the DO_WHILE0 macro is used to prevent some compilers from complaining
- * about these constructs.
- */
-#ifdef ACPI_USE_DO_WHILE_0
-#define ACPI_DO_WHILE0(a) do a while(0)
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) addr
#else
-#define ACPI_DO_WHILE0(a) a
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL
#endif
-#define return_VOID ACPI_DO_WHILE0 ({ \
- AcpiUtExit (ACPI_DEBUG_PARAMETERS); \
- return;})
-/*
- * There are two versions of most of the return macros. The default version is
- * safer, since it avoids side-effects by guaranteeing that the argument will
- * not be evaluated twice.
- *
- * A less-safe version of the macros is provided for optional use if the
- * compiler uses excessive CPU stack (for example, this may happen in the
- * debug case if code optimzation is disabled.)
- */
-#ifndef ACPI_SIMPLE_RETURN_MACROS
-
-#define return_ACPI_STATUS(s) ACPI_DO_WHILE0 ({ \
- register ACPI_STATUS _s = (s); \
- AcpiUtStatusExit (ACPI_DEBUG_PARAMETERS, _s); \
- return (_s); })
-#define return_PTR(s) ACPI_DO_WHILE0 ({ \
- register void *_s = (void *) (s); \
- AcpiUtPtrExit (ACPI_DEBUG_PARAMETERS, (UINT8 *) _s); \
- return (_s); })
-#define return_VALUE(s) ACPI_DO_WHILE0 ({ \
- register UINT64 _s = (s); \
- AcpiUtValueExit (ACPI_DEBUG_PARAMETERS, _s); \
- return (_s); })
-#define return_UINT8(s) ACPI_DO_WHILE0 ({ \
- register UINT8 _s = (UINT8) (s); \
- AcpiUtValueExit (ACPI_DEBUG_PARAMETERS, (UINT64) _s); \
- return (_s); })
-#define return_UINT32(s) ACPI_DO_WHILE0 ({ \
- register UINT32 _s = (UINT32) (s); \
- AcpiUtValueExit (ACPI_DEBUG_PARAMETERS, (UINT64) _s); \
- return (_s); })
-#else /* Use original less-safe macros */
-
-#define return_ACPI_STATUS(s) ACPI_DO_WHILE0 ({ \
- AcpiUtStatusExit (ACPI_DEBUG_PARAMETERS, (s)); \
- return((s)); })
-#define return_PTR(s) ACPI_DO_WHILE0 ({ \
- AcpiUtPtrExit (ACPI_DEBUG_PARAMETERS, (UINT8 *) (s)); \
- return((s)); })
-#define return_VALUE(s) ACPI_DO_WHILE0 ({ \
- AcpiUtValueExit (ACPI_DEBUG_PARAMETERS, (UINT64) (s)); \
- return((s)); })
-#define return_UINT8(s) return_VALUE(s)
-#define return_UINT32(s) return_VALUE(s)
-
-#endif /* ACPI_SIMPLE_RETURN_MACROS */
-
-
-/* Conditional execution */
-
-#define ACPI_DEBUG_EXEC(a) a
-#define ACPI_DEBUG_ONLY_MEMBERS(a) a;
-#define _VERBOSE_STRUCTURES
-
-
-/* Various object display routines for debug */
-
-#define ACPI_DUMP_STACK_ENTRY(a) AcpiExDumpOperand((a), 0)
-#define ACPI_DUMP_OPERANDS(a, b ,c) AcpiExDumpOperands(a, b, c)
-#define ACPI_DUMP_ENTRY(a, b) AcpiNsDumpEntry (a, b)
-#define ACPI_DUMP_PATHNAME(a, b, c, d) AcpiNsDumpPathname(a, b, c, d)
-#define ACPI_DUMP_BUFFER(a, b) AcpiUtDumpBuffer((UINT8 *) a, b, DB_BYTE_DISPLAY, _COMPONENT)
-
-#else
-/*
- * This is the non-debug case -- make everything go away,
- * leaving no executable debug code!
- */
-#define ACPI_DEBUG_EXEC(a)
-#define ACPI_DEBUG_ONLY_MEMBERS(a)
-#define ACPI_FUNCTION_TRACE(a)
-#define ACPI_FUNCTION_TRACE_PTR(a, b)
-#define ACPI_FUNCTION_TRACE_U32(a, b)
-#define ACPI_FUNCTION_TRACE_STR(a, b)
-#define ACPI_FUNCTION_EXIT
-#define ACPI_FUNCTION_STATUS_EXIT(s)
-#define ACPI_FUNCTION_VALUE_EXIT(s)
-#define ACPI_FUNCTION_ENTRY()
-#define ACPI_DUMP_STACK_ENTRY(a)
-#define ACPI_DUMP_OPERANDS(a, b, c)
-#define ACPI_DUMP_ENTRY(a, b)
-#define ACPI_DUMP_TABLES(a, b)
-#define ACPI_DUMP_PATHNAME(a, b, c, d)
-#define ACPI_DUMP_BUFFER(a, b)
-#define ACPI_DEBUG_PRINT(pl)
-#define ACPI_DEBUG_PRINT_RAW(pl)
-
-#define return_VOID return
-#define return_ACPI_STATUS(s) return(s)
-#define return_VALUE(s) return(s)
-#define return_UINT8(s) return(s)
-#define return_UINT32(s) return(s)
-#define return_PTR(s) return(s)
-
-#endif /* ACPI_DEBUG_OUTPUT */
-
-/*
- * Some code only gets executed when the debugger is built in.
- * Note that this is entirely independent of whether the
- * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not.
- */
-#ifdef ACPI_DEBUGGER
-#define ACPI_DEBUGGER_EXEC(a) a
-#else
-#define ACPI_DEBUGGER_EXEC(a)
-#endif
-
-
-/*
- * Memory allocation tracking (DEBUG ONLY)
- */
-#define ACPI_MEM_PARAMETERS _COMPONENT, _AcpiModuleName, __LINE__
-
-#ifndef ACPI_DBG_TRACK_ALLOCATIONS
-
-/* Memory allocation */
-
-#define ACPI_ALLOCATE(a) AcpiUtAllocate((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
-#define ACPI_ALLOCATE_ZEROED(a) AcpiUtAllocateZeroed((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
-#define ACPI_FREE(a) AcpiOsFree(a)
-#define ACPI_MEM_TRACKING(a)
-
-#else
-
-/* Memory allocation */
-
-#define ACPI_ALLOCATE(a) AcpiUtAllocateAndTrack((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
-#define ACPI_ALLOCATE_ZEROED(a) AcpiUtAllocateZeroedAndTrack((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
-#define ACPI_FREE(a) AcpiUtFreeAndTrack(a, ACPI_MEM_PARAMETERS)
-#define ACPI_MEM_TRACKING(a) a
-
-#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
-
/*
* Macros used for ACPICA utilities only
diff --git a/usr/src/uts/intel/sys/acpi/acnames.h b/usr/src/uts/intel/sys/acpi/acnames.h
index e08302dd30..4710323e0c 100644
--- a/usr/src/uts/intel/sys/acpi/acnames.h
+++ b/usr/src/uts/intel/sys/acpi/acnames.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,28 +46,38 @@
/* Method names - these methods can appear anywhere in the namespace */
-#define METHOD_NAME__HID "_HID"
-#define METHOD_NAME__CID "_CID"
-#define METHOD_NAME__UID "_UID"
#define METHOD_NAME__ADR "_ADR"
-#define METHOD_NAME__INI "_INI"
-#define METHOD_NAME__STA "_STA"
-#define METHOD_NAME__REG "_REG"
-#define METHOD_NAME__SEG "_SEG"
+#define METHOD_NAME__AEI "_AEI"
#define METHOD_NAME__BBN "_BBN"
-#define METHOD_NAME__PRT "_PRT"
+#define METHOD_NAME__CBA "_CBA"
+#define METHOD_NAME__CID "_CID"
+#define METHOD_NAME__CLS "_CLS"
#define METHOD_NAME__CRS "_CRS"
+#define METHOD_NAME__DDN "_DDN"
+#define METHOD_NAME__HID "_HID"
+#define METHOD_NAME__INI "_INI"
+#define METHOD_NAME__PLD "_PLD"
+#define METHOD_NAME__DSD "_DSD"
#define METHOD_NAME__PRS "_PRS"
+#define METHOD_NAME__PRT "_PRT"
#define METHOD_NAME__PRW "_PRW"
+#define METHOD_NAME__PS0 "_PS0"
+#define METHOD_NAME__PS1 "_PS1"
+#define METHOD_NAME__PS2 "_PS2"
+#define METHOD_NAME__PS3 "_PS3"
+#define METHOD_NAME__REG "_REG"
+#define METHOD_NAME__SB_ "_SB_"
+#define METHOD_NAME__SEG "_SEG"
#define METHOD_NAME__SRS "_SRS"
+#define METHOD_NAME__STA "_STA"
+#define METHOD_NAME__SUB "_SUB"
+#define METHOD_NAME__UID "_UID"
/* Method names - these methods must appear at the namespace root */
-#define METHOD_NAME__BFS "\\_BFS"
-#define METHOD_NAME__GTS "\\_GTS"
-#define METHOD_NAME__PTS "\\_PTS"
-#define METHOD_NAME__SST "\\_SI._SST"
-#define METHOD_NAME__WAK "\\_WAK"
+#define METHOD_PATHNAME__PTS "\\_PTS"
+#define METHOD_PATHNAME__SST "\\_SI._SST"
+#define METHOD_PATHNAME__WAK "\\_WAK"
/* Definitions of the predefined namespace names */
@@ -78,8 +88,5 @@
#define ACPI_PREFIX_LOWER (UINT32) 0x69706361 /* "acpi" */
#define ACPI_NS_ROOT_PATH "\\"
-#define ACPI_NS_SYSTEM_BUS "_SB_"
#endif /* __ACNAMES_H__ */
-
-
diff --git a/usr/src/uts/intel/sys/acpi/acnamesp.h b/usr/src/uts/intel/sys/acpi/acnamesp.h
index 77f65d0ae4..11ee70d044 100644
--- a/usr/src/uts/intel/sys/acpi/acnamesp.h
+++ b/usr/src/uts/intel/sys/acpi/acnamesp.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,6 +67,7 @@
#define ACPI_NS_PREFIX_IS_SCOPE 0x10
#define ACPI_NS_EXTERNAL 0x20
#define ACPI_NS_TEMPORARY 0x40
+#define ACPI_NS_OVERRIDE_IF_FOUND 0x80
/* Flags for AcpiNsWalkNamespace */
@@ -77,6 +78,7 @@
/* Object is not a package element */
#define ACPI_NOT_PACKAGE_ELEMENT ACPI_UINT32_MAX
+#define ACPI_ALL_PACKAGE_ELEMENTS (ACPI_UINT32_MAX-1)
/* Always emit warning message, not dependent on node flags */
@@ -92,7 +94,7 @@ AcpiNsInitializeObjects (
ACPI_STATUS
AcpiNsInitializeDevices (
- void);
+ UINT32 Flags);
/*
@@ -117,8 +119,8 @@ AcpiNsWalkNamespace (
ACPI_HANDLE StartObject,
UINT32 MaxDepth,
UINT32 Flags,
- ACPI_WALK_CALLBACK PreOrderVisit,
- ACPI_WALK_CALLBACK PostOrderVisit,
+ ACPI_WALK_CALLBACK DescendingCallback,
+ ACPI_WALK_CALLBACK AscendingCallback,
void *Context,
void **ReturnValue);
@@ -204,6 +206,43 @@ AcpiNsCompareNames (
/*
+ * nsconvert - Dynamic object conversion routines
+ */
+ACPI_STATUS
+AcpiNsConvertToInteger (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsConvertToString (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsConvertToBuffer (
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsConvertToUnicode (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsConvertToResource (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsConvertToReference (
+ ACPI_NAMESPACE_NODE *Scope,
+ ACPI_OPERAND_OBJECT *OriginalObject,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+
+/*
* nsdump - Namespace dump/print utilities
*/
void
@@ -219,14 +258,14 @@ AcpiNsDumpEntry (
void
AcpiNsDumpPathname (
ACPI_HANDLE Handle,
- char *Msg,
+ const char *Msg,
UINT32 Level,
UINT32 Component);
void
AcpiNsPrintPathname (
UINT32 NumSegments,
- char *Pathname);
+ const char *Pathname);
ACPI_STATUS
AcpiNsDumpOneObject (
@@ -243,6 +282,14 @@ AcpiNsDumpObjects (
ACPI_OWNER_ID OwnerId,
ACPI_HANDLE StartHandle);
+void
+AcpiNsDumpObjectPaths (
+ ACPI_OBJECT_TYPE Type,
+ UINT8 DisplayType,
+ UINT32 MaxDepth,
+ ACPI_OWNER_ID OwnerId,
+ ACPI_HANDLE StartHandle);
+
/*
* nseval - Namespace evaluation functions
@@ -257,26 +304,53 @@ AcpiNsExecModuleCodeList (
/*
- * nspredef - Support for predefined/reserved names
+ * nsarguments - Argument count/type checking for predefined/reserved names
*/
-ACPI_STATUS
-AcpiNsCheckPredefinedNames (
- ACPI_NAMESPACE_NODE *Node,
- UINT32 UserParamCount,
- ACPI_STATUS ReturnStatus,
- ACPI_OPERAND_OBJECT **ReturnObject);
-
-const ACPI_PREDEFINED_INFO *
-AcpiNsCheckForPredefinedName (
- ACPI_NAMESPACE_NODE *Node);
-
void
-AcpiNsCheckParameterCount (
+AcpiNsCheckArgumentCount (
char *Pathname,
ACPI_NAMESPACE_NODE *Node,
UINT32 UserParamCount,
const ACPI_PREDEFINED_INFO *Info);
+void
+AcpiNsCheckAcpiCompliance (
+ char *Pathname,
+ ACPI_NAMESPACE_NODE *Node,
+ const ACPI_PREDEFINED_INFO *Predefined);
+
+void
+AcpiNsCheckArgumentTypes (
+ ACPI_EVALUATE_INFO *Info);
+
+
+/*
+ * nspredef - Return value checking for predefined/reserved names
+ */
+ACPI_STATUS
+AcpiNsCheckReturnValue (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_EVALUATE_INFO *Info,
+ UINT32 UserParamCount,
+ ACPI_STATUS ReturnStatus,
+ ACPI_OPERAND_OBJECT **ReturnObject);
+
+ACPI_STATUS
+AcpiNsCheckObjectType (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr,
+ UINT32 ExpectedBtypes,
+ UINT32 PackageIndex);
+
+
+/*
+ * nsprepkg - Validation of predefined name packages
+ */
+ACPI_STATUS
+AcpiNsCheckPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT **ReturnObjectPtr);
+
/*
* nsnames - Name and Scope manipulation
@@ -285,16 +359,22 @@ UINT32
AcpiNsOpensScope (
ACPI_OBJECT_TYPE Type);
-ACPI_STATUS
-AcpiNsBuildExternalPath (
- ACPI_NAMESPACE_NODE *Node,
- ACPI_SIZE Size,
- char *NameBuffer);
-
char *
AcpiNsGetExternalPathname (
ACPI_NAMESPACE_NODE *Node);
+UINT32
+AcpiNsBuildNormalizedPath (
+ ACPI_NAMESPACE_NODE *Node,
+ char *FullPath,
+ UINT32 PathSize,
+ BOOLEAN NoTrailing);
+
+char *
+AcpiNsGetNormalizedPathname (
+ ACPI_NAMESPACE_NODE *Node,
+ BOOLEAN NoTrailing);
+
char *
AcpiNsNameOfCurrentScope (
ACPI_WALK_STATE *WalkState);
@@ -302,7 +382,8 @@ AcpiNsNameOfCurrentScope (
ACPI_STATUS
AcpiNsHandleToPathname (
ACPI_HANDLE TargetHandle,
- ACPI_BUFFER *Buffer);
+ ACPI_BUFFER *Buffer,
+ BOOLEAN NoTrailing);
BOOLEAN
AcpiNsPatternMatch (
@@ -361,27 +442,28 @@ AcpiNsGetAttachedData (
* predefined methods/objects
*/
ACPI_STATUS
-AcpiNsRepairObject (
- ACPI_PREDEFINED_DATA *Data,
+AcpiNsSimpleRepair (
+ ACPI_EVALUATE_INFO *Info,
UINT32 ExpectedBtypes,
UINT32 PackageIndex,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
ACPI_STATUS
-AcpiNsRepairPackageList (
- ACPI_PREDEFINED_DATA *Data,
+AcpiNsWrapWithPackage (
+ ACPI_EVALUATE_INFO *Info,
+ ACPI_OPERAND_OBJECT *OriginalObject,
ACPI_OPERAND_OBJECT **ObjDescPtr);
ACPI_STATUS
AcpiNsRepairNullElement (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
UINT32 ExpectedBtypes,
UINT32 PackageIndex,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
void
AcpiNsRemoveNullElements (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
UINT8 PackageType,
ACPI_OPERAND_OBJECT *ObjDesc);
@@ -392,7 +474,7 @@ AcpiNsRemoveNullElements (
*/
ACPI_STATUS
AcpiNsComplexRepairs (
- ACPI_PREDEFINED_DATA *Data,
+ ACPI_EVALUATE_INFO *Info,
ACPI_NAMESPACE_NODE *Node,
ACPI_STATUS ValidateStatus,
ACPI_OPERAND_OBJECT **ReturnObjectPtr);
@@ -429,10 +511,6 @@ AcpiNsInstallNode (
/*
* nsutils - Utility functions
*/
-BOOLEAN
-AcpiNsValidRootPrefix (
- char Prefix);
-
ACPI_OBJECT_TYPE
AcpiNsGetType (
ACPI_NAMESPACE_NODE *Node);
diff --git a/usr/src/uts/intel/sys/acpi/acobject.h b/usr/src/uts/intel/sys/acpi/acobject.h
index 27a21e6858..83f29ec3c9 100644
--- a/usr/src/uts/intel/sys/acpi/acobject.h
+++ b/usr/src/uts/intel/sys/acpi/acobject.h
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Name: acobject.h - Definition of ACPI_OPERAND_OBJECT (Internal object only)
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -94,10 +93,11 @@
#define AOPOBJ_AML_CONSTANT 0x01 /* Integer is an AML constant */
#define AOPOBJ_STATIC_POINTER 0x02 /* Data is part of an ACPI table, don't delete */
-#define AOPOBJ_DATA_VALID 0x04 /* Object is intialized and data is valid */
-#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized, _REG was run */
-#define AOPOBJ_SETUP_COMPLETE 0x10 /* Region setup is complete */
-#define AOPOBJ_INVALID 0x20 /* Host OS won't allow a Region address */
+#define AOPOBJ_DATA_VALID 0x04 /* Object is initialized and data is valid */
+#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized */
+#define AOPOBJ_REG_CONNECTED 0x10 /* _REG was run */
+#define AOPOBJ_SETUP_COMPLETE 0x20 /* Region setup is complete */
+#define AOPOBJ_INVALID 0x40 /* Host OS won't allow a Region address */
/******************************************************************************
@@ -123,8 +123,8 @@ typedef struct acpi_object_integer
/*
- * Note: The String and Buffer object must be identical through the Pointer
- * and length elements. There is code that depends on this.
+ * Note: The String and Buffer object must be identical through the
+ * pointer and length elements. There is code that depends on this.
*
* Fields common to both Strings and Buffers
*/
@@ -214,6 +214,7 @@ typedef struct acpi_object_method
UINT8 ParamCount;
UINT8 SyncLevel;
union acpi_operand_object *Mutex;
+ union acpi_operand_object *Node;
UINT8 *AmlStart;
union
{
@@ -233,12 +234,13 @@ typedef struct acpi_object_method
#define ACPI_METHOD_INTERNAL_ONLY 0x02 /* Method is implemented internally (_OSI) */
#define ACPI_METHOD_SERIALIZED 0x04 /* Method is serialized */
#define ACPI_METHOD_SERIALIZED_PENDING 0x08 /* Method is to be marked serialized */
-#define ACPI_METHOD_MODIFIED_NAMESPACE 0x10 /* Method modified the namespace */
+#define ACPI_METHOD_IGNORE_SYNC_LEVEL 0x10 /* Method was auto-serialized at table load time */
+#define ACPI_METHOD_MODIFIED_NAMESPACE 0x20 /* Method modified the namespace */
/******************************************************************************
*
- * Objects that can be notified. All share a common NotifyInfo area.
+ * Objects that can be notified. All share a common NotifyInfo area.
*
*****************************************************************************/
@@ -246,8 +248,7 @@ typedef struct acpi_object_method
* Common fields for objects that support ASL notifications
*/
#define ACPI_COMMON_NOTIFY_INFO \
- union acpi_operand_object *SystemNotify; /* Handler for system notifies */\
- union acpi_operand_object *DeviceNotify; /* Handler for driver notifies */\
+ union acpi_operand_object *NotifyList[2]; /* Handlers for system/device notifies */\
union acpi_operand_object *Handler; /* Handler for Address space */
@@ -302,7 +303,7 @@ typedef struct acpi_object_thermal_zone
/******************************************************************************
*
- * Fields. All share a common header/info field.
+ * Fields. All share a common header/info field.
*
*****************************************************************************/
@@ -320,6 +321,7 @@ typedef struct acpi_object_thermal_zone
UINT32 BaseByteOffset; /* Byte offset within containing object */\
UINT32 Value; /* Value to store into the Bank or Index register */\
UINT8 StartFieldBitOffset;/* Bit offset within first field datum (0-63) */\
+ UINT8 AccessLength; /* For serial regions/fields */
typedef struct acpi_object_field_common /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */
@@ -335,7 +337,10 @@ typedef struct acpi_object_region_field
{
ACPI_OBJECT_COMMON_HEADER
ACPI_COMMON_FIELD_INFO
+ UINT16 ResourceLength;
union acpi_operand_object *RegionObj; /* Containing OpRegion object */
+ UINT8 *ResourceBuffer; /* ResourceTemplate for serial regions/fields */
+ UINT16 PinNumberIndex; /* Index relative to previous Connection/Template */
} ACPI_OBJECT_REGION_FIELD;
@@ -386,8 +391,10 @@ typedef struct acpi_object_notify_handler
{
ACPI_OBJECT_COMMON_HEADER
ACPI_NAMESPACE_NODE *Node; /* Parent device */
- ACPI_NOTIFY_HANDLER Handler;
+ UINT32 HandlerType; /* Type: Device/System/Both */
+ ACPI_NOTIFY_HANDLER Handler; /* Handler address */
void *Context;
+ union acpi_operand_object *Next[2]; /* Device and System handler lists */
} ACPI_OBJECT_NOTIFY_HANDLER;
@@ -401,7 +408,7 @@ typedef struct acpi_object_addr_handler
ACPI_NAMESPACE_NODE *Node; /* Parent device */
void *Context;
ACPI_ADR_SPACE_SETUP Setup;
- union acpi_operand_object *RegionList; /* regions using this handler */
+ union acpi_operand_object *RegionList; /* Regions using this handler */
union acpi_operand_object *Next;
} ACPI_OBJECT_ADDR_HANDLER;
@@ -425,13 +432,14 @@ typedef struct acpi_object_addr_handler
typedef struct acpi_object_reference
{
ACPI_OBJECT_COMMON_HEADER
- UINT8 Class; /* Reference Class */
- UINT8 TargetType; /* Used for Index Op */
- UINT8 Reserved;
- void *Object; /* NameOp=>HANDLE to obj, IndexOp=>ACPI_OPERAND_OBJECT */
- ACPI_NAMESPACE_NODE *Node; /* RefOf or Namepath */
- union acpi_operand_object **Where; /* Target of Index */
- UINT32 Value; /* Used for Local/Arg/Index/DdbHandle */
+ UINT8 Class; /* Reference Class */
+ UINT8 TargetType; /* Used for Index Op */
+ UINT8 Reserved;
+ void *Object; /* NameOp=>HANDLE to obj, IndexOp=>ACPI_OPERAND_OBJECT */
+ ACPI_NAMESPACE_NODE *Node; /* RefOf or Namepath */
+ union acpi_operand_object **Where; /* Target of Index */
+ UINT8 *IndexPointer; /* Used for Buffers and Strings */
+ UINT32 Value; /* Used for Local/Arg/Index/DdbHandle */
} ACPI_OBJECT_REFERENCE;
@@ -463,6 +471,7 @@ typedef struct acpi_object_extra
{
ACPI_OBJECT_COMMON_HEADER
ACPI_NAMESPACE_NODE *Method_REG; /* _REG method for this region (if any) */
+ ACPI_NAMESPACE_NODE *ScopeNode;
void *RegionContext; /* Region-specific data */
UINT8 *AmlStart;
UINT32 AmlLength;
diff --git a/usr/src/uts/intel/sys/acpi/acopcode.h b/usr/src/uts/intel/sys/acpi/acopcode.h
index d8e1c75a7b..a278d4a154 100644
--- a/usr/src/uts/intel/sys/acpi/acopcode.h
+++ b/usr/src/uts/intel/sys/acpi/acopcode.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,7 +54,7 @@
#define _UNK 0x6B
/*
- * Reserved ASCII characters. Do not use any of these for
+ * Reserved ASCII characters. Do not use any of these for
* internal opcodes, since they are used to differentiate
* name strings from AML opcodes
*/
@@ -64,7 +64,7 @@
/*
- * All AML opcodes and the parse-time arguments for each. Used by the AML
+ * All AML opcodes and the parse-time arguments for each. Used by the AML
* parser Each list is compressed into a 32-bit number and stored in the
* master opcode table (in psopcode.c).
*/
@@ -93,7 +93,8 @@
#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
-#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME)
+#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_NAME_OR_REF,ARGP_TARGET)
+#define ARGP_CONNECTFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_CONTINUE_OP ARG_NONE
#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME)
#define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME)
@@ -111,6 +112,7 @@
#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA)
#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST)
#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME)
+#define ARGP_EXTERNAL_OP ARGP_LIST3 (ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_BYTEDATA)
#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG)
#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST)
#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
@@ -151,13 +153,14 @@
#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_NOOP_OP ARG_NONE
#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
+#define ARGP_OBJECT_TYPE_OP ARGP_LIST1 (ARGP_NAME_OR_REF)
#define ARGP_ONE_OP ARG_NONE
#define ARGP_ONES_OP ARG_NONE
#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST)
#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST)
#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST)
#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA)
-#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_NAME_OR_REF)
#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG)
#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
@@ -165,6 +168,7 @@
#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG)
#define ARGP_REVISION_OP ARG_NONE
#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST)
+#define ARGP_SERIALFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME)
@@ -183,7 +187,6 @@
#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
-#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST)
#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
@@ -193,7 +196,7 @@
/*
- * All AML opcodes and the runtime arguments for each. Used by the AML
+ * All AML opcodes and the runtime arguments for each. Used by the AML
* interpreter Each list is compressed into a 32-bit number and stored
* in the master opcode table (in psopcode.c).
*
@@ -210,7 +213,7 @@
#define ARGI_ARG4 ARG_NONE
#define ARGI_ARG5 ARG_NONE
#define ARGI_ARG6 ARG_NONE
-#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE
+#define ARGI_BANK_FIELD_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
@@ -222,9 +225,10 @@
#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_BYTE_OP ARGI_INVALID_OPCODE
#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE
-#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF)
+#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_ANYTYPE, ARGI_ANYTYPE, ARGI_TARGETREF)
#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF)
#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF)
+#define ARGI_CONNECTFIELD_OP ARGI_INVALID_OPCODE
#define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE
#define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET)
#define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE)
@@ -242,6 +246,7 @@
#define ARGI_DWORD_OP ARGI_INVALID_OPCODE
#define ARGI_ELSE_OP ARGI_INVALID_OPCODE
#define ARGI_EVENT_OP ARGI_INVALID_OPCODE
+#define ARGI_EXTERNAL_OP ARGI_LIST3 (ARGI_STRING, ARGI_INTEGER, ARGI_INTEGER)
#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER)
#define ARGI_FIELD_OP ARGI_INVALID_OPCODE
#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
@@ -282,6 +287,7 @@
#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE
#define ARGI_NOOP_OP ARG_NONE
#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER)
+#define ARGI_OBJECT_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE)
#define ARGI_ONE_OP ARG_NONE
#define ARGI_ONES_OP ARG_NONE
#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
@@ -296,6 +302,7 @@
#define ARGI_RETURN_OP ARGI_INVALID_OPCODE
#define ARGI_REVISION_OP ARG_NONE
#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE
+#define ARGI_SERIALFIELD_OP ARGI_INVALID_OPCODE
#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT)
@@ -303,7 +310,7 @@
#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE
-#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF)
+#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_STORE_TARGET)
#define ARGI_STRING_OP ARGI_INVALID_OPCODE
#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE
@@ -314,7 +321,6 @@
#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET)
-#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE)
#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE)
#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER)
diff --git a/usr/src/uts/intel/sys/acpi/acoutput.h b/usr/src/uts/intel/sys/acpi/acoutput.h
index c8007e21fb..4fb718208c 100644
--- a/usr/src/uts/intel/sys/acpi/acoutput.h
+++ b/usr/src/uts/intel/sys/acpi/acoutput.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -72,6 +72,7 @@
#define ACPI_EXAMPLE 0x00004000
#define ACPI_DRIVER 0x00008000
#define DT_COMPILER 0x00010000
+#define ASL_PREPROCESSOR 0x00020000
#define ACPI_ALL_COMPONENTS 0x0001FFFF
#define ACPI_COMPONENT_DEFAULT (ACPI_ALL_COMPONENTS)
@@ -88,7 +89,8 @@
#define ACPI_LV_DEBUG_OBJECT 0x00000002
#define ACPI_LV_INFO 0x00000004
#define ACPI_LV_REPAIR 0x00000008
-#define ACPI_LV_ALL_EXCEPTIONS 0x0000000F
+#define ACPI_LV_TRACE_POINT 0x00000010
+#define ACPI_LV_ALL_EXCEPTIONS 0x0000001F
/* Trace verbosity level 1 [Standard Trace Level] */
@@ -148,6 +150,7 @@
#define ACPI_DB_DEBUG_OBJECT ACPI_DEBUG_LEVEL (ACPI_LV_DEBUG_OBJECT)
#define ACPI_DB_INFO ACPI_DEBUG_LEVEL (ACPI_LV_INFO)
#define ACPI_DB_REPAIR ACPI_DEBUG_LEVEL (ACPI_LV_REPAIR)
+#define ACPI_DB_TRACE_POINT ACPI_DEBUG_LEVEL (ACPI_LV_TRACE_POINT)
#define ACPI_DB_ALL_EXCEPTIONS ACPI_DEBUG_LEVEL (ACPI_LV_ALL_EXCEPTIONS)
/* Trace level -- also used in the global "DebugLevel" */
@@ -184,6 +187,21 @@
#define ACPI_DEBUG_ALL (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
+/*
+ * Global trace flags
+ */
+#define ACPI_TRACE_ENABLED ((UINT32) 4)
+#define ACPI_TRACE_ONESHOT ((UINT32) 2)
+#define ACPI_TRACE_OPCODE ((UINT32) 1)
+
+/* Defaults for trace debugging level/layer */
+
+#define ACPI_TRACE_LEVEL_ALL ACPI_LV_ALL
+#define ACPI_TRACE_LAYER_ALL 0x000001FF
+#define ACPI_TRACE_LEVEL_DEFAULT ACPI_LV_TRACE_POINT
+#define ACPI_TRACE_LAYER_DEFAULT ACPI_EXECUTER
+
+
#if defined (ACPI_DEBUG_OUTPUT) || !defined (ACPI_NO_ERROR_MESSAGES)
/*
* The module name is used primarily for error and debug messages.
@@ -216,6 +234,8 @@
#define ACPI_WARNING(plist) AcpiWarning plist
#define ACPI_EXCEPTION(plist) AcpiException plist
#define ACPI_ERROR(plist) AcpiError plist
+#define ACPI_BIOS_WARNING(plist) AcpiBiosWarning plist
+#define ACPI_BIOS_ERROR(plist) AcpiBiosError plist
#define ACPI_DEBUG_OBJECT(obj,l,i) AcpiExDoDebugObject(obj,l,i)
#else
@@ -226,6 +246,8 @@
#define ACPI_WARNING(plist)
#define ACPI_EXCEPTION(plist)
#define ACPI_ERROR(plist)
+#define ACPI_BIOS_WARNING(plist)
+#define ACPI_BIOS_ERROR(plist)
#define ACPI_DEBUG_OBJECT(obj,l,i)
#endif /* ACPI_NO_ERROR_MESSAGES */
@@ -245,7 +267,7 @@
#define ACPI_GET_FUNCTION_NAME _AcpiFunctionName
/*
- * The Name parameter should be the procedure name as a quoted string.
+ * The Name parameter should be the procedure name as a non-quoted string.
* The function name is also used by the function exit macros below.
* Note: (const char) is used to be compatible with the debug interfaces
* and macros such as __FUNCTION__.
@@ -262,25 +284,215 @@
* Common parameters used for debug output functions:
* line number, function name, module(file) name, component ID
*/
-#define ACPI_DEBUG_PARAMETERS __LINE__, ACPI_GET_FUNCTION_NAME, _AcpiModuleName, _COMPONENT
+#define ACPI_DEBUG_PARAMETERS \
+ __LINE__, ACPI_GET_FUNCTION_NAME, _AcpiModuleName, _COMPONENT
+
+/* Check if debug output is currently dynamically enabled */
+
+#define ACPI_IS_DEBUG_ENABLED(Level, Component) \
+ ((Level & AcpiDbgLevel) && (Component & AcpiDbgLayer))
/*
* Master debug print macros
* Print message if and only if:
* 1) Debug print for the current component is enabled
* 2) Debug error level or trace level for the print statement is enabled
+ *
+ * November 2012: Moved the runtime check for whether to actually emit the
+ * debug message outside of the print function itself. This improves overall
+ * performance at a relatively small code cost. Implementation involves the
+ * use of variadic macros supported by C99.
+ *
+ * Note: the ACPI_DO_WHILE0 macro is used to prevent some compilers from
+ * complaining about these constructs. On other compilers the do...while
+ * adds some extra code, so this feature is optional.
*/
+#ifdef ACPI_USE_DO_WHILE_0
+#define ACPI_DO_WHILE0(a) do a while(0)
+#else
+#define ACPI_DO_WHILE0(a) a
+#endif
+
+/* DEBUG_PRINT functions */
+
+#ifndef COMPILER_VA_MACRO
+
#define ACPI_DEBUG_PRINT(plist) AcpiDebugPrint plist
#define ACPI_DEBUG_PRINT_RAW(plist) AcpiDebugPrintRaw plist
#else
+
+/* Helper macros for DEBUG_PRINT */
+
+#define ACPI_DO_DEBUG_PRINT(Function, Level, Line, Filename, Modulename, Component, ...) \
+ ACPI_DO_WHILE0 ({ \
+ if (ACPI_IS_DEBUG_ENABLED (Level, Component)) \
+ { \
+ Function (Level, Line, Filename, Modulename, Component, __VA_ARGS__); \
+ } \
+ })
+
+#define ACPI_ACTUAL_DEBUG(Level, Line, Filename, Modulename, Component, ...) \
+ ACPI_DO_DEBUG_PRINT (AcpiDebugPrint, Level, Line, \
+ Filename, Modulename, Component, __VA_ARGS__)
+
+#define ACPI_ACTUAL_DEBUG_RAW(Level, Line, Filename, Modulename, Component, ...) \
+ ACPI_DO_DEBUG_PRINT (AcpiDebugPrintRaw, Level, Line, \
+ Filename, Modulename, Component, __VA_ARGS__)
+
+#define ACPI_DEBUG_PRINT(plist) ACPI_ACTUAL_DEBUG plist
+#define ACPI_DEBUG_PRINT_RAW(plist) ACPI_ACTUAL_DEBUG_RAW plist
+
+#endif
+
+
+/*
+ * Function entry tracing
+ *
+ * The name of the function is emitted as a local variable that is
+ * intended to be used by both the entry trace and the exit trace.
+ */
+
+/* Helper macro */
+
+#define ACPI_TRACE_ENTRY(Name, Function, Type, Param) \
+ ACPI_FUNCTION_NAME (Name) \
+ Function (ACPI_DEBUG_PARAMETERS, (Type) (Param))
+
+/* The actual entry trace macros */
+
+#define ACPI_FUNCTION_TRACE(Name) \
+ ACPI_FUNCTION_NAME(Name) \
+ AcpiUtTrace (ACPI_DEBUG_PARAMETERS)
+
+#define ACPI_FUNCTION_TRACE_PTR(Name, Pointer) \
+ ACPI_TRACE_ENTRY (Name, AcpiUtTracePtr, void *, Pointer)
+
+#define ACPI_FUNCTION_TRACE_U32(Name, Value) \
+ ACPI_TRACE_ENTRY (Name, AcpiUtTraceU32, UINT32, Value)
+
+#define ACPI_FUNCTION_TRACE_STR(Name, String) \
+ ACPI_TRACE_ENTRY (Name, AcpiUtTraceStr, const char *, String)
+
+#define ACPI_FUNCTION_ENTRY() \
+ AcpiUtTrackStackPtr()
+
+
+/*
+ * Function exit tracing
+ *
+ * These macros include a return statement. This is usually considered
+ * bad form, but having a separate exit macro before the actual return
+ * is very ugly and difficult to maintain.
+ *
+ * One of the FUNCTION_TRACE macros above must be used in conjunction
+ * with these macros so that "_AcpiFunctionName" is defined.
+ *
+ * There are two versions of most of the return macros. The default version is
+ * safer, since it avoids side-effects by guaranteeing that the argument will
+ * not be evaluated twice.
+ *
+ * A less-safe version of the macros is provided for optional use if the
+ * compiler uses excessive CPU stack (for example, this may happen in the
+ * debug case if code optimzation is disabled.)
+ */
+
+/* Exit trace helper macro */
+
+#ifndef ACPI_SIMPLE_RETURN_MACROS
+
+#define ACPI_TRACE_EXIT(Function, Type, Param) \
+ ACPI_DO_WHILE0 ({ \
+ register Type _Param = (Type) (Param); \
+ Function (ACPI_DEBUG_PARAMETERS, _Param); \
+ return (_Param); \
+ })
+
+#else /* Use original less-safe macros */
+
+#define ACPI_TRACE_EXIT(Function, Type, Param) \
+ ACPI_DO_WHILE0 ({ \
+ Function (ACPI_DEBUG_PARAMETERS, (Type) (Param)); \
+ return (Param); \
+ })
+
+#endif /* ACPI_SIMPLE_RETURN_MACROS */
+
+/* The actual exit macros */
+
+#define return_VOID \
+ ACPI_DO_WHILE0 ({ \
+ AcpiUtExit (ACPI_DEBUG_PARAMETERS); \
+ return; \
+ })
+
+#define return_ACPI_STATUS(Status) \
+ ACPI_TRACE_EXIT (AcpiUtStatusExit, ACPI_STATUS, Status)
+
+#define return_PTR(Pointer) \
+ ACPI_TRACE_EXIT (AcpiUtPtrExit, void *, Pointer)
+
+#define return_STR(String) \
+ ACPI_TRACE_EXIT (AcpiUtStrExit, const char *, String)
+
+#define return_VALUE(Value) \
+ ACPI_TRACE_EXIT (AcpiUtValueExit, UINT64, Value)
+
+#define return_UINT32(Value) \
+ ACPI_TRACE_EXIT (AcpiUtValueExit, UINT32, Value)
+
+#define return_UINT8(Value) \
+ ACPI_TRACE_EXIT (AcpiUtValueExit, UINT8, Value)
+
+/* Conditional execution */
+
+#define ACPI_DEBUG_EXEC(a) a
+#define ACPI_DEBUG_ONLY_MEMBERS(a) a;
+#define _VERBOSE_STRUCTURES
+
+
+/* Various object display routines for debug */
+
+#define ACPI_DUMP_STACK_ENTRY(a) AcpiExDumpOperand((a), 0)
+#define ACPI_DUMP_OPERANDS(a, b ,c) AcpiExDumpOperands(a, b, c)
+#define ACPI_DUMP_ENTRY(a, b) AcpiNsDumpEntry (a, b)
+#define ACPI_DUMP_PATHNAME(a, b, c, d) AcpiNsDumpPathname(a, b, c, d)
+#define ACPI_DUMP_BUFFER(a, b) AcpiUtDebugDumpBuffer((UINT8 *) a, b, DB_BYTE_DISPLAY, _COMPONENT)
+
+#define ACPI_TRACE_POINT(a, b, c, d) AcpiTracePoint (a, b, c, d)
+
+#else /* ACPI_DEBUG_OUTPUT */
/*
* This is the non-debug case -- make everything go away,
* leaving no executable debug code!
*/
-#define ACPI_FUNCTION_NAME(a)
#define ACPI_DEBUG_PRINT(pl)
#define ACPI_DEBUG_PRINT_RAW(pl)
+#define ACPI_DEBUG_EXEC(a)
+#define ACPI_DEBUG_ONLY_MEMBERS(a)
+#define ACPI_FUNCTION_NAME(a)
+#define ACPI_FUNCTION_TRACE(a)
+#define ACPI_FUNCTION_TRACE_PTR(a, b)
+#define ACPI_FUNCTION_TRACE_U32(a, b)
+#define ACPI_FUNCTION_TRACE_STR(a, b)
+#define ACPI_FUNCTION_ENTRY()
+#define ACPI_DUMP_STACK_ENTRY(a)
+#define ACPI_DUMP_OPERANDS(a, b, c)
+#define ACPI_DUMP_ENTRY(a, b)
+#define ACPI_DUMP_PATHNAME(a, b, c, d)
+#define ACPI_DUMP_BUFFER(a, b)
+#define ACPI_IS_DEBUG_ENABLED(Level, Component) 0
+#define ACPI_TRACE_POINT(a, b, c, d)
+
+/* Return macros must have a return statement at the minimum */
+
+#define return_VOID return
+#define return_ACPI_STATUS(s) return(s)
+#define return_PTR(s) return(s)
+#define return_STR(s) return(s)
+#define return_VALUE(s) return(s)
+#define return_UINT8(s) return(s)
+#define return_UINT32(s) return(s)
#endif /* ACPI_DEBUG_OUTPUT */
diff --git a/usr/src/uts/intel/sys/acpi/acparser.h b/usr/src/uts/intel/sys/acpi/acparser.h
index 83b3c4fc06..dd2782199d 100644
--- a/usr/src/uts/intel/sys/acpi/acparser.h
+++ b/usr/src/uts/intel/sys/acpi/acparser.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,6 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#ifndef __ACPARSER_H__
#define __ACPARSER_H__
@@ -73,6 +72,9 @@
*
*****************************************************************************/
+extern const UINT8 AcpiGbl_ShortOpIndex[];
+extern const UINT8 AcpiGbl_LongOpIndex[];
+
/*
* psxface - Parser external interfaces
@@ -104,7 +106,12 @@ AcpiPsGetNextNamepath (
ACPI_WALK_STATE *WalkState,
ACPI_PARSE_STATE *ParserState,
ACPI_PARSE_OBJECT *Arg,
- BOOLEAN MethodCall);
+ BOOLEAN PossibleMethodCall);
+
+/* Values for BOOLEAN above */
+
+#define ACPI_NOT_METHOD_CALL FALSE
+#define ACPI_POSSIBLE_METHOD_CALL TRUE
ACPI_STATUS
AcpiPsGetNextArg (
@@ -129,13 +136,42 @@ AcpiPsGetParent (
/*
- * psopcode - AML Opcode information
+ * psobject - support for parse object processing
+ */
+ACPI_STATUS
+AcpiPsBuildNamedOp (
+ ACPI_WALK_STATE *WalkState,
+ UINT8 *AmlOpStart,
+ ACPI_PARSE_OBJECT *UnnamedOp,
+ ACPI_PARSE_OBJECT **Op);
+
+ACPI_STATUS
+AcpiPsCreateOp (
+ ACPI_WALK_STATE *WalkState,
+ UINT8 *AmlOpStart,
+ ACPI_PARSE_OBJECT **NewOp);
+
+ACPI_STATUS
+AcpiPsCompleteOp (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT **Op,
+ ACPI_STATUS Status);
+
+ACPI_STATUS
+AcpiPsCompleteFinalOp (
+ ACPI_WALK_STATE *WalkState,
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_STATUS Status);
+
+
+/*
+ * psopinfo - AML Opcode information
*/
const ACPI_OPCODE_INFO *
AcpiPsGetOpcodeInfo (
UINT16 Opcode);
-char *
+const char *
AcpiPsGetOpcodeName (
UINT16 Opcode);
@@ -275,7 +311,7 @@ AcpiPsDeleteParseTree (
*/
ACPI_PARSE_OBJECT *
AcpiPsCreateScopeOp (
- void);
+ UINT8 *Aml);
void
AcpiPsInitOp (
@@ -284,7 +320,8 @@ AcpiPsInitOp (
ACPI_PARSE_OBJECT *
AcpiPsAllocOp (
- UINT16 opcode);
+ UINT16 Opcode,
+ UINT8 *Aml);
void
AcpiPsFreeOp (
@@ -294,10 +331,6 @@ BOOLEAN
AcpiPsIsLeadingChar (
UINT32 c);
-BOOLEAN
-AcpiPsIsPrefixChar (
- UINT32 c);
-
UINT32
AcpiPsGetName(
ACPI_PARSE_OBJECT *op);
diff --git a/usr/src/uts/intel/sys/acpi/acpi.h b/usr/src/uts/intel/sys/acpi/acpi.h
index 0f4f607120..105cb52568 100644
--- a/usr/src/uts/intel/sys/acpi/acpi.h
+++ b/usr/src/uts/intel/sys/acpi/acpi.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -62,5 +62,6 @@
#include "acrestyp.h" /* Resource Descriptor structs */
#include "acpiosxf.h" /* OSL interfaces (ACPICA-to-OS) */
#include "acpixf.h" /* ACPI core subsystem external interfaces */
+#include "platform/acenvex.h" /* Extra environment-specific items */
#endif /* __ACPI_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acpi_pci.h b/usr/src/uts/intel/sys/acpi/acpi_pci.h
index d994eb8e2d..b4fd3393ab 100644
--- a/usr/src/uts/intel/sys/acpi/acpi_pci.h
+++ b/usr/src/uts/intel/sys/acpi/acpi_pci.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _ACPI_PCI_H
@@ -32,13 +33,10 @@ extern "C" {
/*
* Memory mapped configuration space address description table documented
- * in ACPI 3.0. These definitions are currently not included in the
- * actbl.h from Intel. This file will need to be removed once Intel has
- * updated their include file with these information.
+ * in ACPI 3.0. These definitions are currently not the same as in the
+ * actbl2.h from Intel. This file might be removed if the code can be ported
+ * to use the definition provided by Intel.
*/
-#define MCFG_SIG "MCFG" /* Memory mapped config space address */
- /* description table's signature */
-
#pragma pack(1)
typedef struct cfg_base_addr_alloc {
diff --git a/usr/src/uts/intel/sys/acpi/acpiosxf.h b/usr/src/uts/intel/sys/acpi/acpiosxf.h
index 85b0ff6108..eb61d9a8ce 100644
--- a/usr/src/uts/intel/sys/acpi/acpiosxf.h
+++ b/usr/src/uts/intel/sys/acpi/acpiosxf.h
@@ -1,15 +1,13 @@
-
/******************************************************************************
*
- * Name: acpiosxf.h - All interfaces to the OS Services Layer (OSL). These
+ * Name: acpiosxf.h - All interfaces to the OS Services Layer (OSL). These
* interfaces must be implemented by OSL to interface the
* ACPI components to the host operating system.
*
*****************************************************************************/
-
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -59,7 +57,8 @@ typedef enum
OSL_GLOBAL_LOCK_HANDLER,
OSL_NOTIFY_HANDLER,
OSL_GPE_HANDLER,
- OSL_DEBUGGER_THREAD,
+ OSL_DEBUGGER_MAIN_THREAD,
+ OSL_DEBUGGER_EXEC_THREAD,
OSL_EC_POLL_HANDLER,
OSL_EC_BURST_HANDLER
@@ -86,77 +85,111 @@ typedef struct acpi_signal_fatal_info
/*
* OSL Initialization and shutdown primitives
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsInitialize
ACPI_STATUS
AcpiOsInitialize (
void);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsTerminate
ACPI_STATUS
AcpiOsTerminate (
void);
+#endif
/*
* ACPI Table interfaces
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetRootPointer
ACPI_PHYSICAL_ADDRESS
AcpiOsGetRootPointer (
void);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsPredefinedOverride
ACPI_STATUS
AcpiOsPredefinedOverride (
const ACPI_PREDEFINED_NAMES *InitVal,
ACPI_STRING *NewVal);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsTableOverride
ACPI_STATUS
AcpiOsTableOverride (
ACPI_TABLE_HEADER *ExistingTable,
ACPI_TABLE_HEADER **NewTable);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsPhysicalTableOverride
+ACPI_STATUS
+AcpiOsPhysicalTableOverride (
+ ACPI_TABLE_HEADER *ExistingTable,
+ ACPI_PHYSICAL_ADDRESS *NewAddress,
+ UINT32 *NewTableLength);
+#endif
/*
* Spinlock primitives
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCreateLock
ACPI_STATUS
AcpiOsCreateLock (
ACPI_SPINLOCK *OutHandle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsDeleteLock
void
AcpiOsDeleteLock (
ACPI_SPINLOCK Handle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAcquireLock
ACPI_CPU_FLAGS
AcpiOsAcquireLock (
ACPI_SPINLOCK Handle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReleaseLock
void
AcpiOsReleaseLock (
ACPI_SPINLOCK Handle,
ACPI_CPU_FLAGS Flags);
+#endif
/*
* Semaphore primitives
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCreateSemaphore
ACPI_STATUS
AcpiOsCreateSemaphore (
UINT32 MaxUnits,
UINT32 InitialUnits,
ACPI_SEMAPHORE *OutHandle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsDeleteSemaphore
ACPI_STATUS
AcpiOsDeleteSemaphore (
ACPI_SEMAPHORE Handle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWaitSemaphore
ACPI_STATUS
AcpiOsWaitSemaphore (
ACPI_SEMAPHORE Handle,
UINT32 Units,
UINT16 Timeout);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsSignalSemaphore
ACPI_STATUS
AcpiOsSignalSemaphore (
ACPI_SEMAPHORE Handle,
UINT32 Units);
+#endif
/*
@@ -165,151 +198,208 @@ AcpiOsSignalSemaphore (
*/
#if (ACPI_MUTEX_TYPE != ACPI_BINARY_SEMAPHORE)
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCreateMutex
ACPI_STATUS
AcpiOsCreateMutex (
ACPI_MUTEX *OutHandle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsDeleteMutex
void
AcpiOsDeleteMutex (
ACPI_MUTEX Handle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAcquireMutex
ACPI_STATUS
AcpiOsAcquireMutex (
ACPI_MUTEX Handle,
UINT16 Timeout);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReleaseMutex
void
AcpiOsReleaseMutex (
ACPI_MUTEX Handle);
#endif
+#endif
+
/*
* Memory allocation and mapping
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAllocate
void *
AcpiOsAllocate (
ACPI_SIZE Size);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAllocateZeroed
+void *
+AcpiOsAllocateZeroed (
+ ACPI_SIZE Size);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsFree
void
AcpiOsFree (
void * Memory);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsMapMemory
void *
AcpiOsMapMemory (
ACPI_PHYSICAL_ADDRESS Where,
ACPI_SIZE Length);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsUnmapMemory
void
AcpiOsUnmapMemory (
void *LogicalAddress,
ACPI_SIZE Size);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetPhysicalAddress
ACPI_STATUS
AcpiOsGetPhysicalAddress (
void *LogicalAddress,
ACPI_PHYSICAL_ADDRESS *PhysicalAddress);
+#endif
/*
* Memory/Object Cache
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCreateCache
ACPI_STATUS
AcpiOsCreateCache (
char *CacheName,
UINT16 ObjectSize,
UINT16 MaxDepth,
ACPI_CACHE_T **ReturnCache);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsDeleteCache
ACPI_STATUS
AcpiOsDeleteCache (
ACPI_CACHE_T *Cache);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsPurgeCache
ACPI_STATUS
AcpiOsPurgeCache (
ACPI_CACHE_T *Cache);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAcquireObject
void *
AcpiOsAcquireObject (
ACPI_CACHE_T *Cache);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReleaseObject
ACPI_STATUS
AcpiOsReleaseObject (
ACPI_CACHE_T *Cache,
void *Object);
+#endif
/*
* Interrupt handlers
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsInstallInterruptHandler
ACPI_STATUS
AcpiOsInstallInterruptHandler (
UINT32 InterruptNumber,
ACPI_OSD_HANDLER ServiceRoutine,
void *Context);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsRemoveInterruptHandler
ACPI_STATUS
AcpiOsRemoveInterruptHandler (
UINT32 InterruptNumber,
ACPI_OSD_HANDLER ServiceRoutine);
+#endif
/*
* Threads and Scheduling
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetThreadId
ACPI_THREAD_ID
AcpiOsGetThreadId (
void);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsExecute
ACPI_STATUS
AcpiOsExecute (
ACPI_EXECUTE_TYPE Type,
ACPI_OSD_EXEC_CALLBACK Function,
void *Context);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWaitEventsComplete
void
AcpiOsWaitEventsComplete (
- void *Context);
+ void);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsSleep
void
AcpiOsSleep (
UINT64 Milliseconds);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsStall
void
AcpiOsStall (
UINT32 Microseconds);
+#endif
/*
* Platform and hardware-independent I/O interfaces
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadPort
ACPI_STATUS
AcpiOsReadPort (
ACPI_IO_ADDRESS Address,
UINT32 *Value,
UINT32 Width);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWritePort
ACPI_STATUS
AcpiOsWritePort (
ACPI_IO_ADDRESS Address,
UINT32 Value,
UINT32 Width);
+#endif
/*
* Platform and hardware-independent physical memory interfaces
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadMemory
ACPI_STATUS
AcpiOsReadMemory (
ACPI_PHYSICAL_ADDRESS Address,
- UINT32 *Value,
+ UINT64 *Value,
UINT32 Width);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWriteMemory
ACPI_STATUS
AcpiOsWriteMemory (
ACPI_PHYSICAL_ADDRESS Address,
- UINT32 Value,
+ UINT64 Value,
UINT32 Width);
+#endif
/*
@@ -317,80 +407,131 @@ AcpiOsWriteMemory (
* Note: Can't use "Register" as a parameter, changed to "Reg" --
* certain compilers complain.
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadPciConfiguration
ACPI_STATUS
AcpiOsReadPciConfiguration (
ACPI_PCI_ID *PciId,
UINT32 Reg,
UINT64 *Value,
UINT32 Width);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWritePciConfiguration
ACPI_STATUS
AcpiOsWritePciConfiguration (
ACPI_PCI_ID *PciId,
UINT32 Reg,
UINT64 Value,
UINT32 Width);
+#endif
/*
* Miscellaneous
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadable
BOOLEAN
AcpiOsReadable (
void *Pointer,
ACPI_SIZE Length);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWritable
BOOLEAN
AcpiOsWritable (
void *Pointer,
ACPI_SIZE Length);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTimer
UINT64
AcpiOsGetTimer (
void);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsSignal
ACPI_STATUS
AcpiOsSignal (
UINT32 Function,
void *Info);
+#endif
/*
* Debug print routines
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsPrintf
void ACPI_INTERNAL_VAR_XFACE
AcpiOsPrintf (
const char *Format,
...);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsVprintf
void
AcpiOsVprintf (
const char *Format,
va_list Args);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsRedirectOutput
void
AcpiOsRedirectOutput (
void *Destination);
+#endif
/*
* Debug input
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetLine
ACPI_STATUS
AcpiOsGetLine (
char *Buffer,
UINT32 BufferLength,
UINT32 *BytesRead);
+#endif
+
+
+/*
+ * Obtain ACPI table(s)
+ */
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByName
+ACPI_STATUS
+AcpiOsGetTableByName (
+ char *Signature,
+ UINT32 Instance,
+ ACPI_TABLE_HEADER **Table,
+ ACPI_PHYSICAL_ADDRESS *Address);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByIndex
+ACPI_STATUS
+AcpiOsGetTableByIndex (
+ UINT32 Index,
+ ACPI_TABLE_HEADER **Table,
+ UINT32 *Instance,
+ ACPI_PHYSICAL_ADDRESS *Address);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByAddress
+ACPI_STATUS
+AcpiOsGetTableByAddress (
+ ACPI_PHYSICAL_ADDRESS Address,
+ ACPI_TABLE_HEADER **Table);
+#endif
/*
* Directory manipulation
*/
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsOpenDirectory
void *
AcpiOsOpenDirectory (
char *Pathname,
char *WildcardSpec,
char RequestedFileType);
+#endif
/* RequesteFileType values */
@@ -398,13 +539,75 @@ AcpiOsOpenDirectory (
#define REQUEST_DIR_ONLY 1
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetNextFilename
char *
AcpiOsGetNextFilename (
void *DirHandle);
+#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCloseDirectory
void
AcpiOsCloseDirectory (
void *DirHandle);
+#endif
+
+
+/*
+ * File I/O and related support
+ */
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsOpenFile
+ACPI_FILE
+AcpiOsOpenFile (
+ const char *Path,
+ UINT8 Modes);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCloseFile
+void
+AcpiOsCloseFile (
+ ACPI_FILE File);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadFile
+int
+AcpiOsReadFile (
+ ACPI_FILE File,
+ void *Buffer,
+ ACPI_SIZE Size,
+ ACPI_SIZE Count);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWriteFile
+int
+AcpiOsWriteFile (
+ ACPI_FILE File,
+ void *Buffer,
+ ACPI_SIZE Size,
+ ACPI_SIZE Count);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetFileOffset
+long
+AcpiOsGetFileOffset (
+ ACPI_FILE File);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsSetFileOffset
+ACPI_STATUS
+AcpiOsSetFileOffset (
+ ACPI_FILE File,
+ long Offset,
+ UINT8 From);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsTracePoint
+void
+AcpiOsTracePoint (
+ ACPI_TRACE_EVENT_TYPE Type,
+ BOOLEAN Begin,
+ UINT8 *Aml,
+ char *Pathname);
+#endif
#endif /* __ACPIOSXF_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acpixf.h b/usr/src/uts/intel/sys/acpi/acpixf.h
index 82b677e615..b35e48997a 100644
--- a/usr/src/uts/intel/sys/acpi/acpixf.h
+++ b/usr/src/uts/intel/sys/acpi/acpixf.h
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Name: acpixf.h - External interfaces to the ACPI subsystem
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,452 +41,932 @@
* POSSIBILITY OF SUCH DAMAGES.
*/
-
#ifndef __ACXFACE_H__
#define __ACXFACE_H__
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20110527
+#define ACPI_CA_VERSION 0x20160527
+#include "acconfig.h"
#include "actypes.h"
#include "actbl.h"
+#include "acbuffer.h"
+
+
+/*****************************************************************************
+ *
+ * Macros used for ACPICA globals and configuration
+ *
+ ****************************************************************************/
+
+/*
+ * Ensure that global variables are defined and initialized only once.
+ *
+ * The use of these macros allows for a single list of globals (here)
+ * in order to simplify maintenance of the code.
+ */
+#ifdef DEFINE_ACPI_GLOBALS
+#define ACPI_GLOBAL(type,name) \
+ extern type name; \
+ type name
+
+#define ACPI_INIT_GLOBAL(type,name,value) \
+ type name=value
+
+#else
+#ifndef ACPI_GLOBAL
+#define ACPI_GLOBAL(type,name) \
+ extern type name
+#endif
+
+#ifndef ACPI_INIT_GLOBAL
+#define ACPI_INIT_GLOBAL(type,name,value) \
+ extern type name
+#endif
+#endif
+
+/*
+ * These macros configure the various ACPICA interfaces. They are
+ * useful for generating stub inline functions for features that are
+ * configured out of the current kernel or ACPICA application.
+ */
+#ifndef ACPI_EXTERNAL_RETURN_STATUS
+#define ACPI_EXTERNAL_RETURN_STATUS(Prototype) \
+ Prototype;
+#endif
+
+#ifndef ACPI_EXTERNAL_RETURN_OK
+#define ACPI_EXTERNAL_RETURN_OK(Prototype) \
+ Prototype;
+#endif
+
+#ifndef ACPI_EXTERNAL_RETURN_VOID
+#define ACPI_EXTERNAL_RETURN_VOID(Prototype) \
+ Prototype;
+#endif
+
+#ifndef ACPI_EXTERNAL_RETURN_UINT32
+#define ACPI_EXTERNAL_RETURN_UINT32(Prototype) \
+ Prototype;
+#endif
+
+#ifndef ACPI_EXTERNAL_RETURN_PTR
+#define ACPI_EXTERNAL_RETURN_PTR(Prototype) \
+ Prototype;
+#endif
+
+
+/*****************************************************************************
+ *
+ * Public globals and runtime configuration options
+ *
+ ****************************************************************************/
+
+/*
+ * Enable "slack mode" of the AML interpreter? Default is FALSE, and the
+ * interpreter strictly follows the ACPI specification. Setting to TRUE
+ * allows the interpreter to ignore certain errors and/or bad AML constructs.
+ *
+ * Currently, these features are enabled by this flag:
+ *
+ * 1) Allow "implicit return" of last value in a control method
+ * 2) Allow access beyond the end of an operation region
+ * 3) Allow access to uninitialized locals/args (auto-init to integer 0)
+ * 4) Allow ANY object type to be a source operand for the Store() operator
+ * 5) Allow unresolved references (invalid target name) in package objects
+ * 6) Enable warning messages for behavior that is not ACPI spec compliant
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_EnableInterpreterSlack, FALSE);
+
+/*
+ * Automatically serialize all methods that create named objects? Default
+ * is TRUE, meaning that all NonSerialized methods are scanned once at
+ * table load time to determine those that create named objects. Methods
+ * that create named objects are marked Serialized in order to prevent
+ * possible run-time problems if they are entered by more than one thread.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_AutoSerializeMethods, TRUE);
+
+/*
+ * Create the predefined _OSI method in the namespace? Default is TRUE
+ * because ACPICA is fully compatible with other ACPI implementations.
+ * Changing this will revert ACPICA (and machine ASL) to pre-OSI behavior.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_CreateOsiMethod, TRUE);
+
+/*
+ * Optionally use default values for the ACPI register widths. Set this to
+ * TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_UseDefaultRegisterWidths, TRUE);
+
+/*
+ * Whether or not to verify the table checksum before installation. Set
+ * this to TRUE to verify the table checksum before install it to the table
+ * manager. Note that enabling this option causes errors to happen in some
+ * OSPMs during early initialization stages. Default behavior is to do such
+ * verification.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_VerifyTableChecksum, TRUE);
+
+/*
+ * Optionally enable output from the AML Debug Object.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_EnableAmlDebugObject, FALSE);
+
+/*
+ * Optionally copy the entire DSDT to local memory (instead of simply
+ * mapping it.) There are some BIOSs that corrupt or replace the original
+ * DSDT, creating the need for this option. Default is FALSE, do not copy
+ * the DSDT.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_CopyDsdtLocally, FALSE);
+
+/*
+ * Optionally ignore an XSDT if present and use the RSDT instead.
+ * Although the ACPI specification requires that an XSDT be used instead
+ * of the RSDT, the XSDT has been found to be corrupt or ill-formed on
+ * some machines. Default behavior is to use the XSDT if present.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DoNotUseXsdt, FALSE);
+
+/*
+ * Optionally support group module level code.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_GroupModuleLevelCode, FALSE);
+
+/*
+ * Optionally use 32-bit FADT addresses if and when there is a conflict
+ * (address mismatch) between the 32-bit and 64-bit versions of the
+ * address. Although ACPICA adheres to the ACPI specification which
+ * requires the use of the corresponding 64-bit address if it is non-zero,
+ * some machines have been found to have a corrupted non-zero 64-bit
+ * address. Default is FALSE, do not favor the 32-bit addresses.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_Use32BitFadtAddresses, FALSE);
+
+/*
+ * Optionally use 32-bit FACS table addresses.
+ * It is reported that some platforms fail to resume from system suspending
+ * if 64-bit FACS table address is selected:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=74021
+ * Default is TRUE, favor the 32-bit addresses.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_Use32BitFacsAddresses, TRUE);
+
+/*
+ * Optionally truncate I/O addresses to 16 bits. Provides compatibility
+ * with other ACPI implementations. NOTE: During ACPICA initialization,
+ * this value is set to TRUE if any Windows OSI strings have been
+ * requested by the BIOS.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_TruncateIoAddresses, FALSE);
+
+/*
+ * Disable runtime checking and repair of values returned by control methods.
+ * Use only if the repair is causing a problem on a particular machine.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisableAutoRepair, FALSE);
+
+/*
+ * Optionally do not install any SSDTs from the RSDT/XSDT during initialization.
+ * This can be useful for debugging ACPI problems on some machines.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisableSsdtTableInstall, FALSE);
+
+/*
+ * Optionally enable runtime namespace override.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_RuntimeNamespaceOverride, TRUE);
+
+/*
+ * We keep track of the latest version of Windows that has been requested by
+ * the BIOS. ACPI 5.0.
+ */
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_OsiData, 0);
+
+/*
+ * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning
+ * that the ACPI hardware is no longer required. A flag in the FADT indicates
+ * a reduced HW machine, and that flag is duplicated here for convenience.
+ */
+ACPI_INIT_GLOBAL (BOOLEAN, AcpiGbl_ReducedHardware, FALSE);
+
+/*
+ * This mechanism is used to trace a specified AML method. The method is
+ * traced each time it is executed.
+ */
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceFlags, 0);
+ACPI_INIT_GLOBAL (const char *, AcpiGbl_TraceMethodName, NULL);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceDbgLevel, ACPI_TRACE_LEVEL_DEFAULT);
+ACPI_INIT_GLOBAL (UINT32, AcpiGbl_TraceDbgLayer, ACPI_TRACE_LAYER_DEFAULT);
+
+/*
+ * Runtime configuration of debug output control masks. We want the debug
+ * switches statically initialized so they are already set when the debugger
+ * is entered.
+ */
+#ifdef ACPI_DEBUG_OUTPUT
+ACPI_INIT_GLOBAL (UINT32, AcpiDbgLevel, ACPI_DEBUG_DEFAULT);
+#else
+ACPI_INIT_GLOBAL (UINT32, AcpiDbgLevel, ACPI_NORMAL_DEFAULT);
+#endif
+ACPI_INIT_GLOBAL (UINT32, AcpiDbgLayer, ACPI_COMPONENT_DEFAULT);
+
+/* Optionally enable timer output with Debug Object output */
+
+ACPI_INIT_GLOBAL (UINT8, AcpiGbl_DisplayDebugTimer, FALSE);
+
+/*
+ * Other miscellaneous globals
+ */
+ACPI_GLOBAL (ACPI_TABLE_FADT, AcpiGbl_FADT);
+ACPI_GLOBAL (UINT32, AcpiCurrentGpeCount);
+ACPI_GLOBAL (BOOLEAN, AcpiGbl_SystemAwakeAndRunning);
+
+
+/*****************************************************************************
+ *
+ * ACPICA public interface configuration.
+ *
+ * Interfaces that are configured out of the ACPICA build are replaced
+ * by inlined stubs by default.
+ *
+ ****************************************************************************/
+
+/*
+ * Hardware-reduced prototypes (default: Not hardware reduced).
+ *
+ * All ACPICA hardware-related interfaces that use these macros will be
+ * configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag
+ * is set to TRUE.
+ *
+ * Note: This static build option for reduced hardware is intended to
+ * reduce ACPICA code size if desired or necessary. However, even if this
+ * option is not specified, the runtime behavior of ACPICA is dependent
+ * on the actual FADT reduced hardware flag (HW_REDUCED_ACPI). If set,
+ * the flag will enable similar behavior -- ACPICA will not attempt
+ * to access any ACPI-relate hardware (SCI, GPEs, Fixed Events, etc.)
+ */
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(Prototype) \
+ ACPI_EXTERNAL_RETURN_STATUS(Prototype)
+
+#define ACPI_HW_DEPENDENT_RETURN_OK(Prototype) \
+ ACPI_EXTERNAL_RETURN_OK(Prototype)
+
+#define ACPI_HW_DEPENDENT_RETURN_VOID(Prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(Prototype)
+
+#else
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(Prototype) \
+ static ACPI_INLINE Prototype {return(AE_NOT_CONFIGURED);}
+
+#define ACPI_HW_DEPENDENT_RETURN_OK(Prototype) \
+ static ACPI_INLINE Prototype {return(AE_OK);}
+
+#define ACPI_HW_DEPENDENT_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+
+/*
+ * Error message prototypes (default: error messages enabled).
+ *
+ * All interfaces related to error and warning messages
+ * will be configured out of the ACPICA build if the
+ * ACPI_NO_ERROR_MESSAGE flag is defined.
+ */
+#ifndef ACPI_NO_ERROR_MESSAGES
+#define ACPI_MSG_DEPENDENT_RETURN_VOID(Prototype) \
+ Prototype;
+
+#else
+#define ACPI_MSG_DEPENDENT_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
+
+#endif /* ACPI_NO_ERROR_MESSAGES */
+
+
+/*
+ * Debugging output prototypes (default: no debug output).
+ *
+ * All interfaces related to debug output messages
+ * will be configured out of the ACPICA build unless the
+ * ACPI_DEBUG_OUTPUT flag is defined.
+ */
+#ifdef ACPI_DEBUG_OUTPUT
+#define ACPI_DBG_DEPENDENT_RETURN_VOID(Prototype) \
+ Prototype;
+
+#else
+#define ACPI_DBG_DEPENDENT_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
+
+#endif /* ACPI_DEBUG_OUTPUT */
+
+
+/*
+ * Application prototypes
+ *
+ * All interfaces used by application will be configured
+ * out of the ACPICA build unless the ACPI_APPLICATION
+ * flag is defined.
+ */
+#ifdef ACPI_APPLICATION
+#define ACPI_APP_DEPENDENT_RETURN_VOID(Prototype) \
+ Prototype;
+
+#else
+#define ACPI_APP_DEPENDENT_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
+
+#endif /* ACPI_APPLICATION */
+
/*
- * Globals that are publically available
+ * Debugger prototypes
+ *
+ * All interfaces used by debugger will be configured
+ * out of the ACPICA build unless the ACPI_DEBUGGER
+ * flag is defined.
*/
-extern UINT32 AcpiCurrentGpeCount;
-extern ACPI_TABLE_FADT AcpiGbl_FADT;
-extern BOOLEAN AcpiGbl_SystemAwakeAndRunning;
+#ifdef ACPI_DEBUGGER
+#define ACPI_DBR_DEPENDENT_RETURN_OK(Prototype) \
+ ACPI_EXTERNAL_RETURN_OK(Prototype)
-/* Runtime configuration of debug print levels */
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(Prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(Prototype)
-extern UINT32 AcpiDbgLevel;
-extern UINT32 AcpiDbgLayer;
+#else
+#define ACPI_DBR_DEPENDENT_RETURN_OK(Prototype) \
+ static ACPI_INLINE Prototype {return(AE_OK);}
-/* ACPICA runtime options */
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
-extern UINT8 AcpiGbl_EnableInterpreterSlack;
-extern UINT8 AcpiGbl_AllMethodsSerialized;
-extern UINT8 AcpiGbl_CreateOsiMethod;
-extern UINT8 AcpiGbl_UseDefaultRegisterWidths;
-extern ACPI_NAME AcpiGbl_TraceMethodName;
-extern UINT32 AcpiGbl_TraceFlags;
-extern UINT8 AcpiGbl_EnableAmlDebugObject;
-extern UINT8 AcpiGbl_CopyDsdtLocally;
-extern UINT8 AcpiGbl_TruncateIoAddresses;
+#endif /* ACPI_DEBUGGER */
+/*****************************************************************************
+ *
+ * ACPICA public interface prototypes
+ *
+ ****************************************************************************/
+
/*
* Initialization
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInitializeTables (
ACPI_TABLE_DESC *InitialStorage,
UINT32 InitialTableCount,
- BOOLEAN AllowResize);
+ BOOLEAN AllowResize))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInitializeSubsystem (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiEnableSubsystem (
- UINT32 Flags);
+ UINT32 Flags))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInitializeObjects (
- UINT32 Flags);
+ UINT32 Flags))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiTerminate (
- void);
+ void))
/*
* Miscellaneous global interfaces
*/
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiEnable (
- void);
+ void))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiDisable (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiSubsystemStatus (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetSystemInfo (
- ACPI_BUFFER *RetBuffer);
+ ACPI_BUFFER *RetBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetStatistics (
- ACPI_STATISTICS *Stats);
+ ACPI_STATISTICS *Stats))
+ACPI_EXTERNAL_RETURN_PTR (
const char *
AcpiFormatException (
- ACPI_STATUS Exception);
+ ACPI_STATUS Exception))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiPurgeCachedObjects (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallInterface (
- ACPI_STRING InterfaceName);
+ ACPI_STRING InterfaceName))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveInterface (
- ACPI_STRING InterfaceName);
+ ACPI_STRING InterfaceName))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiUpdateInterfaces (
+ UINT8 Action))
+
+ACPI_EXTERNAL_RETURN_UINT32 (
+UINT32
+AcpiCheckAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ ACPI_SIZE Length,
+ BOOLEAN Warn))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiDecodePldBuffer (
+ UINT8 *InBuffer,
+ ACPI_SIZE Length,
+ ACPI_PLD_INFO **ReturnBuffer))
/*
- * ACPI Memory management
+ * ACPI table load/unload interfaces
*/
-void *
-AcpiAllocate (
- UINT32 Size);
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiInstallTable (
+ ACPI_PHYSICAL_ADDRESS Address,
+ BOOLEAN Physical))
-void *
-AcpiCallocate (
- UINT32 Size);
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiLoadTable (
+ ACPI_TABLE_HEADER *Table))
-void
-AcpiFree (
- void *Address);
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiUnloadParentTable (
+ ACPI_HANDLE Object))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiLoadTables (
+ void))
/*
* ACPI table manipulation interfaces
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiReallocateRootTable (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiFindRootPointer (
- ACPI_SIZE *RsdpAddress);
-
-ACPI_STATUS
-AcpiLoadTables (
- void);
+ ACPI_PHYSICAL_ADDRESS *RsdpAddress))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetTableHeader (
ACPI_STRING Signature,
UINT32 Instance,
- ACPI_TABLE_HEADER *OutTableHeader);
+ ACPI_TABLE_HEADER *OutTableHeader))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetTable (
ACPI_STRING Signature,
UINT32 Instance,
- ACPI_TABLE_HEADER **OutTable);
+ ACPI_TABLE_HEADER **OutTable))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetTableByIndex (
UINT32 TableIndex,
- ACPI_TABLE_HEADER **OutTable);
+ ACPI_TABLE_HEADER **OutTable))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallTableHandler (
ACPI_TABLE_HANDLER Handler,
- void *Context);
+ void *Context))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveTableHandler (
- ACPI_TABLE_HANDLER Handler);
+ ACPI_TABLE_HANDLER Handler))
/*
* Namespace and name interfaces
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiWalkNamespace (
ACPI_OBJECT_TYPE Type,
ACPI_HANDLE StartObject,
UINT32 MaxDepth,
- ACPI_WALK_CALLBACK PreOrderVisit,
- ACPI_WALK_CALLBACK PostOrderVisit,
+ ACPI_WALK_CALLBACK DescendingCallback,
+ ACPI_WALK_CALLBACK AscendingCallback,
void *Context,
- void **ReturnValue);
+ void **ReturnValue))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetDevices (
char *HID,
ACPI_WALK_CALLBACK UserFunction,
void *Context,
- void **ReturnValue);
+ void **ReturnValue))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetName (
ACPI_HANDLE Object,
UINT32 NameType,
- ACPI_BUFFER *RetPathPtr);
+ ACPI_BUFFER *RetPathPtr))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetHandle (
ACPI_HANDLE Parent,
ACPI_STRING Pathname,
- ACPI_HANDLE *RetHandle);
+ ACPI_HANDLE *RetHandle))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiAttachData (
ACPI_HANDLE Object,
ACPI_OBJECT_HANDLER Handler,
- void *Data);
+ void *Data))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiDetachData (
ACPI_HANDLE Object,
- ACPI_OBJECT_HANDLER Handler);
+ ACPI_OBJECT_HANDLER Handler))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetData (
ACPI_HANDLE Object,
ACPI_OBJECT_HANDLER Handler,
- void **Data);
+ void **Data))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiDebugTrace (
- char *Name,
+ const char *Name,
UINT32 DebugLevel,
UINT32 DebugLayer,
- UINT32 Flags);
+ UINT32 Flags))
/*
* Object manipulation and enumeration
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiEvaluateObject (
ACPI_HANDLE Object,
ACPI_STRING Pathname,
ACPI_OBJECT_LIST *ParameterObjects,
- ACPI_BUFFER *ReturnObjectBuffer);
+ ACPI_BUFFER *ReturnObjectBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiEvaluateObjectTyped (
ACPI_HANDLE Object,
ACPI_STRING Pathname,
ACPI_OBJECT_LIST *ExternalParams,
ACPI_BUFFER *ReturnBuffer,
- ACPI_OBJECT_TYPE ReturnType);
+ ACPI_OBJECT_TYPE ReturnType))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetObjectInfo (
ACPI_HANDLE Object,
- ACPI_DEVICE_INFO **ReturnBuffer);
+ ACPI_DEVICE_INFO **ReturnBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallMethod (
- UINT8 *Buffer);
+ UINT8 *Buffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetNextObject (
ACPI_OBJECT_TYPE Type,
ACPI_HANDLE Parent,
ACPI_HANDLE Child,
- ACPI_HANDLE *OutHandle);
+ ACPI_HANDLE *OutHandle))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetType (
ACPI_HANDLE Object,
- ACPI_OBJECT_TYPE *OutType);
+ ACPI_OBJECT_TYPE *OutType))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetParent (
ACPI_HANDLE Object,
- ACPI_HANDLE *OutHandle);
+ ACPI_HANDLE *OutHandle))
/*
* Handler interfaces
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallInitializationHandler (
ACPI_INIT_HANDLER Handler,
- UINT32 Function);
+ UINT32 Function))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiInstallSciHandler (
+ ACPI_SCI_HANDLER Address,
+ void *Context))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiRemoveSciHandler (
+ ACPI_SCI_HANDLER Address))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiInstallGlobalEventHandler (
ACPI_GBL_EVENT_HANDLER Handler,
- void *Context);
+ void *Context))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiInstallFixedEventHandler (
UINT32 AcpiEvent,
ACPI_EVENT_HANDLER Handler,
- void *Context);
+ void *Context))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveFixedEventHandler (
UINT32 AcpiEvent,
- ACPI_EVENT_HANDLER Handler);
+ ACPI_EVENT_HANDLER Handler))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiInstallGpeHandler (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
UINT32 Type,
ACPI_GPE_HANDLER Address,
- void *Context);
+ void *Context))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiInstallGpeRawHandler (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber,
+ UINT32 Type,
+ ACPI_GPE_HANDLER Address,
+ void *Context))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveGpeHandler (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
- ACPI_GPE_HANDLER Address);
+ ACPI_GPE_HANDLER Address))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallNotifyHandler (
ACPI_HANDLE Device,
UINT32 HandlerType,
ACPI_NOTIFY_HANDLER Handler,
- void *Context);
+ void *Context))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveNotifyHandler (
ACPI_HANDLE Device,
UINT32 HandlerType,
- ACPI_NOTIFY_HANDLER Handler);
+ ACPI_NOTIFY_HANDLER Handler))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallAddressSpaceHandler (
ACPI_HANDLE Device,
ACPI_ADR_SPACE_TYPE SpaceId,
ACPI_ADR_SPACE_HANDLER Handler,
ACPI_ADR_SPACE_SETUP Setup,
- void *Context);
+ void *Context))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveAddressSpaceHandler (
ACPI_HANDLE Device,
ACPI_ADR_SPACE_TYPE SpaceId,
- ACPI_ADR_SPACE_HANDLER Handler);
+ ACPI_ADR_SPACE_HANDLER Handler))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallExceptionHandler (
- ACPI_EXCEPTION_HANDLER Handler);
+ ACPI_EXCEPTION_HANDLER Handler))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiInstallInterfaceHandler (
- ACPI_INTERFACE_HANDLER Handler);
+ ACPI_INTERFACE_HANDLER Handler))
/*
* Global Lock interfaces
*/
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiAcquireGlobalLock (
UINT16 Timeout,
- UINT32 *Handle);
+ UINT32 *Handle))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiReleaseGlobalLock (
- UINT32 Handle);
+ UINT32 Handle))
+
+
+/*
+ * Interfaces to AML mutex objects
+ */
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiAcquireMutex (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname,
+ UINT16 Timeout))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiReleaseMutex (
+ ACPI_HANDLE Handle,
+ ACPI_STRING Pathname))
/*
* Fixed Event interfaces
*/
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiEnableEvent (
UINT32 Event,
- UINT32 Flags);
+ UINT32 Flags))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiDisableEvent (
UINT32 Event,
- UINT32 Flags);
+ UINT32 Flags))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiClearEvent (
- UINT32 Event);
+ UINT32 Event))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiGetEventStatus (
UINT32 Event,
- ACPI_EVENT_STATUS *EventStatus);
+ ACPI_EVENT_STATUS *EventStatus))
/*
* General Purpose Event (GPE) Interfaces
*/
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiUpdateAllGpes (
- void);
+ void))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiEnableGpe (
ACPI_HANDLE GpeDevice,
- UINT32 GpeNumber);
+ UINT32 GpeNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiDisableGpe (
ACPI_HANDLE GpeDevice,
- UINT32 GpeNumber);
+ UINT32 GpeNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiClearGpe (
ACPI_HANDLE GpeDevice,
- UINT32 GpeNumber);
+ UINT32 GpeNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiSetGpe (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
- UINT8 Action);
+ UINT8 Action))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiFinishGpe (
ACPI_HANDLE GpeDevice,
- UINT32 GpeNumber);
+ UINT32 GpeNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiMarkGpeForWake (
+ ACPI_HANDLE GpeDevice,
+ UINT32 GpeNumber))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiSetupGpeForWake (
ACPI_HANDLE ParentDevice,
ACPI_HANDLE GpeDevice,
- UINT32 GpeNumber);
+ UINT32 GpeNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiSetGpeWakeMask (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
- UINT8 Action);
+ UINT8 Action))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiGetGpeStatus (
ACPI_HANDLE GpeDevice,
UINT32 GpeNumber,
- ACPI_EVENT_STATUS *EventStatus);
+ ACPI_EVENT_STATUS *EventStatus))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiDisableAllGpes (
- void);
+ void))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiEnableAllRuntimeGpes (
- void);
+ void))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiEnableAllWakeupGpes (
+ void))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiGetGpeDevice (
UINT32 GpeIndex,
- ACPI_HANDLE *GpeDevice);
+ ACPI_HANDLE *GpeDevice))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiInstallGpeBlock (
ACPI_HANDLE GpeDevice,
ACPI_GENERIC_ADDRESS *GpeBlockAddress,
UINT32 RegisterCount,
- UINT32 InterruptNumber);
+ UINT32 InterruptNumber))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiRemoveGpeBlock (
- ACPI_HANDLE GpeDevice);
+ ACPI_HANDLE GpeDevice))
/*
@@ -498,144 +977,232 @@ ACPI_STATUS (*ACPI_WALK_RESOURCE_CALLBACK) (
ACPI_RESOURCE *Resource,
void *Context);
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetVendorResource (
ACPI_HANDLE Device,
char *Name,
ACPI_VENDOR_UUID *Uuid,
- ACPI_BUFFER *RetBuffer);
+ ACPI_BUFFER *RetBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetCurrentResources (
ACPI_HANDLE Device,
- ACPI_BUFFER *RetBuffer);
+ ACPI_BUFFER *RetBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetPossibleResources (
ACPI_HANDLE Device,
- ACPI_BUFFER *RetBuffer);
+ ACPI_BUFFER *RetBuffer))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiGetEventResources (
+ ACPI_HANDLE DeviceHandle,
+ ACPI_BUFFER *RetBuffer))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiWalkResourceBuffer (
+ ACPI_BUFFER *Buffer,
+ ACPI_WALK_RESOURCE_CALLBACK UserFunction,
+ void *Context))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiWalkResources (
ACPI_HANDLE Device,
char *Name,
ACPI_WALK_RESOURCE_CALLBACK UserFunction,
- void *Context);
+ void *Context))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiSetCurrentResources (
ACPI_HANDLE Device,
- ACPI_BUFFER *InBuffer);
+ ACPI_BUFFER *InBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetIrqRoutingTable (
ACPI_HANDLE Device,
- ACPI_BUFFER *RetBuffer);
+ ACPI_BUFFER *RetBuffer))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiResourceToAddress64 (
ACPI_RESOURCE *Resource,
- ACPI_RESOURCE_ADDRESS64 *Out);
+ ACPI_RESOURCE_ADDRESS64 *Out))
+
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiBufferToResource (
+ UINT8 *AmlBuffer,
+ UINT16 AmlBufferLength,
+ ACPI_RESOURCE **ResourcePtr))
/*
* Hardware (ACPI device) interfaces
*/
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiReset (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiRead (
UINT64 *Value,
- ACPI_GENERIC_ADDRESS *Reg);
+ ACPI_GENERIC_ADDRESS *Reg))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiWrite (
UINT64 Value,
- ACPI_GENERIC_ADDRESS *Reg);
+ ACPI_GENERIC_ADDRESS *Reg))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiReadBitRegister (
UINT32 RegisterId,
- UINT32 *ReturnValue);
+ UINT32 *ReturnValue))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiWriteBitRegister (
UINT32 RegisterId,
- UINT32 Value);
+ UINT32 Value))
+
+/*
+ * Sleep/Wake interfaces
+ */
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiGetSleepTypeData (
UINT8 SleepState,
UINT8 *Slp_TypA,
- UINT8 *Slp_TypB);
+ UINT8 *Slp_TypB))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiEnterSleepStatePrep (
- UINT8 SleepState);
+ UINT8 SleepState))
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiEnterSleepState (
- UINT8 SleepState);
+ UINT8 SleepState))
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiEnterSleepStateS4bios (
- void);
+ void))
+ACPI_EXTERNAL_RETURN_STATUS (
+ACPI_STATUS
+AcpiLeaveSleepStatePrep (
+ UINT8 SleepState))
+
+ACPI_EXTERNAL_RETURN_STATUS (
ACPI_STATUS
AcpiLeaveSleepState (
- UINT8 SleepState)
- ;
+ UINT8 SleepState))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
AcpiSetFirmwareWakingVector (
- UINT32 PhysicalAddress);
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress,
+ ACPI_PHYSICAL_ADDRESS PhysicalAddress64))
+
-#if ACPI_MACHINE_WIDTH == 64
+/*
+ * ACPI Timer interfaces
+ */
+ACPI_HW_DEPENDENT_RETURN_STATUS (
ACPI_STATUS
-AcpiSetFirmwareWakingVector64 (
- UINT64 PhysicalAddress);
-#endif
+AcpiGetTimerResolution (
+ UINT32 *Resolution))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiGetTimer (
+ UINT32 *Ticks))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS (
+ACPI_STATUS
+AcpiGetTimerDuration (
+ UINT32 StartTicks,
+ UINT32 EndTicks,
+ UINT32 *TimeElapsed))
/*
* Error/Warning output
*/
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(3)
void ACPI_INTERNAL_VAR_XFACE
AcpiError (
const char *ModuleName,
UINT32 LineNumber,
const char *Format,
- ...) ACPI_PRINTF_LIKE(3);
+ ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(4)
void ACPI_INTERNAL_VAR_XFACE
AcpiException (
const char *ModuleName,
UINT32 LineNumber,
ACPI_STATUS Status,
const char *Format,
- ...) ACPI_PRINTF_LIKE(4);
+ ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(3)
void ACPI_INTERNAL_VAR_XFACE
AcpiWarning (
const char *ModuleName,
UINT32 LineNumber,
const char *Format,
- ...) ACPI_PRINTF_LIKE(3);
+ ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(1)
void ACPI_INTERNAL_VAR_XFACE
AcpiInfo (
+ const char *Format,
+ ...))
+
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(3)
+void ACPI_INTERNAL_VAR_XFACE
+AcpiBiosError (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ const char *Format,
+ ...))
+
+ACPI_MSG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(3)
+void ACPI_INTERNAL_VAR_XFACE
+AcpiBiosWarning (
const char *ModuleName,
UINT32 LineNumber,
const char *Format,
- ...) ACPI_PRINTF_LIKE(3);
+ ...))
/*
* Debug output
*/
-#ifdef ACPI_DEBUG_OUTPUT
-
+/* ARGSUSED */
+ACPI_DBG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(6)
void ACPI_INTERNAL_VAR_XFACE
AcpiDebugPrint (
UINT32 RequestedDebugLevel,
@@ -644,8 +1211,11 @@ AcpiDebugPrint (
const char *ModuleName,
UINT32 ComponentId,
const char *Format,
- ...) ACPI_PRINTF_LIKE(6);
+ ...))
+/* ARGSUSED */
+ACPI_DBG_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(6)
void ACPI_INTERNAL_VAR_XFACE
AcpiDebugPrintRaw (
UINT32 RequestedDebugLevel,
@@ -654,7 +1224,35 @@ AcpiDebugPrintRaw (
const char *ModuleName,
UINT32 ComponentId,
const char *Format,
- ...) ACPI_PRINTF_LIKE(6);
-#endif
+ ...))
+
+/* ARGSUSED */
+ACPI_DBG_DEPENDENT_RETURN_VOID (
+void
+AcpiTracePoint (
+ ACPI_TRACE_EVENT_TYPE Type,
+ BOOLEAN Begin,
+ UINT8 *Aml,
+ char *Pathname))
+
+/* ARGSUSED */
+ACPI_APP_DEPENDENT_RETURN_VOID (
+ACPI_PRINTF_LIKE(1)
+void ACPI_INTERNAL_VAR_XFACE
+AcpiLogError (
+ const char *Format,
+ ...))
+
+ACPI_STATUS
+AcpiInitializeDebugger (
+ void);
+
+void
+AcpiTerminateDebugger (
+ void);
+
+void
+AcpiSetDebuggerThreadId (
+ ACPI_THREAD_ID ThreadId);
#endif /* __ACXFACE_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acpredef.h b/usr/src/uts/intel/sys/acpi/acpredef.h
index 0496767a86..ab61c53d7b 100644
--- a/usr/src/uts/intel/sys/acpi/acpredef.h
+++ b/usr/src/uts/intel/sys/acpi/acpredef.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,7 +49,7 @@
*
* Return Package types
*
- * 1) PTYPE1 packages do not contain sub-packages.
+ * 1) PTYPE1 packages do not contain subpackages.
*
* ACPI_PTYPE1_FIXED: Fixed-length length, 1 or 2 object types:
* object type
@@ -57,24 +57,26 @@
* object type
* count
*
- * ACPI_PTYPE1_VAR: Variable-length length:
+ * ACPI_PTYPE1_VAR: Variable-length length. Zero-length package is allowed:
* object type (Int/Buf/Ref)
*
* ACPI_PTYPE1_OPTION: Package has some required and some optional elements
* (Used for _PRW)
*
*
- * 2) PTYPE2 packages contain a Variable-length number of sub-packages. Each
- * of the different types describe the contents of each of the sub-packages.
+ * 2) PTYPE2 packages contain a Variable-length number of subpackages. Each
+ * of the different types describe the contents of each of the subpackages.
*
- * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types:
+ * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types. Zero-length
+ * parent package is allowed:
* object type
* count
* object type
* count
* (Used for _ALR,_MLS,_PSS,_TRT,_TSS)
*
- * ACPI_PTYPE2_COUNT: Each subpackage has a count as first element:
+ * ACPI_PTYPE2_COUNT: Each subpackage has a count as first element.
+ * Zero-length parent package is allowed:
* object type
* (Used for _CSD,_PSD,_TSD)
*
@@ -85,15 +87,35 @@
* count
* (Used for _CST)
*
- * ACPI_PTYPE2_FIXED: Each subpackage is of Fixed-length
+ * ACPI_PTYPE2_FIXED: Each subpackage is of Fixed-length. Zero-length
+ * parent package is allowed.
* (Used for _PRT)
*
- * ACPI_PTYPE2_MIN: Each subpackage has a Variable-length but minimum length
+ * ACPI_PTYPE2_MIN: Each subpackage has a Variable-length but minimum length.
+ * Zero-length parent package is allowed:
* (Used for _HPX)
*
* ACPI_PTYPE2_REV_FIXED: Revision at start, each subpackage is Fixed-length
* (Used for _ART, _FPS)
*
+ * ACPI_PTYPE2_FIX_VAR: Each subpackage consists of some fixed-length elements
+ * followed by an optional element. Zero-length parent package is allowed.
+ * object type
+ * count
+ * object type
+ * count = 0 (optional)
+ * (Used for _DLM)
+ *
+ * ACPI_PTYPE2_VAR_VAR: Variable number of subpackages, each of either a
+ * constant or variable length. The subpackages are preceded by a
+ * constant number of objects.
+ * (Used for _LPI, _RDI)
+ *
+ * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID
+ * defines the format of the package. Zero-length parent package is
+ * allowed.
+ * (Used for _DSD)
+ *
*****************************************************************************/
enum AcpiReturnPackageTypes
@@ -106,22 +128,68 @@ enum AcpiReturnPackageTypes
ACPI_PTYPE2_PKG_COUNT = 6,
ACPI_PTYPE2_FIXED = 7,
ACPI_PTYPE2_MIN = 8,
- ACPI_PTYPE2_REV_FIXED = 9
+ ACPI_PTYPE2_REV_FIXED = 9,
+ ACPI_PTYPE2_FIX_VAR = 10,
+ ACPI_PTYPE2_VAR_VAR = 11,
+ ACPI_PTYPE2_UUID_PAIR = 12,
+ ACPI_PTYPE_CUSTOM = 13
};
+/* Support macros for users of the predefined info table */
+
+#define METHOD_PREDEF_ARGS_MAX 4
+#define METHOD_ARG_BIT_WIDTH 3
+#define METHOD_ARG_MASK 0x0007
+#define ARG_COUNT_IS_MINIMUM 0x8000
+#define METHOD_MAX_ARG_TYPE ACPI_TYPE_PACKAGE
+
+#define METHOD_GET_ARG_COUNT(ArgList) ((ArgList) & METHOD_ARG_MASK)
+#define METHOD_GET_NEXT_TYPE(ArgList) (((ArgList) >>= METHOD_ARG_BIT_WIDTH) & METHOD_ARG_MASK)
+
+/* Macros used to build the predefined info table */
+
+#define METHOD_0ARGS 0
+#define METHOD_1ARGS(a1) (1 | (a1 << 3))
+#define METHOD_2ARGS(a1,a2) (2 | (a1 << 3) | (a2 << 6))
+#define METHOD_3ARGS(a1,a2,a3) (3 | (a1 << 3) | (a2 << 6) | (a3 << 9))
+#define METHOD_4ARGS(a1,a2,a3,a4) (4 | (a1 << 3) | (a2 << 6) | (a3 << 9) | (a4 << 12))
+
+#define METHOD_RETURNS(type) (type)
+#define METHOD_NO_RETURN_VALUE 0
+
+#define PACKAGE_INFO(a,b,c,d,e,f) {{{(a),(b),(c),(d)}, ((((UINT16)(f)) << 8) | (e)), 0}}
+
+
+/* Support macros for the resource descriptor info table */
+
+#define WIDTH_1 0x0001
+#define WIDTH_2 0x0002
+#define WIDTH_3 0x0004
+#define WIDTH_8 0x0008
+#define WIDTH_16 0x0010
+#define WIDTH_32 0x0020
+#define WIDTH_64 0x0040
+#define VARIABLE_DATA 0x0080
+#define NUM_RESOURCE_WIDTHS 8
+
+#define WIDTH_ADDRESS WIDTH_16 | WIDTH_32 | WIDTH_64
+
+
#ifdef ACPI_CREATE_PREDEFINED_TABLE
-/*
+/******************************************************************************
+ *
* Predefined method/object information table.
*
* These are the names that can actually be evaluated via AcpiEvaluateObject.
* Not present in this table are the following:
*
- * 1) Predefined/Reserved names that are never evaluated via
+ * 1) Predefined/Reserved names that are not usually evaluated via
* AcpiEvaluateObject:
* _Lxx and _Exx GPE methods
* _Qxx EC methods
* _T_x compiler temporary variables
+ * _Wxx wake events
*
* 2) Predefined names that never actually exist within the AML code:
* Predefined resource descriptor field names
@@ -129,13 +197,13 @@ enum AcpiReturnPackageTypes
* 3) Predefined names that are implemented within ACPICA:
* _OSI
*
- * 4) Some predefined names that are not documented within the ACPI spec.
- * _WDG, _WED
- *
* The main entries in the table each contain the following items:
*
* Name - The ACPI reserved name
- * ParamCount - Number of arguments to the method
+ * ArgumentList - Contains (in 16 bits), the number of required
+ * arguments to the method (3 bits), and a 3-bit type
+ * field for each argument (up to 4 arguments). The
+ * METHOD_?ARGS macros generate the correct packed data.
* ExpectedBtypes - Allowed type(s) for the return value.
* 0 means that no return value is expected.
*
@@ -145,228 +213,524 @@ enum AcpiReturnPackageTypes
* overall size of the stored data.
*
* Note: The additional braces are intended to promote portability.
- */
-static const ACPI_PREDEFINED_INFO PredefinedNames[] =
+ *
+ * Note2: Table is used by the kernel-resident subsystem, the iASL compiler,
+ * and the AcpiHelp utility.
+ *
+ * TBD: _PRT - currently ignore reversed entries. Attempt to fix in nsrepair.
+ * Possibly fixing package elements like _BIF, etc.
+ *
+ *****************************************************************************/
+
+const ACPI_PREDEFINED_INFO AcpiGbl_PredefinedMethods[] =
{
- {{"_AC0", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC1", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC2", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC3", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC4", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC5", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC6", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC7", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC8", 0, ACPI_RTYPE_INTEGER}},
- {{"_AC9", 0, ACPI_RTYPE_INTEGER}},
- {{"_ADR", 0, ACPI_RTYPE_INTEGER}},
- {{"_AL0", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL1", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL2", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL3", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL4", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL5", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL6", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL7", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL8", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_AL9", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_ALC", 0, ACPI_RTYPE_INTEGER}},
- {{"_ALI", 0, ACPI_RTYPE_INTEGER}},
- {{"_ALP", 0, ACPI_RTYPE_INTEGER}},
- {{"_ALR", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 2 (Ints) */
- {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2,0}, 0,0}},
-
- {{"_ALT", 0, ACPI_RTYPE_INTEGER}},
- {{"_ART", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(rev), n Pkg (2 Ref/11 Int) */
- {{{ACPI_PTYPE2_REV_FIXED,ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER}, 11,0}},
-
- {{"_BBN", 0, ACPI_RTYPE_INTEGER}},
- {{"_BCL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}},
-
- {{"_BCM", 1, 0}},
- {{"_BCT", 1, ACPI_RTYPE_INTEGER}},
- {{"_BDN", 0, ACPI_RTYPE_INTEGER}},
- {{"_BFS", 1, 0}},
- {{"_BIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (9 Int),(4 Str) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, ACPI_RTYPE_STRING}, 4,0}},
-
- {{"_BIX", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int),(4 Str) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, ACPI_RTYPE_STRING}, 4,0}},
-
- {{"_BLT", 3, 0}},
- {{"_BMA", 1, ACPI_RTYPE_INTEGER}},
- {{"_BMC", 1, 0}},
- {{"_BMD", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (5 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5,0}, 0,0}},
-
- {{"_BMS", 1, ACPI_RTYPE_INTEGER}},
- {{"_BQC", 0, ACPI_RTYPE_INTEGER}},
- {{"_BST", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}},
-
- {{"_BTM", 1, ACPI_RTYPE_INTEGER}},
- {{"_BTP", 1, 0}},
- {{"_CBA", 0, ACPI_RTYPE_INTEGER}}, /* See PCI firmware spec 3.0 */
- {{"_CDM", 0, ACPI_RTYPE_INTEGER}},
- {{"_CID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints/Strs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0,0}, 0,0}},
-
- {{"_CRS", 0, ACPI_RTYPE_BUFFER}},
- {{"_CRT", 0, ACPI_RTYPE_INTEGER}},
- {{"_CSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n-1 Int) */
- {{{ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0,0}, 0,0}},
-
- {{"_CST", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */
- {{{ACPI_PTYPE2_PKG_COUNT,ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_INTEGER}, 3,0}},
-
- {{"_DCK", 1, ACPI_RTYPE_INTEGER}},
- {{"_DCS", 0, ACPI_RTYPE_INTEGER}},
- {{"_DDC", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER}},
- {{"_DDN", 0, ACPI_RTYPE_STRING}},
- {{"_DGS", 0, ACPI_RTYPE_INTEGER}},
- {{"_DIS", 0, 0}},
- {{"_DMA", 0, ACPI_RTYPE_BUFFER}},
- {{"_DOD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}},
-
- {{"_DOS", 1, 0}},
- {{"_DSM", 4, ACPI_RTYPE_ALL}}, /* Must return a type, but it can be of any type */
- {{"_DSS", 1, 0}},
- {{"_DSW", 3, 0}},
- {{"_DTI", 1, 0}},
- {{"_EC_", 0, ACPI_RTYPE_INTEGER}},
- {{"_EDL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs)*/
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_EJ0", 1, 0}},
- {{"_EJ1", 1, 0}},
- {{"_EJ2", 1, 0}},
- {{"_EJ3", 1, 0}},
- {{"_EJ4", 1, 0}},
- {{"_EJD", 0, ACPI_RTYPE_STRING}},
- {{"_FDE", 0, ACPI_RTYPE_BUFFER}},
- {{"_FDI", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16,0}, 0,0}},
-
- {{"_FDM", 1, 0}},
- {{"_FIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}},
-
- {{"_FIX", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}},
-
- {{"_FPS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(rev), n Pkg (5 Int) */
- {{{ACPI_PTYPE2_REV_FIXED,ACPI_RTYPE_INTEGER, 5, 0}, 0,0}},
-
- {{"_FSL", 1, 0}},
- {{"_FST", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (3 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3,0}, 0,0}},
-
-
- {{"_GAI", 0, ACPI_RTYPE_INTEGER}},
- {{"_GHL", 0, ACPI_RTYPE_INTEGER}},
- {{"_GLK", 0, ACPI_RTYPE_INTEGER}},
- {{"_GPD", 0, ACPI_RTYPE_INTEGER}},
- {{"_GPE", 0, ACPI_RTYPE_INTEGER}}, /* _GPE method, not _GPE scope */
- {{"_GSB", 0, ACPI_RTYPE_INTEGER}},
- {{"_GTF", 0, ACPI_RTYPE_BUFFER}},
- {{"_GTM", 0, ACPI_RTYPE_BUFFER}},
- {{"_GTS", 1, 0}},
- {{"_HID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}},
- {{"_HOT", 0, ACPI_RTYPE_INTEGER}},
- {{"_HPP", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}},
+ {{"_AC0", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC1", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC2", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC3", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC4", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC5", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC6", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC7", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC8", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AC9", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ADR", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_AEI", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_AL0", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL1", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL2", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL3", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL4", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL5", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL6", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL7", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL8", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_AL9", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_ALC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALI", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ALR", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 (Ints) */
+ PACKAGE_INFO (ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2,0,0,0),
+
+ {{"_ALT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ART", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Ref/11 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER, 11,0),
+
+ {{"_BBN", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BCL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0,0,0),
+
+ {{"_BCM", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BCT", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BDN", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BFS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BIF", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (9 Int),(4 Str) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, ACPI_RTYPE_STRING, 4,0),
+
+ {{"_BIX", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int),(4 Str) */
+ PACKAGE_INFO (ACPI_PTYPE_CUSTOM, ACPI_RTYPE_INTEGER, 16, ACPI_RTYPE_STRING, 4,0),
+
+ {{"_BLT", METHOD_3ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BMA", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BMC", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BMD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (5 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5,0,0,0),
+
+ {{"_BMS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BQC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BST", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0,0,0),
+
+ {{"_BTH", METHOD_1ARGS (ACPI_TYPE_INTEGER), /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_BTM", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_BTP", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_CBA", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* See PCI firmware spec 3.0 */
+
+ {{"_CCA", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* ACPI 5.1 */
+
+ {{"_CDM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_CID", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Strs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0,0,0,0),
+
+ {{"_CLS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3,0,0,0),
+
+ {{"_CPC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Bufs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0,0,0,0),
+
+ {{"_CR3", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_CRS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_CRT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_CSD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n-1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0,0,0,0),
+
+ {{"_CST", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2_PKG_COUNT,ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_INTEGER, 3,0),
+
+ {{"_CWS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_DCK", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_DCS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_DDC", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER)}},
+
+ {{"_DDN", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_STRING)}},
+
+ {{"_DEP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_DGS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_DIS", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DLM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Ref, 0/1 Optional Buf/Ref) */
+ PACKAGE_INFO (ACPI_PTYPE2_FIX_VAR, ACPI_RTYPE_REFERENCE, 1, ACPI_RTYPE_REFERENCE | ACPI_RTYPE_BUFFER, 0,0),
+
+ {{"_DMA", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_DOD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0,0,0),
+
+ {{"_DOS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DSD", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */
+ PACKAGE_INFO (ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_PACKAGE, 1,0),
+
+ {{"_DSM", METHOD_4ARGS (ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_PACKAGE),
+ METHOD_RETURNS (ACPI_RTYPE_ALL)}}, /* Must return a value, but it can be of any type */
+
+ {{"_DSS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DSW", METHOD_3ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_DTI", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EC_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_EDL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs)*/
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_EJ0", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ1", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ2", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ3", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJ4", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_EJD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_STRING)}},
+
+ {{"_ERR", METHOD_3ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_STRING, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* Internal use only, used by ACPICA test suites */
+
+ {{"_EVT", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FDE", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_FDI", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16,0,0,0),
+
+ {{"_FDM", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FIF", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0,0,0),
+
+ {{"_FIT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}}, /* ACPI 6.0 */
+
+ {{"_FIX", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0,0,0),
+
+ {{"_FPS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (5 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2_REV_FIXED,ACPI_RTYPE_INTEGER, 5, 0,0,0),
+
+ {{"_FSL", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_FST", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3,0,0,0),
+
+ {{"_GAI", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GCP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GHL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GLK", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GPD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GPE", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* _GPE method, not _GPE scope */
+
+ {{"_GRT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_GSB", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_GTF", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_GTM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_GTS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_GWS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_HID", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}},
+
+ {{"_HOT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_HPP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0,0,0),
/*
- * For _HPX, a single package is returned, containing a Variable-length number
- * of sub-packages. Each sub-package contains a PCI record setting.
+ * For _HPX, a single package is returned, containing a variable-length number
+ * of subpackages. Each subpackage contains a PCI record setting.
* There are several different type of record settings, of different
* lengths, but all elements of all settings are Integers.
*/
- {{"_HPX", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (var Ints) */
- {{{ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5,0}, 0,0}},
-
- {{"_IFT", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */
- {{"_INI", 0, 0}},
- {{"_IRC", 0, 0}},
- {{"_LCK", 1, 0}},
- {{"_LID", 0, ACPI_RTYPE_INTEGER}},
- {{"_MAT", 0, ACPI_RTYPE_BUFFER}},
- {{"_MBM", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (8 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 8,0}, 0,0}},
-
- {{"_MLS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (2 Str) */
- {{{ACPI_PTYPE2, ACPI_RTYPE_STRING, 2,0}, 0,0}},
-
- {{"_MSG", 1, 0}},
- {{"_MSM", 4, ACPI_RTYPE_INTEGER}},
- {{"_NTT", 0, ACPI_RTYPE_INTEGER}},
- {{"_OFF", 0, 0}},
- {{"_ON_", 0, 0}},
- {{"_OS_", 0, ACPI_RTYPE_STRING}},
- {{"_OSC", 4, ACPI_RTYPE_BUFFER}},
- {{"_OST", 3, 0}},
- {{"_PAI", 1, ACPI_RTYPE_INTEGER}},
- {{"_PCL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PCT", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Buf) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0}, 0,0}},
-
- {{"_PDC", 1, 0}},
- {{"_PDL", 0, ACPI_RTYPE_INTEGER}},
- {{"_PIC", 1, 0}},
- {{"_PIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (3 Int),(3 Str) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, ACPI_RTYPE_STRING}, 3,0}},
-
- {{"_PLD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Bufs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0,0}, 0,0}},
-
- {{"_PMC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (11 Int),(3 Str) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 11, ACPI_RTYPE_STRING}, 3,0}},
-
- {{"_PMD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PMM", 0, ACPI_RTYPE_INTEGER}},
- {{"_PPC", 0, ACPI_RTYPE_INTEGER}},
- {{"_PPE", 0, ACPI_RTYPE_INTEGER}}, /* See dig64 spec */
- {{"_PR0", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PR1", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PR2", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PR3", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PRL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PRS", 0, ACPI_RTYPE_BUFFER}},
+ {{"_HPX", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (var Ints) */
+ PACKAGE_INFO (ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5,0,0,0),
+
+ {{"_HRV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_IFT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */
+
+ {{"_INI", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_IRC", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_LCK", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_LID", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_LPD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2,0,0,0),
+
+ {{"_LPI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (3 Int, n Pkg (10 Int/Buf) */
+ PACKAGE_INFO (ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 3,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER | ACPI_RTYPE_STRING, 10,0),
+
+ {{"_MAT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_MBM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (8 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 8,0,0,0),
+
+ {{"_MLS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Str/1 Buf) */
+ PACKAGE_INFO (ACPI_PTYPE2, ACPI_RTYPE_STRING, 1, ACPI_RTYPE_BUFFER, 1,0),
+
+ {{"_MSG", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_MSM", METHOD_4ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_MTL", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_NTT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_OFF", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_ON_", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_OS_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_STRING)}},
+
+ {{"_OSC", METHOD_4ARGS (ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_BUFFER),
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_OST", METHOD_3ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PAI", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PCL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PCT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0,0,0),
+
+ {{"_PDC", METHOD_1ARGS (ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PDL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PIC", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PIF", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int),(3 Str) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, ACPI_RTYPE_STRING, 3,0),
+
+ {{"_PLD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Bufs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0,0,0,0),
+
+ {{"_PMC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (11 Int),(3 Str) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 11, ACPI_RTYPE_STRING, 3,0),
+
+ {{"_PMD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PMM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PPC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PPE", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* See dig64 spec */
+
+ {{"_PR0", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PR1", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PR2", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PR3", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PRE", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PRL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PRR", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Ref) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_REFERENCE, 1,0,0,0),
+
+ {{"_PRS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
/*
* For _PRT, many BIOSs reverse the 3rd and 4th Package elements (Source
@@ -376,46 +740,94 @@ static const ACPI_PREDEFINED_INFO PredefinedNames[] =
* warning, add the ACPI_RTYPE_REFERENCE type to the 4th element (index 3)
* in the statement below.
*/
- {{"_PRT", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (4): Int,Int,Int/Ref,Int */
- {{{ACPI_PTYPE2_FIXED, 4, ACPI_RTYPE_INTEGER,ACPI_RTYPE_INTEGER},
- ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE,
- ACPI_RTYPE_INTEGER}},
-
- {{"_PRW", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each: Pkg/Int,Int,[Variable-length Refs] (Pkg is Ref/Int) */
- {{{ACPI_PTYPE1_OPTION, 2, ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE,
- ACPI_RTYPE_INTEGER}, ACPI_RTYPE_REFERENCE,0}},
-
- {{"_PS0", 0, 0}},
- {{"_PS1", 0, 0}},
- {{"_PS2", 0, 0}},
- {{"_PS3", 0, 0}},
- {{"_PSC", 0, ACPI_RTYPE_INTEGER}},
- {{"_PSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (5 Int) with count */
- {{{ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER,0,0}, 0,0}},
-
- {{"_PSL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_PSR", 0, ACPI_RTYPE_INTEGER}},
- {{"_PSS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (6 Int) */
- {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6,0}, 0,0}},
-
- {{"_PSV", 0, ACPI_RTYPE_INTEGER}},
- {{"_PSW", 1, 0}},
- {{"_PTC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Buf) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0}, 0,0}},
-
- {{"_PTP", 2, ACPI_RTYPE_INTEGER}},
- {{"_PTS", 1, 0}},
- {{"_PUR", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,0}, 0,0}},
-
- {{"_PXM", 0, ACPI_RTYPE_INTEGER}},
- {{"_REG", 2, 0}},
- {{"_REV", 0, ACPI_RTYPE_INTEGER}},
- {{"_RMV", 0, ACPI_RTYPE_INTEGER}},
- {{"_ROM", 2, ACPI_RTYPE_BUFFER}},
- {{"_RTV", 0, ACPI_RTYPE_INTEGER}},
+ {{"_PRT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (4): Int,Int,Int/Ref,Int */
+ PACKAGE_INFO (ACPI_PTYPE2_FIXED, 4, ACPI_RTYPE_INTEGER, ACPI_RTYPE_INTEGER,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE, ACPI_RTYPE_INTEGER),
+
+ {{"_PRW", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: Pkg/Int,Int,[Variable-length Refs] (Pkg is Ref/Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_OPTION, 2, ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE,
+ ACPI_RTYPE_INTEGER, ACPI_RTYPE_REFERENCE, 0),
+
+ {{"_PS0", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS1", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS2", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PS3", METHOD_0ARGS,
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PSC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (5 Int) with count */
+ PACKAGE_INFO (ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0,0,0,0),
+
+ {{"_PSE", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PSL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_PSR", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (6 Int) */
+ PACKAGE_INFO (ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6,0,0,0),
+
+ {{"_PSV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PSW", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PTC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0,0,0),
+
+ {{"_PTP", METHOD_2ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_PTS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_PUR", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,0,0,0),
+
+ {{"_PXM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_RDI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int, n Pkg (m Ref)) */
+ PACKAGE_INFO (ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 1,
+ ACPI_RTYPE_REFERENCE,0,0),
+
+ {{"_REG", METHOD_2ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_REV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_RMV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_ROM", METHOD_2ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_RST", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_RTV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
/*
* For _S0_ through _S5_, the ACPI spec defines a return Package
@@ -423,106 +835,295 @@ static const ACPI_PREDEFINED_INFO PredefinedNames[] =
* Allow this by making the objects "Variable-length length", but all elements
* must be Integers.
*/
- {{"_S0_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S1_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S2_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S3_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S4_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S5_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}},
-
- {{"_S1D", 0, ACPI_RTYPE_INTEGER}},
- {{"_S2D", 0, ACPI_RTYPE_INTEGER}},
- {{"_S3D", 0, ACPI_RTYPE_INTEGER}},
- {{"_S4D", 0, ACPI_RTYPE_INTEGER}},
- {{"_S0W", 0, ACPI_RTYPE_INTEGER}},
- {{"_S1W", 0, ACPI_RTYPE_INTEGER}},
- {{"_S2W", 0, ACPI_RTYPE_INTEGER}},
- {{"_S3W", 0, ACPI_RTYPE_INTEGER}},
- {{"_S4W", 0, ACPI_RTYPE_INTEGER}},
- {{"_SBS", 0, ACPI_RTYPE_INTEGER}},
- {{"_SCP", 0x13, 0}}, /* Acpi 1.0 allowed 1 arg. Acpi 3.0 expanded to 3 args. Allow both. */
- /* Note: the 3-arg definition may be removed for ACPI 4.0 */
- {{"_SDD", 1, 0}},
- {{"_SEG", 0, ACPI_RTYPE_INTEGER}},
- {{"_SHL", 1, ACPI_RTYPE_INTEGER}},
- {{"_SLI", 0, ACPI_RTYPE_BUFFER}},
- {{"_SPD", 1, ACPI_RTYPE_INTEGER}},
- {{"_SRS", 1, 0}},
- {{"_SRV", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */
- {{"_SST", 1, 0}},
- {{"_STA", 0, ACPI_RTYPE_INTEGER}},
- {{"_STM", 3, 0}},
- {{"_STP", 2, ACPI_RTYPE_INTEGER}},
- {{"_STR", 0, ACPI_RTYPE_BUFFER}},
- {{"_STV", 2, ACPI_RTYPE_INTEGER}},
- {{"_SUN", 0, ACPI_RTYPE_INTEGER}},
- {{"_SWS", 0, ACPI_RTYPE_INTEGER}},
- {{"_TC1", 0, ACPI_RTYPE_INTEGER}},
- {{"_TC2", 0, ACPI_RTYPE_INTEGER}},
- {{"_TDL", 0, ACPI_RTYPE_INTEGER}},
- {{"_TIP", 1, ACPI_RTYPE_INTEGER}},
- {{"_TIV", 1, ACPI_RTYPE_INTEGER}},
- {{"_TMP", 0, ACPI_RTYPE_INTEGER}},
- {{"_TPC", 0, ACPI_RTYPE_INTEGER}},
- {{"_TPT", 1, 0}},
- {{"_TRT", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 2Ref/6Int */
- {{{ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER}, 6, 0}},
-
- {{"_TSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 5Int with count */
- {{{ACPI_PTYPE2_COUNT,ACPI_RTYPE_INTEGER, 5,0}, 0,0}},
-
- {{"_TSP", 0, ACPI_RTYPE_INTEGER}},
- {{"_TSS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 5Int */
- {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5,0}, 0,0}},
-
- {{"_TST", 0, ACPI_RTYPE_INTEGER}},
- {{"_TTS", 1, 0}},
- {{"_TZD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */
- {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}},
-
- {{"_TZM", 0, ACPI_RTYPE_REFERENCE}},
- {{"_TZP", 0, ACPI_RTYPE_INTEGER}},
- {{"_UID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}},
- {{"_UPC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}},
-
- {{"_UPD", 0, ACPI_RTYPE_INTEGER}},
- {{"_UPP", 0, ACPI_RTYPE_INTEGER}},
- {{"_VPO", 0, ACPI_RTYPE_INTEGER}},
+ {{"_S0_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S1_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S2_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S3_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S4_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S5_", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0,0,0),
+
+ {{"_S1D", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S2D", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S3D", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S4D", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S0W", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S1W", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S2W", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S3W", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_S4W", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SBS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SCP", METHOD_1ARGS (ACPI_TYPE_INTEGER) | ARG_COUNT_IS_MINIMUM,
+ METHOD_NO_RETURN_VALUE}}, /* Acpi 1.0 allowed 1 integer arg. Acpi 3.0 expanded to 3 args. Allow both. */
+
+ {{"_SDD", METHOD_1ARGS (ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_SEG", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SHL", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SLI", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_SPD", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SRS", METHOD_1ARGS (ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_SRT", METHOD_1ARGS (ACPI_TYPE_BUFFER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SRV", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */
+
+ {{"_SST", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_STA", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_STM", METHOD_3ARGS (ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_STP", METHOD_2ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_STR", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_STV", METHOD_2ARGS (ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SUB", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_STRING)}},
+
+ {{"_SUN", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_SWS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TC1", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TC2", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TDL", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TFP", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TIP", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TIV", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TMP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TPC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TPT", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_TRT", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 Ref/6 Int */
+ PACKAGE_INFO (ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER, 6, 0),
+
+ {{"_TSD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */
+ PACKAGE_INFO (ACPI_PTYPE2_COUNT,ACPI_RTYPE_INTEGER, 5,0,0,0),
+
+ {{"_TSN", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS (ACPI_RTYPE_REFERENCE)}},
+
+ {{"_TSP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TSS", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int */
+ PACKAGE_INFO (ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5,0,0,0),
+
+ {{"_TST", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_TTS", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_NO_RETURN_VALUE}},
+
+ {{"_TZD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
+ PACKAGE_INFO (ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0,0,0),
+
+ {{"_TZM", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_REFERENCE)}},
+
+ {{"_TZP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_UID", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}},
+
+ {{"_UPC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0,0,0),
+
+ {{"_UPD", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_UPP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
+
+ {{"_VPO", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}},
/* Acpi 1.0 defined _WAK with no return value. Later, it was changed to return a package */
- {{"_WAK", 1, ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE}},
- {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,0}, 0,0}}, /* Fixed-length (2 Int), but is optional */
+ {{"_WAK", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE)}},
+ PACKAGE_INFO (ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,0,0,0), /* Fixed-length (2 Int), but is optional */
/* _WDG/_WED are MS extensions defined by "Windows Instrumentation" */
- {{"_WDG", 0, ACPI_RTYPE_BUFFER}},
- {{"_WED", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER}},
+ {{"_WDG", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_BUFFER)}},
+
+ {{"_WED", METHOD_1ARGS (ACPI_TYPE_INTEGER),
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER)}},
+
+ {{"_WPC", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* ACPI 6.1 */
- {{{0,0,0,0}, 0,0}} /* Table terminator */
+ {{"_WPP", METHOD_0ARGS,
+ METHOD_RETURNS (ACPI_RTYPE_INTEGER)}}, /* ACPI 6.1 */
+
+ PACKAGE_INFO (0,0,0,0,0,0) /* Table terminator */
};
+#else
+extern const ACPI_PREDEFINED_INFO AcpiGbl_PredefinedMethods[];
+#endif
-#if 0
- /* This is an internally implemented control method, no need to check */
- {{"_OSI", 1, ACPI_RTYPE_INTEGER}},
+#if (defined ACPI_CREATE_RESOURCE_TABLE && defined ACPI_APPLICATION)
+/******************************************************************************
+ *
+ * Predefined names for use in Resource Descriptors. These names do not
+ * appear in the global Predefined Name table (since these names never
+ * appear in actual AML byte code, only in the original ASL)
+ *
+ * Note: Used by iASL compiler and AcpiHelp utility only.
+ *
+ *****************************************************************************/
- /* TBD: */
+const ACPI_PREDEFINED_INFO AcpiGbl_ResourceNames[] =
+{
+ {{"_ADR", WIDTH_16 | WIDTH_64, 0}},
+ {{"_ALN", WIDTH_8 | WIDTH_16 | WIDTH_32, 0}},
+ {{"_ASI", WIDTH_8, 0}},
+ {{"_ASZ", WIDTH_8, 0}},
+ {{"_ATT", WIDTH_64, 0}},
+ {{"_BAS", WIDTH_16 | WIDTH_32, 0}},
+ {{"_BM_", WIDTH_1, 0}},
+ {{"_DBT", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_DEC", WIDTH_1, 0}},
+ {{"_DMA", WIDTH_8, 0}},
+ {{"_DPL", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_DRS", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_END", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_FLC", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_GRA", WIDTH_ADDRESS, 0}},
+ {{"_HE_", WIDTH_1, 0}},
+ {{"_INT", WIDTH_16 | WIDTH_32, 0}},
+ {{"_IOR", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_LEN", WIDTH_8 | WIDTH_ADDRESS, 0}},
+ {{"_LIN", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_LL_", WIDTH_1, 0}},
+ {{"_MAF", WIDTH_1, 0}},
+ {{"_MAX", WIDTH_ADDRESS, 0}},
+ {{"_MEM", WIDTH_2, 0}},
+ {{"_MIF", WIDTH_1, 0}},
+ {{"_MIN", WIDTH_ADDRESS, 0}},
+ {{"_MOD", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_MTP", WIDTH_2, 0}},
+ {{"_PAR", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_PHA", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_PIN", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_PPI", WIDTH_8, 0}}, /* Acpi 5.0 */
+ {{"_POL", WIDTH_1 | WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_RBO", WIDTH_8, 0}},
+ {{"_RBW", WIDTH_8, 0}},
+ {{"_RNG", WIDTH_1, 0}},
+ {{"_RT_", WIDTH_8, 0}}, /* Acpi 3.0 */
+ {{"_RW_", WIDTH_1, 0}},
+ {{"_RXL", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_SHR", WIDTH_2, 0}},
+ {{"_SIZ", WIDTH_2, 0}},
+ {{"_SLV", WIDTH_1, 0}}, /* Acpi 5.0 */
+ {{"_SPE", WIDTH_32, 0}}, /* Acpi 5.0 */
+ {{"_STB", WIDTH_2, 0}}, /* Acpi 5.0 */
+ {{"_TRA", WIDTH_ADDRESS, 0}},
+ {{"_TRS", WIDTH_1, 0}},
+ {{"_TSF", WIDTH_8, 0}}, /* Acpi 3.0 */
+ {{"_TTP", WIDTH_1, 0}},
+ {{"_TXL", WIDTH_16, 0}}, /* Acpi 5.0 */
+ {{"_TYP", WIDTH_2 | WIDTH_16, 0}},
+ {{"_VEN", VARIABLE_DATA, 0}}, /* Acpi 5.0 */
+ PACKAGE_INFO (0,0,0,0,0,0) /* Table terminator */
+};
- _PRT - currently ignore reversed entries. Attempt to fix here?
- Think about possibly fixing package elements like _BIF, etc.
-#endif
+const ACPI_PREDEFINED_INFO AcpiGbl_ScopeNames[] = {
+ {{"_GPE", 0, 0}},
+ {{"_PR_", 0, 0}},
+ {{"_SB_", 0, 0}},
+ {{"_SI_", 0, 0}},
+ {{"_TZ_", 0, 0}},
+ PACKAGE_INFO (0,0,0,0,0,0) /* Table terminator */
+};
+#else
+extern const ACPI_PREDEFINED_INFO AcpiGbl_ResourceNames[];
#endif
+
#endif
diff --git a/usr/src/uts/intel/sys/acpi/acresrc.h b/usr/src/uts/intel/sys/acpi/acresrc.h
index 00c4bb2530..1d077caac0 100644
--- a/usr/src/uts/intel/sys/acpi/acresrc.h
+++ b/usr/src/uts/intel/sys/acpi/acresrc.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -75,28 +75,42 @@ typedef const struct acpi_rsconvert_info
/* Resource conversion opcodes */
-#define ACPI_RSC_INITGET 0
-#define ACPI_RSC_INITSET 1
-#define ACPI_RSC_FLAGINIT 2
-#define ACPI_RSC_1BITFLAG 3
-#define ACPI_RSC_2BITFLAG 4
-#define ACPI_RSC_COUNT 5
-#define ACPI_RSC_COUNT16 6
-#define ACPI_RSC_LENGTH 7
-#define ACPI_RSC_MOVE8 8
-#define ACPI_RSC_MOVE16 9
-#define ACPI_RSC_MOVE32 10
-#define ACPI_RSC_MOVE64 11
-#define ACPI_RSC_SET8 12
-#define ACPI_RSC_DATA8 13
-#define ACPI_RSC_ADDRESS 14
-#define ACPI_RSC_SOURCE 15
-#define ACPI_RSC_SOURCEX 16
-#define ACPI_RSC_BITMASK 17
-#define ACPI_RSC_BITMASK16 18
-#define ACPI_RSC_EXIT_NE 19
-#define ACPI_RSC_EXIT_LE 20
-#define ACPI_RSC_EXIT_EQ 21
+typedef enum
+{
+ ACPI_RSC_INITGET = 0,
+ ACPI_RSC_INITSET,
+ ACPI_RSC_FLAGINIT,
+ ACPI_RSC_1BITFLAG,
+ ACPI_RSC_2BITFLAG,
+ ACPI_RSC_3BITFLAG,
+ ACPI_RSC_ADDRESS,
+ ACPI_RSC_BITMASK,
+ ACPI_RSC_BITMASK16,
+ ACPI_RSC_COUNT,
+ ACPI_RSC_COUNT16,
+ ACPI_RSC_COUNT_GPIO_PIN,
+ ACPI_RSC_COUNT_GPIO_RES,
+ ACPI_RSC_COUNT_GPIO_VEN,
+ ACPI_RSC_COUNT_SERIAL_RES,
+ ACPI_RSC_COUNT_SERIAL_VEN,
+ ACPI_RSC_DATA8,
+ ACPI_RSC_EXIT_EQ,
+ ACPI_RSC_EXIT_LE,
+ ACPI_RSC_EXIT_NE,
+ ACPI_RSC_LENGTH,
+ ACPI_RSC_MOVE_GPIO_PIN,
+ ACPI_RSC_MOVE_GPIO_RES,
+ ACPI_RSC_MOVE_SERIAL_RES,
+ ACPI_RSC_MOVE_SERIAL_VEN,
+ ACPI_RSC_MOVE8,
+ ACPI_RSC_MOVE16,
+ ACPI_RSC_MOVE32,
+ ACPI_RSC_MOVE64,
+ ACPI_RSC_SET8,
+ ACPI_RSC_SOURCE,
+ ACPI_RSC_SOURCEX
+
+} ACPI_RSCONVERT_OPCODES;
/* Resource Conversion sub-opcodes */
@@ -109,31 +123,41 @@ typedef const struct acpi_rsconvert_info
#define AML_OFFSET(f) (UINT8) ACPI_OFFSET (AML_RESOURCE,f)
+/*
+ * Individual entry for the resource dump tables
+ */
typedef const struct acpi_rsdump_info
{
UINT8 Opcode;
UINT8 Offset;
- char *Name;
+ const char *Name;
const char **Pointer;
} ACPI_RSDUMP_INFO;
/* Values for the Opcode field above */
-#define ACPI_RSD_TITLE 0
-#define ACPI_RSD_LITERAL 1
-#define ACPI_RSD_STRING 2
-#define ACPI_RSD_UINT8 3
-#define ACPI_RSD_UINT16 4
-#define ACPI_RSD_UINT32 5
-#define ACPI_RSD_UINT64 6
-#define ACPI_RSD_1BITFLAG 7
-#define ACPI_RSD_2BITFLAG 8
-#define ACPI_RSD_SHORTLIST 9
-#define ACPI_RSD_LONGLIST 10
-#define ACPI_RSD_DWORDLIST 11
-#define ACPI_RSD_ADDRESS 12
-#define ACPI_RSD_SOURCE 13
+typedef enum
+{
+ ACPI_RSD_TITLE = 0,
+ ACPI_RSD_1BITFLAG,
+ ACPI_RSD_2BITFLAG,
+ ACPI_RSD_3BITFLAG,
+ ACPI_RSD_ADDRESS,
+ ACPI_RSD_DWORDLIST,
+ ACPI_RSD_LITERAL,
+ ACPI_RSD_LONGLIST,
+ ACPI_RSD_SHORTLIST,
+ ACPI_RSD_SHORTLISTX,
+ ACPI_RSD_SOURCE,
+ ACPI_RSD_STRING,
+ ACPI_RSD_UINT8,
+ ACPI_RSD_UINT16,
+ ACPI_RSD_UINT32,
+ ACPI_RSD_UINT64,
+ ACPI_RSD_WORDLIST
+
+} ACPI_RSDUMP_OPCODES;
/* restore default alignment */
@@ -143,13 +167,16 @@ typedef const struct acpi_rsdump_info
/* Resource tables indexed by internal resource type */
extern const UINT8 AcpiGbl_AmlResourceSizes[];
+extern const UINT8 AcpiGbl_AmlResourceSerialBusSizes[];
extern ACPI_RSCONVERT_INFO *AcpiGbl_SetResourceDispatch[];
/* Resource tables indexed by raw AML resource descriptor type */
extern const UINT8 AcpiGbl_ResourceStructSizes[];
+extern const UINT8 AcpiGbl_ResourceStructSerialBusSizes[];
extern ACPI_RSCONVERT_INFO *AcpiGbl_GetResourceDispatch[];
+extern ACPI_RSCONVERT_INFO *AcpiGbl_ConvertResourceSerialBusDispatch[];
typedef struct acpi_vendor_walk_info
{
@@ -170,7 +197,7 @@ AcpiRsCreateResourceList (
ACPI_STATUS
AcpiRsCreateAmlResources (
- ACPI_RESOURCE *LinkedListBuffer,
+ ACPI_BUFFER *ResourceList,
ACPI_BUFFER *OutputBuffer);
ACPI_STATUS
@@ -200,7 +227,7 @@ AcpiRsGetPrsMethodData (
ACPI_STATUS
AcpiRsGetMethodData (
ACPI_HANDLE Handle,
- char *Path,
+ const char *Path,
ACPI_BUFFER *RetBuffer);
ACPI_STATUS
@@ -208,6 +235,10 @@ AcpiRsSetSrsMethodData (
ACPI_NAMESPACE_NODE *Node,
ACPI_BUFFER *RetBuffer);
+ACPI_STATUS
+AcpiRsGetAeiMethodData (
+ ACPI_NAMESPACE_NODE *Node,
+ ACPI_BUFFER *RetBuffer);
/*
* rscalc
@@ -220,7 +251,8 @@ AcpiRsGetListLength (
ACPI_STATUS
AcpiRsGetAmlLength (
- ACPI_RESOURCE *LinkedListBuffer,
+ ACPI_RESOURCE *ResourceList,
+ ACPI_SIZE ResourceListSize,
ACPI_SIZE *SizeNeeded);
ACPI_STATUS
@@ -234,7 +266,7 @@ AcpiRsConvertAmlToResources (
UINT32 Length,
UINT32 Offset,
UINT8 ResourceIndex,
- void *Context);
+ void **Context);
ACPI_STATUS
AcpiRsConvertResourcesToAml (
@@ -320,8 +352,9 @@ AcpiRsSetResourceLength (
/*
- * rsdump
+ * rsdump - Debugger support
*/
+#ifdef ACPI_DEBUGGER
void
AcpiRsDumpResourceList (
ACPI_RESOURCE *Resource);
@@ -329,6 +362,7 @@ AcpiRsDumpResourceList (
void
AcpiRsDumpIrqList (
UINT8 *RouteTable);
+#endif
/*
@@ -348,6 +382,11 @@ extern ACPI_RSCONVERT_INFO AcpiRsConvertAddress16[];
extern ACPI_RSCONVERT_INFO AcpiRsConvertExtIrq[];
extern ACPI_RSCONVERT_INFO AcpiRsConvertAddress64[];
extern ACPI_RSCONVERT_INFO AcpiRsConvertExtAddress64[];
+extern ACPI_RSCONVERT_INFO AcpiRsConvertGpio[];
+extern ACPI_RSCONVERT_INFO AcpiRsConvertFixedDma[];
+extern ACPI_RSCONVERT_INFO AcpiRsConvertI2cSerialBus[];
+extern ACPI_RSCONVERT_INFO AcpiRsConvertSpiSerialBus[];
+extern ACPI_RSCONVERT_INFO AcpiRsConvertUartSerialBus[];
/* These resources require separate get/set tables */
@@ -366,20 +405,24 @@ extern ACPI_RSCONVERT_INFO AcpiRsSetVendor[];
* rsinfo
*/
extern ACPI_RSDUMP_INFO *AcpiGbl_DumpResourceDispatch[];
+extern ACPI_RSDUMP_INFO *AcpiGbl_DumpSerialBusDispatch[];
/*
- * rsdump
+ * rsdumpinfo
*/
extern ACPI_RSDUMP_INFO AcpiRsDumpIrq[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpPrt[];
extern ACPI_RSDUMP_INFO AcpiRsDumpDma[];
extern ACPI_RSDUMP_INFO AcpiRsDumpStartDpf[];
extern ACPI_RSDUMP_INFO AcpiRsDumpEndDpf[];
extern ACPI_RSDUMP_INFO AcpiRsDumpIo[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpIoFlags[];
extern ACPI_RSDUMP_INFO AcpiRsDumpFixedIo[];
extern ACPI_RSDUMP_INFO AcpiRsDumpVendor[];
extern ACPI_RSDUMP_INFO AcpiRsDumpEndTag[];
extern ACPI_RSDUMP_INFO AcpiRsDumpMemory24[];
extern ACPI_RSDUMP_INFO AcpiRsDumpMemory32[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpMemoryFlags[];
extern ACPI_RSDUMP_INFO AcpiRsDumpFixedMemory32[];
extern ACPI_RSDUMP_INFO AcpiRsDumpAddress16[];
extern ACPI_RSDUMP_INFO AcpiRsDumpAddress32[];
@@ -387,6 +430,13 @@ extern ACPI_RSDUMP_INFO AcpiRsDumpAddress64[];
extern ACPI_RSDUMP_INFO AcpiRsDumpExtAddress64[];
extern ACPI_RSDUMP_INFO AcpiRsDumpExtIrq[];
extern ACPI_RSDUMP_INFO AcpiRsDumpGenericReg[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpGpio[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpFixedDma[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpCommonSerialBus[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpI2cSerialBus[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpSpiSerialBus[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpUartSerialBus[];
+extern ACPI_RSDUMP_INFO AcpiRsDumpGeneralFlags[];
#endif
#endif /* __ACRESRC_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acrestyp.h b/usr/src/uts/intel/sys/acpi/acrestyp.h
index 03e7e2ce08..72f0d16310 100644
--- a/usr/src/uts/intel/sys/acpi/acrestyp.h
+++ b/usr/src/uts/intel/sys/acpi/acrestyp.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -62,11 +62,14 @@ typedef UINT32 ACPI_RSDESC_SIZE; /* Max Resource Descr
#define ACPI_WRITE_COMBINING_MEMORY (UINT8) 0x02
#define ACPI_PREFETCHABLE_MEMORY (UINT8) 0x03
+/*! [Begin] no source code translation */
/*
* IO Attributes
* The ISA IO ranges are: n000-n0FFh, n400-n4FFh, n800-n8FFh, nC00-nCFFh.
* The non-ISA IO ranges are: n100-n3FFh, n500-n7FFh, n900-nBFFh, nCD0-nFFFh.
*/
+/*! [End] no source code translation !*/
+
#define ACPI_NON_ISA_ONLY_RANGES (UINT8) 0x01
#define ACPI_ISA_ONLY_RANGES (UINT8) 0x02
#define ACPI_ENTIRE_RANGE (ACPI_NON_ISA_ONLY_RANGES | ACPI_ISA_ONLY_RANGES)
@@ -82,17 +85,30 @@ typedef UINT32 ACPI_RSDESC_SIZE; /* Max Resource Descr
#define ACPI_DECODE_16 (UINT8) 0x01 /* 16-bit IO address decode */
/*
- * IRQ Attributes
+ * Interrupt attributes - used in multiple descriptors
*/
+
+/* Triggering */
+
#define ACPI_LEVEL_SENSITIVE (UINT8) 0x00
#define ACPI_EDGE_SENSITIVE (UINT8) 0x01
+/* Polarity */
+
#define ACPI_ACTIVE_HIGH (UINT8) 0x00
#define ACPI_ACTIVE_LOW (UINT8) 0x01
+#define ACPI_ACTIVE_BOTH (UINT8) 0x02
+
+/* Sharing */
#define ACPI_EXCLUSIVE (UINT8) 0x00
#define ACPI_SHARED (UINT8) 0x01
+/* Wake */
+
+#define ACPI_NOT_WAKE_CAPABLE (UINT8) 0x00
+#define ACPI_WAKE_CAPABLE (UINT8) 0x01
+
/*
* DMA Attributes
*/
@@ -128,6 +144,8 @@ typedef UINT32 ACPI_RSDESC_SIZE; /* Max Resource Descr
#define ACPI_POS_DECODE (UINT8) 0x00
#define ACPI_SUB_DECODE (UINT8) 0x01
+/* Producer/Consumer */
+
#define ACPI_PRODUCER (UINT8) 0x00
#define ACPI_CONSUMER (UINT8) 0x01
@@ -162,12 +180,13 @@ typedef struct acpi_resource_irq
UINT8 Triggering;
UINT8 Polarity;
UINT8 Sharable;
+ UINT8 WakeCapable;
UINT8 InterruptCount;
UINT8 Interrupts[1];
} ACPI_RESOURCE_IRQ;
-typedef struct ACPI_RESOURCE_DMA
+typedef struct acpi_resource_dma
{
UINT8 Type;
UINT8 BusMaster;
@@ -209,6 +228,24 @@ typedef struct acpi_resource_fixed_io
} ACPI_RESOURCE_FIXED_IO;
+typedef struct acpi_resource_fixed_dma
+{
+ UINT16 RequestLines;
+ UINT16 Channels;
+ UINT8 Width;
+
+} ACPI_RESOURCE_FIXED_DMA;
+
+/* Values for Width field above */
+
+#define ACPI_DMA_WIDTH8 0
+#define ACPI_DMA_WIDTH16 1
+#define ACPI_DMA_WIDTH32 2
+#define ACPI_DMA_WIDTH64 3
+#define ACPI_DMA_WIDTH128 4
+#define ACPI_DMA_WIDTH256 5
+
+
typedef struct acpi_resource_vendor
{
UINT16 ByteLength;
@@ -308,6 +345,36 @@ typedef struct acpi_resource_source
UINT8 MaxAddressFixed; \
ACPI_RESOURCE_ATTRIBUTE Info;
+typedef struct acpi_address16_attribute
+{
+ UINT16 Granularity;
+ UINT16 Minimum;
+ UINT16 Maximum;
+ UINT16 TranslationOffset;
+ UINT16 AddressLength;
+
+} ACPI_ADDRESS16_ATTRIBUTE;
+
+typedef struct acpi_address32_attribute
+{
+ UINT32 Granularity;
+ UINT32 Minimum;
+ UINT32 Maximum;
+ UINT32 TranslationOffset;
+ UINT32 AddressLength;
+
+} ACPI_ADDRESS32_ATTRIBUTE;
+
+typedef struct acpi_address64_attribute
+{
+ UINT64 Granularity;
+ UINT64 Minimum;
+ UINT64 Maximum;
+ UINT64 TranslationOffset;
+ UINT64 AddressLength;
+
+} ACPI_ADDRESS64_ATTRIBUTE;
+
typedef struct acpi_resource_address
{
ACPI_RESOURCE_ADDRESS_COMMON
@@ -317,11 +384,7 @@ typedef struct acpi_resource_address
typedef struct acpi_resource_address16
{
ACPI_RESOURCE_ADDRESS_COMMON
- UINT16 Granularity;
- UINT16 Minimum;
- UINT16 Maximum;
- UINT16 TranslationOffset;
- UINT16 AddressLength;
+ ACPI_ADDRESS16_ATTRIBUTE Address;
ACPI_RESOURCE_SOURCE ResourceSource;
} ACPI_RESOURCE_ADDRESS16;
@@ -329,11 +392,7 @@ typedef struct acpi_resource_address16
typedef struct acpi_resource_address32
{
ACPI_RESOURCE_ADDRESS_COMMON
- UINT32 Granularity;
- UINT32 Minimum;
- UINT32 Maximum;
- UINT32 TranslationOffset;
- UINT32 AddressLength;
+ ACPI_ADDRESS32_ATTRIBUTE Address;
ACPI_RESOURCE_SOURCE ResourceSource;
} ACPI_RESOURCE_ADDRESS32;
@@ -341,11 +400,7 @@ typedef struct acpi_resource_address32
typedef struct acpi_resource_address64
{
ACPI_RESOURCE_ADDRESS_COMMON
- UINT64 Granularity;
- UINT64 Minimum;
- UINT64 Maximum;
- UINT64 TranslationOffset;
- UINT64 AddressLength;
+ ACPI_ADDRESS64_ATTRIBUTE Address;
ACPI_RESOURCE_SOURCE ResourceSource;
} ACPI_RESOURCE_ADDRESS64;
@@ -354,11 +409,7 @@ typedef struct acpi_resource_extended_address64
{
ACPI_RESOURCE_ADDRESS_COMMON
UINT8 RevisionID;
- UINT64 Granularity;
- UINT64 Minimum;
- UINT64 Maximum;
- UINT64 TranslationOffset;
- UINT64 AddressLength;
+ ACPI_ADDRESS64_ATTRIBUTE Address;
UINT64 TypeSpecific;
} ACPI_RESOURCE_EXTENDED_ADDRESS64;
@@ -369,6 +420,7 @@ typedef struct acpi_resource_extended_irq
UINT8 Triggering;
UINT8 Polarity;
UINT8 Sharable;
+ UINT8 WakeCapable;
UINT8 InterruptCount;
ACPI_RESOURCE_SOURCE ResourceSource;
UINT32 Interrupts[1];
@@ -385,6 +437,186 @@ typedef struct acpi_resource_generic_register
} ACPI_RESOURCE_GENERIC_REGISTER;
+typedef struct acpi_resource_gpio
+{
+ UINT8 RevisionId;
+ UINT8 ConnectionType;
+ UINT8 ProducerConsumer; /* For values, see Producer/Consumer above */
+ UINT8 PinConfig;
+ UINT8 Sharable; /* For values, see Interrupt Attributes above */
+ UINT8 WakeCapable; /* For values, see Interrupt Attributes above */
+ UINT8 IoRestriction;
+ UINT8 Triggering; /* For values, see Interrupt Attributes above */
+ UINT8 Polarity; /* For values, see Interrupt Attributes above */
+ UINT16 DriveStrength;
+ UINT16 DebounceTimeout;
+ UINT16 PinTableLength;
+ UINT16 VendorLength;
+ ACPI_RESOURCE_SOURCE ResourceSource;
+ UINT16 *PinTable;
+ UINT8 *VendorData;
+
+} ACPI_RESOURCE_GPIO;
+
+/* Values for GPIO ConnectionType field above */
+
+#define ACPI_RESOURCE_GPIO_TYPE_INT 0
+#define ACPI_RESOURCE_GPIO_TYPE_IO 1
+
+/* Values for PinConfig field above */
+
+#define ACPI_PIN_CONFIG_DEFAULT 0
+#define ACPI_PIN_CONFIG_PULLUP 1
+#define ACPI_PIN_CONFIG_PULLDOWN 2
+#define ACPI_PIN_CONFIG_NOPULL 3
+
+/* Values for IoRestriction field above */
+
+#define ACPI_IO_RESTRICT_NONE 0
+#define ACPI_IO_RESTRICT_INPUT 1
+#define ACPI_IO_RESTRICT_OUTPUT 2
+#define ACPI_IO_RESTRICT_NONE_PRESERVE 3
+
+
+/* Common structure for I2C, SPI, and UART serial descriptors */
+
+#define ACPI_RESOURCE_SERIAL_COMMON \
+ UINT8 RevisionId; \
+ UINT8 Type; \
+ UINT8 ProducerConsumer; /* For values, see Producer/Consumer above */\
+ UINT8 SlaveMode; \
+ UINT8 ConnectionSharing; \
+ UINT8 TypeRevisionId; \
+ UINT16 TypeDataLength; \
+ UINT16 VendorLength; \
+ ACPI_RESOURCE_SOURCE ResourceSource; \
+ UINT8 *VendorData;
+
+typedef struct acpi_resource_common_serialbus
+{
+ ACPI_RESOURCE_SERIAL_COMMON
+
+} ACPI_RESOURCE_COMMON_SERIALBUS;
+
+/* Values for the Type field above */
+
+#define ACPI_RESOURCE_SERIAL_TYPE_I2C 1
+#define ACPI_RESOURCE_SERIAL_TYPE_SPI 2
+#define ACPI_RESOURCE_SERIAL_TYPE_UART 3
+
+/* Values for SlaveMode field above */
+
+#define ACPI_CONTROLLER_INITIATED 0
+#define ACPI_DEVICE_INITIATED 1
+
+
+typedef struct acpi_resource_i2c_serialbus
+{
+ ACPI_RESOURCE_SERIAL_COMMON
+ UINT8 AccessMode;
+ UINT16 SlaveAddress;
+ UINT32 ConnectionSpeed;
+
+} ACPI_RESOURCE_I2C_SERIALBUS;
+
+/* Values for AccessMode field above */
+
+#define ACPI_I2C_7BIT_MODE 0
+#define ACPI_I2C_10BIT_MODE 1
+
+
+typedef struct acpi_resource_spi_serialbus
+{
+ ACPI_RESOURCE_SERIAL_COMMON
+ UINT8 WireMode;
+ UINT8 DevicePolarity;
+ UINT8 DataBitLength;
+ UINT8 ClockPhase;
+ UINT8 ClockPolarity;
+ UINT16 DeviceSelection;
+ UINT32 ConnectionSpeed;
+
+} ACPI_RESOURCE_SPI_SERIALBUS;
+
+/* Values for WireMode field above */
+
+#define ACPI_SPI_4WIRE_MODE 0
+#define ACPI_SPI_3WIRE_MODE 1
+
+/* Values for DevicePolarity field above */
+
+#define ACPI_SPI_ACTIVE_LOW 0
+#define ACPI_SPI_ACTIVE_HIGH 1
+
+/* Values for ClockPhase field above */
+
+#define ACPI_SPI_FIRST_PHASE 0
+#define ACPI_SPI_SECOND_PHASE 1
+
+/* Values for ClockPolarity field above */
+
+#define ACPI_SPI_START_LOW 0
+#define ACPI_SPI_START_HIGH 1
+
+
+typedef struct acpi_resource_uart_serialbus
+{
+ ACPI_RESOURCE_SERIAL_COMMON
+ UINT8 Endian;
+ UINT8 DataBits;
+ UINT8 StopBits;
+ UINT8 FlowControl;
+ UINT8 Parity;
+ UINT8 LinesEnabled;
+ UINT16 RxFifoSize;
+ UINT16 TxFifoSize;
+ UINT32 DefaultBaudRate;
+
+} ACPI_RESOURCE_UART_SERIALBUS;
+
+/* Values for Endian field above */
+
+#define ACPI_UART_LITTLE_ENDIAN 0
+#define ACPI_UART_BIG_ENDIAN 1
+
+/* Values for DataBits field above */
+
+#define ACPI_UART_5_DATA_BITS 0
+#define ACPI_UART_6_DATA_BITS 1
+#define ACPI_UART_7_DATA_BITS 2
+#define ACPI_UART_8_DATA_BITS 3
+#define ACPI_UART_9_DATA_BITS 4
+
+/* Values for StopBits field above */
+
+#define ACPI_UART_NO_STOP_BITS 0
+#define ACPI_UART_1_STOP_BIT 1
+#define ACPI_UART_1P5_STOP_BITS 2
+#define ACPI_UART_2_STOP_BITS 3
+
+/* Values for FlowControl field above */
+
+#define ACPI_UART_FLOW_CONTROL_NONE 0
+#define ACPI_UART_FLOW_CONTROL_HW 1
+#define ACPI_UART_FLOW_CONTROL_XON_XOFF 2
+
+/* Values for Parity field above */
+
+#define ACPI_UART_PARITY_NONE 0
+#define ACPI_UART_PARITY_EVEN 1
+#define ACPI_UART_PARITY_ODD 2
+#define ACPI_UART_PARITY_MARK 3
+#define ACPI_UART_PARITY_SPACE 4
+
+/* Values for LinesEnabled bitfield above */
+
+#define ACPI_UART_CARRIER_DETECT (1<<2)
+#define ACPI_UART_RING_INDICATOR (1<<3)
+#define ACPI_UART_DATA_SET_READY (1<<4)
+#define ACPI_UART_DATA_TERMINAL_READY (1<<5)
+#define ACPI_UART_CLEAR_TO_SEND (1<<6)
+#define ACPI_UART_REQUEST_TO_SEND (1<<7)
+
/* ACPI_RESOURCE_TYPEs */
@@ -405,7 +637,10 @@ typedef struct acpi_resource_generic_register
#define ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 14 /* ACPI 3.0 */
#define ACPI_RESOURCE_TYPE_EXTENDED_IRQ 15
#define ACPI_RESOURCE_TYPE_GENERIC_REGISTER 16
-#define ACPI_RESOURCE_TYPE_MAX 16
+#define ACPI_RESOURCE_TYPE_GPIO 17 /* ACPI 5.0 */
+#define ACPI_RESOURCE_TYPE_FIXED_DMA 18 /* ACPI 5.0 */
+#define ACPI_RESOURCE_TYPE_SERIAL_BUS 19 /* ACPI 5.0 */
+#define ACPI_RESOURCE_TYPE_MAX 19
/* Master union for resource descriptors */
@@ -416,6 +651,7 @@ typedef union acpi_resource_data
ACPI_RESOURCE_START_DEPENDENT StartDpf;
ACPI_RESOURCE_IO Io;
ACPI_RESOURCE_FIXED_IO FixedIo;
+ ACPI_RESOURCE_FIXED_DMA FixedDma;
ACPI_RESOURCE_VENDOR Vendor;
ACPI_RESOURCE_VENDOR_TYPED VendorTyped;
ACPI_RESOURCE_END_TAG EndTag;
@@ -428,6 +664,11 @@ typedef union acpi_resource_data
ACPI_RESOURCE_EXTENDED_ADDRESS64 ExtAddress64;
ACPI_RESOURCE_EXTENDED_IRQ ExtendedIrq;
ACPI_RESOURCE_GENERIC_REGISTER GenericReg;
+ ACPI_RESOURCE_GPIO Gpio;
+ ACPI_RESOURCE_I2C_SERIALBUS I2cSerialBus;
+ ACPI_RESOURCE_SPI_SERIALBUS SpiSerialBus;
+ ACPI_RESOURCE_UART_SERIALBUS UartSerialBus;
+ ACPI_RESOURCE_COMMON_SERIALBUS CommonSerialBus;
/* Common fields */
@@ -455,7 +696,10 @@ typedef struct acpi_resource
#define ACPI_RS_SIZE_MIN (UINT32) ACPI_ROUND_UP_TO_NATIVE_WORD (12)
#define ACPI_RS_SIZE(Type) (UINT32) (ACPI_RS_SIZE_NO_DATA + sizeof (Type))
-#define ACPI_NEXT_RESOURCE(Res) (ACPI_RESOURCE *)((UINT8 *) Res + Res->Length)
+/* Macro for walking resource templates with multiple descriptors */
+
+#define ACPI_NEXT_RESOURCE(Res) \
+ ACPI_ADD_PTR (ACPI_RESOURCE, (Res), (Res)->Length)
typedef struct acpi_pci_routing_table
@@ -469,4 +713,3 @@ typedef struct acpi_pci_routing_table
} ACPI_PCI_ROUTING_TABLE;
#endif /* __ACRESTYP_H__ */
-
diff --git a/usr/src/uts/intel/sys/acpi/acstruct.h b/usr/src/uts/intel/sys/acpi/acstruct.h
index ff588f5234..f7538aded9 100644
--- a/usr/src/uts/intel/sys/acpi/acstruct.h
+++ b/usr/src/uts/intel/sys/acpi/acstruct.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,7 +54,7 @@
/*
- * Walk state - current state of a parse tree walk. Used for both a leisurely
+ * Walk state - current state of a parse tree walk. Used for both a leisurely
* stroll through the tree (for whatever reason), and for control method
* execution.
*/
@@ -69,11 +69,6 @@
#define ACPI_WALK_METHOD 0x01
#define ACPI_WALK_METHOD_RESTART 0x02
-/* Flags for iASL compiler only */
-
-#define ACPI_WALK_CONST_REQUIRED 0x10
-#define ACPI_WALK_CONST_OPTIONAL 0x20
-
typedef struct acpi_walk_state
{
@@ -90,9 +85,10 @@ typedef struct acpi_walk_state
UINT8 ReturnUsed;
UINT8 ScopeDepth;
UINT8 PassNumber; /* Parse pass during table load */
+ BOOLEAN NamespaceOverride; /* Override existing objects */
UINT8 ResultSize; /* Total elements for the result stack */
UINT8 ResultCount; /* Current number of occupied elements of result stack */
- UINT32 AmlOffset;
+ UINT8 *Aml;
UINT32 ArgTypes;
UINT32 MethodBreakpoint; /* For single stepping */
UINT32 UserBreakpoint; /* User AML breakpoint */
@@ -139,6 +135,9 @@ typedef struct acpi_init_walk_info
UINT32 TableIndex;
UINT32 ObjectCount;
UINT32 MethodCount;
+ UINT32 SerialMethodCount;
+ UINT32 NonSerialMethodCount;
+ UINT32 SerializedMethodCount;
UINT32 DeviceCount;
UINT32 OpRegionCount;
UINT32 FieldCount;
@@ -195,27 +194,43 @@ typedef union acpi_aml_operands
/*
- * Structure used to pass object evaluation parameters.
+ * Structure used to pass object evaluation information and parameters.
* Purpose is to reduce CPU stack use.
*/
typedef struct acpi_evaluate_info
{
- ACPI_NAMESPACE_NODE *PrefixNode;
- char *Pathname;
- ACPI_OPERAND_OBJECT *ObjDesc;
- ACPI_OPERAND_OBJECT **Parameters;
- ACPI_NAMESPACE_NODE *ResolvedNode;
- ACPI_OPERAND_OBJECT *ReturnObject;
- UINT8 ParamCount;
- UINT8 PassNumber;
- UINT8 ReturnObjectType;
- UINT8 Flags;
+ /* The first 3 elements are passed by the caller to AcpiNsEvaluate */
+
+ ACPI_NAMESPACE_NODE *PrefixNode; /* Input: starting node */
+ const char *RelativePathname; /* Input: path relative to PrefixNode */
+ ACPI_OPERAND_OBJECT **Parameters; /* Input: argument list */
+
+ ACPI_NAMESPACE_NODE *Node; /* Resolved node (PrefixNode:RelativePathname) */
+ ACPI_OPERAND_OBJECT *ObjDesc; /* Object attached to the resolved node */
+ char *FullPathname; /* Full pathname of the resolved node */
+
+ const ACPI_PREDEFINED_INFO *Predefined; /* Used if Node is a predefined name */
+ ACPI_OPERAND_OBJECT *ReturnObject; /* Object returned from the evaluation */
+ union acpi_operand_object *ParentPackage; /* Used if return object is a Package */
+
+ UINT32 ReturnFlags; /* Used for return value analysis */
+ UINT32 ReturnBtype; /* Bitmapped type of the returned object */
+ UINT16 ParamCount; /* Count of the input argument list */
+ UINT8 PassNumber; /* Parser pass number */
+ UINT8 ReturnObjectType; /* Object type of the returned object */
+ UINT8 NodeFlags; /* Same as Node->Flags */
+ UINT8 Flags; /* General flags */
} ACPI_EVALUATE_INFO;
/* Values for Flags above */
-#define ACPI_IGNORE_RETURN_VALUE 1
+#define ACPI_IGNORE_RETURN_VALUE 1
+
+/* Defines for ReturnFlags field above */
+
+#define ACPI_OBJECT_REPAIRED 1
+#define ACPI_OBJECT_WRAPPED 2
/* Info used by AcpiNsInitializeDevices */
diff --git a/usr/src/uts/intel/sys/acpi/actables.h b/usr/src/uts/intel/sys/acpi/actables.h
index 6aad862bc9..e1a4a6eb63 100644
--- a/usr/src/uts/intel/sys/acpi/actables.h
+++ b/usr/src/uts/intel/sys/acpi/actables.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,11 +50,72 @@ AcpiAllocateRootTable (
UINT32 InitialTableCount);
/*
+ * tbxfroot - Root pointer utilities
+ */
+UINT32
+AcpiTbGetRsdpLength (
+ ACPI_TABLE_RSDP *Rsdp);
+
+ACPI_STATUS
+AcpiTbValidateRsdp (
+ ACPI_TABLE_RSDP *Rsdp);
+
+UINT8 *
+AcpiTbScanMemoryForRsdp (
+ UINT8 *StartAddress,
+ UINT32 Length);
+
+
+/*
+ * tbdata - table data structure management
+ */
+ACPI_STATUS
+AcpiTbGetNextTableDescriptor (
+ UINT32 *TableIndex,
+ ACPI_TABLE_DESC **TableDesc);
+
+void
+AcpiTbInitTableDescriptor (
+ ACPI_TABLE_DESC *TableDesc,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT8 Flags,
+ ACPI_TABLE_HEADER *Table);
+
+ACPI_STATUS
+AcpiTbAcquireTempTable (
+ ACPI_TABLE_DESC *TableDesc,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT8 Flags);
+
+void
+AcpiTbReleaseTempTable (
+ ACPI_TABLE_DESC *TableDesc);
+
+ACPI_STATUS
+AcpiTbValidateTempTable (
+ ACPI_TABLE_DESC *TableDesc);
+
+ACPI_STATUS
+AcpiTbVerifyTempTable (
+ ACPI_TABLE_DESC *TableDesc,
+ char *Signature);
+
+BOOLEAN
+AcpiTbIsTableLoaded (
+ UINT32 TableIndex);
+
+void
+AcpiTbSetTableLoadedFlag (
+ UINT32 TableIndex,
+ BOOLEAN IsLoaded);
+
+
+/*
* tbfadt - FADT parse/convert/validate
*/
void
AcpiTbParseFadt (
- UINT32 TableIndex);
+ void);
void
AcpiTbCreateLocalFadt (
@@ -81,24 +142,40 @@ AcpiTbResizeRootTableList (
void);
ACPI_STATUS
-AcpiTbVerifyTable (
+AcpiTbValidateTable (
+ ACPI_TABLE_DESC *TableDesc);
+
+void
+AcpiTbInvalidateTable (
ACPI_TABLE_DESC *TableDesc);
+void
+AcpiTbOverrideTable (
+ ACPI_TABLE_DESC *OldTableDesc);
+
ACPI_STATUS
-AcpiTbAddTable (
+AcpiTbAcquireTable (
ACPI_TABLE_DESC *TableDesc,
- UINT32 *TableIndex);
+ ACPI_TABLE_HEADER **TablePtr,
+ UINT32 *TableLength,
+ UINT8 *TableFlags);
+
+void
+AcpiTbReleaseTable (
+ ACPI_TABLE_HEADER *Table,
+ UINT32 TableLength,
+ UINT8 TableFlags);
ACPI_STATUS
-AcpiTbStoreTable (
+AcpiTbInstallStandardTable (
ACPI_PHYSICAL_ADDRESS Address,
- ACPI_TABLE_HEADER *Table,
- UINT32 Length,
UINT8 Flags,
+ BOOLEAN Reload,
+ BOOLEAN Override,
UINT32 *TableIndex);
void
-AcpiTbDeleteTable (
+AcpiTbUninstallTable (
ACPI_TABLE_DESC *TableDesc);
void
@@ -122,15 +199,6 @@ AcpiTbGetOwnerId (
UINT32 TableIndex,
ACPI_OWNER_ID *OwnerId);
-BOOLEAN
-AcpiTbIsTableLoaded (
- UINT32 TableIndex);
-
-void
-AcpiTbSetTableLoadedFlag (
- UINT32 TableIndex,
- BOOLEAN IsLoaded);
-
/*
* tbutils - table manager utilities
@@ -139,10 +207,6 @@ ACPI_STATUS
AcpiTbInitializeFacs (
void);
-BOOLEAN
-AcpiTbTablesLoaded (
- void);
-
void
AcpiTbPrintTableHeader(
ACPI_PHYSICAL_ADDRESS Address,
@@ -167,13 +231,27 @@ AcpiTbCopyDsdt (
UINT32 TableIndex);
void
-AcpiTbInstallTable (
+AcpiTbInstallTableWithOverride (
+ ACPI_TABLE_DESC *NewTableDesc,
+ BOOLEAN Override,
+ UINT32 *TableIndex);
+
+ACPI_STATUS
+AcpiTbInstallFixedTable (
ACPI_PHYSICAL_ADDRESS Address,
char *Signature,
- UINT32 TableIndex);
+ UINT32 *TableIndex);
ACPI_STATUS
AcpiTbParseRootTable (
ACPI_PHYSICAL_ADDRESS RsdpAddress);
+
+/*
+ * tbxfload
+ */
+ACPI_STATUS
+AcpiTbLoadNamespace (
+ void);
+
#endif /* __ACTABLES_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/actbl.h b/usr/src/uts/intel/sys/acpi/actbl.h
index e632291cd0..faeb744962 100644
--- a/usr/src/uts/intel/sys/acpi/actbl.h
+++ b/usr/src/uts/intel/sys/acpi/actbl.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,6 +67,7 @@
#define ACPI_SIG_DSDT "DSDT" /* Differentiated System Description Table */
#define ACPI_SIG_FADT "FACP" /* Fixed ACPI Description Table */
#define ACPI_SIG_FACS "FACS" /* Firmware ACPI Control Structure */
+#define ACPI_SIG_OSDT "OSDT" /* Override System Description Table */
#define ACPI_SIG_PSDT "PSDT" /* Persistent System Description Table */
#define ACPI_SIG_RSDP "RSD PTR " /* Root System Description Pointer */
#define ACPI_SIG_RSDT "RSDT" /* Root System Description Table */
@@ -82,9 +83,15 @@
#pragma pack(1)
/*
- * Note about bitfields: The UINT8 type is used for bitfields in ACPI tables.
- * This is the only type that is even remotely portable. Anything else is not
- * portable, so do not use any other bitfield types.
+ * Note: C bitfields are not used for this reason:
+ *
+ * "Bitfields are great and easy to read, but unfortunately the C language
+ * does not specify the layout of bitfields in memory, which means they are
+ * essentially useless for dealing with packed data in on-disk formats or
+ * binary wire protocols." (Or ACPI tables and buffers.) "If you ask me,
+ * this decision was a design error in C. Ritchie could have picked an order
+ * and stuck with it." Norman Ramsey.
+ * See http://stackoverflow.com/a/1053662/41661
*/
@@ -99,7 +106,7 @@ typedef struct acpi_table_header
{
char Signature[ACPI_NAME_SIZE]; /* ASCII table signature */
UINT32 Length; /* Length of table in bytes, including this header */
- UINT8 Revision; /* ACPI Specification minor version # */
+ UINT8 Revision; /* ACPI Specification minor version number */
UINT8 Checksum; /* To make sum of entire table == 0 */
char OemId[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */
char OemTableId[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */
@@ -115,7 +122,7 @@ typedef struct acpi_table_header
* GAS - Generic Address Structure (ACPI 2.0+)
*
* Note: Since this structure is used in the ACPI tables, it is byte aligned.
- * If misaliged access is not supported by the hardware, accesses to the
+ * If misaligned access is not supported by the hardware, accesses to the
* 64-bit Address field must be performed with care.
*
******************************************************************************/
@@ -197,6 +204,9 @@ typedef struct acpi_table_xsdt
} ACPI_TABLE_XSDT;
+#define ACPI_RSDT_ENTRY_SIZE (sizeof (UINT32))
+#define ACPI_XSDT_ENTRY_SIZE (sizeof (UINT64))
+
/*******************************************************************************
*
@@ -238,7 +248,7 @@ typedef struct acpi_table_facs
/*******************************************************************************
*
* FADT - Fixed ACPI Description Table (Signature "FACP")
- * Version 4
+ * Version 6
*
******************************************************************************/
@@ -253,18 +263,18 @@ typedef struct acpi_table_fadt
UINT8 PreferredProfile; /* Conveys preferred power management profile to OSPM. */
UINT16 SciInterrupt; /* System vector of SCI interrupt */
UINT32 SmiCommand; /* 32-bit Port address of SMI command port */
- UINT8 AcpiEnable; /* Value to write to smi_cmd to enable ACPI */
- UINT8 AcpiDisable; /* Value to write to smi_cmd to disable ACPI */
- UINT8 S4BiosRequest; /* Value to write to SMI CMD to enter S4BIOS state */
+ UINT8 AcpiEnable; /* Value to write to SMI_CMD to enable ACPI */
+ UINT8 AcpiDisable; /* Value to write to SMI_CMD to disable ACPI */
+ UINT8 S4BiosRequest; /* Value to write to SMI_CMD to enter S4BIOS state */
UINT8 PstateControl; /* Processor performance state control*/
- UINT32 Pm1aEventBlock; /* 32-bit Port address of Power Mgt 1a Event Reg Blk */
- UINT32 Pm1bEventBlock; /* 32-bit Port address of Power Mgt 1b Event Reg Blk */
- UINT32 Pm1aControlBlock; /* 32-bit Port address of Power Mgt 1a Control Reg Blk */
- UINT32 Pm1bControlBlock; /* 32-bit Port address of Power Mgt 1b Control Reg Blk */
- UINT32 Pm2ControlBlock; /* 32-bit Port address of Power Mgt 2 Control Reg Blk */
- UINT32 PmTimerBlock; /* 32-bit Port address of Power Mgt Timer Ctrl Reg Blk */
- UINT32 Gpe0Block; /* 32-bit Port address of General Purpose Event 0 Reg Blk */
- UINT32 Gpe1Block; /* 32-bit Port address of General Purpose Event 1 Reg Blk */
+ UINT32 Pm1aEventBlock; /* 32-bit port address of Power Mgt 1a Event Reg Blk */
+ UINT32 Pm1bEventBlock; /* 32-bit port address of Power Mgt 1b Event Reg Blk */
+ UINT32 Pm1aControlBlock; /* 32-bit port address of Power Mgt 1a Control Reg Blk */
+ UINT32 Pm1bControlBlock; /* 32-bit port address of Power Mgt 1b Control Reg Blk */
+ UINT32 Pm2ControlBlock; /* 32-bit port address of Power Mgt 2 Control Reg Blk */
+ UINT32 PmTimerBlock; /* 32-bit port address of Power Mgt Timer Ctrl Reg Blk */
+ UINT32 Gpe0Block; /* 32-bit port address of General Purpose Event 0 Reg Blk */
+ UINT32 Gpe1Block; /* 32-bit port address of General Purpose Event 1 Reg Blk */
UINT8 Pm1EventLength; /* Byte Length of ports at Pm1xEventBlock */
UINT8 Pm1ControlLength; /* Byte Length of ports at Pm1xControlBlock */
UINT8 Pm2ControlLength; /* Byte Length of ports at Pm2ControlBlock */
@@ -272,12 +282,12 @@ typedef struct acpi_table_fadt
UINT8 Gpe0BlockLength; /* Byte Length of ports at Gpe0Block */
UINT8 Gpe1BlockLength; /* Byte Length of ports at Gpe1Block */
UINT8 Gpe1Base; /* Offset in GPE number space where GPE1 events start */
- UINT8 CstControl; /* Support for the _CST object and C States change notification */
+ UINT8 CstControl; /* Support for the _CST object and C-States change notification */
UINT16 C2Latency; /* Worst case HW latency to enter/exit C2 state */
UINT16 C3Latency; /* Worst case HW latency to enter/exit C3 state */
- UINT16 FlushSize; /* Processor's memory cache line width, in bytes */
+ UINT16 FlushSize; /* Processor memory cache line width, in bytes */
UINT16 FlushStride; /* Number of flush strides that need to be read */
- UINT8 DutyOffset; /* Processor duty cycle index in processor's P_CNT reg */
+ UINT8 DutyOffset; /* Processor duty cycle index in processor P_CNT reg */
UINT8 DutyWidth; /* Processor duty cycle value bit width in P_CNT register */
UINT8 DayAlarm; /* Index to day-of-month alarm in RTC CMOS RAM */
UINT8 MonthAlarm; /* Index to month-of-year alarm in RTC CMOS RAM */
@@ -287,7 +297,8 @@ typedef struct acpi_table_fadt
UINT32 Flags; /* Miscellaneous flag bits (see below for individual flags) */
ACPI_GENERIC_ADDRESS ResetRegister; /* 64-bit address of the Reset register */
UINT8 ResetValue; /* Value to write to the ResetRegister port to reset the system */
- UINT8 Reserved4[3]; /* Reserved, must be zero */
+ UINT16 ArmBootFlags; /* ARM-Specific Boot Flags (see below for individual flags) (ACPI 5.1) */
+ UINT8 MinorRevision; /* FADT Minor Revision (ACPI 5.1) */
UINT64 XFacs; /* 64-bit physical address of FACS */
UINT64 XDsdt; /* 64-bit physical address of DSDT */
ACPI_GENERIC_ADDRESS XPm1aEventBlock; /* 64-bit Extended Power Mgt 1a Event Reg Blk address */
@@ -298,27 +309,36 @@ typedef struct acpi_table_fadt
ACPI_GENERIC_ADDRESS XPmTimerBlock; /* 64-bit Extended Power Mgt Timer Ctrl Reg Blk address */
ACPI_GENERIC_ADDRESS XGpe0Block; /* 64-bit Extended General Purpose Event 0 Reg Blk address */
ACPI_GENERIC_ADDRESS XGpe1Block; /* 64-bit Extended General Purpose Event 1 Reg Blk address */
+ ACPI_GENERIC_ADDRESS SleepControl; /* 64-bit Sleep Control register (ACPI 5.0) */
+ ACPI_GENERIC_ADDRESS SleepStatus; /* 64-bit Sleep Status register (ACPI 5.0) */
+ UINT64 HypervisorId; /* Hypervisor Vendor ID (ACPI 6.0) */
} ACPI_TABLE_FADT;
-/* Masks for FADT Boot Architecture Flags (BootFlags) */
+/* Masks for FADT IA-PC Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */
#define ACPI_FADT_LEGACY_DEVICES (1) /* 00: [V2] System has LPC or ISA bus devices */
#define ACPI_FADT_8042 (1<<1) /* 01: [V3] System has an 8042 controller on port 60/64 */
#define ACPI_FADT_NO_VGA (1<<2) /* 02: [V4] It is not safe to probe for VGA hardware */
#define ACPI_FADT_NO_MSI (1<<3) /* 03: [V4] Message Signaled Interrupts (MSI) must not be enabled */
#define ACPI_FADT_NO_ASPM (1<<4) /* 04: [V4] PCIe ASPM control must not be enabled */
+#define ACPI_FADT_NO_CMOS_RTC (1<<5) /* 05: [V5] No CMOS real-time clock present */
+
+/* Masks for FADT ARM Boot Architecture Flags (arm_boot_flags) ACPI 5.1 */
+
+#define ACPI_FADT_PSCI_COMPLIANT (1) /* 00: [V5+] PSCI 0.2+ is implemented */
+#define ACPI_FADT_PSCI_USE_HVC (1<<1) /* 01: [V5+] HVC must be used instead of SMC as the PSCI conduit */
/* Masks for FADT flags */
-#define ACPI_FADT_WBINVD (1) /* 00: [V1] The wbinvd instruction works properly */
-#define ACPI_FADT_WBINVD_FLUSH (1<<1) /* 01: [V1] wbinvd flushes but does not invalidate caches */
+#define ACPI_FADT_WBINVD (1) /* 00: [V1] The WBINVD instruction works properly */
+#define ACPI_FADT_WBINVD_FLUSH (1<<1) /* 01: [V1] WBINVD flushes but does not invalidate caches */
#define ACPI_FADT_C1_SUPPORTED (1<<2) /* 02: [V1] All processors support C1 state */
#define ACPI_FADT_C2_MP_SUPPORTED (1<<3) /* 03: [V1] C2 state works on MP system */
#define ACPI_FADT_POWER_BUTTON (1<<4) /* 04: [V1] Power button is handled as a control method device */
#define ACPI_FADT_SLEEP_BUTTON (1<<5) /* 05: [V1] Sleep button is handled as a control method device */
-#define ACPI_FADT_FIXED_RTC (1<<6) /* 06: [V1] RTC wakeup status not in fixed register space */
+#define ACPI_FADT_FIXED_RTC (1<<6) /* 06: [V1] RTC wakeup status is not in fixed register space */
#define ACPI_FADT_S4_RTC_WAKE (1<<7) /* 07: [V1] RTC alarm can wake system from S4 */
#define ACPI_FADT_32BIT_TIMER (1<<8) /* 08: [V1] ACPI timer width is 32-bit (0=24-bit) */
#define ACPI_FADT_DOCKING_SUPPORTED (1<<9) /* 09: [V1] Docking supported */
@@ -332,11 +352,13 @@ typedef struct acpi_table_fadt
#define ACPI_FADT_REMOTE_POWER_ON (1<<17) /* 17: [V4] System is compatible with remote power on (ACPI 3.0) */
#define ACPI_FADT_APIC_CLUSTER (1<<18) /* 18: [V4] All local APICs must use cluster model (ACPI 3.0) */
#define ACPI_FADT_APIC_PHYSICAL (1<<19) /* 19: [V4] All local xAPICs must use physical dest mode (ACPI 3.0) */
+#define ACPI_FADT_HW_REDUCED (1<<20) /* 20: [V5] ACPI hardware is not implemented (ACPI 5.0) */
+#define ACPI_FADT_LOW_POWER_S0 (1<<21) /* 21: [V5] S0 power savings are equal or better than S3 (ACPI 5.0) */
-/* Values for PreferredProfile (Prefered Power Management Profiles) */
+/* Values for PreferredProfile (Preferred Power Management Profiles) */
-enum AcpiPreferedPmProfiles
+enum AcpiPreferredPmProfiles
{
PM_UNSPECIFIED = 0,
PM_DESKTOP = 1,
@@ -344,9 +366,18 @@ enum AcpiPreferedPmProfiles
PM_WORKSTATION = 3,
PM_ENTERPRISE_SERVER = 4,
PM_SOHO_SERVER = 5,
- PM_APPLIANCE_PC = 6
+ PM_APPLIANCE_PC = 6,
+ PM_PERFORMANCE_SERVER = 7,
+ PM_TABLET = 8
};
+/* Values for SleepStatus and SleepControl registers (V5+ FADT) */
+
+#define ACPI_X_WAKE_STATUS 0x80
+#define ACPI_X_SLEEP_TYPE_MASK 0x1C
+#define ACPI_X_SLEEP_TYPE_POSITION 0x02
+#define ACPI_X_SLEEP_ENABLE 0x20
+
/* Reset to default packing */
@@ -370,7 +401,7 @@ typedef struct acpi_table_desc
{
ACPI_PHYSICAL_ADDRESS Address;
ACPI_TABLE_HEADER *Pointer;
- UINT32 Length; /* Length fixed at 32 bits */
+ UINT32 Length; /* Length fixed at 32 bits (fixed in table header) */
ACPI_NAME_UNION Signature;
ACPI_OWNER_ID OwnerId;
UINT8 Flags;
@@ -379,12 +410,11 @@ typedef struct acpi_table_desc
/* Masks for Flags field above */
-#define ACPI_TABLE_ORIGIN_UNKNOWN (0)
-#define ACPI_TABLE_ORIGIN_MAPPED (1)
-#define ACPI_TABLE_ORIGIN_ALLOCATED (2)
-#define ACPI_TABLE_ORIGIN_OVERRIDE (4)
-#define ACPI_TABLE_ORIGIN_MASK (7)
-#define ACPI_TABLE_IS_LOADED (8)
+#define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */
+#define ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL (1) /* Physical address, internally mapped */
+#define ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL (2) /* Virtual address, internallly allocated */
+#define ACPI_TABLE_ORIGIN_MASK (3)
+#define ACPI_TABLE_IS_LOADED (8)
/*
@@ -392,10 +422,11 @@ typedef struct acpi_table_desc
*/
#include "actbl1.h"
#include "actbl2.h"
+#include "actbl3.h"
/* Macros used to generate offsets to specific table fields */
-#define ACPI_FADT_OFFSET(f) (UINT8) ACPI_OFFSET (ACPI_TABLE_FADT, f)
+#define ACPI_FADT_OFFSET(f) (UINT16) ACPI_OFFSET (ACPI_TABLE_FADT, f)
/*
* Sizes of the various flavors of FADT. We need to look closely
@@ -405,12 +436,19 @@ typedef struct acpi_table_desc
* FADT is the bottom line as to what the version really is.
*
* For reference, the values below are as follows:
- * FADT V1 size: 0x74
- * FADT V2 size: 0x84
- * FADT V3+ size: 0xF4
+ * FADT V1 size: 0x074
+ * FADT V2 size: 0x084
+ * FADT V3 size: 0x0F4
+ * FADT V4 size: 0x0F4
+ * FADT V5 size: 0x10C
+ * FADT V6 size: 0x114
*/
#define ACPI_FADT_V1_SIZE (UINT32) (ACPI_FADT_OFFSET (Flags) + 4)
-#define ACPI_FADT_V2_SIZE (UINT32) (ACPI_FADT_OFFSET (Reserved4[0]) + 3)
-#define ACPI_FADT_V3_SIZE (UINT32) (sizeof (ACPI_TABLE_FADT))
+#define ACPI_FADT_V2_SIZE (UINT32) (ACPI_FADT_OFFSET (MinorRevision) + 1)
+#define ACPI_FADT_V3_SIZE (UINT32) (ACPI_FADT_OFFSET (SleepControl))
+#define ACPI_FADT_V5_SIZE (UINT32) (ACPI_FADT_OFFSET (HypervisorId))
+#define ACPI_FADT_V6_SIZE (UINT32) (sizeof (ACPI_TABLE_FADT))
+
+#define ACPI_FADT_CONFORMANCE "ACPI 6.1 (FADT version 6)"
#endif /* __ACTBL_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/actbl1.h b/usr/src/uts/intel/sys/acpi/actbl1.h
index ad6c82fc3c..0fe925a84b 100644
--- a/usr/src/uts/intel/sys/acpi/actbl1.h
+++ b/usr/src/uts/intel/sys/acpi/actbl1.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -73,6 +73,7 @@
#define ACPI_SIG_SBST "SBST" /* Smart Battery Specification Table */
#define ACPI_SIG_SLIT "SLIT" /* System Locality Distance Information Table */
#define ACPI_SIG_SRAT "SRAT" /* System Resource Affinity Table */
+#define ACPI_SIG_NFIT "NFIT" /* NVDIMM Firmware Interface Table */
/*
@@ -82,9 +83,15 @@
#pragma pack(1)
/*
- * Note about bitfields: The UINT8 type is used for bitfields in ACPI tables.
- * This is the only type that is even remotely portable. Anything else is not
- * portable, so do not use any other bitfield types.
+ * Note: C bitfields are not used for this reason:
+ *
+ * "Bitfields are great and easy to read, but unfortunately the C language
+ * does not specify the layout of bitfields in memory, which means they are
+ * essentially useless for dealing with packed data in on-disk formats or
+ * binary wire protocols." (Or ACPI tables and buffers.) "If you ask me,
+ * this decision was a design error in C. Ritchie could have picked an order
+ * and stuck with it." Norman Ramsey.
+ * See http://stackoverflow.com/a/1053662/41661
*/
@@ -130,7 +137,7 @@ typedef struct acpi_table_bert
{
ACPI_TABLE_HEADER Header; /* Common ACPI table header */
UINT32 RegionLength; /* Length of the boot error region */
- UINT64 Address; /* Physical addresss of the error region */
+ UINT64 Address; /* Physical address of the error region */
} ACPI_TABLE_BERT;
@@ -252,16 +259,18 @@ typedef struct acpi_einj_entry
enum AcpiEinjActions
{
- ACPI_EINJ_BEGIN_OPERATION = 0,
- ACPI_EINJ_GET_TRIGGER_TABLE = 1,
- ACPI_EINJ_SET_ERROR_TYPE = 2,
- ACPI_EINJ_GET_ERROR_TYPE = 3,
- ACPI_EINJ_END_OPERATION = 4,
- ACPI_EINJ_EXECUTE_OPERATION = 5,
- ACPI_EINJ_CHECK_BUSY_STATUS = 6,
- ACPI_EINJ_GET_COMMAND_STATUS = 7,
- ACPI_EINJ_ACTION_RESERVED = 8, /* 8 and greater are reserved */
- ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */
+ ACPI_EINJ_BEGIN_OPERATION = 0,
+ ACPI_EINJ_GET_TRIGGER_TABLE = 1,
+ ACPI_EINJ_SET_ERROR_TYPE = 2,
+ ACPI_EINJ_GET_ERROR_TYPE = 3,
+ ACPI_EINJ_END_OPERATION = 4,
+ ACPI_EINJ_EXECUTE_OPERATION = 5,
+ ACPI_EINJ_CHECK_BUSY_STATUS = 6,
+ ACPI_EINJ_GET_COMMAND_STATUS = 7,
+ ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8,
+ ACPI_EINJ_GET_EXECUTE_TIMINGS = 9,
+ ACPI_EINJ_ACTION_RESERVED = 10, /* 10 and greater are reserved */
+ ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */
};
/* Values for Instruction field above */
@@ -273,9 +282,33 @@ enum AcpiEinjInstructions
ACPI_EINJ_WRITE_REGISTER = 2,
ACPI_EINJ_WRITE_REGISTER_VALUE = 3,
ACPI_EINJ_NOOP = 4,
- ACPI_EINJ_INSTRUCTION_RESERVED = 5 /* 5 and greater are reserved */
+ ACPI_EINJ_FLUSH_CACHELINE = 5,
+ ACPI_EINJ_INSTRUCTION_RESERVED = 6 /* 6 and greater are reserved */
};
+typedef struct acpi_einj_error_type_with_addr
+{
+ UINT32 ErrorType;
+ UINT32 VendorStructOffset;
+ UINT32 Flags;
+ UINT32 ApicId;
+ UINT64 Address;
+ UINT64 Range;
+ UINT32 PcieId;
+
+} ACPI_EINJ_ERROR_TYPE_WITH_ADDR;
+
+typedef struct acpi_einj_vendor
+{
+ UINT32 Length;
+ UINT32 PcieId;
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT8 RevisionId;
+ UINT8 Reserved[3];
+
+} ACPI_EINJ_VENDOR;
+
/* EINJ Trigger Error Action Table */
@@ -313,6 +346,7 @@ enum AcpiEinjCommandStatus
#define ACPI_EINJ_PLATFORM_CORRECTABLE (1<<9)
#define ACPI_EINJ_PLATFORM_UNCORRECTABLE (1<<10)
#define ACPI_EINJ_PLATFORM_FATAL (1<<11)
+#define ACPI_EINJ_VENDOR_DEFINED (1<<31)
/*******************************************************************************
@@ -364,7 +398,8 @@ enum AcpiErstActions
ACPI_ERST_GET_ERROR_RANGE = 13,
ACPI_ERST_GET_ERROR_LENGTH = 14,
ACPI_ERST_GET_ERROR_ATTRIBUTES = 15,
- ACPI_ERST_ACTION_RESERVED = 16 /* 16 and greater are reserved */
+ ACPI_ERST_EXECUTE_TIMINGS = 16,
+ ACPI_ERST_ACTION_RESERVED = 17 /* 17 and greater are reserved */
};
/* Values for Instruction field above */
@@ -456,7 +491,8 @@ enum AcpiHestTypes
ACPI_HEST_TYPE_AER_ENDPOINT = 7,
ACPI_HEST_TYPE_AER_BRIDGE = 8,
ACPI_HEST_TYPE_GENERIC_ERROR = 9,
- ACPI_HEST_TYPE_RESERVED = 10 /* 10 and greater are reserved */
+ ACPI_HEST_TYPE_GENERIC_ERROR_V2 = 10,
+ ACPI_HEST_TYPE_RESERVED = 11 /* 11 and greater are reserved */
};
@@ -492,7 +528,7 @@ typedef struct acpi_hest_aer_common
UINT8 Enabled;
UINT32 RecordsToPreallocate;
UINT32 MaxSectionsPerRecord;
- UINT32 Bus;
+ UINT32 Bus; /* Bus and Segment numbers */
UINT16 Device;
UINT16 Function;
UINT16 DeviceControl;
@@ -509,6 +545,14 @@ typedef struct acpi_hest_aer_common
#define ACPI_HEST_FIRMWARE_FIRST (1)
#define ACPI_HEST_GLOBAL (1<<1)
+/*
+ * Macros to access the bus/segment numbers in Bus field above:
+ * Bus number is encoded in bits 7:0
+ * Segment number is encoded in bits 23:8
+ */
+#define ACPI_HEST_BUS(Bus) ((Bus) & 0xFF)
+#define ACPI_HEST_SEGMENT(Bus) (((Bus) >> 8) & 0xFFFF)
+
/* Hardware Error Notification */
@@ -535,7 +579,13 @@ enum AcpiHestNotifyTypes
ACPI_HEST_NOTIFY_LOCAL = 2,
ACPI_HEST_NOTIFY_SCI = 3,
ACPI_HEST_NOTIFY_NMI = 4,
- ACPI_HEST_NOTIFY_RESERVED = 5 /* 5 and greater are reserved */
+ ACPI_HEST_NOTIFY_CMCI = 5, /* ACPI 5.0 */
+ ACPI_HEST_NOTIFY_MCE = 6, /* ACPI 5.0 */
+ ACPI_HEST_NOTIFY_GPIO = 7, /* ACPI 6.0 */
+ ACPI_HEST_NOTIFY_SEA = 8, /* ACPI 6.1 */
+ ACPI_HEST_NOTIFY_SEI = 9, /* ACPI 6.1 */
+ ACPI_HEST_NOTIFY_GSIV = 10, /* ACPI 6.1 */
+ ACPI_HEST_NOTIFY_RESERVED = 11 /* 11 and greater are reserved */
};
/* Values for ConfigWriteEnable bitfield above */
@@ -654,6 +704,27 @@ typedef struct acpi_hest_generic
} ACPI_HEST_GENERIC;
+/* 10: Generic Hardware Error Source, version 2 */
+
+typedef struct acpi_hest_generic_v2
+{
+ ACPI_HEST_HEADER Header;
+ UINT16 RelatedSourceId;
+ UINT8 Reserved;
+ UINT8 Enabled;
+ UINT32 RecordsToPreallocate;
+ UINT32 MaxSectionsPerRecord;
+ UINT32 MaxRawDataLength;
+ ACPI_GENERIC_ADDRESS ErrorStatusAddress;
+ ACPI_HEST_NOTIFY Notify;
+ UINT32 ErrorBlockLength;
+ ACPI_GENERIC_ADDRESS ReadAckRegister;
+ UINT64 ReadAckPreserve;
+ UINT64 ReadAckWrite;
+
+} ACPI_HEST_GENERIC_V2;
+
+
/* Generic Error Status block */
typedef struct acpi_hest_generic_status
@@ -690,6 +761,35 @@ typedef struct acpi_hest_generic_data
} ACPI_HEST_GENERIC_DATA;
+/* Extension for revision 0x0300 */
+
+typedef struct acpi_hest_generic_data_v300
+{
+ UINT8 SectionType[16];
+ UINT32 ErrorSeverity;
+ UINT16 Revision;
+ UINT8 ValidationBits;
+ UINT8 Flags;
+ UINT32 ErrorDataLength;
+ UINT8 FruId[16];
+ UINT8 FruText[20];
+ UINT64 TimeStamp;
+
+} ACPI_HEST_GENERIC_DATA_V300;
+
+/* Values for ErrorSeverity above */
+
+#define ACPI_HEST_GEN_ERROR_RECOVERABLE 0
+#define ACPI_HEST_GEN_ERROR_FATAL 1
+#define ACPI_HEST_GEN_ERROR_CORRECTED 2
+#define ACPI_HEST_GEN_ERROR_NONE 3
+
+/* Flags for ValidationBits above */
+
+#define ACPI_HEST_GEN_VALID_FRU_ID (1)
+#define ACPI_HEST_GEN_VALID_FRU_STRING (1<<1)
+#define ACPI_HEST_GEN_VALID_TIMESTAMP (1<<2)
+
/*******************************************************************************
*
@@ -720,23 +820,28 @@ typedef struct acpi_table_madt
enum AcpiMadtType
{
- ACPI_MADT_TYPE_LOCAL_APIC = 0,
- ACPI_MADT_TYPE_IO_APIC = 1,
- ACPI_MADT_TYPE_INTERRUPT_OVERRIDE = 2,
- ACPI_MADT_TYPE_NMI_SOURCE = 3,
- ACPI_MADT_TYPE_LOCAL_APIC_NMI = 4,
- ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE = 5,
- ACPI_MADT_TYPE_IO_SAPIC = 6,
- ACPI_MADT_TYPE_LOCAL_SAPIC = 7,
- ACPI_MADT_TYPE_INTERRUPT_SOURCE = 8,
- ACPI_MADT_TYPE_LOCAL_X2APIC = 9,
- ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10,
- ACPI_MADT_TYPE_RESERVED = 11 /* 11 and greater are reserved */
+ ACPI_MADT_TYPE_LOCAL_APIC = 0,
+ ACPI_MADT_TYPE_IO_APIC = 1,
+ ACPI_MADT_TYPE_INTERRUPT_OVERRIDE = 2,
+ ACPI_MADT_TYPE_NMI_SOURCE = 3,
+ ACPI_MADT_TYPE_LOCAL_APIC_NMI = 4,
+ ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE = 5,
+ ACPI_MADT_TYPE_IO_SAPIC = 6,
+ ACPI_MADT_TYPE_LOCAL_SAPIC = 7,
+ ACPI_MADT_TYPE_INTERRUPT_SOURCE = 8,
+ ACPI_MADT_TYPE_LOCAL_X2APIC = 9,
+ ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10,
+ ACPI_MADT_TYPE_GENERIC_INTERRUPT = 11,
+ ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12,
+ ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13,
+ ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14,
+ ACPI_MADT_TYPE_GENERIC_TRANSLATOR = 15,
+ ACPI_MADT_TYPE_RESERVED = 16 /* 16 and greater are reserved */
};
/*
- * MADT Sub-tables, correspond to Type in ACPI_SUBTABLE_HEADER
+ * MADT Subtables, correspond to Type in ACPI_SUBTABLE_HEADER
*/
/* 0: Processor Local APIC */
@@ -886,11 +991,112 @@ typedef struct acpi_madt_local_x2apic_nmi
} ACPI_MADT_LOCAL_X2APIC_NMI;
+/* 11: Generic Interrupt (ACPI 5.0 + ACPI 6.0 changes) */
+
+typedef struct acpi_madt_generic_interrupt
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT16 Reserved; /* Reserved - must be zero */
+ UINT32 CpuInterfaceNumber;
+ UINT32 Uid;
+ UINT32 Flags;
+ UINT32 ParkingVersion;
+ UINT32 PerformanceInterrupt;
+ UINT64 ParkedAddress;
+ UINT64 BaseAddress;
+ UINT64 GicvBaseAddress;
+ UINT64 GichBaseAddress;
+ UINT32 VgicInterrupt;
+ UINT64 GicrBaseAddress;
+ UINT64 ArmMpidr;
+ UINT8 EfficiencyClass;
+ UINT8 Reserved2[3];
+
+} ACPI_MADT_GENERIC_INTERRUPT;
+
+/* Masks for Flags field above */
+
+/* ACPI_MADT_ENABLED (1) Processor is usable if set */
+#define ACPI_MADT_PERFORMANCE_IRQ_MODE (1<<1) /* 01: Performance Interrupt Mode */
+#define ACPI_MADT_VGIC_IRQ_MODE (1<<2) /* 02: VGIC Maintenance Interrupt mode */
+
+
+/* 12: Generic Distributor (ACPI 5.0 + ACPI 6.0 changes) */
+
+typedef struct acpi_madt_generic_distributor
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT16 Reserved; /* Reserved - must be zero */
+ UINT32 GicId;
+ UINT64 BaseAddress;
+ UINT32 GlobalIrqBase;
+ UINT8 Version;
+ UINT8 Reserved2[3]; /* Reserved - must be zero */
+
+} ACPI_MADT_GENERIC_DISTRIBUTOR;
+
+/* Values for Version field above */
+
+enum AcpiMadtGicVersion
+{
+ ACPI_MADT_GIC_VERSION_NONE = 0,
+ ACPI_MADT_GIC_VERSION_V1 = 1,
+ ACPI_MADT_GIC_VERSION_V2 = 2,
+ ACPI_MADT_GIC_VERSION_V3 = 3,
+ ACPI_MADT_GIC_VERSION_V4 = 4,
+ ACPI_MADT_GIC_VERSION_RESERVED = 5 /* 5 and greater are reserved */
+};
+
+
+/* 13: Generic MSI Frame (ACPI 5.1) */
+
+typedef struct acpi_madt_generic_msi_frame
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT16 Reserved; /* Reserved - must be zero */
+ UINT32 MsiFrameId;
+ UINT64 BaseAddress;
+ UINT32 Flags;
+ UINT16 SpiCount;
+ UINT16 SpiBase;
+
+} ACPI_MADT_GENERIC_MSI_FRAME;
+
+/* Masks for Flags field above */
+
+#define ACPI_MADT_OVERRIDE_SPI_VALUES (1)
+
+
+/* 14: Generic Redistributor (ACPI 5.1) */
+
+typedef struct acpi_madt_generic_redistributor
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT16 Reserved; /* reserved - must be zero */
+ UINT64 BaseAddress;
+ UINT32 Length;
+
+} ACPI_MADT_GENERIC_REDISTRIBUTOR;
+
+
+/* 15: Generic Translator (ACPI 6.0) */
+
+typedef struct acpi_madt_generic_translator
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT16 Reserved; /* reserved - must be zero */
+ UINT32 TranslationId;
+ UINT64 BaseAddress;
+ UINT32 Reserved2;
+
+} ACPI_MADT_GENERIC_TRANSLATOR;
+
+
/*
* Common flags fields for MADT subtables
*/
-/* MADT Local APIC flags (LapicFlags) */
+/* MADT Local APIC flags */
#define ACPI_MADT_ENABLED (1) /* 00: Processor is usable if set */
@@ -946,6 +1152,194 @@ typedef struct acpi_msct_proximity
/*******************************************************************************
*
+ * NFIT - NVDIMM Interface Table (ACPI 6.0+)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_nfit
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT32 Reserved; /* Reserved, must be zero */
+
+} ACPI_TABLE_NFIT;
+
+/* Subtable header for NFIT */
+
+typedef struct acpi_nfit_header
+{
+ UINT16 Type;
+ UINT16 Length;
+
+} ACPI_NFIT_HEADER;
+
+
+/* Values for subtable type in ACPI_NFIT_HEADER */
+
+enum AcpiNfitType
+{
+ ACPI_NFIT_TYPE_SYSTEM_ADDRESS = 0,
+ ACPI_NFIT_TYPE_MEMORY_MAP = 1,
+ ACPI_NFIT_TYPE_INTERLEAVE = 2,
+ ACPI_NFIT_TYPE_SMBIOS = 3,
+ ACPI_NFIT_TYPE_CONTROL_REGION = 4,
+ ACPI_NFIT_TYPE_DATA_REGION = 5,
+ ACPI_NFIT_TYPE_FLUSH_ADDRESS = 6,
+ ACPI_NFIT_TYPE_RESERVED = 7 /* 7 and greater are reserved */
+};
+
+/*
+ * NFIT Subtables
+ */
+
+/* 0: System Physical Address Range Structure */
+
+typedef struct acpi_nfit_system_address
+{
+ ACPI_NFIT_HEADER Header;
+ UINT16 RangeIndex;
+ UINT16 Flags;
+ UINT32 Reserved; /* Reseved, must be zero */
+ UINT32 ProximityDomain;
+ UINT8 RangeGuid[16];
+ UINT64 Address;
+ UINT64 Length;
+ UINT64 MemoryMapping;
+
+} ACPI_NFIT_SYSTEM_ADDRESS;
+
+/* Flags */
+
+#define ACPI_NFIT_ADD_ONLINE_ONLY (1) /* 00: Add/Online Operation Only */
+#define ACPI_NFIT_PROXIMITY_VALID (1<<1) /* 01: Proximity Domain Valid */
+
+/* Range Type GUIDs appear in the include/acuuid.h file */
+
+
+/* 1: Memory Device to System Address Range Map Structure */
+
+typedef struct acpi_nfit_memory_map
+{
+ ACPI_NFIT_HEADER Header;
+ UINT32 DeviceHandle;
+ UINT16 PhysicalId;
+ UINT16 RegionId;
+ UINT16 RangeIndex;
+ UINT16 RegionIndex;
+ UINT64 RegionSize;
+ UINT64 RegionOffset;
+ UINT64 Address;
+ UINT16 InterleaveIndex;
+ UINT16 InterleaveWays;
+ UINT16 Flags;
+ UINT16 Reserved; /* Reserved, must be zero */
+
+} ACPI_NFIT_MEMORY_MAP;
+
+/* Flags */
+
+#define ACPI_NFIT_MEM_SAVE_FAILED (1) /* 00: Last SAVE to Memory Device failed */
+#define ACPI_NFIT_MEM_RESTORE_FAILED (1<<1) /* 01: Last RESTORE from Memory Device failed */
+#define ACPI_NFIT_MEM_FLUSH_FAILED (1<<2) /* 02: Platform flush failed */
+#define ACPI_NFIT_MEM_NOT_ARMED (1<<3) /* 03: Memory Device is not armed */
+#define ACPI_NFIT_MEM_HEALTH_OBSERVED (1<<4) /* 04: Memory Device observed SMART/health events */
+#define ACPI_NFIT_MEM_HEALTH_ENABLED (1<<5) /* 05: SMART/health events enabled */
+#define ACPI_NFIT_MEM_MAP_FAILED (1<<6) /* 06: Mapping to SPA failed */
+
+
+/* 2: Interleave Structure */
+
+typedef struct acpi_nfit_interleave
+{
+ ACPI_NFIT_HEADER Header;
+ UINT16 InterleaveIndex;
+ UINT16 Reserved; /* Reserved, must be zero */
+ UINT32 LineCount;
+ UINT32 LineSize;
+ UINT32 LineOffset[1]; /* Variable length */
+
+} ACPI_NFIT_INTERLEAVE;
+
+
+/* 3: SMBIOS Management Information Structure */
+
+typedef struct acpi_nfit_smbios
+{
+ ACPI_NFIT_HEADER Header;
+ UINT32 Reserved; /* Reserved, must be zero */
+ UINT8 Data[1]; /* Variable length */
+
+} ACPI_NFIT_SMBIOS;
+
+
+/* 4: NVDIMM Control Region Structure */
+
+typedef struct acpi_nfit_control_region
+{
+ ACPI_NFIT_HEADER Header;
+ UINT16 RegionIndex;
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT16 RevisionId;
+ UINT16 SubsystemVendorId;
+ UINT16 SubsystemDeviceId;
+ UINT16 SubsystemRevisionId;
+ UINT8 ValidFields;
+ UINT8 ManufacturingLocation;
+ UINT16 ManufacturingDate;
+ UINT8 Reserved[2]; /* Reserved, must be zero */
+ UINT32 SerialNumber;
+ UINT16 Code;
+ UINT16 Windows;
+ UINT64 WindowSize;
+ UINT64 CommandOffset;
+ UINT64 CommandSize;
+ UINT64 StatusOffset;
+ UINT64 StatusSize;
+ UINT16 Flags;
+ UINT8 Reserved1[6]; /* Reserved, must be zero */
+
+} ACPI_NFIT_CONTROL_REGION;
+
+/* Flags */
+
+#define ACPI_NFIT_CONTROL_BUFFERED (1) /* Block Data Windows implementation is buffered */
+
+/* ValidFields bits */
+
+#define ACPI_NFIT_CONTROL_MFG_INFO_VALID (1) /* Manufacturing fields are valid */
+
+
+/* 5: NVDIMM Block Data Window Region Structure */
+
+typedef struct acpi_nfit_data_region
+{
+ ACPI_NFIT_HEADER Header;
+ UINT16 RegionIndex;
+ UINT16 Windows;
+ UINT64 Offset;
+ UINT64 Size;
+ UINT64 Capacity;
+ UINT64 StartAddress;
+
+} ACPI_NFIT_DATA_REGION;
+
+
+/* 6: Flush Hint Address Structure */
+
+typedef struct acpi_nfit_flush_address
+{
+ ACPI_NFIT_HEADER Header;
+ UINT32 DeviceHandle;
+ UINT16 HintCount;
+ UINT8 Reserved[6]; /* Reserved, must be zero */
+ UINT64 HintAddress[1]; /* Variable length */
+
+} ACPI_NFIT_FLUSH_ADDRESS;
+
+
+/*******************************************************************************
+ *
* SBST - Smart Battery Specification Table
* Version 1
*
@@ -999,11 +1393,12 @@ enum AcpiSratType
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
- ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
+ ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
+ ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */
};
/*
- * SRAT Sub-tables, correspond to Type in ACPI_SUBTABLE_HEADER
+ * SRAT Subtables, correspond to Type in ACPI_SUBTABLE_HEADER
*/
/* 0: Processor Local APIC/SAPIC Affinity */
@@ -1016,7 +1411,7 @@ typedef struct acpi_srat_cpu_affinity
UINT32 Flags;
UINT8 LocalSapicEid;
UINT8 ProximityDomainHi[3];
- UINT32 Reserved; /* Reserved, must be zero */
+ UINT32 ClockDomain;
} ACPI_SRAT_CPU_AFFINITY;
@@ -1066,6 +1461,23 @@ typedef struct acpi_srat_x2apic_cpu_affinity
#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
+/* 3: GICC Affinity (ACPI 5.1) */
+
+typedef struct acpi_srat_gicc_affinity
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT32 ProximityDomain;
+ UINT32 AcpiProcessorUid;
+ UINT32 Flags;
+ UINT32 ClockDomain;
+
+} ACPI_SRAT_GICC_AFFINITY;
+
+/* Flags for ACPI_SRAT_GICC_AFFINITY */
+
+#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */
+
+
/* Reset to default packing */
#pragma pack()
diff --git a/usr/src/uts/intel/sys/acpi/actbl2.h b/usr/src/uts/intel/sys/acpi/actbl2.h
index e4f01e3f6b..625d33fab9 100644
--- a/usr/src/uts/intel/sys/acpi/actbl2.h
+++ b/usr/src/uts/intel/sys/acpi/actbl2.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -52,8 +52,8 @@
* These tables are not consumed directly by the ACPICA subsystem, but are
* included here to support device drivers and the AML disassembler.
*
- * The tables in this file are defined by third-party specifications, and are
- * not defined directly by the ACPI specification itself.
+ * Generally, the tables in this file are defined by third-party specifications,
+ * and are not defined directly by the ACPI specification itself.
*
******************************************************************************/
@@ -65,18 +65,26 @@
*/
#define ACPI_SIG_ASF "ASF!" /* Alert Standard Format table */
#define ACPI_SIG_BOOT "BOOT" /* Simple Boot Flag Table */
+#define ACPI_SIG_CSRT "CSRT" /* Core System Resource Table */
+#define ACPI_SIG_DBG2 "DBG2" /* Debug Port table type 2 */
#define ACPI_SIG_DBGP "DBGP" /* Debug Port table */
#define ACPI_SIG_DMAR "DMAR" /* DMA Remapping table */
#define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */
#define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */
+#define ACPI_SIG_IORT "IORT" /* IO Remapping Table */
#define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */
+#define ACPI_SIG_LPIT "LPIT" /* Low Power Idle Table */
#define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */
#define ACPI_SIG_MCHI "MCHI" /* Management Controller Host Interface table */
+#define ACPI_SIG_MSDM "MSDM" /* Microsoft Data Management Table */
+#define ACPI_SIG_MTMR "MTMR" /* MID Timer table */
#define ACPI_SIG_SLIC "SLIC" /* Software Licensing Description Table */
#define ACPI_SIG_SPCR "SPCR" /* Serial Port Console Redirection table */
#define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */
#define ACPI_SIG_TCPA "TCPA" /* Trusted Computing Platform Alliance table */
+#define ACPI_SIG_TPM2 "TPM2" /* Trusted Platform Module 2.0 H/W interface table */
#define ACPI_SIG_UEFI "UEFI" /* Uefi Boot Optimization Table */
+#define ACPI_SIG_VRTC "VRTC" /* Virtual Real Time Clock Table */
#define ACPI_SIG_WAET "WAET" /* Windows ACPI Emulated devices Table */
#define ACPI_SIG_WDAT "WDAT" /* Watchdog Action Table */
#define ACPI_SIG_WDDT "WDDT" /* Watchdog Timer Description Table */
@@ -98,9 +106,15 @@
#pragma pack(1)
/*
- * Note about bitfields: The UINT8 type is used for bitfields in ACPI tables.
- * This is the only type that is even remotely portable. Anything else is not
- * portable, so do not use any other bitfield types.
+ * Note: C bitfields are not used for this reason:
+ *
+ * "Bitfields are great and easy to read, but unfortunately the C language
+ * does not specify the layout of bitfields in memory, which means they are
+ * essentially useless for dealing with packed data in on-disk formats or
+ * binary wire protocols." (Or ACPI tables and buffers.) "If you ask me,
+ * this decision was a design error in C. Ritchie could have picked an order
+ * and stuck with it." Norman Ramsey.
+ * See http://stackoverflow.com/a/1053662/41661
*/
@@ -264,6 +278,163 @@ typedef struct acpi_table_boot
/*******************************************************************************
*
+ * CSRT - Core System Resource Table
+ * Version 0
+ *
+ * Conforms to the "Core System Resource Table (CSRT)", November 14, 2011
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_csrt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+
+} ACPI_TABLE_CSRT;
+
+
+/* Resource Group subtable */
+
+typedef struct acpi_csrt_group
+{
+ UINT32 Length;
+ UINT32 VendorId;
+ UINT32 SubvendorId;
+ UINT16 DeviceId;
+ UINT16 SubdeviceId;
+ UINT16 Revision;
+ UINT16 Reserved;
+ UINT32 SharedInfoLength;
+
+ /* Shared data immediately follows (Length = SharedInfoLength) */
+
+} ACPI_CSRT_GROUP;
+
+/* Shared Info subtable */
+
+typedef struct acpi_csrt_shared_info
+{
+ UINT16 MajorVersion;
+ UINT16 MinorVersion;
+ UINT32 MmioBaseLow;
+ UINT32 MmioBaseHigh;
+ UINT32 GsiInterrupt;
+ UINT8 InterruptPolarity;
+ UINT8 InterruptMode;
+ UINT8 NumChannels;
+ UINT8 DmaAddressWidth;
+ UINT16 BaseRequestLine;
+ UINT16 NumHandshakeSignals;
+ UINT32 MaxBlockSize;
+
+ /* Resource descriptors immediately follow (Length = Group Length - SharedInfoLength) */
+
+} ACPI_CSRT_SHARED_INFO;
+
+/* Resource Descriptor subtable */
+
+typedef struct acpi_csrt_descriptor
+{
+ UINT32 Length;
+ UINT16 Type;
+ UINT16 Subtype;
+ UINT32 Uid;
+
+ /* Resource-specific information immediately follows */
+
+} ACPI_CSRT_DESCRIPTOR;
+
+
+/* Resource Types */
+
+#define ACPI_CSRT_TYPE_INTERRUPT 0x0001
+#define ACPI_CSRT_TYPE_TIMER 0x0002
+#define ACPI_CSRT_TYPE_DMA 0x0003
+
+/* Resource Subtypes */
+
+#define ACPI_CSRT_XRUPT_LINE 0x0000
+#define ACPI_CSRT_XRUPT_CONTROLLER 0x0001
+#define ACPI_CSRT_TIMER 0x0000
+#define ACPI_CSRT_DMA_CHANNEL 0x0000
+#define ACPI_CSRT_DMA_CONTROLLER 0x0001
+
+
+/*******************************************************************************
+ *
+ * DBG2 - Debug Port Table 2
+ * Version 0 (Both main table and subtables)
+ *
+ * Conforms to "Microsoft Debug Port Table 2 (DBG2)", December 10, 2015
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_dbg2
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT32 InfoOffset;
+ UINT32 InfoCount;
+
+} ACPI_TABLE_DBG2;
+
+
+typedef struct acpi_dbg2_header
+{
+ UINT32 InfoOffset;
+ UINT32 InfoCount;
+
+} ACPI_DBG2_HEADER;
+
+
+/* Debug Device Information Subtable */
+
+typedef struct acpi_dbg2_device
+{
+ UINT8 Revision;
+ UINT16 Length;
+ UINT8 RegisterCount; /* Number of BaseAddress registers */
+ UINT16 NamepathLength;
+ UINT16 NamepathOffset;
+ UINT16 OemDataLength;
+ UINT16 OemDataOffset;
+ UINT16 PortType;
+ UINT16 PortSubtype;
+ UINT16 Reserved;
+ UINT16 BaseAddressOffset;
+ UINT16 AddressSizeOffset;
+ /*
+ * Data that follows:
+ * BaseAddress (required) - Each in 12-byte Generic Address Structure format.
+ * AddressSize (required) - Array of UINT32 sizes corresponding to each BaseAddress register.
+ * Namepath (required) - Null terminated string. Single dot if not supported.
+ * OemData (optional) - Length is OemDataLength.
+ */
+} ACPI_DBG2_DEVICE;
+
+/* Types for PortType field above */
+
+#define ACPI_DBG2_SERIAL_PORT 0x8000
+#define ACPI_DBG2_1394_PORT 0x8001
+#define ACPI_DBG2_USB_PORT 0x8002
+#define ACPI_DBG2_NET_PORT 0x8003
+
+/* Subtypes for PortSubtype field above */
+
+#define ACPI_DBG2_16550_COMPATIBLE 0x0000
+#define ACPI_DBG2_16550_SUBSET 0x0001
+#define ACPI_DBG2_ARM_PL011 0x0003
+#define ACPI_DBG2_ARM_SBSA_32BIT 0x000D
+#define ACPI_DBG2_ARM_SBSA_GENERIC 0x000E
+#define ACPI_DBG2_ARM_DCC 0x000F
+#define ACPI_DBG2_BCM2835 0x0010
+
+#define ACPI_DBG2_1394_STANDARD 0x0000
+
+#define ACPI_DBG2_USB_XHCI 0x0000
+#define ACPI_DBG2_USB_EHCI 0x0001
+
+
+/*******************************************************************************
+ *
* DBGP - Debug Port table
* Version 1
*
@@ -287,7 +458,7 @@ typedef struct acpi_table_dbgp
* Version 1
*
* Conforms to "Intel Virtualization Technology for Directed I/O",
- * Version 1.2, Sept. 2008
+ * Version 2.3, October 2014
*
******************************************************************************/
@@ -303,6 +474,8 @@ typedef struct acpi_table_dmar
/* Masks for Flags field above */
#define ACPI_DMAR_INTR_REMAP (1)
+#define ACPI_DMAR_X2APIC_OPT_OUT (1<<1)
+#define ACPI_DMAR_X2APIC_MODE (1<<2)
/* DMAR subtable header */
@@ -320,9 +493,10 @@ enum AcpiDmarType
{
ACPI_DMAR_TYPE_HARDWARE_UNIT = 0,
ACPI_DMAR_TYPE_RESERVED_MEMORY = 1,
- ACPI_DMAR_TYPE_ATSR = 2,
- ACPI_DMAR_HARDWARE_AFFINITY = 3,
- ACPI_DMAR_TYPE_RESERVED = 4 /* 4 and greater are reserved */
+ ACPI_DMAR_TYPE_ROOT_ATS = 2,
+ ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3,
+ ACPI_DMAR_TYPE_NAMESPACE = 4,
+ ACPI_DMAR_TYPE_RESERVED = 5 /* 5 and greater are reserved */
};
@@ -338,7 +512,7 @@ typedef struct acpi_dmar_device_scope
} ACPI_DMAR_DEVICE_SCOPE;
-/* Values for EntryType in ACPI_DMAR_DEVICE_SCOPE */
+/* Values for EntryType in ACPI_DMAR_DEVICE_SCOPE - device types */
enum AcpiDmarScopeType
{
@@ -347,7 +521,8 @@ enum AcpiDmarScopeType
ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2,
ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3,
ACPI_DMAR_SCOPE_TYPE_HPET = 4,
- ACPI_DMAR_SCOPE_TYPE_RESERVED = 5 /* 5 and greater are reserved */
+ ACPI_DMAR_SCOPE_TYPE_NAMESPACE = 5,
+ ACPI_DMAR_SCOPE_TYPE_RESERVED = 6 /* 6 and greater are reserved */
};
typedef struct acpi_dmar_pci_path
@@ -359,7 +534,7 @@ typedef struct acpi_dmar_pci_path
/*
- * DMAR Sub-tables, correspond to Type in ACPI_DMAR_HEADER
+ * DMAR Subtables, correspond to Type in ACPI_DMAR_HEADER
*/
/* 0: Hardware Unit Definition */
@@ -424,6 +599,18 @@ typedef struct acpi_dmar_rhsa
} ACPI_DMAR_RHSA;
+/* 4: ACPI Namespace Device Declaration Structure */
+
+typedef struct acpi_dmar_andd
+{
+ ACPI_DMAR_HEADER Header;
+ UINT8 Reserved[3];
+ UINT8 DeviceNumber;
+ char DeviceName[1];
+
+} ACPI_DMAR_ANDD;
+
+
/*******************************************************************************
*
* HPET - High Precision Event Timer table
@@ -574,6 +761,177 @@ typedef struct acpi_ibft_target
/*******************************************************************************
*
+ * IORT - IO Remapping Table
+ *
+ * Conforms to "IO Remapping Table System Software on ARM Platforms",
+ * Document number: ARM DEN 0049B, October 2015
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_iort
+{
+ ACPI_TABLE_HEADER Header;
+ UINT32 NodeCount;
+ UINT32 NodeOffset;
+ UINT32 Reserved;
+
+} ACPI_TABLE_IORT;
+
+
+/*
+ * IORT subtables
+ */
+typedef struct acpi_iort_node
+{
+ UINT8 Type;
+ UINT16 Length;
+ UINT8 Revision;
+ UINT32 Reserved;
+ UINT32 MappingCount;
+ UINT32 MappingOffset;
+ char NodeData[1];
+
+} ACPI_IORT_NODE;
+
+/* Values for subtable Type above */
+
+enum AcpiIortNodeType
+{
+ ACPI_IORT_NODE_ITS_GROUP = 0x00,
+ ACPI_IORT_NODE_NAMED_COMPONENT = 0x01,
+ ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02,
+ ACPI_IORT_NODE_SMMU = 0x03,
+ ACPI_IORT_NODE_SMMU_V3 = 0x04
+};
+
+
+typedef struct acpi_iort_id_mapping
+{
+ UINT32 InputBase; /* Lowest value in input range */
+ UINT32 IdCount; /* Number of IDs */
+ UINT32 OutputBase; /* Lowest value in output range */
+ UINT32 OutputReference; /* A reference to the output node */
+ UINT32 Flags;
+
+} ACPI_IORT_ID_MAPPING;
+
+/* Masks for Flags field above for IORT subtable */
+
+#define ACPI_IORT_ID_SINGLE_MAPPING (1)
+
+
+typedef struct acpi_iort_memory_access
+{
+ UINT32 CacheCoherency;
+ UINT8 Hints;
+ UINT16 Reserved;
+ UINT8 MemoryFlags;
+
+} ACPI_IORT_MEMORY_ACCESS;
+
+/* Values for CacheCoherency field above */
+
+#define ACPI_IORT_NODE_COHERENT 0x00000001 /* The device node is fully coherent */
+#define ACPI_IORT_NODE_NOT_COHERENT 0x00000000 /* The device node is not coherent */
+
+/* Masks for Hints field above */
+
+#define ACPI_IORT_HT_TRANSIENT (1)
+#define ACPI_IORT_HT_WRITE (1<<1)
+#define ACPI_IORT_HT_READ (1<<2)
+#define ACPI_IORT_HT_OVERRIDE (1<<3)
+
+/* Masks for MemoryFlags field above */
+
+#define ACPI_IORT_MF_COHERENCY (1)
+#define ACPI_IORT_MF_ATTRIBUTES (1<<1)
+
+
+/*
+ * IORT node specific subtables
+ */
+typedef struct acpi_iort_its_group
+{
+ UINT32 ItsCount;
+ UINT32 Identifiers[1]; /* GIC ITS identifier arrary */
+
+} ACPI_IORT_ITS_GROUP;
+
+
+typedef struct acpi_iort_named_component
+{
+ UINT32 NodeFlags;
+ UINT64 MemoryProperties; /* Memory access properties */
+ UINT8 MemoryAddressLimit; /* Memory address size limit */
+ char DeviceName[1]; /* Path of namespace object */
+
+} ACPI_IORT_NAMED_COMPONENT;
+
+
+typedef struct acpi_iort_root_complex
+{
+ UINT64 MemoryProperties; /* Memory access properties */
+ UINT32 AtsAttribute;
+ UINT32 PciSegmentNumber;
+
+} ACPI_IORT_ROOT_COMPLEX;
+
+/* Values for AtsAttribute field above */
+
+#define ACPI_IORT_ATS_SUPPORTED 0x00000001 /* The root complex supports ATS */
+#define ACPI_IORT_ATS_UNSUPPORTED 0x00000000 /* The root complex doesn't support ATS */
+
+
+typedef struct acpi_iort_smmu
+{
+ UINT64 BaseAddress; /* SMMU base address */
+ UINT64 Span; /* Length of memory range */
+ UINT32 Model;
+ UINT32 Flags;
+ UINT32 GlobalInterruptOffset;
+ UINT32 ContextInterruptCount;
+ UINT32 ContextInterruptOffset;
+ UINT32 PmuInterruptCount;
+ UINT32 PmuInterruptOffset;
+ UINT64 Interrupts[1]; /* Interrupt array */
+
+} ACPI_IORT_SMMU;
+
+/* Values for Model field above */
+
+#define ACPI_IORT_SMMU_V1 0x00000000 /* Generic SMMUv1 */
+#define ACPI_IORT_SMMU_V2 0x00000001 /* Generic SMMUv2 */
+#define ACPI_IORT_SMMU_CORELINK_MMU400 0x00000002 /* ARM Corelink MMU-400 */
+#define ACPI_IORT_SMMU_CORELINK_MMU500 0x00000003 /* ARM Corelink MMU-500 */
+
+/* Masks for Flags field above */
+
+#define ACPI_IORT_SMMU_DVM_SUPPORTED (1)
+#define ACPI_IORT_SMMU_COHERENT_WALK (1<<1)
+
+
+typedef struct acpi_iort_smmu_v3
+{
+ UINT64 BaseAddress; /* SMMUv3 base address */
+ UINT32 Flags;
+ UINT32 Reserved;
+ UINT64 VatosAddress;
+ UINT32 Model; /* O: generic SMMUv3 */
+ UINT32 EventGsiv;
+ UINT32 PriGsiv;
+ UINT32 GerrGsiv;
+ UINT32 SyncGsiv;
+
+} ACPI_IORT_SMMU_V3;
+
+/* Masks for Flags field above */
+
+#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE (1)
+#define ACPI_IORT_SMMU_V3_HTTU_OVERRIDE (1<<1)
+
+
+/*******************************************************************************
+ *
* IVRS - I/O Virtualization Reporting Structure
* Version 1
*
@@ -772,7 +1130,65 @@ typedef struct acpi_ivrs_memory
/*******************************************************************************
*
- * MCFG - PCI Memory Mapped Configuration table and sub-table
+ * LPIT - Low Power Idle Table
+ *
+ * Conforms to "ACPI Low Power Idle Table (LPIT)" July 2014.
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_lpit
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+
+} ACPI_TABLE_LPIT;
+
+
+/* LPIT subtable header */
+
+typedef struct acpi_lpit_header
+{
+ UINT32 Type; /* Subtable type */
+ UINT32 Length; /* Subtable length */
+ UINT16 UniqueId;
+ UINT16 Reserved;
+ UINT32 Flags;
+
+} ACPI_LPIT_HEADER;
+
+/* Values for subtable Type above */
+
+enum AcpiLpitType
+{
+ ACPI_LPIT_TYPE_NATIVE_CSTATE = 0x00,
+ ACPI_LPIT_TYPE_RESERVED = 0x01 /* 1 and above are reserved */
+};
+
+/* Masks for Flags field above */
+
+#define ACPI_LPIT_STATE_DISABLED (1)
+#define ACPI_LPIT_NO_COUNTER (1<<1)
+
+/*
+ * LPIT subtables, correspond to Type in ACPI_LPIT_HEADER
+ */
+
+/* 0x00: Native C-state instruction based LPI structure */
+
+typedef struct acpi_lpit_native
+{
+ ACPI_LPIT_HEADER Header;
+ ACPI_GENERIC_ADDRESS EntryTrigger;
+ UINT32 Residency;
+ UINT32 Latency;
+ ACPI_GENERIC_ADDRESS ResidencyCounter;
+ UINT64 CounterFrequency;
+
+} ACPI_LPIT_NATIVE;
+
+
+/*******************************************************************************
+ *
+ * MCFG - PCI Memory Mapped Configuration table and subtable
* Version 1
*
* Conforms to "PCI Firmware Specification", Revision 3.0, June 20, 2005
@@ -831,86 +1247,75 @@ typedef struct acpi_table_mchi
/*******************************************************************************
*
- * SLIC - Software Licensing Description Table
- * Version 1
+ * MSDM - Microsoft Data Management table
*
- * Conforms to "OEM Activation 2.0 for Windows Vista Operating Systems",
- * Copyright 2006
+ * Conforms to "Microsoft Software Licensing Tables (SLIC and MSDM)",
+ * November 29, 2011. Copyright 2011 Microsoft
*
******************************************************************************/
-/* Basic SLIC table is only the common ACPI header */
+/* Basic MSDM table is only the common ACPI header */
-typedef struct acpi_table_slic
+typedef struct acpi_table_msdm
{
ACPI_TABLE_HEADER Header; /* Common ACPI table header */
-} ACPI_TABLE_SLIC;
+} ACPI_TABLE_MSDM;
-/* Common SLIC subtable header */
+/*******************************************************************************
+ *
+ * MTMR - MID Timer Table
+ * Version 1
+ *
+ * Conforms to "Simple Firmware Interface Specification",
+ * Draft 0.8.2, Oct 19, 2010
+ * NOTE: The ACPI MTMR is equivalent to the SFI MTMR table.
+ *
+ ******************************************************************************/
-typedef struct acpi_slic_header
+typedef struct acpi_table_mtmr
{
- UINT32 Type;
- UINT32 Length;
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
-} ACPI_SLIC_HEADER;
+} ACPI_TABLE_MTMR;
-/* Values for Type field above */
+/* MTMR entry */
-enum AcpiSlicType
+typedef struct acpi_mtmr_entry
{
- ACPI_SLIC_TYPE_PUBLIC_KEY = 0,
- ACPI_SLIC_TYPE_WINDOWS_MARKER = 1,
- ACPI_SLIC_TYPE_RESERVED = 2 /* 2 and greater are reserved */
-};
+ ACPI_GENERIC_ADDRESS PhysicalAddress;
+ UINT32 Frequency;
+ UINT32 Irq;
+} ACPI_MTMR_ENTRY;
-/*
- * SLIC Sub-tables, correspond to Type in ACPI_SLIC_HEADER
- */
-
-/* 0: Public Key Structure */
-
-typedef struct acpi_slic_key
-{
- ACPI_SLIC_HEADER Header;
- UINT8 KeyType;
- UINT8 Version;
- UINT16 Reserved;
- UINT32 Algorithm;
- char Magic[4];
- UINT32 BitLength;
- UINT32 Exponent;
- UINT8 Modulus[128];
-
-} ACPI_SLIC_KEY;
+/*******************************************************************************
+ *
+ * SLIC - Software Licensing Description Table
+ *
+ * Conforms to "Microsoft Software Licensing Tables (SLIC and MSDM)",
+ * November 29, 2011. Copyright 2011 Microsoft
+ *
+ ******************************************************************************/
-/* 1: Windows Marker Structure */
+/* Basic SLIC table is only the common ACPI header */
-typedef struct acpi_slic_marker
+typedef struct acpi_table_slic
{
- ACPI_SLIC_HEADER Header;
- UINT32 Version;
- char OemId[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */
- char OemTableId[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */
- char WindowsFlag[8];
- UINT32 SlicVersion;
- UINT8 Reserved[16];
- UINT8 Signature[128];
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
-} ACPI_SLIC_MARKER;
+} ACPI_TABLE_SLIC;
/*******************************************************************************
*
* SPCR - Serial Port Console Redirection table
- * Version 1
+ * Version 2
*
* Conforms to "Serial Port Console Redirection Table",
- * Version 1.00, January 11, 2002
+ * Version 1.03, August 10, 2015
*
******************************************************************************/
@@ -944,6 +1349,8 @@ typedef struct acpi_table_spcr
#define ACPI_SPCR_DO_NOT_DISABLE (1)
+/* Values for Interface Type: See the definition of the DBG2 table */
+
/*******************************************************************************
*
@@ -992,21 +1399,103 @@ enum AcpiSpmiInterfaceTypes
/*******************************************************************************
*
* TCPA - Trusted Computing Platform Alliance table
- * Version 1
+ * Version 2
*
- * Conforms to "TCG PC Specific Implementation Specification",
- * Version 1.1, August 18, 2003
+ * Conforms to "TCG ACPI Specification, Family 1.2 and 2.0",
+ * December 19, 2014
+ *
+ * NOTE: There are two versions of the table with the same signature --
+ * the client version and the server version. The common PlatformClass
+ * field is used to differentiate the two types of tables.
*
******************************************************************************/
-typedef struct acpi_table_tcpa
+typedef struct acpi_table_tcpa_hdr
{
ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT16 PlatformClass;
+
+} ACPI_TABLE_TCPA_HDR;
+
+/*
+ * Values for PlatformClass above.
+ * This is how the client and server subtables are differentiated
+ */
+#define ACPI_TCPA_CLIENT_TABLE 0
+#define ACPI_TCPA_SERVER_TABLE 1
+
+
+typedef struct acpi_table_tcpa_client
+{
+ UINT32 MinimumLogLength; /* Minimum length for the event log area */
+ UINT64 LogAddress; /* Address of the event log area */
+
+} ACPI_TABLE_TCPA_CLIENT;
+
+typedef struct acpi_table_tcpa_server
+{
UINT16 Reserved;
- UINT32 MaxLogLength; /* Maximum length for the event log area */
+ UINT64 MinimumLogLength; /* Minimum length for the event log area */
UINT64 LogAddress; /* Address of the event log area */
+ UINT16 SpecRevision;
+ UINT8 DeviceFlags;
+ UINT8 InterruptFlags;
+ UINT8 GpeNumber;
+ UINT8 Reserved2[3];
+ UINT32 GlobalInterrupt;
+ ACPI_GENERIC_ADDRESS Address;
+ UINT32 Reserved3;
+ ACPI_GENERIC_ADDRESS ConfigAddress;
+ UINT8 Group;
+ UINT8 Bus; /* PCI Bus/Segment/Function numbers */
+ UINT8 Device;
+ UINT8 Function;
+
+} ACPI_TABLE_TCPA_SERVER;
+
+/* Values for DeviceFlags above */
+
+#define ACPI_TCPA_PCI_DEVICE (1)
+#define ACPI_TCPA_BUS_PNP (1<<1)
+#define ACPI_TCPA_ADDRESS_VALID (1<<2)
-} ACPI_TABLE_TCPA;
+/* Values for InterruptFlags above */
+
+#define ACPI_TCPA_INTERRUPT_MODE (1)
+#define ACPI_TCPA_INTERRUPT_POLARITY (1<<1)
+#define ACPI_TCPA_SCI_VIA_GPE (1<<2)
+#define ACPI_TCPA_GLOBAL_INTERRUPT (1<<3)
+
+
+/*******************************************************************************
+ *
+ * TPM2 - Trusted Platform Module (TPM) 2.0 Hardware Interface Table
+ * Version 4
+ *
+ * Conforms to "TCG ACPI Specification, Family 1.2 and 2.0",
+ * December 19, 2014
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_tpm2
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT16 PlatformClass;
+ UINT16 Reserved;
+ UINT64 ControlAddress;
+ UINT32 StartMethod;
+
+ /* Platform-specific data follows */
+
+} ACPI_TABLE_TPM2;
+
+/* Values for StartMethod above */
+
+#define ACPI_TPM2_NOT_ALLOWED 0
+#define ACPI_TPM2_START_METHOD 2
+#define ACPI_TPM2_MEMORY_MAPPED 6
+#define ACPI_TPM2_COMMAND_BUFFER 7
+#define ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD 8
/*******************************************************************************
@@ -1030,6 +1519,33 @@ typedef struct acpi_table_uefi
/*******************************************************************************
*
+ * VRTC - Virtual Real Time Clock Table
+ * Version 1
+ *
+ * Conforms to "Simple Firmware Interface Specification",
+ * Draft 0.8.2, Oct 19, 2010
+ * NOTE: The ACPI VRTC is equivalent to The SFI MRTC table.
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_vrtc
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+
+} ACPI_TABLE_VRTC;
+
+/* VRTC entry */
+
+typedef struct acpi_vrtc_entry
+{
+ ACPI_GENERIC_ADDRESS PhysicalAddress;
+ UINT32 Irq;
+
+} ACPI_VRTC_ENTRY;
+
+
+/*******************************************************************************
+ *
* WAET - Windows ACPI Emulated devices Table
* Version 1
*
@@ -1204,4 +1720,3 @@ typedef struct acpi_table_wdrt
#pragma pack()
#endif /* __ACTBL2_H__ */
-
diff --git a/usr/src/uts/intel/sys/acpi/actbl3.h b/usr/src/uts/intel/sys/acpi/actbl3.h
new file mode 100644
index 0000000000..ef40f19fc0
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/actbl3.h
@@ -0,0 +1,925 @@
+/******************************************************************************
+ *
+ * Name: actbl3.h - ACPI Table Definitions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACTBL3_H__
+#define __ACTBL3_H__
+
+
+/*******************************************************************************
+ *
+ * Additional ACPI Tables (3)
+ *
+ * These tables are not consumed directly by the ACPICA subsystem, but are
+ * included here to support device drivers and the AML disassembler.
+ *
+ * In general, the tables in this file are fully defined within the ACPI
+ * specification.
+ *
+ ******************************************************************************/
+
+
+/*
+ * Values for description table header signatures for tables defined in this
+ * file. Useful because they make it more difficult to inadvertently type in
+ * the wrong signature.
+ */
+#define ACPI_SIG_BGRT "BGRT" /* Boot Graphics Resource Table */
+#define ACPI_SIG_DRTM "DRTM" /* Dynamic Root of Trust for Measurement table */
+#define ACPI_SIG_FPDT "FPDT" /* Firmware Performance Data Table */
+#define ACPI_SIG_GTDT "GTDT" /* Generic Timer Description Table */
+#define ACPI_SIG_MPST "MPST" /* Memory Power State Table */
+#define ACPI_SIG_PCCT "PCCT" /* Platform Communications Channel Table */
+#define ACPI_SIG_PMTT "PMTT" /* Platform Memory Topology Table */
+#define ACPI_SIG_RASF "RASF" /* RAS Feature table */
+#define ACPI_SIG_STAO "STAO" /* Status Override table */
+#define ACPI_SIG_WPBT "WPBT" /* Windows Platform Binary Table */
+#define ACPI_SIG_XENV "XENV" /* Xen Environment table */
+
+#define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */
+#define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */
+
+/* Reserved table signatures */
+
+#define ACPI_SIG_MATR "MATR" /* Memory Address Translation Table */
+#define ACPI_SIG_MSDM "MSDM" /* Microsoft Data Management Table */
+
+/*
+ * All tables must be byte-packed to match the ACPI specification, since
+ * the tables are provided by the system BIOS.
+ */
+#pragma pack(1)
+
+/*
+ * Note: C bitfields are not used for this reason:
+ *
+ * "Bitfields are great and easy to read, but unfortunately the C language
+ * does not specify the layout of bitfields in memory, which means they are
+ * essentially useless for dealing with packed data in on-disk formats or
+ * binary wire protocols." (Or ACPI tables and buffers.) "If you ask me,
+ * this decision was a design error in C. Ritchie could have picked an order
+ * and stuck with it." Norman Ramsey.
+ * See http://stackoverflow.com/a/1053662/41661
+ */
+
+
+/*******************************************************************************
+ *
+ * BGRT - Boot Graphics Resource Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_bgrt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT16 Version;
+ UINT8 Status;
+ UINT8 ImageType;
+ UINT64 ImageAddress;
+ UINT32 ImageOffsetX;
+ UINT32 ImageOffsetY;
+
+} ACPI_TABLE_BGRT;
+
+
+/*******************************************************************************
+ *
+ * DRTM - Dynamic Root of Trust for Measurement table
+ * Conforms to "TCG D-RTM Architecture" June 17 2013, Version 1.0.0
+ * Table version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_drtm
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT64 EntryBaseAddress;
+ UINT64 EntryLength;
+ UINT32 EntryAddress32;
+ UINT64 EntryAddress64;
+ UINT64 ExitAddress;
+ UINT64 LogAreaAddress;
+ UINT32 LogAreaLength;
+ UINT64 ArchDependentAddress;
+ UINT32 Flags;
+
+} ACPI_TABLE_DRTM;
+
+/* Flag Definitions for above */
+
+#define ACPI_DRTM_ACCESS_ALLOWED (1)
+#define ACPI_DRTM_ENABLE_GAP_CODE (1<<1)
+#define ACPI_DRTM_INCOMPLETE_MEASUREMENTS (1<<2)
+#define ACPI_DRTM_AUTHORITY_ORDER (1<<3)
+
+
+/* 1) Validated Tables List (64-bit addresses) */
+
+typedef struct acpi_drtm_vtable_list
+{
+ UINT32 ValidatedTableCount;
+ UINT64 ValidatedTables[1];
+
+} ACPI_DRTM_VTABLE_LIST;
+
+/* 2) Resources List (of Resource Descriptors) */
+
+/* Resource Descriptor */
+
+typedef struct acpi_drtm_resource
+{
+ UINT8 Size[7];
+ UINT8 Type;
+ UINT64 Address;
+
+} ACPI_DRTM_RESOURCE;
+
+typedef struct acpi_drtm_resource_list
+{
+ UINT32 ResourceCount;
+ ACPI_DRTM_RESOURCE Resources[1];
+
+} ACPI_DRTM_RESOURCE_LIST;
+
+/* 3) Platform-specific Identifiers List */
+
+typedef struct acpi_drtm_dps_id
+{
+ UINT32 DpsIdLength;
+ UINT8 DpsId[16];
+
+} ACPI_DRTM_DPS_ID;
+
+
+/*******************************************************************************
+ *
+ * FPDT - Firmware Performance Data Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_fpdt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+
+} ACPI_TABLE_FPDT;
+
+
+/* FPDT subtable header (Performance Record Structure) */
+
+typedef struct acpi_fpdt_header
+{
+ UINT16 Type;
+ UINT8 Length;
+ UINT8 Revision;
+
+} ACPI_FPDT_HEADER;
+
+/* Values for Type field above */
+
+enum AcpiFpdtType
+{
+ ACPI_FPDT_TYPE_BOOT = 0,
+ ACPI_FPDT_TYPE_S3PERF = 1
+};
+
+
+/*
+ * FPDT subtables
+ */
+
+/* 0: Firmware Basic Boot Performance Record */
+
+typedef struct acpi_fpdt_boot_pointer
+{
+ ACPI_FPDT_HEADER Header;
+ UINT8 Reserved[4];
+ UINT64 Address;
+
+} ACPI_FPDT_BOOT_POINTER;
+
+
+/* 1: S3 Performance Table Pointer Record */
+
+typedef struct acpi_fpdt_s3pt_pointer
+{
+ ACPI_FPDT_HEADER Header;
+ UINT8 Reserved[4];
+ UINT64 Address;
+
+} ACPI_FPDT_S3PT_POINTER;
+
+
+/*
+ * S3PT - S3 Performance Table. This table is pointed to by the
+ * S3 Pointer Record above.
+ */
+typedef struct acpi_table_s3pt
+{
+ UINT8 Signature[4]; /* "S3PT" */
+ UINT32 Length;
+
+} ACPI_TABLE_S3PT;
+
+
+/*
+ * S3PT Subtables (Not part of the actual FPDT)
+ */
+
+/* Values for Type field in S3PT header */
+
+enum AcpiS3ptType
+{
+ ACPI_S3PT_TYPE_RESUME = 0,
+ ACPI_S3PT_TYPE_SUSPEND = 1,
+ ACPI_FPDT_BOOT_PERFORMANCE = 2
+};
+
+typedef struct acpi_s3pt_resume
+{
+ ACPI_FPDT_HEADER Header;
+ UINT32 ResumeCount;
+ UINT64 FullResume;
+ UINT64 AverageResume;
+
+} ACPI_S3PT_RESUME;
+
+typedef struct acpi_s3pt_suspend
+{
+ ACPI_FPDT_HEADER Header;
+ UINT64 SuspendStart;
+ UINT64 SuspendEnd;
+
+} ACPI_S3PT_SUSPEND;
+
+
+/*
+ * FPDT Boot Performance Record (Not part of the actual FPDT)
+ */
+typedef struct acpi_fpdt_boot
+{
+ ACPI_FPDT_HEADER Header;
+ UINT8 Reserved[4];
+ UINT64 ResetEnd;
+ UINT64 LoadStart;
+ UINT64 StartupStart;
+ UINT64 ExitServicesEntry;
+ UINT64 ExitServicesExit;
+
+} ACPI_FPDT_BOOT;
+
+
+/*******************************************************************************
+ *
+ * GTDT - Generic Timer Description Table (ACPI 5.1)
+ * Version 2
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_gtdt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT64 CounterBlockAddresss;
+ UINT32 Reserved;
+ UINT32 SecureEl1Interrupt;
+ UINT32 SecureEl1Flags;
+ UINT32 NonSecureEl1Interrupt;
+ UINT32 NonSecureEl1Flags;
+ UINT32 VirtualTimerInterrupt;
+ UINT32 VirtualTimerFlags;
+ UINT32 NonSecureEl2Interrupt;
+ UINT32 NonSecureEl2Flags;
+ UINT64 CounterReadBlockAddress;
+ UINT32 PlatformTimerCount;
+ UINT32 PlatformTimerOffset;
+
+} ACPI_TABLE_GTDT;
+
+/* Flag Definitions: Timer Block Physical Timers and Virtual timers */
+
+#define ACPI_GTDT_INTERRUPT_MODE (1)
+#define ACPI_GTDT_INTERRUPT_POLARITY (1<<1)
+#define ACPI_GTDT_ALWAYS_ON (1<<2)
+
+
+/* Common GTDT subtable header */
+
+typedef struct acpi_gtdt_header
+{
+ UINT8 Type;
+ UINT16 Length;
+
+} ACPI_GTDT_HEADER;
+
+/* Values for GTDT subtable type above */
+
+enum AcpiGtdtType
+{
+ ACPI_GTDT_TYPE_TIMER_BLOCK = 0,
+ ACPI_GTDT_TYPE_WATCHDOG = 1,
+ ACPI_GTDT_TYPE_RESERVED = 2 /* 2 and greater are reserved */
+};
+
+
+/* GTDT Subtables, correspond to Type in acpi_gtdt_header */
+
+/* 0: Generic Timer Block */
+
+typedef struct acpi_gtdt_timer_block
+{
+ ACPI_GTDT_HEADER Header;
+ UINT8 Reserved;
+ UINT64 BlockAddress;
+ UINT32 TimerCount;
+ UINT32 TimerOffset;
+
+} ACPI_GTDT_TIMER_BLOCK;
+
+/* Timer Sub-Structure, one per timer */
+
+typedef struct acpi_gtdt_timer_entry
+{
+ UINT8 FrameNumber;
+ UINT8 Reserved[3];
+ UINT64 BaseAddress;
+ UINT64 El0BaseAddress;
+ UINT32 TimerInterrupt;
+ UINT32 TimerFlags;
+ UINT32 VirtualTimerInterrupt;
+ UINT32 VirtualTimerFlags;
+ UINT32 CommonFlags;
+
+} ACPI_GTDT_TIMER_ENTRY;
+
+/* Flag Definitions: TimerFlags and VirtualTimerFlags above */
+
+#define ACPI_GTDT_GT_IRQ_MODE (1)
+#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1)
+
+/* Flag Definitions: CommonFlags above */
+
+#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
+#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
+
+
+/* 1: SBSA Generic Watchdog Structure */
+
+typedef struct acpi_gtdt_watchdog
+{
+ ACPI_GTDT_HEADER Header;
+ UINT8 Reserved;
+ UINT64 RefreshFrameAddress;
+ UINT64 ControlFrameAddress;
+ UINT32 TimerInterrupt;
+ UINT32 TimerFlags;
+
+} ACPI_GTDT_WATCHDOG;
+
+/* Flag Definitions: TimerFlags above */
+
+#define ACPI_GTDT_WATCHDOG_IRQ_MODE (1)
+#define ACPI_GTDT_WATCHDOG_IRQ_POLARITY (1<<1)
+#define ACPI_GTDT_WATCHDOG_SECURE (1<<2)
+
+
+/*******************************************************************************
+ *
+ * MPST - Memory Power State Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+#define ACPI_MPST_CHANNEL_INFO \
+ UINT8 ChannelId; \
+ UINT8 Reserved1[3]; \
+ UINT16 PowerNodeCount; \
+ UINT16 Reserved2;
+
+/* Main table */
+
+typedef struct acpi_table_mpst
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ ACPI_MPST_CHANNEL_INFO /* Platform Communication Channel */
+
+} ACPI_TABLE_MPST;
+
+
+/* Memory Platform Communication Channel Info */
+
+typedef struct acpi_mpst_channel
+{
+ ACPI_MPST_CHANNEL_INFO /* Platform Communication Channel */
+
+} ACPI_MPST_CHANNEL;
+
+
+/* Memory Power Node Structure */
+
+typedef struct acpi_mpst_power_node
+{
+ UINT8 Flags;
+ UINT8 Reserved1;
+ UINT16 NodeId;
+ UINT32 Length;
+ UINT64 RangeAddress;
+ UINT64 RangeLength;
+ UINT32 NumPowerStates;
+ UINT32 NumPhysicalComponents;
+
+} ACPI_MPST_POWER_NODE;
+
+/* Values for Flags field above */
+
+#define ACPI_MPST_ENABLED 1
+#define ACPI_MPST_POWER_MANAGED 2
+#define ACPI_MPST_HOT_PLUG_CAPABLE 4
+
+
+/* Memory Power State Structure (follows POWER_NODE above) */
+
+typedef struct acpi_mpst_power_state
+{
+ UINT8 PowerState;
+ UINT8 InfoIndex;
+
+} ACPI_MPST_POWER_STATE;
+
+
+/* Physical Component ID Structure (follows POWER_STATE above) */
+
+typedef struct acpi_mpst_component
+{
+ UINT16 ComponentId;
+
+} ACPI_MPST_COMPONENT;
+
+
+/* Memory Power State Characteristics Structure (follows all POWER_NODEs) */
+
+typedef struct acpi_mpst_data_hdr
+{
+ UINT16 CharacteristicsCount;
+ UINT16 Reserved;
+
+} ACPI_MPST_DATA_HDR;
+
+typedef struct acpi_mpst_power_data
+{
+ UINT8 StructureId;
+ UINT8 Flags;
+ UINT16 Reserved1;
+ UINT32 AveragePower;
+ UINT32 PowerSaving;
+ UINT64 ExitLatency;
+ UINT64 Reserved2;
+
+} ACPI_MPST_POWER_DATA;
+
+/* Values for Flags field above */
+
+#define ACPI_MPST_PRESERVE 1
+#define ACPI_MPST_AUTOENTRY 2
+#define ACPI_MPST_AUTOEXIT 4
+
+
+/* Shared Memory Region (not part of an ACPI table) */
+
+typedef struct acpi_mpst_shared
+{
+ UINT32 Signature;
+ UINT16 PccCommand;
+ UINT16 PccStatus;
+ UINT32 CommandRegister;
+ UINT32 StatusRegister;
+ UINT32 PowerStateId;
+ UINT32 PowerNodeId;
+ UINT64 EnergyConsumed;
+ UINT64 AveragePower;
+
+} ACPI_MPST_SHARED;
+
+
+/*******************************************************************************
+ *
+ * PCCT - Platform Communications Channel Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_pcct
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT32 Flags;
+ UINT64 Reserved;
+
+} ACPI_TABLE_PCCT;
+
+/* Values for Flags field above */
+
+#define ACPI_PCCT_DOORBELL 1
+
+/* Values for subtable type in ACPI_SUBTABLE_HEADER */
+
+enum AcpiPcctType
+{
+ ACPI_PCCT_TYPE_GENERIC_SUBSPACE = 0,
+ ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE = 1,
+ ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2 = 2, /* ACPI 6.1 */
+ ACPI_PCCT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
+};
+
+/*
+ * PCCT Subtables, correspond to Type in ACPI_SUBTABLE_HEADER
+ */
+
+/* 0: Generic Communications Subspace */
+
+typedef struct acpi_pcct_subspace
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT8 Reserved[6];
+ UINT64 BaseAddress;
+ UINT64 Length;
+ ACPI_GENERIC_ADDRESS DoorbellRegister;
+ UINT64 PreserveMask;
+ UINT64 WriteMask;
+ UINT32 Latency;
+ UINT32 MaxAccessRate;
+ UINT16 MinTurnaroundTime;
+
+} ACPI_PCCT_SUBSPACE;
+
+
+/* 1: HW-reduced Communications Subspace (ACPI 5.1) */
+
+typedef struct acpi_pcct_hw_reduced
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT32 DoorbellInterrupt;
+ UINT8 Flags;
+ UINT8 Reserved;
+ UINT64 BaseAddress;
+ UINT64 Length;
+ ACPI_GENERIC_ADDRESS DoorbellRegister;
+ UINT64 PreserveMask;
+ UINT64 WriteMask;
+ UINT32 Latency;
+ UINT32 MaxAccessRate;
+ UINT16 MinTurnaroundTime;
+
+} ACPI_PCCT_HW_REDUCED;
+
+
+/* 2: HW-reduced Communications Subspace Type 2 (ACPI 6.1) */
+
+typedef struct acpi_pcct_hw_reduced_type2
+{
+ ACPI_SUBTABLE_HEADER Header;
+ UINT32 DoorbellInterrupt;
+ UINT8 Flags;
+ UINT8 Reserved;
+ UINT64 BaseAddress;
+ UINT64 Length;
+ ACPI_GENERIC_ADDRESS DoorbellRegister;
+ UINT64 PreserveMask;
+ UINT64 WriteMask;
+ UINT32 Latency;
+ UINT32 MaxAccessRate;
+ UINT16 MinTurnaroundTime;
+ ACPI_GENERIC_ADDRESS DoorbellAckRegister;
+ UINT64 AckPreserveMask;
+ UINT64 AckWriteMask;
+
+} ACPI_PCCT_HW_REDUCED_TYPE2;
+
+
+/* Values for doorbell flags above */
+
+#define ACPI_PCCT_INTERRUPT_POLARITY (1)
+#define ACPI_PCCT_INTERRUPT_MODE (1<<1)
+
+
+/*
+ * PCC memory structures (not part of the ACPI table)
+ */
+
+/* Shared Memory Region */
+
+typedef struct acpi_pcct_shared_memory
+{
+ UINT32 Signature;
+ UINT16 Command;
+ UINT16 Status;
+
+} ACPI_PCCT_SHARED_MEMORY;
+
+
+/*******************************************************************************
+ *
+ * PMTT - Platform Memory Topology Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_pmtt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT32 Reserved;
+
+} ACPI_TABLE_PMTT;
+
+
+/* Common header for PMTT subtables that follow main table */
+
+typedef struct acpi_pmtt_header
+{
+ UINT8 Type;
+ UINT8 Reserved1;
+ UINT16 Length;
+ UINT16 Flags;
+ UINT16 Reserved2;
+
+} ACPI_PMTT_HEADER;
+
+/* Values for Type field above */
+
+#define ACPI_PMTT_TYPE_SOCKET 0
+#define ACPI_PMTT_TYPE_CONTROLLER 1
+#define ACPI_PMTT_TYPE_DIMM 2
+#define ACPI_PMTT_TYPE_RESERVED 3 /* 0x03-0xFF are reserved */
+
+/* Values for Flags field above */
+
+#define ACPI_PMTT_TOP_LEVEL 0x0001
+#define ACPI_PMTT_PHYSICAL 0x0002
+#define ACPI_PMTT_MEMORY_TYPE 0x000C
+
+
+/*
+ * PMTT subtables, correspond to Type in acpi_pmtt_header
+ */
+
+
+/* 0: Socket Structure */
+
+typedef struct acpi_pmtt_socket
+{
+ ACPI_PMTT_HEADER Header;
+ UINT16 SocketId;
+ UINT16 Reserved;
+
+} ACPI_PMTT_SOCKET;
+
+
+/* 1: Memory Controller subtable */
+
+typedef struct acpi_pmtt_controller
+{
+ ACPI_PMTT_HEADER Header;
+ UINT32 ReadLatency;
+ UINT32 WriteLatency;
+ UINT32 ReadBandwidth;
+ UINT32 WriteBandwidth;
+ UINT16 AccessWidth;
+ UINT16 Alignment;
+ UINT16 Reserved;
+ UINT16 DomainCount;
+
+} ACPI_PMTT_CONTROLLER;
+
+/* 1a: Proximity Domain substructure */
+
+typedef struct acpi_pmtt_domain
+{
+ UINT32 ProximityDomain;
+
+} ACPI_PMTT_DOMAIN;
+
+
+/* 2: Physical Component Identifier (DIMM) */
+
+typedef struct acpi_pmtt_physical_component
+{
+ ACPI_PMTT_HEADER Header;
+ UINT16 ComponentId;
+ UINT16 Reserved;
+ UINT32 MemorySize;
+ UINT32 BiosHandle;
+
+} ACPI_PMTT_PHYSICAL_COMPONENT;
+
+
+/*******************************************************************************
+ *
+ * RASF - RAS Feature Table (ACPI 5.0)
+ * Version 1
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_rasf
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT8 ChannelId[12];
+
+} ACPI_TABLE_RASF;
+
+/* RASF Platform Communication Channel Shared Memory Region */
+
+typedef struct acpi_rasf_shared_memory
+{
+ UINT32 Signature;
+ UINT16 Command;
+ UINT16 Status;
+ UINT16 Version;
+ UINT8 Capabilities[16];
+ UINT8 SetCapabilities[16];
+ UINT16 NumParameterBlocks;
+ UINT32 SetCapabilitiesStatus;
+
+} ACPI_RASF_SHARED_MEMORY;
+
+/* RASF Parameter Block Structure Header */
+
+typedef struct acpi_rasf_parameter_block
+{
+ UINT16 Type;
+ UINT16 Version;
+ UINT16 Length;
+
+} ACPI_RASF_PARAMETER_BLOCK;
+
+/* RASF Parameter Block Structure for PATROL_SCRUB */
+
+typedef struct acpi_rasf_patrol_scrub_parameter
+{
+ ACPI_RASF_PARAMETER_BLOCK Header;
+ UINT16 PatrolScrubCommand;
+ UINT64 RequestedAddressRange[2];
+ UINT64 ActualAddressRange[2];
+ UINT16 Flags;
+ UINT8 RequestedSpeed;
+
+} ACPI_RASF_PATROL_SCRUB_PARAMETER;
+
+/* Masks for Flags and Speed fields above */
+
+#define ACPI_RASF_SCRUBBER_RUNNING 1
+#define ACPI_RASF_SPEED (7<<1)
+#define ACPI_RASF_SPEED_SLOW (0<<1)
+#define ACPI_RASF_SPEED_MEDIUM (4<<1)
+#define ACPI_RASF_SPEED_FAST (7<<1)
+
+/* Channel Commands */
+
+enum AcpiRasfCommands
+{
+ ACPI_RASF_EXECUTE_RASF_COMMAND = 1
+};
+
+/* Platform RAS Capabilities */
+
+enum AcpiRasfCapabiliities
+{
+ ACPI_HW_PATROL_SCRUB_SUPPORTED = 0,
+ ACPI_SW_PATROL_SCRUB_EXPOSED = 1
+};
+
+/* Patrol Scrub Commands */
+
+enum AcpiRasfPatrolScrubCommands
+{
+ ACPI_RASF_GET_PATROL_PARAMETERS = 1,
+ ACPI_RASF_START_PATROL_SCRUBBER = 2,
+ ACPI_RASF_STOP_PATROL_SCRUBBER = 3
+};
+
+/* Channel Command flags */
+
+#define ACPI_RASF_GENERATE_SCI (1<<15)
+
+/* Status values */
+
+enum AcpiRasfStatus
+{
+ ACPI_RASF_SUCCESS = 0,
+ ACPI_RASF_NOT_VALID = 1,
+ ACPI_RASF_NOT_SUPPORTED = 2,
+ ACPI_RASF_BUSY = 3,
+ ACPI_RASF_FAILED = 4,
+ ACPI_RASF_ABORTED = 5,
+ ACPI_RASF_INVALID_DATA = 6
+};
+
+/* Status flags */
+
+#define ACPI_RASF_COMMAND_COMPLETE (1)
+#define ACPI_RASF_SCI_DOORBELL (1<<1)
+#define ACPI_RASF_ERROR (1<<2)
+#define ACPI_RASF_STATUS (0x1F<<3)
+
+
+/*******************************************************************************
+ *
+ * STAO - Status Override Table (_STA override) - ACPI 6.0
+ * Version 1
+ *
+ * Conforms to "ACPI Specification for Status Override Table"
+ * 6 January 2015
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_stao
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT8 IgnoreUart;
+
+} ACPI_TABLE_STAO;
+
+
+/*******************************************************************************
+ *
+ * WPBT - Windows Platform Environment Table (ACPI 6.0)
+ * Version 1
+ *
+ * Conforms to "Windows Platform Binary Table (WPBT)" 29 November 2011
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_wpbt
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT32 HandoffSize;
+ UINT64 HandoffAddress;
+ UINT8 Layout;
+ UINT8 Type;
+ UINT16 ArgumentsLength;
+
+} ACPI_TABLE_WPBT;
+
+
+/*******************************************************************************
+ *
+ * XENV - Xen Environment Table (ACPI 6.0)
+ * Version 1
+ *
+ * Conforms to "ACPI Specification for Xen Environment Table" 4 January 2015
+ *
+ ******************************************************************************/
+
+typedef struct acpi_table_xenv
+{
+ ACPI_TABLE_HEADER Header; /* Common ACPI table header */
+ UINT64 GrantTableAddress;
+ UINT64 GrantTableSize;
+ UINT32 EventInterrupt;
+ UINT8 EventFlags;
+
+} ACPI_TABLE_XENV;
+
+
+/* Reset to default packing */
+
+#pragma pack()
+
+#endif /* __ACTBL3_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/actypes.h b/usr/src/uts/intel/sys/acpi/actypes.h
index 095e589607..395b915383 100644
--- a/usr/src/uts/intel/sys/acpi/actypes.h
+++ b/usr/src/uts/intel/sys/acpi/actypes.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -55,8 +55,6 @@
#error ACPI_MACHINE_WIDTH not defined
#endif
-/*! [Begin] no source code translation */
-
/*
* Data type ranges
* Note: These macros are designed to be compiler independent as well as
@@ -124,13 +122,16 @@
*
******************************************************************************/
+#ifndef ACPI_USE_SYSTEM_INTTYPES
+
typedef unsigned char BOOLEAN;
typedef unsigned char UINT8;
typedef unsigned short UINT16;
+typedef short INT16;
typedef COMPILER_DEPENDENT_UINT64 UINT64;
typedef COMPILER_DEPENDENT_INT64 INT64;
-/*! [End] no source code translation !*/
+#endif /* ACPI_USE_SYSTEM_INTTYPES */
/*
* Value returned by AcpiOsGetThreadId. There is no standard "thread_id"
@@ -151,12 +152,12 @@ typedef COMPILER_DEPENDENT_INT64 INT64;
#if ACPI_MACHINE_WIDTH == 64
-/*! [Begin] no source code translation (keep the typedefs as-is) */
+#ifndef ACPI_USE_SYSTEM_INTTYPES
typedef unsigned int UINT32;
typedef int INT32;
-/*! [End] no source code translation !*/
+#endif /* ACPI_USE_SYSTEM_INTTYPES */
typedef INT64 ACPI_NATIVE_INT;
@@ -190,19 +191,39 @@ typedef UINT64 ACPI_PHYSICAL_ADDRESS;
#elif ACPI_MACHINE_WIDTH == 32
-/*! [Begin] no source code translation (keep the typedefs as-is) */
+#ifndef ACPI_USE_SYSTEM_INTTYPES
typedef unsigned int UINT32;
typedef int INT32;
-/*! [End] no source code translation !*/
+#endif /* ACPI_USE_SYSTEM_INTTYPES */
typedef INT32 ACPI_NATIVE_INT;
typedef UINT32 ACPI_SIZE;
+
+#ifdef ACPI_32BIT_PHYSICAL_ADDRESS
+
+/*
+ * OSPMs can define this to shrink the size of the structures for 32-bit
+ * none PAE environment. ASL compiler may always define this to generate
+ * 32-bit OSPM compliant tables.
+ */
typedef UINT32 ACPI_IO_ADDRESS;
typedef UINT32 ACPI_PHYSICAL_ADDRESS;
+#else /* ACPI_32BIT_PHYSICAL_ADDRESS */
+
+/*
+ * It is reported that, after some calculations, the physical addresses can
+ * wrap over the 32-bit boundary on 32-bit PAE environment.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=87971
+ */
+typedef UINT64 ACPI_IO_ADDRESS;
+typedef UINT64 ACPI_PHYSICAL_ADDRESS;
+
+#endif /* ACPI_32BIT_PHYSICAL_ADDRESS */
+
#define ACPI_MAX_PTR ACPI_UINT32_MAX
#define ACPI_SIZE_MAX ACPI_UINT32_MAX
@@ -295,7 +316,7 @@ typedef UINT32 ACPI_PHYSICAL_ADDRESS;
/*
* Some compilers complain about unused variables. Sometimes we don't want to
* use all the variables (for example, _AcpiModuleName). This allows us
- * to to tell the compiler in a per-variable manner that a variable
+ * to tell the compiler in a per-variable manner that a variable
* is unused
*/
#ifndef ACPI_UNUSED_VAR
@@ -303,13 +324,69 @@ typedef UINT32 ACPI_PHYSICAL_ADDRESS;
#endif
/*
- * All ACPICA functions that are available to the rest of the kernel are
- * tagged with this macro which can be defined as appropriate for the host.
+ * All ACPICA external functions that are available to the rest of the kernel
+ * are tagged with thes macros which can be defined as appropriate for the host.
+ *
+ * Notes:
+ * ACPI_EXPORT_SYMBOL_INIT is used for initialization and termination
+ * interfaces that may need special processing.
+ * ACPI_EXPORT_SYMBOL is used for all other public external functions.
*/
+#ifndef ACPI_EXPORT_SYMBOL_INIT
+#define ACPI_EXPORT_SYMBOL_INIT(Symbol)
+#endif
+
#ifndef ACPI_EXPORT_SYMBOL
#define ACPI_EXPORT_SYMBOL(Symbol)
#endif
+/*
+ * Compiler/Clibrary-dependent debug initialization. Used for ACPICA
+ * utilities only.
+ */
+#ifndef ACPI_DEBUG_INITIALIZE
+#define ACPI_DEBUG_INITIALIZE()
+#endif
+
+
+/*******************************************************************************
+ *
+ * Configuration
+ *
+ ******************************************************************************/
+
+#ifdef ACPI_NO_MEM_ALLOCATIONS
+
+#define ACPI_ALLOCATE(a) NULL
+#define ACPI_ALLOCATE_ZEROED(a) NULL
+#define ACPI_FREE(a)
+#define ACPI_MEM_TRACKING(a)
+
+#else /* ACPI_NO_MEM_ALLOCATIONS */
+
+#ifdef ACPI_DBG_TRACK_ALLOCATIONS
+/*
+ * Memory allocation tracking (used by AcpiExec to detect memory leaks)
+ */
+#define ACPI_MEM_PARAMETERS _COMPONENT, _AcpiModuleName, __LINE__
+#define ACPI_ALLOCATE(a) AcpiUtAllocateAndTrack ((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
+#define ACPI_ALLOCATE_ZEROED(a) AcpiUtAllocateZeroedAndTrack ((ACPI_SIZE) (a), ACPI_MEM_PARAMETERS)
+#define ACPI_FREE(a) AcpiUtFreeAndTrack (a, ACPI_MEM_PARAMETERS)
+#define ACPI_MEM_TRACKING(a) a
+
+#else
+/*
+ * Normal memory allocation directly via the OS services layer
+ */
+#define ACPI_ALLOCATE(a) AcpiOsAllocate ((ACPI_SIZE) (a))
+#define ACPI_ALLOCATE_ZEROED(a) AcpiOsAllocateZeroed ((ACPI_SIZE) (a))
+#define ACPI_FREE(a) AcpiOsFree (a)
+#define ACPI_MEM_TRACKING(a)
+
+#endif /* ACPI_DBG_TRACK_ALLOCATIONS */
+
+#endif /* ACPI_NO_MEM_ALLOCATIONS */
+
/******************************************************************************
*
@@ -327,6 +404,7 @@ typedef UINT32 ACPI_PHYSICAL_ADDRESS;
#define ACPI_PM1_REGISTER_WIDTH 16
#define ACPI_PM2_REGISTER_WIDTH 8
#define ACPI_PM_TIMER_WIDTH 32
+#define ACPI_RESET_REGISTER_WIDTH 8
/* Names within the namespace are 4 bytes long */
@@ -346,7 +424,7 @@ typedef UINT32 ACPI_PHYSICAL_ADDRESS;
/* PM Timer ticks per second (HZ) */
-#define PM_TIMER_FREQUENCY 3579545
+#define ACPI_PM_TIMER_FREQUENCY 3579545
/*******************************************************************************
@@ -381,6 +459,22 @@ typedef char * ACPI_STRING; /* Null terminated ASCII
typedef void * ACPI_HANDLE; /* Actually a ptr to a NS Node */
+/* Time constants for timer calculations */
+
+#define ACPI_MSEC_PER_SEC 1000L
+
+#define ACPI_USEC_PER_MSEC 1000L
+#define ACPI_USEC_PER_SEC 1000000L
+
+#define ACPI_100NSEC_PER_USEC 10L
+#define ACPI_100NSEC_PER_MSEC 10000L
+#define ACPI_100NSEC_PER_SEC 10000000L
+
+#define ACPI_NSEC_PER_USEC 1000L
+#define ACPI_NSEC_PER_MSEC 1000000L
+#define ACPI_NSEC_PER_SEC 1000000000L
+
+
/* Owner IDs are used to track namespace nodes for selective deletion */
typedef UINT8 ACPI_OWNER_ID;
@@ -444,22 +538,32 @@ typedef UINT64 ACPI_INTEGER;
#define ACPI_CAST_PTR(t, p) ((t *) (ACPI_UINTPTR_T) (p))
#define ACPI_CAST_INDIRECT_PTR(t, p) ((t **) (ACPI_UINTPTR_T) (p))
#define ACPI_ADD_PTR(t, a, b) ACPI_CAST_PTR (t, (ACPI_CAST_PTR (UINT8, (a)) + (ACPI_SIZE)(b)))
+#define ACPI_SUB_PTR(t, a, b) ACPI_CAST_PTR (t, (ACPI_CAST_PTR (UINT8, (a)) - (ACPI_SIZE)(b)))
#define ACPI_PTR_DIFF(a, b) (ACPI_SIZE) (ACPI_CAST_PTR (UINT8, (a)) - ACPI_CAST_PTR (UINT8, (b)))
/* Pointer/Integer type conversions */
#define ACPI_TO_POINTER(i) ACPI_ADD_PTR (void, (void *) NULL,(ACPI_SIZE) i)
#define ACPI_TO_INTEGER(p) ACPI_PTR_DIFF (p, (void *) NULL)
-#define ACPI_OFFSET(d, f) (ACPI_SIZE) ACPI_PTR_DIFF (&(((d *)0)->f), (void *) NULL)
+#define ACPI_OFFSET(d, f) ACPI_PTR_DIFF (&(((d *) 0)->f), (void *) NULL)
#define ACPI_PHYSADDR_TO_PTR(i) ACPI_TO_POINTER(i)
#define ACPI_PTR_TO_PHYSADDR(i) ACPI_TO_INTEGER(i)
+/* Optimizations for 4-character (32-bit) ACPI_NAME manipulation */
+
#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED
#define ACPI_COMPARE_NAME(a,b) (*ACPI_CAST_PTR (UINT32, (a)) == *ACPI_CAST_PTR (UINT32, (b)))
+#define ACPI_MOVE_NAME(dest,src) (*ACPI_CAST_PTR (UINT32, (dest)) = *ACPI_CAST_PTR (UINT32, (src)))
#else
-#define ACPI_COMPARE_NAME(a,b) (!ACPI_STRNCMP (ACPI_CAST_PTR (char, (a)), ACPI_CAST_PTR (char, (b)), ACPI_NAME_SIZE))
+#define ACPI_COMPARE_NAME(a,b) (!strncmp (ACPI_CAST_PTR (char, (a)), ACPI_CAST_PTR (char, (b)), ACPI_NAME_SIZE))
+#define ACPI_MOVE_NAME(dest,src) (strncpy (ACPI_CAST_PTR (char, (dest)), ACPI_CAST_PTR (char, (src)), ACPI_NAME_SIZE))
#endif
+/* Support for the special RSDP signature (8 characters) */
+
+#define ACPI_VALIDATE_RSDP_SIG(a) (!strncmp (ACPI_CAST_PTR (char, (a)), ACPI_SIG_RSDP, 8))
+#define ACPI_MAKE_RSDP_SIG(dest) (memcpy (ACPI_CAST_PTR (char, (dest)), ACPI_SIG_RSDP, 8))
+
/*******************************************************************************
*
@@ -478,6 +582,7 @@ typedef UINT64 ACPI_INTEGER;
#define ACPI_NO_ACPI_ENABLE 0x10
#define ACPI_NO_DEVICE_INIT 0x20
#define ACPI_NO_OBJECT_INIT 0x40
+#define ACPI_NO_FACS_INIT 0x80
/*
* Initialization state
@@ -534,8 +639,11 @@ typedef UINT64 ACPI_INTEGER;
#define ACPI_NOTIFY_DEVICE_PLD_CHECK (UINT8) 0x09
#define ACPI_NOTIFY_RESERVED (UINT8) 0x0A
#define ACPI_NOTIFY_LOCALITY_UPDATE (UINT8) 0x0B
+#define ACPI_NOTIFY_SHUTDOWN_REQUEST (UINT8) 0x0C
+#define ACPI_NOTIFY_AFFINITY_UPDATE (UINT8) 0x0D
-#define ACPI_NOTIFY_MAX 0x0B
+#define ACPI_GENERIC_NOTIFY_MAX 0x0D
+#define ACPI_SPECIFIC_NOTIFY_MAX 0x84
/*
* Types associated with ACPI names and objects. The first group of
@@ -567,6 +675,7 @@ typedef UINT32 ACPI_OBJECT_TYPE;
#define ACPI_TYPE_DEBUG_OBJECT 0x10
#define ACPI_TYPE_EXTERNAL_MAX 0x10
+#define ACPI_NUM_TYPES (ACPI_TYPE_EXTERNAL_MAX + 1)
/*
* These are object types that do not map directly to the ACPI
@@ -588,10 +697,11 @@ typedef UINT32 ACPI_OBJECT_TYPE;
#define ACPI_TYPE_LOCAL_SCOPE 0x1B /* 1 Name, multiple ObjectList Nodes */
#define ACPI_TYPE_NS_NODE_MAX 0x1B /* Last typecode used within a NS Node */
+#define ACPI_TOTAL_TYPES (ACPI_TYPE_NS_NODE_MAX + 1)
/*
* These are special object types that never appear in
- * a Namespace node, only in an ACPI_OPERAND_OBJECT
+ * a Namespace node, only in an object of ACPI_OPERAND_OBJECT
*/
#define ACPI_TYPE_LOCAL_EXTRA 0x1C
#define ACPI_TYPE_LOCAL_DATA 0x1D
@@ -635,28 +745,26 @@ typedef UINT32 ACPI_EVENT_TYPE;
* The encoding of ACPI_EVENT_STATUS is illustrated below.
* Note that a set bit (1) indicates the property is TRUE
* (e.g. if bit 0 is set then the event is enabled).
- * +-------------+-+-+-+
- * | Bits 31:3 |2|1|0|
- * +-------------+-+-+-+
- * | | | |
- * | | | +- Enabled?
- * | | +--- Enabled for wake?
- * | +----- Set?
- * +----------- <Reserved>
+ * +-------------+-+-+-+-+-+
+ * | Bits 31:5 |4|3|2|1|0|
+ * +-------------+-+-+-+-+-+
+ * | | | | | |
+ * | | | | | +- Enabled?
+ * | | | | +--- Enabled for wake?
+ * | | | +----- Status bit set?
+ * | | +------- Enable bit set?
+ * | +--------- Has a handler?
+ * +--------------- <Reserved>
*/
typedef UINT32 ACPI_EVENT_STATUS;
#define ACPI_EVENT_FLAG_DISABLED (ACPI_EVENT_STATUS) 0x00
#define ACPI_EVENT_FLAG_ENABLED (ACPI_EVENT_STATUS) 0x01
#define ACPI_EVENT_FLAG_WAKE_ENABLED (ACPI_EVENT_STATUS) 0x02
-#define ACPI_EVENT_FLAG_SET (ACPI_EVENT_STATUS) 0x04
-
-/*
- * General Purpose Events (GPE)
- */
-#define ACPI_GPE_INVALID 0xFF
-#define ACPI_GPE_MAX 0xFF
-#define ACPI_NUM_GPE 256
+#define ACPI_EVENT_FLAG_STATUS_SET (ACPI_EVENT_STATUS) 0x04
+#define ACPI_EVENT_FLAG_ENABLE_SET (ACPI_EVENT_STATUS) 0x08
+#define ACPI_EVENT_FLAG_HAS_HANDLER (ACPI_EVENT_STATUS) 0x10
+#define ACPI_EVENT_FLAG_SET ACPI_EVENT_FLAG_STATUS_SET
/* Actions for AcpiSetGpe, AcpiGpeWakeup, AcpiHwLowSetGpe */
@@ -667,7 +775,7 @@ typedef UINT32 ACPI_EVENT_STATUS;
/*
* GPE info flags - Per GPE
* +-------+-+-+---+
- * | 7:4 |3|2|1:0|
+ * | 7:5 |4|3|2:0|
* +-------+-+-+---+
* | | | |
* | | | +-- Type of dispatch:to method, handler, notify, or none
@@ -679,13 +787,15 @@ typedef UINT32 ACPI_EVENT_STATUS;
#define ACPI_GPE_DISPATCH_METHOD (UINT8) 0x01
#define ACPI_GPE_DISPATCH_HANDLER (UINT8) 0x02
#define ACPI_GPE_DISPATCH_NOTIFY (UINT8) 0x03
-#define ACPI_GPE_DISPATCH_MASK (UINT8) 0x03
+#define ACPI_GPE_DISPATCH_RAW_HANDLER (UINT8) 0x04
+#define ACPI_GPE_DISPATCH_MASK (UINT8) 0x07
+#define ACPI_GPE_DISPATCH_TYPE(flags) ((UINT8) ((flags) & ACPI_GPE_DISPATCH_MASK))
-#define ACPI_GPE_LEVEL_TRIGGERED (UINT8) 0x04
+#define ACPI_GPE_LEVEL_TRIGGERED (UINT8) 0x08
#define ACPI_GPE_EDGE_TRIGGERED (UINT8) 0x00
-#define ACPI_GPE_XRUPT_TYPE_MASK (UINT8) 0x04
+#define ACPI_GPE_XRUPT_TYPE_MASK (UINT8) 0x08
-#define ACPI_GPE_CAN_WAKE (UINT8) 0x08
+#define ACPI_GPE_CAN_WAKE (UINT8) 0x10
/*
* Flags for GPE and Lock interfaces
@@ -700,8 +810,13 @@ typedef UINT32 ACPI_EVENT_STATUS;
#define ACPI_DEVICE_NOTIFY 0x2
#define ACPI_ALL_NOTIFY (ACPI_SYSTEM_NOTIFY | ACPI_DEVICE_NOTIFY)
#define ACPI_MAX_NOTIFY_HANDLER_TYPE 0x3
+#define ACPI_NUM_NOTIFY_TYPES 2
-#define ACPI_MAX_SYS_NOTIFY 0x7f
+#define ACPI_MAX_SYS_NOTIFY 0x7F
+#define ACPI_MAX_DEVICE_SPECIFIC_NOTIFY 0xBF
+
+#define ACPI_SYSTEM_HANDLER_LIST 0 /* Used as index, must be SYSTEM_NOTIFY -1 */
+#define ACPI_DEVICE_HANDLER_LIST 1 /* Used as index, must be DEVICE_NOTIFY -1 */
/* Address Space (Operation Region) Types */
@@ -716,8 +831,11 @@ typedef UINT8 ACPI_ADR_SPACE_TYPE;
#define ACPI_ADR_SPACE_CMOS (ACPI_ADR_SPACE_TYPE) 5
#define ACPI_ADR_SPACE_PCI_BAR_TARGET (ACPI_ADR_SPACE_TYPE) 6
#define ACPI_ADR_SPACE_IPMI (ACPI_ADR_SPACE_TYPE) 7
+#define ACPI_ADR_SPACE_GPIO (ACPI_ADR_SPACE_TYPE) 8
+#define ACPI_ADR_SPACE_GSBUS (ACPI_ADR_SPACE_TYPE) 9
+#define ACPI_ADR_SPACE_PLATFORM_COMM (ACPI_ADR_SPACE_TYPE) 10
-#define ACPI_NUM_PREDEFINED_REGIONS 8
+#define ACPI_NUM_PREDEFINED_REGIONS 11
/*
* Special Address Spaces
@@ -790,6 +908,19 @@ typedef UINT8 ACPI_ADR_SPACE_TYPE;
#define ACPI_DISABLE_EVENT 0
+/* Sleep function dispatch */
+
+typedef ACPI_STATUS (*ACPI_SLEEP_FUNCTION) (
+ UINT8 SleepState);
+
+typedef struct acpi_sleep_functions
+{
+ ACPI_SLEEP_FUNCTION LegacyFunction;
+ ACPI_SLEEP_FUNCTION ExtendedFunction;
+
+} ACPI_SLEEP_FUNCTIONS;
+
+
/*
* External ACPI object definition
*/
@@ -868,8 +999,18 @@ typedef struct acpi_object_list
* Miscellaneous common Data Structures used by the interfaces
*/
#define ACPI_NO_BUFFER 0
-#define ACPI_ALLOCATE_BUFFER (ACPI_SIZE) (-1)
-#define ACPI_ALLOCATE_LOCAL_BUFFER (ACPI_SIZE) (-2)
+
+#ifdef ACPI_NO_MEM_ALLOCATIONS
+
+#define ACPI_ALLOCATE_BUFFER (ACPI_SIZE) (0)
+#define ACPI_ALLOCATE_LOCAL_BUFFER (ACPI_SIZE) (0)
+
+#else /* ACPI_NO_MEM_ALLOCATIONS */
+
+#define ACPI_ALLOCATE_BUFFER (ACPI_SIZE) (-1) /* Let ACPICA allocate buffer */
+#define ACPI_ALLOCATE_LOCAL_BUFFER (ACPI_SIZE) (-2) /* For internal use only (enables tracking) */
+
+#endif /* ACPI_NO_MEM_ALLOCATIONS */
typedef struct acpi_buffer
{
@@ -884,7 +1025,8 @@ typedef struct acpi_buffer
*/
#define ACPI_FULL_PATHNAME 0
#define ACPI_SINGLE_NAME 1
-#define ACPI_NAME_TYPE_MAX 1
+#define ACPI_FULL_PATHNAME_NO_TRAILING 2
+#define ACPI_NAME_TYPE_MAX 2
/*
@@ -892,7 +1034,7 @@ typedef struct acpi_buffer
*/
typedef struct acpi_predefined_names
{
- char *Name;
+ const char *Name;
UINT8 Type;
char *Val;
@@ -959,6 +1101,10 @@ typedef void
* Various handlers and callback procedures
*/
typedef
+UINT32 (*ACPI_SCI_HANDLER) (
+ void *Context);
+
+typedef
void (*ACPI_GBL_EVENT_HANDLER) (
UINT32 EventType,
ACPI_HANDLE Device,
@@ -1030,6 +1176,17 @@ ACPI_STATUS (*ACPI_ADR_SPACE_HANDLER) (
#define ACPI_DEFAULT_HANDLER NULL
+/* Special Context data for GenericSerialBus/GeneralPurposeIo (ACPI 5.0) */
+
+typedef struct acpi_connection_info
+{
+ UINT8 *Connection;
+ UINT16 Length;
+ UINT8 AccessLength;
+
+} ACPI_CONNECTION_INFO;
+
+
typedef
ACPI_STATUS (*ACPI_ADR_SPACE_SETUP) (
ACPI_HANDLE RegionHandle,
@@ -1071,23 +1228,27 @@ UINT32 (*ACPI_INTERFACE_HANDLER) (
#define ACPI_UUID_LENGTH 16
+/* Length of 3-byte PCI class code values when converted back to a string */
+
+#define ACPI_PCICLS_STRING_SIZE 7 /* Includes null terminator */
+
/* Structures used for device/processor HID, UID, CID */
-typedef struct acpi_device_id
+typedef struct acpi_pnp_device_id
{
UINT32 Length; /* Length of string + null */
char *String;
-} ACPI_DEVICE_ID;
+} ACPI_PNP_DEVICE_ID;
-typedef struct acpi_device_id_list
+typedef struct acpi_pnp_device_id_list
{
UINT32 Count; /* Number of IDs in Ids array */
UINT32 ListSize; /* Size of list, including ID strings */
- ACPI_DEVICE_ID Ids[1]; /* ID array */
+ ACPI_PNP_DEVICE_ID Ids[1]; /* ID array */
-} ACPI_DEVICE_ID_LIST;
+} ACPI_PNP_DEVICE_ID_LIST;
/*
* Structure returned from AcpiGetObjectInfo.
@@ -1099,15 +1260,16 @@ typedef struct acpi_device_info
UINT32 Name; /* ACPI object Name */
ACPI_OBJECT_TYPE Type; /* ACPI object Type */
UINT8 ParamCount; /* If a method, required parameter count */
- UINT8 Valid; /* Indicates which optional fields are valid */
+ UINT16 Valid; /* Indicates which optional fields are valid */
UINT8 Flags; /* Miscellaneous info */
UINT8 HighestDstates[4]; /* _SxD values: 0xFF indicates not valid */
UINT8 LowestDstates[5]; /* _SxW values: 0xFF indicates not valid */
UINT32 CurrentStatus; /* _STA value */
UINT64 Address; /* _ADR value */
- ACPI_DEVICE_ID HardwareId; /* _HID value */
- ACPI_DEVICE_ID UniqueId; /* _UID value */
- ACPI_DEVICE_ID_LIST CompatibleIdList; /* _CID list <must be last> */
+ ACPI_PNP_DEVICE_ID HardwareId; /* _HID value */
+ ACPI_PNP_DEVICE_ID UniqueId; /* _UID value */
+ ACPI_PNP_DEVICE_ID ClassCode; /* _CLS value */
+ ACPI_PNP_DEVICE_ID_LIST CompatibleIdList; /* _CID list <must be last> */
} ACPI_DEVICE_INFO;
@@ -1117,13 +1279,14 @@ typedef struct acpi_device_info
/* Flags for Valid field above (AcpiGetObjectInfo) */
-#define ACPI_VALID_STA 0x01
-#define ACPI_VALID_ADR 0x02
-#define ACPI_VALID_HID 0x04
-#define ACPI_VALID_UID 0x08
-#define ACPI_VALID_CID 0x10
-#define ACPI_VALID_SXDS 0x20
-#define ACPI_VALID_SXWS 0x40
+#define ACPI_VALID_STA 0x0001
+#define ACPI_VALID_ADR 0x0002
+#define ACPI_VALID_HID 0x0004
+#define ACPI_VALID_UID 0x0008
+#define ACPI_VALID_CID 0x0020
+#define ACPI_VALID_CLS 0x0040
+#define ACPI_VALID_SXDS 0x0100
+#define ACPI_VALID_SXWS 0x0200
/* Flags for _STA method */
@@ -1162,12 +1325,11 @@ typedef struct acpi_mem_space_context
*/
typedef struct acpi_memory_list
{
- char *ListName;
+ const char *ListName;
void *ListHead;
UINT16 ObjectSize;
UINT16 MaxDepth;
UINT16 CurrentDepth;
- UINT16 LinkOffset;
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
@@ -1185,4 +1347,59 @@ typedef struct acpi_memory_list
} ACPI_MEMORY_LIST;
+/* Definitions of trace event types */
+
+typedef enum
+{
+ ACPI_TRACE_AML_METHOD,
+ ACPI_TRACE_AML_OPCODE,
+ ACPI_TRACE_AML_REGION
+
+} ACPI_TRACE_EVENT_TYPE;
+
+
+/* Definitions of _OSI support */
+
+#define ACPI_VENDOR_STRINGS 0x01
+#define ACPI_FEATURE_STRINGS 0x02
+#define ACPI_ENABLE_INTERFACES 0x00
+#define ACPI_DISABLE_INTERFACES 0x04
+
+#define ACPI_DISABLE_ALL_VENDOR_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS)
+#define ACPI_DISABLE_ALL_FEATURE_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_FEATURE_STRINGS)
+#define ACPI_DISABLE_ALL_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS)
+#define ACPI_ENABLE_ALL_VENDOR_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS)
+#define ACPI_ENABLE_ALL_FEATURE_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_FEATURE_STRINGS)
+#define ACPI_ENABLE_ALL_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS)
+
+#define ACPI_OSI_WIN_2000 0x01
+#define ACPI_OSI_WIN_XP 0x02
+#define ACPI_OSI_WIN_XP_SP1 0x03
+#define ACPI_OSI_WINSRV_2003 0x04
+#define ACPI_OSI_WIN_XP_SP2 0x05
+#define ACPI_OSI_WINSRV_2003_SP1 0x06
+#define ACPI_OSI_WIN_VISTA 0x07
+#define ACPI_OSI_WINSRV_2008 0x08
+#define ACPI_OSI_WIN_VISTA_SP1 0x09
+#define ACPI_OSI_WIN_VISTA_SP2 0x0A
+#define ACPI_OSI_WIN_7 0x0B
+#define ACPI_OSI_WIN_8 0x0C
+#define ACPI_OSI_WIN_10 0x0D
+
+
+/* Definitions of file IO */
+
+#define ACPI_FILE_READING 0x01
+#define ACPI_FILE_WRITING 0x02
+#define ACPI_FILE_BINARY 0x04
+
+#define ACPI_FILE_BEGIN 0x01
+#define ACPI_FILE_END 0x02
+
+
+/* Definitions of getopt */
+
+#define ACPI_OPT_END -1
+
+
#endif /* __ACTYPES_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/acutils.h b/usr/src/uts/intel/sys/acpi/acutils.h
index 489f195eb9..7e53961e5b 100644
--- a/usr/src/uts/intel/sys/acpi/acutils.h
+++ b/usr/src/uts/intel/sys/acpi/acutils.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,10 +46,11 @@
extern const UINT8 AcpiGbl_ResourceAmlSizes[];
+extern const UINT8 AcpiGbl_ResourceAmlSerialBusSizes[];
/* Strings used by the disassembler and debugger resource dump routines */
-#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER)
+#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER)
extern const char *AcpiGbl_BmDecode[];
extern const char *AcpiGbl_ConfigDecode[];
@@ -69,8 +70,78 @@ extern const char *AcpiGbl_SizDecode[];
extern const char *AcpiGbl_TrsDecode[];
extern const char *AcpiGbl_TtpDecode[];
extern const char *AcpiGbl_TypDecode[];
+extern const char *AcpiGbl_PpcDecode[];
+extern const char *AcpiGbl_IorDecode[];
+extern const char *AcpiGbl_DtsDecode[];
+extern const char *AcpiGbl_CtDecode[];
+extern const char *AcpiGbl_SbtDecode[];
+extern const char *AcpiGbl_AmDecode[];
+extern const char *AcpiGbl_SmDecode[];
+extern const char *AcpiGbl_WmDecode[];
+extern const char *AcpiGbl_CphDecode[];
+extern const char *AcpiGbl_CpoDecode[];
+extern const char *AcpiGbl_DpDecode[];
+extern const char *AcpiGbl_EdDecode[];
+extern const char *AcpiGbl_BpbDecode[];
+extern const char *AcpiGbl_SbDecode[];
+extern const char *AcpiGbl_FcDecode[];
+extern const char *AcpiGbl_PtDecode[];
#endif
+/*
+ * For the iASL compiler case, the output is redirected to stderr so that
+ * any of the various ACPI errors and warnings do not appear in the output
+ * files, for either the compiler or disassembler portions of the tool.
+ */
+#ifdef ACPI_ASL_COMPILER
+
+#include <stdio.h>
+
+#define ACPI_MSG_REDIRECT_BEGIN \
+ FILE *OutputFile = AcpiGbl_OutputFile; \
+ AcpiOsRedirectOutput (stderr);
+
+#define ACPI_MSG_REDIRECT_END \
+ AcpiOsRedirectOutput (OutputFile);
+
+#else
+/*
+ * non-iASL case - no redirection, nothing to do
+ */
+#define ACPI_MSG_REDIRECT_BEGIN
+#define ACPI_MSG_REDIRECT_END
+#endif
+
+/*
+ * Common error message prefixes
+ */
+#ifndef ACPI_MSG_ERROR
+#define ACPI_MSG_ERROR "ACPI Error: "
+#endif
+#ifndef ACPI_MSG_EXCEPTION
+#define ACPI_MSG_EXCEPTION "ACPI Exception: "
+#endif
+#ifndef ACPI_MSG_WARNING
+#define ACPI_MSG_WARNING "ACPI Warning: "
+#endif
+#ifndef ACPI_MSG_INFO
+#define ACPI_MSG_INFO "ACPI: "
+#endif
+
+#ifndef ACPI_MSG_BIOS_ERROR
+#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): "
+#endif
+#ifndef ACPI_MSG_BIOS_WARNING
+#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): "
+#endif
+
+/*
+ * Common message suffix
+ */
+#define ACPI_MSG_SUFFIX \
+ AcpiOsPrintf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, ModuleName, LineNumber)
+
+
/* Types for Resource descriptor entries */
#define ACPI_INVALID_RESOURCE 0
@@ -84,7 +155,7 @@ ACPI_STATUS (*ACPI_WALK_AML_CALLBACK) (
UINT32 Length,
UINT32 Offset,
UINT8 ResourceIndex,
- void *Context);
+ void **Context);
typedef
ACPI_STATUS (*ACPI_PKG_CALLBACK) (
@@ -102,9 +173,10 @@ typedef struct acpi_pkg_info
} ACPI_PKG_INFO;
+/* Object reference counts */
+
#define REF_INCREMENT (UINT16) 0
#define REF_DECREMENT (UINT16) 1
-#define REF_FORCE_DELETE (UINT16) 2
/* AcpiUtDumpBuffer */
@@ -115,6 +187,54 @@ typedef struct acpi_pkg_info
/*
+ * utascii - ASCII utilities
+ */
+BOOLEAN
+AcpiUtValidNameseg (
+ char *Signature);
+
+BOOLEAN
+AcpiUtValidNameChar (
+ char Character,
+ UINT32 Position);
+
+void
+AcpiUtCheckAndRepairAscii (
+ UINT8 *Name,
+ char *RepairedName,
+ UINT32 Count);
+
+
+/*
+ * utnonansi - Non-ANSI C library functions
+ */
+void
+AcpiUtStrupr (
+ char *SrcString);
+
+void
+AcpiUtStrlwr (
+ char *SrcString);
+
+int
+AcpiUtStricmp (
+ char *String1,
+ char *String2);
+
+ACPI_STATUS
+AcpiUtStrtoul64 (
+ char *String,
+ UINT32 Base,
+ UINT32 MaxIntegerByteWidth,
+ UINT64 *RetInteger);
+
+/* Values for MaxIntegerByteWidth above */
+
+#define ACPI_MAX32_BYTE_WIDTH 4
+#define ACPI_MAX64_BYTE_WIDTH 8
+
+
+/*
* utglobal - Global data structures and procedures
*/
ACPI_STATUS
@@ -123,25 +243,25 @@ AcpiUtInitGlobals (
#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
-char *
+const char *
AcpiUtGetMutexName (
UINT32 MutexId);
const char *
AcpiUtGetNotifyName (
- UINT32 NotifyValue);
-
+ UINT32 NotifyValue,
+ ACPI_OBJECT_TYPE Type);
#endif
-char *
+const char *
AcpiUtGetTypeName (
ACPI_OBJECT_TYPE Type);
-char *
+const char *
AcpiUtGetNodeName (
void *Object);
-char *
+const char *
AcpiUtGetDescriptorName (
void *Object);
@@ -149,15 +269,15 @@ const char *
AcpiUtGetReferenceName (
ACPI_OPERAND_OBJECT *Object);
-char *
+const char *
AcpiUtGetObjectTypeName (
ACPI_OPERAND_OBJECT *ObjDesc);
-char *
+const char *
AcpiUtGetRegionName (
UINT8 SpaceId);
-char *
+const char *
AcpiUtGetEventName (
UINT32 EventId);
@@ -166,6 +286,10 @@ AcpiUtHexToAsciiChar (
UINT64 Integer,
UINT32 Position);
+UINT8
+AcpiUtAsciiCharToHex (
+ int HexChar);
+
BOOLEAN
AcpiUtValidObjectType (
ACPI_OBJECT_TYPE Type);
@@ -184,111 +308,6 @@ AcpiUtSubsystemShutdown (
/*
- * utclib - Local implementations of C library functions
- */
-#ifndef ACPI_USE_SYSTEM_CLIBRARY
-
-ACPI_SIZE
-AcpiUtStrlen (
- const char *String);
-
-char *
-AcpiUtStrcpy (
- char *DstString,
- const char *SrcString);
-
-char *
-AcpiUtStrncpy (
- char *DstString,
- const char *SrcString,
- ACPI_SIZE Count);
-
-int
-AcpiUtMemcmp (
- const char *Buffer1,
- const char *Buffer2,
- ACPI_SIZE Count);
-
-int
-AcpiUtStrncmp (
- const char *String1,
- const char *String2,
- ACPI_SIZE Count);
-
-int
-AcpiUtStrcmp (
- const char *String1,
- const char *String2);
-
-char *
-AcpiUtStrcat (
- char *DstString,
- const char *SrcString);
-
-char *
-AcpiUtStrncat (
- char *DstString,
- const char *SrcString,
- ACPI_SIZE Count);
-
-UINT32
-AcpiUtStrtoul (
- const char *String,
- char **Terminator,
- UINT32 Base);
-
-char *
-AcpiUtStrstr (
- char *String1,
- char *String2);
-
-void *
-AcpiUtMemcpy (
- void *Dest,
- const void *Src,
- ACPI_SIZE Count);
-
-void *
-AcpiUtMemset (
- void *Dest,
- UINT8 Value,
- ACPI_SIZE Count);
-
-int
-AcpiUtToUpper (
- int c);
-
-int
-AcpiUtToLower (
- int c);
-
-extern const UINT8 _acpi_ctype[];
-
-#define _ACPI_XA 0x00 /* extra alphabetic - not supported */
-#define _ACPI_XS 0x40 /* extra space */
-#define _ACPI_BB 0x00 /* BEL, BS, etc. - not supported */
-#define _ACPI_CN 0x20 /* CR, FF, HT, NL, VT */
-#define _ACPI_DI 0x04 /* '0'-'9' */
-#define _ACPI_LO 0x02 /* 'a'-'z' */
-#define _ACPI_PU 0x10 /* punctuation */
-#define _ACPI_SP 0x08 /* space */
-#define _ACPI_UP 0x01 /* 'A'-'Z' */
-#define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */
-
-#define ACPI_IS_DIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_DI))
-#define ACPI_IS_SPACE(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_SP))
-#define ACPI_IS_XDIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_XD))
-#define ACPI_IS_UPPER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_UP))
-#define ACPI_IS_LOWER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO))
-#define ACPI_IS_PRINT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP | _ACPI_DI | _ACPI_SP | _ACPI_PU))
-#define ACPI_IS_ALPHA(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP))
-
-#endif /* !ACPI_USE_SYSTEM_CLIBRARY */
-
-#define ACPI_IS_ASCII(c) ((c) < 0x80)
-
-
-/*
* utcopy - Object construction and conversion interfaces
*/
ACPI_STATUS
@@ -359,7 +378,7 @@ AcpiUtTracePtr (
const char *FunctionName,
const char *ModuleName,
UINT32 ComponentId,
- void *Pointer);
+ const void *Pointer);
void
AcpiUtTraceU32 (
@@ -375,7 +394,7 @@ AcpiUtTraceStr (
const char *FunctionName,
const char *ModuleName,
UINT32 ComponentId,
- char *String);
+ const char *String);
void
AcpiUtExit (
@@ -409,17 +428,36 @@ AcpiUtPtrExit (
UINT8 *Ptr);
void
+AcpiUtStrExit (
+ UINT32 LineNumber,
+ const char *FunctionName,
+ const char *ModuleName,
+ UINT32 ComponentId,
+ const char *String);
+
+void
+AcpiUtDebugDumpBuffer (
+ UINT8 *Buffer,
+ UINT32 Count,
+ UINT32 Display,
+ UINT32 ComponentId);
+
+void
AcpiUtDumpBuffer (
UINT8 *Buffer,
UINT32 Count,
UINT32 Display,
- UINT32 componentId);
+ UINT32 Offset);
+#ifdef ACPI_APPLICATION
void
-AcpiUtDumpBuffer2 (
+AcpiUtDumpBufferToFile (
+ ACPI_FILE File,
UINT8 *Buffer,
UINT32 Count,
- UINT32 Display);
+ UINT32 Display,
+ UINT32 BaseOffset);
+#endif
void
AcpiUtReportError (
@@ -436,6 +474,7 @@ AcpiUtReportWarning (
char *ModuleName,
UINT32 LineNumber);
+
/*
* utdelete - Object deletion and reference counts
*/
@@ -466,13 +505,13 @@ AcpiUtDeleteInternalObjectList (
ACPI_STATUS
AcpiUtEvaluateObject (
ACPI_NAMESPACE_NODE *PrefixNode,
- char *Path,
+ const char *Path,
UINT32 ExpectedReturnBtypes,
ACPI_OPERAND_OBJECT **ReturnDesc);
ACPI_STATUS
AcpiUtEvaluateNumericObject (
- char *ObjectName,
+ const char *ObjectName,
ACPI_NAMESPACE_NODE *DeviceNode,
UINT64 *Value);
@@ -495,17 +534,22 @@ AcpiUtExecutePowerMethods (
ACPI_STATUS
AcpiUtExecute_HID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID **ReturnId);
+ ACPI_PNP_DEVICE_ID **ReturnId);
ACPI_STATUS
AcpiUtExecute_UID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID **ReturnId);
+ ACPI_PNP_DEVICE_ID **ReturnId);
ACPI_STATUS
AcpiUtExecute_CID (
ACPI_NAMESPACE_NODE *DeviceNode,
- ACPI_DEVICE_ID_LIST **ReturnCidList);
+ ACPI_PNP_DEVICE_ID_LIST **ReturnCidList);
+
+ACPI_STATUS
+AcpiUtExecute_CLS (
+ ACPI_NAMESPACE_NODE *DeviceNode,
+ ACPI_PNP_DEVICE_ID **ReturnId);
/*
@@ -592,7 +636,7 @@ ACPI_STATUS
AcpiUtInitializeInterfaces (
void);
-void
+ACPI_STATUS
AcpiUtInterfaceTerminate (
void);
@@ -604,6 +648,10 @@ ACPI_STATUS
AcpiUtRemoveInterface (
ACPI_STRING InterfaceName);
+ACPI_STATUS
+AcpiUtUpdateInterfaces (
+ UINT8 Action);
+
ACPI_INTERFACE_INFO *
AcpiUtGetInterface (
ACPI_STRING InterfaceName);
@@ -614,6 +662,40 @@ AcpiUtOsiImplementation (
/*
+ * utpredef - support for predefined names
+ */
+const ACPI_PREDEFINED_INFO *
+AcpiUtGetNextPredefinedMethod (
+ const ACPI_PREDEFINED_INFO *ThisName);
+
+const ACPI_PREDEFINED_INFO *
+AcpiUtMatchPredefinedMethod (
+ char *Name);
+
+void
+AcpiUtGetExpectedReturnTypes (
+ char *Buffer,
+ UINT32 ExpectedBtypes);
+
+#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
+const ACPI_PREDEFINED_INFO *
+AcpiUtMatchResourceName (
+ char *Name);
+
+void
+AcpiUtDisplayPredefinedMethod (
+ char *Buffer,
+ const ACPI_PREDEFINED_INFO *ThisName,
+ BOOLEAN MultiLine);
+
+UINT32
+AcpiUtGetResourceBitWidth (
+ char *Buffer,
+ UINT16 Types);
+#endif
+
+
+/*
* utstate - Generic state creation/cache routines
*/
void
@@ -651,13 +733,6 @@ AcpiUtCreateUpdateStateAndPush (
UINT16 Action,
ACPI_GENERIC_STATE **StateList);
-ACPI_STATUS
-AcpiUtCreatePkgStateAndPush (
- void *InternalObject,
- void *ExternalObject,
- UINT16 Index,
- ACPI_GENERIC_STATE **StateList);
-
ACPI_GENERIC_STATE *
AcpiUtCreateControlState (
void);
@@ -684,10 +759,11 @@ AcpiUtShortDivide (
UINT64 *OutQuotient,
UINT32 *OutRemainder);
+
/*
* utmisc
*/
-const char *
+const ACPI_EXCEPTION_INFO *
AcpiUtValidateException (
ACPI_STATUS Status);
@@ -695,17 +771,11 @@ BOOLEAN
AcpiUtIsPciRootBridge (
char *Id);
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_NAMES_APP)
BOOLEAN
AcpiUtIsAmlTable (
ACPI_TABLE_HEADER *Table);
-
-ACPI_STATUS
-AcpiUtAllocateOwnerId (
- ACPI_OWNER_ID *OwnerId);
-
-void
-AcpiUtReleaseOwnerId (
- ACPI_OWNER_ID *OwnerId);
+#endif
ACPI_STATUS
AcpiUtWalkPackageTree (
@@ -714,42 +784,11 @@ AcpiUtWalkPackageTree (
ACPI_PKG_CALLBACK WalkCallback,
void *Context);
-void
-AcpiUtStrupr (
- char *SrcString);
-
-void
-AcpiUtStrlwr (
- char *SrcString);
-
-void
-AcpiUtPrintString (
- char *String,
- UINT8 MaxLength);
-
-BOOLEAN
-AcpiUtValidAcpiName (
- UINT32 Name);
-
-void
-AcpiUtRepairName (
- char *Name);
-
-BOOLEAN
-AcpiUtValidAcpiChar (
- char Character,
- UINT32 Position);
-
-ACPI_STATUS
-AcpiUtStrtoul64 (
- char *String,
- UINT32 Base,
- UINT64 *RetInteger);
-
/* Values for Base above (16=Hex, 10=Decimal) */
#define ACPI_ANY_BASE 0
+
UINT32
AcpiUtDwordByteSwap (
UINT32 Value);
@@ -763,22 +802,36 @@ void
AcpiUtDisplayInitPathname (
UINT8 Type,
ACPI_NAMESPACE_NODE *ObjHandle,
- char *Path);
+ const char *Path);
#endif
/*
+ * utownerid - Support for Table/Method Owner IDs
+ */
+ACPI_STATUS
+AcpiUtAllocateOwnerId (
+ ACPI_OWNER_ID *OwnerId);
+
+void
+AcpiUtReleaseOwnerId (
+ ACPI_OWNER_ID *OwnerId);
+
+
+/*
* utresrc
*/
ACPI_STATUS
AcpiUtWalkAmlResources (
+ ACPI_WALK_STATE *WalkState,
UINT8 *Aml,
ACPI_SIZE AmlLength,
ACPI_WALK_AML_CALLBACK UserFunction,
- void *Context);
+ void **Context);
ACPI_STATUS
AcpiUtValidateResource (
+ ACPI_WALK_STATE *WalkState,
void *Aml,
UINT8 *ReturnIndex);
@@ -805,6 +858,46 @@ AcpiUtGetResourceEndTag (
/*
+ * utstring - String and character utilities
+ */
+void
+AcpiUtPrintString (
+ char *String,
+ UINT16 MaxLength);
+
+#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP
+void
+UtConvertBackslashes (
+ char *Pathname);
+#endif
+
+void
+AcpiUtRepairName (
+ char *Name);
+
+#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION)
+BOOLEAN
+AcpiUtSafeStrcpy (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source);
+
+BOOLEAN
+AcpiUtSafeStrcat (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source);
+
+BOOLEAN
+AcpiUtSafeStrncat (
+ char *Dest,
+ ACPI_SIZE DestSize,
+ char *Source,
+ ACPI_SIZE MaxTransferLength);
+#endif
+
+
+/*
* utmutex - mutex support
*/
ACPI_STATUS
@@ -844,20 +937,6 @@ AcpiUtInitializeBuffer (
ACPI_BUFFER *Buffer,
ACPI_SIZE RequiredLength);
-void *
-AcpiUtAllocate (
- ACPI_SIZE Size,
- UINT32 Component,
- const char *Module,
- UINT32 Line);
-
-void *
-AcpiUtAllocateZeroed (
- ACPI_SIZE Size,
- UINT32 Component,
- const char *Module,
- UINT32 Line);
-
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
void *
AcpiUtAllocateAndTrack (
@@ -891,7 +970,7 @@ AcpiUtDumpAllocations (
ACPI_STATUS
AcpiUtCreateList (
- char *ListName,
+ const char *ListName,
UINT16 ObjectSize,
ACPI_MEMORY_LIST **ReturnCache);
@@ -899,6 +978,33 @@ AcpiUtCreateList (
/*
+ * utaddress - address range check
+ */
+ACPI_STATUS
+AcpiUtAddAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 Length,
+ ACPI_NAMESPACE_NODE *RegionNode);
+
+void
+AcpiUtRemoveAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_NAMESPACE_NODE *RegionNode);
+
+UINT32
+AcpiUtCheckAddressRange (
+ ACPI_ADR_SPACE_TYPE SpaceId,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 Length,
+ BOOLEAN Warn);
+
+void
+AcpiUtDeleteAddressLists (
+ void);
+
+
+/*
* utxferror - various error/warning output functions
*/
void ACPI_INTERNAL_VAR_XFACE
@@ -919,6 +1025,15 @@ AcpiUtPredefinedInfo (
const char *Format,
...);
+void ACPI_INTERNAL_VAR_XFACE
+AcpiUtPredefinedBiosError (
+ const char *ModuleName,
+ UINT32 LineNumber,
+ char *Pathname,
+ UINT8 NodeFlags,
+ const char *Format,
+ ...);
+
void
AcpiUtNamespaceError (
const char *ModuleName,
@@ -935,4 +1050,73 @@ AcpiUtMethodError (
const char *Path,
ACPI_STATUS LookupStatus);
+
+/*
+ * Utility functions for ACPI names and IDs
+ */
+const AH_PREDEFINED_NAME *
+AcpiAhMatchPredefinedName (
+ char *Nameseg);
+
+const AH_DEVICE_ID *
+AcpiAhMatchHardwareId (
+ char *Hid);
+
+const char *
+AcpiAhMatchUuid (
+ UINT8 *Data);
+
+
+/*
+ * utprint - printf/vprintf output functions
+ */
+const char *
+AcpiUtScanNumber (
+ const char *String,
+ UINT64 *NumberPtr);
+
+const char *
+AcpiUtPrintNumber (
+ char *String,
+ UINT64 Number);
+
+int
+AcpiUtVsnprintf (
+ char *String,
+ ACPI_SIZE Size,
+ const char *Format,
+ va_list Args);
+
+int
+AcpiUtSnprintf (
+ char *String,
+ ACPI_SIZE Size,
+ const char *Format,
+ ...);
+
+#ifdef ACPI_APPLICATION
+int
+AcpiUtFileVprintf (
+ ACPI_FILE File,
+ const char *Format,
+ va_list Args);
+
+int
+AcpiUtFilePrintf (
+ ACPI_FILE File,
+ const char *Format,
+ ...);
+#endif
+
+
+/*
+ * utuuid -- UUID support functions
+ */
+#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
+void
+AcpiUtConvertStringToUuid (
+ char *InString,
+ UINT8 *UuidBuffer);
+#endif
+
#endif /* _ACUTILS_H */
diff --git a/usr/src/uts/intel/sys/acpi/acuuid.h b/usr/src/uts/intel/sys/acpi/acuuid.h
new file mode 100644
index 0000000000..5c42990ab9
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/acuuid.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ * Name: acuuid.h - ACPI-related UUID/GUID definitions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACUUID_H__
+#define __ACUUID_H__
+
+/*
+ * Note1: UUIDs and GUIDs are defined to be identical in ACPI.
+ *
+ * Note2: This file is standalone and should remain that way.
+ */
+
+/* Controllers */
+
+#define UUID_GPIO_CONTROLLER "4f248f40-d5e2-499f-834c-27758ea1cd3f"
+#define UUID_USB_CONTROLLER "ce2ee385-00e6-48cb-9f05-2edb927c4899"
+#define UUID_SATA_CONTROLLER "e4db149b-fcfe-425b-a6d8-92357d78fc7f"
+
+/* Devices */
+
+#define UUID_PCI_HOST_BRIDGE "33db4d5b-1ff7-401c-9657-7441c03dd766"
+#define UUID_I2C_DEVICE "3cdff6f7-4267-4555-ad05-b30a3d8938de"
+#define UUID_POWER_BUTTON "dfbcf3c5-e7a5-44e6-9c1f-29c76f6e059c"
+
+/* Interfaces */
+
+#define UUID_DEVICE_LABELING "e5c937d0-3553-4d7a-9117-ea4d19c3434d"
+#define UUID_PHYSICAL_PRESENCE "3dddfaa6-361b-4eb4-a424-8d10089d1653"
+
+/* NVDIMM - NFIT table */
+
+#define UUID_VOLATILE_MEMORY "7305944f-fdda-44e3-b16c-3f22d252e5d0"
+#define UUID_PERSISTENT_MEMORY "66f0d379-b4f3-4074-ac43-0d3318b78cdb"
+#define UUID_CONTROL_REGION "92f701f6-13b4-405d-910b-299367e8234c"
+#define UUID_DATA_REGION "91af0530-5d86-470e-a6b0-0a2db9408249"
+#define UUID_VOLATILE_VIRTUAL_DISK "77ab535a-45fc-624b-5560-f7b281d1f96e"
+#define UUID_VOLATILE_VIRTUAL_CD "3d5abd30-4175-87ce-6d64-d2ade523c4bb"
+#define UUID_PERSISTENT_VIRTUAL_DISK "5cea02c9-4d07-69d3-269f-4496fbe096f9"
+#define UUID_PERSISTENT_VIRTUAL_CD "08018188-42cd-bb48-100f-5387d53ded3d"
+
+/* Miscellaneous */
+
+#define UUID_PLATFORM_CAPABILITIES "0811b06e-4a27-44f9-8d60-3cbbc22e7b48"
+#define UUID_DYNAMIC_ENUMERATION "d8c1a3a6-be9b-4c9b-91bf-c3cb81fc5daf"
+#define UUID_BATTERY_THERMAL_LIMIT "4c2067e3-887d-475c-9720-4af1d3ed602e"
+#define UUID_THERMAL_EXTENSIONS "14d399cd-7a27-4b18-8fb4-7cb7b9f4e500"
+#define UUID_DEVICE_PROPERTIES "daffd814-6eba-4d8c-8a91-bc9bbf4aa301"
+
+
+#endif /* __AUUID_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/amlcode.h b/usr/src/uts/intel/sys/acpi/amlcode.h
index f6e0de3b35..a432c2b7de 100644
--- a/usr/src/uts/intel/sys/acpi/amlcode.h
+++ b/usr/src/uts/intel/sys/acpi/amlcode.h
@@ -7,7 +7,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -65,6 +65,7 @@
#define AML_PACKAGE_OP (UINT16) 0x12
#define AML_VAR_PACKAGE_OP (UINT16) 0x13 /* ACPI 2.0 */
#define AML_METHOD_OP (UINT16) 0x14
+#define AML_EXTERNAL_OP (UINT16) 0x15 /* ACPI 6.0 */
#define AML_DUAL_NAME_PREFIX (UINT16) 0x2e
#define AML_MULTI_NAME_PREFIX_OP (UINT16) 0x2f
#define AML_NAME_CHAR_SUBSEQ (UINT16) 0x30
@@ -119,7 +120,7 @@
#define AML_CREATE_WORD_FIELD_OP (UINT16) 0x8b
#define AML_CREATE_BYTE_FIELD_OP (UINT16) 0x8c
#define AML_CREATE_BIT_FIELD_OP (UINT16) 0x8d
-#define AML_TYPE_OP (UINT16) 0x8e
+#define AML_OBJECT_TYPE_OP (UINT16) 0x8e
#define AML_CREATE_QWORD_FIELD_OP (UINT16) 0x8f /* ACPI 2.0 */
#define AML_LAND_OP (UINT16) 0x90
#define AML_LOR_OP (UINT16) 0x91
@@ -191,6 +192,15 @@
/*
+ * Opcodes for "Field" operators
+ */
+#define AML_FIELD_OFFSET_OP (UINT8) 0x00
+#define AML_FIELD_ACCESS_OP (UINT8) 0x01
+#define AML_FIELD_CONNECTION_OP (UINT8) 0x02 /* ACPI 5.0 */
+#define AML_FIELD_EXT_ACCESS_OP (UINT8) 0x03 /* ACPI 5.0 */
+
+
+/*
* Internal opcodes
* Use only "Unknown" AML opcodes, don't attempt to use
* any valid ACPI ASCII values (A-Z, 0-9, '-')
@@ -200,11 +210,11 @@
#define AML_INT_RESERVEDFIELD_OP (UINT16) 0x0031
#define AML_INT_ACCESSFIELD_OP (UINT16) 0x0032
#define AML_INT_BYTELIST_OP (UINT16) 0x0033
-#define AML_INT_STATICSTRING_OP (UINT16) 0x0034
#define AML_INT_METHODCALL_OP (UINT16) 0x0035
#define AML_INT_RETURN_VALUE_OP (UINT16) 0x0036
#define AML_INT_EVAL_SUBTREE_OP (UINT16) 0x0037
-
+#define AML_INT_CONNECTION_OP (UINT16) 0x0038
+#define AML_INT_EXTACCESSFIELD_OP (UINT16) 0x0039
#define ARG_NONE 0x0
@@ -231,7 +241,8 @@
#define ARGP_TERMLIST 0x0F
#define ARGP_WORDDATA 0x10
#define ARGP_QWORDDATA 0x11
-#define ARGP_SIMPLENAME 0x12
+#define ARGP_SIMPLENAME 0x12 /* NameString | LocalTerm | ArgTerm */
+#define ARGP_NAME_OR_REF 0x13 /* For ObjectType only */
/*
* Resolved argument types for the AML Interpreter
@@ -270,14 +281,15 @@
#define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */
#define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */
#define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */
+#define ARGI_STORE_TARGET 0x12 /* Target for store is TARGETREF + package objects */
/* Multiple/complex types */
-#define ARGI_DATAOBJECT 0x12 /* Buffer, String, package or reference to a Node - Used only by SizeOf operator*/
-#define ARGI_COMPLEXOBJ 0x13 /* Buffer, String, or package (Used by INDEX op only) */
-#define ARGI_REF_OR_STRING 0x14 /* Reference or String (Used by DEREFOF op only) */
-#define ARGI_REGION_OR_BUFFER 0x15 /* Used by LOAD op only */
-#define ARGI_DATAREFOBJ 0x16
+#define ARGI_DATAOBJECT 0x13 /* Buffer, String, package or reference to a Node - Used only by SizeOf operator*/
+#define ARGI_COMPLEXOBJ 0x14 /* Buffer, String, or package (Used by INDEX op only) */
+#define ARGI_REF_OR_STRING 0x15 /* Reference or String (Used by DEREFOF op only) */
+#define ARGI_REGION_OR_BUFFER 0x16 /* Used by LOAD op only */
+#define ARGI_DATAREFOBJ 0x17
/* Note: types above can expand to 0x1F maximum */
@@ -478,13 +490,16 @@ typedef enum
*/
typedef enum
{
- AML_FIELD_ATTRIB_SMB_QUICK = 0x02,
- AML_FIELD_ATTRIB_SMB_SEND_RCV = 0x04,
- AML_FIELD_ATTRIB_SMB_BYTE = 0x06,
- AML_FIELD_ATTRIB_SMB_WORD = 0x08,
- AML_FIELD_ATTRIB_SMB_BLOCK = 0x0A,
- AML_FIELD_ATTRIB_SMB_WORD_CALL = 0x0C,
- AML_FIELD_ATTRIB_SMB_BLOCK_CALL = 0x0D
+ AML_FIELD_ATTRIB_QUICK = 0x02,
+ AML_FIELD_ATTRIB_SEND_RCV = 0x04,
+ AML_FIELD_ATTRIB_BYTE = 0x06,
+ AML_FIELD_ATTRIB_WORD = 0x08,
+ AML_FIELD_ATTRIB_BLOCK = 0x0A,
+ AML_FIELD_ATTRIB_MULTIBYTE = 0x0B,
+ AML_FIELD_ATTRIB_WORD_CALL = 0x0C,
+ AML_FIELD_ATTRIB_BLOCK_CALL = 0x0D,
+ AML_FIELD_ATTRIB_RAW_BYTES = 0x0E,
+ AML_FIELD_ATTRIB_RAW_PROCESS = 0x0F
} AML_ACCESS_ATTRIBUTE;
diff --git a/usr/src/uts/intel/sys/acpi/amlresrc.h b/usr/src/uts/intel/sys/acpi/amlresrc.h
index 1375d96cf8..960da1b310 100644
--- a/usr/src/uts/intel/sys/acpi/amlresrc.h
+++ b/usr/src/uts/intel/sys/acpi/amlresrc.h
@@ -1,4 +1,3 @@
-
/******************************************************************************
*
* Module Name: amlresrc.h - AML resource descriptors
@@ -6,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -59,29 +58,48 @@
#define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT"
#define ACPI_RESTAG_BASEADDRESS "_BAS"
#define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */
+#define ACPI_RESTAG_DEBOUNCETIME "_DBT"
#define ACPI_RESTAG_DECODE "_DEC"
+#define ACPI_RESTAG_DEVICEPOLARITY "_DPL"
#define ACPI_RESTAG_DMA "_DMA"
#define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */
+#define ACPI_RESTAG_DRIVESTRENGTH "_DRS"
+#define ACPI_RESTAG_ENDIANNESS "_END"
+#define ACPI_RESTAG_FLOWCONTROL "_FLC"
#define ACPI_RESTAG_GRANULARITY "_GRA"
#define ACPI_RESTAG_INTERRUPT "_INT"
#define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* ActiveLo(1), ActiveHi(0) */
#define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), NoShare(0) */
#define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */
+#define ACPI_RESTAG_IORESTRICTION "_IOR"
#define ACPI_RESTAG_LENGTH "_LEN"
+#define ACPI_RESTAG_LINE "_LIN"
#define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */
#define ACPI_RESTAG_MEMTYPE "_MEM" /* NonCache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */
#define ACPI_RESTAG_MAXADDR "_MAX"
#define ACPI_RESTAG_MINADDR "_MIN"
#define ACPI_RESTAG_MAXTYPE "_MAF"
#define ACPI_RESTAG_MINTYPE "_MIF"
+#define ACPI_RESTAG_MODE "_MOD"
+#define ACPI_RESTAG_PARITY "_PAR"
+#define ACPI_RESTAG_PHASE "_PHA"
+#define ACPI_RESTAG_PIN "_PIN"
+#define ACPI_RESTAG_PINCONFIG "_PPI"
+#define ACPI_RESTAG_POLARITY "_POL"
#define ACPI_RESTAG_REGISTERBITOFFSET "_RBO"
#define ACPI_RESTAG_REGISTERBITWIDTH "_RBW"
#define ACPI_RESTAG_RANGETYPE "_RNG"
#define ACPI_RESTAG_READWRITETYPE "_RW_" /* ReadOnly(0), Writeable (1) */
+#define ACPI_RESTAG_LENGTH_RX "_RXL"
+#define ACPI_RESTAG_LENGTH_TX "_TXL"
+#define ACPI_RESTAG_SLAVEMODE "_SLV"
+#define ACPI_RESTAG_SPEED "_SPE"
+#define ACPI_RESTAG_STOPBITS "_STB"
#define ACPI_RESTAG_TRANSLATION "_TRA"
#define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */
#define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */
#define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8And16(1), 16(2) */
+#define ACPI_RESTAG_VENDORDATA "_VEN"
/* Default sizes for "small" resource descriptors */
@@ -92,6 +110,7 @@
#define ASL_RDESC_END_DEPEND_SIZE 0x00
#define ASL_RDESC_IO_SIZE 0x07
#define ASL_RDESC_FIXED_IO_SIZE 0x03
+#define ASL_RDESC_FIXED_DMA_SIZE 0x05
#define ASL_RDESC_END_TAG_SIZE 0x01
@@ -103,6 +122,14 @@ typedef struct asl_resource_node
} ASL_RESOURCE_NODE;
+typedef struct asl_resource_info
+{
+ ACPI_PARSE_OBJECT *DescriptorTypeOp; /* Resource descriptor parse node */
+ ACPI_PARSE_OBJECT *MappingOp; /* Used for mapfile support */
+ UINT32 CurrentByteOffset; /* Offset in resource template */
+
+} ASL_RESOURCE_INFO;
+
/* Macros used to generate AML resource length fields */
@@ -214,6 +241,16 @@ typedef struct aml_resource_end_tag
} AML_RESOURCE_END_TAG;
+typedef struct aml_resource_fixed_dma
+{
+ AML_RESOURCE_SMALL_HEADER_COMMON
+ UINT16 RequestLines;
+ UINT16 Channels;
+ UINT8 Width;
+
+} AML_RESOURCE_FIXED_DMA;
+
+
/*
* LARGE descriptors
*/
@@ -368,6 +405,130 @@ typedef struct aml_resource_generic_register
} AML_RESOURCE_GENERIC_REGISTER;
+
+/* Common descriptor for GpioInt and GpioIo (ACPI 5.0) */
+
+typedef struct aml_resource_gpio
+{
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ UINT8 RevisionId;
+ UINT8 ConnectionType;
+ UINT16 Flags;
+ UINT16 IntFlags;
+ UINT8 PinConfig;
+ UINT16 DriveStrength;
+ UINT16 DebounceTimeout;
+ UINT16 PinTableOffset;
+ UINT8 ResSourceIndex;
+ UINT16 ResSourceOffset;
+ UINT16 VendorOffset;
+ UINT16 VendorLength;
+ /*
+ * Optional fields follow immediately:
+ * 1) PIN list (Words)
+ * 2) Resource Source String
+ * 3) Vendor Data bytes
+ */
+
+} AML_RESOURCE_GPIO;
+
+#define AML_RESOURCE_GPIO_REVISION 1 /* ACPI 5.0 */
+
+/* Values for ConnectionType above */
+
+#define AML_RESOURCE_GPIO_TYPE_INT 0
+#define AML_RESOURCE_GPIO_TYPE_IO 1
+#define AML_RESOURCE_MAX_GPIOTYPE 1
+
+
+/* Common preamble for all serial descriptors (ACPI 5.0) */
+
+#define AML_RESOURCE_SERIAL_COMMON \
+ UINT8 RevisionId; \
+ UINT8 ResSourceIndex; \
+ UINT8 Type; \
+ UINT8 Flags; \
+ UINT16 TypeSpecificFlags; \
+ UINT8 TypeRevisionId; \
+ UINT16 TypeDataLength; \
+
+/* Values for the type field above */
+
+#define AML_RESOURCE_I2C_SERIALBUSTYPE 1
+#define AML_RESOURCE_SPI_SERIALBUSTYPE 2
+#define AML_RESOURCE_UART_SERIALBUSTYPE 3
+#define AML_RESOURCE_MAX_SERIALBUSTYPE 3
+#define AML_RESOURCE_VENDOR_SERIALBUSTYPE 192 /* Vendor defined is 0xC0-0xFF (NOT SUPPORTED) */
+
+typedef struct aml_resource_common_serialbus
+{
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON
+
+} AML_RESOURCE_COMMON_SERIALBUS;
+
+typedef struct aml_resource_i2c_serialbus
+{
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON
+ UINT32 ConnectionSpeed;
+ UINT16 SlaveAddress;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+
+} AML_RESOURCE_I2C_SERIALBUS;
+
+#define AML_RESOURCE_I2C_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_I2C_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_I2C_MIN_DATA_LEN 6
+
+typedef struct aml_resource_spi_serialbus
+{
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON
+ UINT32 ConnectionSpeed;
+ UINT8 DataBitLength;
+ UINT8 ClockPhase;
+ UINT8 ClockPolarity;
+ UINT16 DeviceSelection;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+
+} AML_RESOURCE_SPI_SERIALBUS;
+
+#define AML_RESOURCE_SPI_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_SPI_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_SPI_MIN_DATA_LEN 9
+
+
+typedef struct aml_resource_uart_serialbus
+{
+ AML_RESOURCE_LARGE_HEADER_COMMON
+ AML_RESOURCE_SERIAL_COMMON
+ UINT32 DefaultBaudRate;
+ UINT16 RxFifoSize;
+ UINT16 TxFifoSize;
+ UINT8 Parity;
+ UINT8 LinesEnabled;
+ /*
+ * Optional fields follow immediately:
+ * 1) Vendor Data bytes
+ * 2) Resource Source String
+ */
+
+} AML_RESOURCE_UART_SERIALBUS;
+
+#define AML_RESOURCE_UART_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_UART_TYPE_REVISION 1 /* ACPI 5.0 */
+#define AML_RESOURCE_UART_MIN_DATA_LEN 10
+
+
/* restore default alignment */
#pragma pack()
@@ -390,6 +551,7 @@ typedef union aml_resource
AML_RESOURCE_END_DEPENDENT EndDpf;
AML_RESOURCE_IO Io;
AML_RESOURCE_FIXED_IO FixedIo;
+ AML_RESOURCE_FIXED_DMA FixedDma;
AML_RESOURCE_VENDOR_SMALL VendorSmall;
AML_RESOURCE_END_TAG EndTag;
@@ -405,6 +567,11 @@ typedef union aml_resource
AML_RESOURCE_ADDRESS64 Address64;
AML_RESOURCE_EXTENDED_ADDRESS64 ExtAddress64;
AML_RESOURCE_EXTENDED_IRQ ExtendedIrq;
+ AML_RESOURCE_GPIO Gpio;
+ AML_RESOURCE_I2C_SERIALBUS I2cSerialBus;
+ AML_RESOURCE_SPI_SERIALBUS SpiSerialBus;
+ AML_RESOURCE_UART_SERIALBUS UartSerialBus;
+ AML_RESOURCE_COMMON_SERIALBUS CommonSerialBus;
/* Utility overlays */
@@ -415,5 +582,50 @@ typedef union aml_resource
} AML_RESOURCE;
-#endif
+/* Interfaces used by both the disassembler and compiler */
+
+void
+MpSaveGpioInfo (
+ ACPI_PARSE_OBJECT *Op,
+ AML_RESOURCE *Resource,
+ UINT32 PinCount,
+ UINT16 *PinList,
+ char *DeviceName);
+
+void
+MpSaveSerialInfo (
+ ACPI_PARSE_OBJECT *Op,
+ AML_RESOURCE *Resource,
+ char *DeviceName);
+
+char *
+MpGetHidFromParseTree (
+ ACPI_NAMESPACE_NODE *HidNode);
+
+char *
+MpGetHidViaNamestring (
+ char *DeviceName);
+
+char *
+MpGetConnectionInfo (
+ ACPI_PARSE_OBJECT *Op,
+ UINT32 PinIndex,
+ ACPI_NAMESPACE_NODE **TargetNode,
+ char **TargetName);
+
+char *
+MpGetParentDeviceHid (
+ ACPI_PARSE_OBJECT *Op,
+ ACPI_NAMESPACE_NODE **TargetNode,
+ char **ParentDeviceName);
+
+char *
+MpGetDdnValue (
+ char *DeviceName);
+
+char *
+MpGetHidValue (
+ ACPI_NAMESPACE_NODE *DeviceNode);
+
+#endif
diff --git a/usr/src/uts/intel/sys/acpi/platform/accygwin.h b/usr/src/uts/intel/sys/acpi/platform/accygwin.h
index 10ae3e83b7..518fc27d6b 100644
--- a/usr/src/uts/intel/sys/acpi/platform/accygwin.h
+++ b/usr/src/uts/intel/sys/acpi/platform/accygwin.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,6 +50,7 @@
#define ACPI_USE_SYSTEM_CLIBRARY
#define ACPI_USE_DO_WHILE_0
#define ACPI_FLUSH_CPU_CACHE()
+
/*
* This is needed since sem_timedwait does not appear to work properly
* on cygwin (always hangs forever).
@@ -89,4 +90,16 @@
#include "acgcc.h"
+
+/*
+ * The vsnprintf/snprintf functions are defined by c99, but cygwin/gcc
+ * does not enable this prototype when the -ansi flag is set. Also related
+ * to __STRICT_ANSI__. So, we just declare the prototype here.
+ */
+int
+vsnprintf (char *s, size_t n, const char *format, va_list ap);
+
+int
+snprintf (char *s, size_t n, const char *format, ...);
+
#endif /* __ACCYGWIN_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acdragonfly.h b/usr/src/uts/intel/sys/acpi/platform/acdragonfly.h
new file mode 100644
index 0000000000..73a1867f75
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acdragonfly.h
@@ -0,0 +1,128 @@
+/******************************************************************************
+ *
+ * Name: acdragonfly.h - OS specific for DragonFly BSD
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACDRAGONFLY_H_
+#define __ACDRAGONFLY_H_
+
+#include <platform/acgcc.h> /* DragonFly uses GCC */
+#include <sys/types.h>
+
+#ifdef __LP64__
+#define ACPI_MACHINE_WIDTH 64
+#else
+#define ACPI_MACHINE_WIDTH 32
+#define ACPI_USE_NATIVE_DIVIDE
+#endif
+
+#define ACPI_UINTPTR_T uintptr_t
+#define COMPILER_DEPENDENT_INT64 int64_t
+#define COMPILER_DEPENDENT_UINT64 uint64_t
+
+#define ACPI_USE_DO_WHILE_0
+#define ACPI_USE_SYSTEM_CLIBRARY
+
+#ifdef _KERNEL
+
+#include "opt_acpi.h"
+#include <sys/ctype.h>
+#include <sys/systm.h>
+#include <machine/acpica_machdep.h>
+#include <stdarg.h>
+
+#ifdef ACPI_DEBUG
+#define ACPI_DEBUG_OUTPUT /* enable debug output */
+#ifdef DEBUGGER_THREADING
+#undef DEBUGGER_THREADING
+#endif /* DEBUGGER_THREADING */
+#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED /* integrated with DDB */
+#include "opt_ddb.h"
+#ifdef DDB
+#define ACPI_DEBUGGER
+#endif /* DDB */
+#define ACPI_DISASSEMBLER
+#endif
+
+#ifdef ACPI_DEBUG_CACHE
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReleaseObject
+#define AcpiOsReleaseObject(Cache, Object) \
+ _AcpiOsReleaseObject((Cache), (Object), __func__, __LINE__)
+#endif
+
+#ifdef ACPI_DEBUG_LOCKS
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAcquireLock
+#define AcpiOsAcquireLock(Handle) \
+ _AcpiOsAcquireLock((Handle), __func__, __LINE__)
+#endif
+
+#ifdef ACPI_DEBUG_MEMMAP
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsMapMemory
+#define AcpiOsMapMemory(Where, Length) \
+ _AcpiOsMapMemory((Where), (Length), __func__, __LINE__)
+
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsUnmapMemory
+#define AcpiOsUnmapMemory(LogicalAddress, Size) \
+ _AcpiOsUnmapMemory((LogicalAddress), (Size), __func__, __LINE__)
+#endif
+
+/* XXX TBI */
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWaitEventsComplete
+#define AcpiOsWaitEventsComplete()
+
+#define USE_NATIVE_ALLOCATE_ZEROED
+
+#define ACPI_SPINLOCK struct acpi_spinlock *
+struct acpi_spinlock;
+
+#define ACPI_CACHE_T struct acpicache
+struct acpicache;
+
+#else /* _KERNEL */
+
+#define ACPI_USE_STANDARD_HEADERS
+
+#define ACPI_CAST_PTHREAD_T(pthread) ((ACPI_THREAD_ID) ACPI_TO_INTEGER (pthread))
+#define ACPI_FLUSH_CPU_CACHE()
+
+#endif /* _KERNEL */
+
+#endif /* __ACDRAGONFLY_H_ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acdragonflyex.h b/usr/src/uts/intel/sys/acpi/platform/acdragonflyex.h
new file mode 100644
index 0000000000..e1043448fd
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acdragonflyex.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ * Name: acdragonflyex.h - Extra OS specific defines, etc. for DragonFly BSD
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACDRAGONFLYEX_H__
+#define __ACDRAGONFLYEX_H__
+
+#ifdef _KERNEL
+
+#ifdef ACPI_DEBUG_CACHE
+ACPI_STATUS
+_AcpiOsReleaseObject (
+ ACPI_CACHE_T *Cache,
+ void *Object,
+ const char *func,
+ int line);
+#endif
+
+#ifdef ACPI_DEBUG_LOCKS
+ACPI_CPU_FLAGS
+_AcpiOsAcquireLock (
+ ACPI_SPINLOCK Spin,
+ const char *func,
+ int line);
+#endif
+
+#ifdef ACPI_DEBUG_MEMMAP
+void *
+_AcpiOsMapMemory (
+ ACPI_PHYSICAL_ADDRESS Where,
+ ACPI_SIZE Length,
+ const char *caller,
+ int line);
+
+void
+_AcpiOsUnmapMemory (
+ void *LogicalAddress,
+ ACPI_SIZE Length,
+ const char *caller,
+ int line);
+#endif
+
+#endif /* _KERNEL */
+
+#endif /* __ACDRAGONFLYEX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acefi.h b/usr/src/uts/intel/sys/acpi/platform/acefi.h
index 9249ae8d24..1a9e7c402f 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acefi.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acefi.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,10 +44,162 @@
#ifndef __ACEFI_H__
#define __ACEFI_H__
-#include <efi.h>
-#include <efistdarg.h>
-#include <efilib.h>
+#include <stdarg.h>
+#if defined(_GNU_EFI)
+#include <stdint.h>
+#include <unistd.h>
+#endif
+#if defined(__x86_64__)
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
+#define USE_MS_ABI 1
+#endif
+#endif
+
+#ifdef _MSC_EXTENSIONS
+#define EFIAPI __cdecl
+#elif USE_MS_ABI
+#define EFIAPI __attribute__((ms_abi))
+#else
+#define EFIAPI
+#endif
+
+typedef uint8_t UINT8;
+typedef uint16_t UINT16;
+typedef int16_t INT16;
+typedef uint32_t UINT32;
+typedef int32_t INT32;
+typedef uint64_t UINT64;
+typedef int64_t INT64;
+typedef uint8_t BOOLEAN;
+typedef uint16_t CHAR16;
+
+#define VOID void
+
+#if defined(__ia64__) || defined(__x86_64__)
+
+#define ACPI_MACHINE_WIDTH 64
+
+#if defined(__x86_64__)
+
+/* for x86_64, EFI_FUNCTION_WRAPPER must be defined */
+
+#ifndef USE_MS_ABI
+#define USE_EFI_FUNCTION_WRAPPER
+#endif
+
+#ifdef _MSC_EXTENSIONS
+#pragma warning ( disable : 4731 ) /* Suppress warnings about modification of EBP */
+#endif
+
+#endif
+
+typedef uint64_t UINTN;
+typedef int64_t INTN;
+
+#define EFIERR(a) (0x8000000000000000 | a)
+
+#else
+
+#define ACPI_MACHINE_WIDTH 32
+#define ACPI_USE_NATIVE_DIVIDE
+
+typedef uint32_t UINTN;
+typedef int32_t INTN;
+
+#define EFIERR(a) (0x80000000 | a)
+
+#endif
+
+
+#ifdef USE_EFI_FUNCTION_WRAPPER
+#define __VA_NARG__(...) \
+ __VA_NARG_(_0, ## __VA_ARGS__, __RSEQ_N())
+#define __VA_NARG_(...) \
+ __VA_ARG_N(__VA_ARGS__)
+#define __VA_ARG_N( \
+ _0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
+#define __RSEQ_N() \
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define __VA_ARG_NSUFFIX__(prefix,...) \
+ __VA_ARG_NSUFFIX_N(prefix, __VA_NARG__(__VA_ARGS__))
+#define __VA_ARG_NSUFFIX_N(prefix,nargs) \
+ __VA_ARG_NSUFFIX_N_(prefix, nargs)
+#define __VA_ARG_NSUFFIX_N_(prefix,nargs) \
+ prefix ## nargs
+
+/* Prototypes of EFI cdecl -> stdcall trampolines */
+
+UINT64 efi_call0(void *func);
+UINT64 efi_call1(void *func, UINT64 arg1);
+UINT64 efi_call2(void *func, UINT64 arg1, UINT64 arg2);
+UINT64 efi_call3(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3);
+UINT64 efi_call4(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4);
+UINT64 efi_call5(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5);
+UINT64 efi_call6(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5, UINT64 arg6);
+UINT64 efi_call7(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7);
+UINT64 efi_call8(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+ UINT64 arg8);
+UINT64 efi_call9(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+ UINT64 arg8, UINT64 arg9);
+UINT64 efi_call10(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
+ UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
+ UINT64 arg8, UINT64 arg9, UINT64 arg10);
+
+/* Front-ends to efi_callX to avoid compiler warnings */
+
+#define _cast64_efi_call0(f) \
+ efi_call0(f)
+#define _cast64_efi_call1(f,a1) \
+ efi_call1(f, (UINT64)(a1))
+#define _cast64_efi_call2(f,a1,a2) \
+ efi_call2(f, (UINT64)(a1), (UINT64)(a2))
+#define _cast64_efi_call3(f,a1,a2,a3) \
+ efi_call3(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3))
+#define _cast64_efi_call4(f,a1,a2,a3,a4) \
+ efi_call4(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4))
+#define _cast64_efi_call5(f,a1,a2,a3,a4,a5) \
+ efi_call5(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5))
+#define _cast64_efi_call6(f,a1,a2,a3,a4,a5,a6) \
+ efi_call6(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5), (UINT64)(a6))
+#define _cast64_efi_call7(f,a1,a2,a3,a4,a5,a6,a7) \
+ efi_call7(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5), (UINT64)(a6), (UINT64)(a7))
+#define _cast64_efi_call8(f,a1,a2,a3,a4,a5,a6,a7,a8) \
+ efi_call8(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8))
+#define _cast64_efi_call9(f,a1,a2,a3,a4,a5,a6,a7,a8,a9) \
+ efi_call9(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
+ (UINT64)(a9))
+#define _cast64_efi_call10(f,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) \
+ efi_call10(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
+ (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
+ (UINT64)(a9), (UINT64)(a10))
+
+/* main wrapper (va_num ignored) */
+
+#define uefi_call_wrapper(func,va_num,...) \
+ __VA_ARG_NSUFFIX__(_cast64_efi_call, __VA_ARGS__) (func , ##__VA_ARGS__)
+
+#else
+
+#define uefi_call_wrapper(func, va_num, ...) func(__VA_ARGS__)
+
+#endif
+
+/* AED EFI definitions */
+
+#if defined(_AED_EFI)
/* _int64 works for both IA32 and IA64 */
@@ -71,5 +223,53 @@
#pragma warning(disable:4142)
+#endif
+
+
+/* GNU EFI definitions */
+
+#if defined(_GNU_EFI)
+
+/* Using GCC for GNU EFI */
+
+#include "acgcc.h"
+
+#undef ACPI_USE_SYSTEM_CLIBRARY
+#undef ACPI_USE_STANDARD_HEADERS
+#undef ACPI_USE_NATIVE_DIVIDE
+#define ACPI_USE_SYSTEM_INTTYPES
+
+/*
+ * Math helpers
+ */
+#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \
+ do { \
+ UINT64 __n = ((UINT64) n_hi) << 32 | (n_lo); \
+ (q32) = DivU64x32 ((__n), (d32), &(r32)); \
+ } while (0)
+
+#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \
+ do { \
+ (n_lo) >>= 1; \
+ (n_lo) |= (((n_hi) & 1) << 31); \
+ (n_hi) >>= 1; \
+ } while (0)
+
+
+#endif
+
+struct _SIMPLE_TEXT_OUTPUT_INTERFACE;
+struct _SIMPLE_INPUT_INTERFACE;
+struct _EFI_FILE_IO_INTERFACE;
+struct _EFI_FILE_HANDLE;
+struct _EFI_BOOT_SERVICES;
+struct _EFI_SYSTEM_TABLE;
+
+extern struct _EFI_SYSTEM_TABLE *ST;
+extern struct _EFI_BOOT_SERVICES *BS;
+
+#define ACPI_FILE struct _SIMPLE_TEXT_OUTPUT_INTERFACE *
+#define ACPI_FILE_OUT ST->ConOut
+#define ACPI_FILE_ERR ST->ConOut
#endif /* __ACEFI_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acefiex.h b/usr/src/uts/intel/sys/acpi/platform/acefiex.h
new file mode 100644
index 0000000000..7df5012806
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acefiex.h
@@ -0,0 +1,855 @@
+/******************************************************************************
+ *
+ * Name: acefiex.h - Extra OS specific defines, etc. for EFI
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACEFIEX_H__
+#define __ACEFIEX_H__
+
+
+#define EFI_ERROR(a) (((INTN) a) < 0)
+#define EFI_SUCCESS 0
+#define EFI_LOAD_ERROR EFIERR(1)
+#define EFI_INVALID_PARAMETER EFIERR(2)
+#define EFI_UNSUPPORTED EFIERR(3)
+#define EFI_BAD_BUFFER_SIZE EFIERR(4)
+#define EFI_BUFFER_TOO_SMALL EFIERR(5)
+#define EFI_NOT_READY EFIERR(6)
+#define EFI_DEVICE_ERROR EFIERR(7)
+#define EFI_WRITE_PROTECTED EFIERR(8)
+#define EFI_OUT_OF_RESOURCES EFIERR(9)
+#define EFI_VOLUME_CORRUPTED EFIERR(10)
+#define EFI_VOLUME_FULL EFIERR(11)
+#define EFI_NO_MEDIA EFIERR(12)
+#define EFI_MEDIA_CHANGED EFIERR(13)
+#define EFI_NOT_FOUND EFIERR(14)
+#define EFI_ACCESS_DENIED EFIERR(15)
+#define EFI_NO_RESPONSE EFIERR(16)
+#define EFI_NO_MAPPING EFIERR(17)
+#define EFI_TIMEOUT EFIERR(18)
+#define EFI_NOT_STARTED EFIERR(19)
+#define EFI_ALREADY_STARTED EFIERR(20)
+#define EFI_ABORTED EFIERR(21)
+#define EFI_PROTOCOL_ERROR EFIERR(24)
+
+
+typedef UINTN EFI_STATUS;
+typedef VOID *EFI_HANDLE;
+typedef VOID *EFI_EVENT;
+
+typedef struct {
+ UINT32 Data1;
+ UINT16 Data2;
+ UINT16 Data3;
+ UINT8 Data4[8];
+} EFI_GUID;
+
+typedef struct _EFI_DEVICE_PATH {
+ UINT8 Type;
+ UINT8 SubType;
+ UINT8 Length[2];
+} EFI_DEVICE_PATH;
+
+typedef UINT64 EFI_PHYSICAL_ADDRESS;
+typedef UINT64 EFI_VIRTUAL_ADDRESS;
+
+typedef enum {
+ AllocateAnyPages,
+ AllocateMaxAddress,
+ AllocateAddress,
+ MaxAllocateType
+} EFI_ALLOCATE_TYPE;
+
+typedef enum {
+ EfiReservedMemoryType,
+ EfiLoaderCode,
+ EfiLoaderData,
+ EfiBootServicesCode,
+ EfiBootServicesData,
+ EfiRuntimeServicesCode,
+ EfiRuntimeServicesData,
+ EfiConventionalMemory,
+ EfiUnusableMemory,
+ EfiACPIReclaimMemory,
+ EfiACPIMemoryNVS,
+ EfiMemoryMappedIO,
+ EfiMemoryMappedIOPortSpace,
+ EfiPalCode,
+ EfiMaxMemoryType
+} EFI_MEMORY_TYPE;
+
+/* possible caching types for the memory range */
+#define EFI_MEMORY_UC 0x0000000000000001
+#define EFI_MEMORY_WC 0x0000000000000002
+#define EFI_MEMORY_WT 0x0000000000000004
+#define EFI_MEMORY_WB 0x0000000000000008
+#define EFI_MEMORY_UCE 0x0000000000000010
+
+/* physical memory protection on range */
+#define EFI_MEMORY_WP 0x0000000000001000
+#define EFI_MEMORY_RP 0x0000000000002000
+#define EFI_MEMORY_XP 0x0000000000004000
+
+/* range requires a runtime mapping */
+#define EFI_MEMORY_RUNTIME 0x8000000000000000
+
+#define EFI_MEMORY_DESCRIPTOR_VERSION 1
+typedef struct {
+ UINT32 Type;
+ UINT32 Pad;
+ EFI_PHYSICAL_ADDRESS PhysicalStart;
+ EFI_VIRTUAL_ADDRESS VirtualStart;
+ UINT64 NumberOfPages;
+ UINT64 Attribute;
+} EFI_MEMORY_DESCRIPTOR;
+
+typedef struct _EFI_TABLE_HEARDER {
+ UINT64 Signature;
+ UINT32 Revision;
+ UINT32 HeaderSize;
+ UINT32 CRC32;
+ UINT32 Reserved;
+} EFI_TABLE_HEADER;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UNKNOWN_INTERFACE) (
+ void);
+
+
+/*
+ * Text output protocol
+ */
+#define SIMPLE_TEXT_OUTPUT_PROTOCOL \
+ { 0x387477c2, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_RESET) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ BOOLEAN ExtendedVerification);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_OUTPUT_STRING) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ CHAR16 *String);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_TEST_STRING) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ CHAR16 *String);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_QUERY_MODE) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ UINTN ModeNumber,
+ UINTN *Columns,
+ UINTN *Rows);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_MODE) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ UINTN ModeNumber);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_ATTRIBUTE) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ UINTN Attribute);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_CLEAR_SCREEN) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_SET_CURSOR_POSITION) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ UINTN Column,
+ UINTN Row);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_TEXT_ENABLE_CURSOR) (
+ struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This,
+ BOOLEAN Enable);
+
+typedef struct {
+ INT32 MaxMode;
+ INT32 Mode;
+ INT32 Attribute;
+ INT32 CursorColumn;
+ INT32 CursorRow;
+ BOOLEAN CursorVisible;
+} SIMPLE_TEXT_OUTPUT_MODE;
+
+typedef struct _SIMPLE_TEXT_OUTPUT_INTERFACE {
+ EFI_TEXT_RESET Reset;
+
+ EFI_TEXT_OUTPUT_STRING OutputString;
+ EFI_TEXT_TEST_STRING TestString;
+
+ EFI_TEXT_QUERY_MODE QueryMode;
+ EFI_TEXT_SET_MODE SetMode;
+ EFI_TEXT_SET_ATTRIBUTE SetAttribute;
+
+ EFI_TEXT_CLEAR_SCREEN ClearScreen;
+ EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition;
+ EFI_TEXT_ENABLE_CURSOR EnableCursor;
+
+ SIMPLE_TEXT_OUTPUT_MODE *Mode;
+} SIMPLE_TEXT_OUTPUT_INTERFACE;
+
+/*
+ * Text input protocol
+ */
+#define SIMPLE_TEXT_INPUT_PROTOCOL \
+ { 0x387477c1, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+typedef struct {
+ UINT16 ScanCode;
+ CHAR16 UnicodeChar;
+} EFI_INPUT_KEY;
+
+/*
+ * Baseline unicode control chars
+ */
+#define CHAR_NULL 0x0000
+#define CHAR_BACKSPACE 0x0008
+#define CHAR_TAB 0x0009
+#define CHAR_LINEFEED 0x000A
+#define CHAR_CARRIAGE_RETURN 0x000D
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INPUT_RESET) (
+ struct _SIMPLE_INPUT_INTERFACE *This,
+ BOOLEAN ExtendedVerification);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INPUT_READ_KEY) (
+ struct _SIMPLE_INPUT_INTERFACE *This,
+ EFI_INPUT_KEY *Key);
+
+typedef struct _SIMPLE_INPUT_INTERFACE {
+ EFI_INPUT_RESET Reset;
+ EFI_INPUT_READ_KEY ReadKeyStroke;
+ EFI_EVENT WaitForKey;
+} SIMPLE_INPUT_INTERFACE;
+
+
+/*
+ * Simple file system protocol
+ */
+#define SIMPLE_FILE_SYSTEM_PROTOCOL \
+ { 0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_VOLUME_OPEN) (
+ struct _EFI_FILE_IO_INTERFACE *This,
+ struct _EFI_FILE_HANDLE **Root);
+
+#define EFI_FILE_IO_INTERFACE_REVISION 0x00010000
+
+typedef struct _EFI_FILE_IO_INTERFACE {
+ UINT64 Revision;
+ EFI_VOLUME_OPEN OpenVolume;
+} EFI_FILE_IO_INTERFACE;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_OPEN) (
+ struct _EFI_FILE_HANDLE *File,
+ struct _EFI_FILE_HANDLE **NewHandle,
+ CHAR16 *FileName,
+ UINT64 OpenMode,
+ UINT64 Attributes);
+
+/* Values for OpenMode used above */
+
+#define EFI_FILE_MODE_READ 0x0000000000000001
+#define EFI_FILE_MODE_WRITE 0x0000000000000002
+#define EFI_FILE_MODE_CREATE 0x8000000000000000
+
+/* Values for Attribute used above */
+
+#define EFI_FILE_READ_ONLY 0x0000000000000001
+#define EFI_FILE_HIDDEN 0x0000000000000002
+#define EFI_FILE_SYSTEM 0x0000000000000004
+#define EFI_FILE_RESERVIED 0x0000000000000008
+#define EFI_FILE_DIRECTORY 0x0000000000000010
+#define EFI_FILE_ARCHIVE 0x0000000000000020
+#define EFI_FILE_VALID_ATTR 0x0000000000000037
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_CLOSE) (
+ struct _EFI_FILE_HANDLE *File);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_DELETE) (
+ struct _EFI_FILE_HANDLE *File);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_READ) (
+ struct _EFI_FILE_HANDLE *File,
+ UINTN *BufferSize,
+ VOID *Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_WRITE) (
+ struct _EFI_FILE_HANDLE *File,
+ UINTN *BufferSize,
+ VOID *Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_SET_POSITION) (
+ struct _EFI_FILE_HANDLE *File,
+ UINT64 Position);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_GET_POSITION) (
+ struct _EFI_FILE_HANDLE *File,
+ UINT64 *Position);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_GET_INFO) (
+ struct _EFI_FILE_HANDLE *File,
+ EFI_GUID *InformationType,
+ UINTN *BufferSize,
+ VOID *Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_SET_INFO) (
+ struct _EFI_FILE_HANDLE *File,
+ EFI_GUID *InformationType,
+ UINTN BufferSize,
+ VOID *Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FILE_FLUSH) (
+ struct _EFI_FILE_HANDLE *File);
+
+
+#define EFI_FILE_HANDLE_REVISION 0x00010000
+
+typedef struct _EFI_FILE_HANDLE {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ EFI_FILE_SET_INFO SetInfo;
+ EFI_FILE_FLUSH Flush;
+} EFI_FILE, *EFI_FILE_HANDLE;
+
+
+/*
+ * Loaded image protocol
+ */
+#define LOADED_IMAGE_PROTOCOL \
+ { 0x5B1B31A1, 0x9562, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_ENTRY_POINT) (
+ EFI_HANDLE ImageHandle,
+ struct _EFI_SYSTEM_TABLE *SystemTable);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_LOAD) (
+ BOOLEAN BootPolicy,
+ EFI_HANDLE ParentImageHandle,
+ EFI_DEVICE_PATH *FilePath,
+ VOID *SourceBuffer,
+ UINTN SourceSize,
+ EFI_HANDLE *ImageHandle);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_START) (
+ EFI_HANDLE ImageHandle,
+ UINTN *ExitDataSize,
+ CHAR16 **ExitData);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_EXIT) (
+ EFI_HANDLE ImageHandle,
+ EFI_STATUS ExitStatus,
+ UINTN ExitDataSize,
+ CHAR16 *ExitData);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_IMAGE_UNLOAD) (
+ EFI_HANDLE ImageHandle);
+
+
+#define EFI_IMAGE_INFORMATION_REVISION 0x1000
+typedef struct {
+ UINT32 Revision;
+ EFI_HANDLE ParentHandle;
+ struct _EFI_SYSTEM_TABLE *SystemTable;
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH *FilePath;
+ VOID *Reserved;
+ UINT32 LoadOptionsSize;
+ VOID *LoadOptions;
+ VOID *ImageBase;
+ UINT64 ImageSize;
+ EFI_MEMORY_TYPE ImageCodeType;
+ EFI_MEMORY_TYPE ImageDataType;
+ EFI_IMAGE_UNLOAD Unload;
+
+} EFI_LOADED_IMAGE;
+
+
+/*
+ * EFI Memory
+ */
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ALLOCATE_PAGES) (
+ EFI_ALLOCATE_TYPE Type,
+ EFI_MEMORY_TYPE MemoryType,
+ UINTN NoPages,
+ EFI_PHYSICAL_ADDRESS *Memory);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FREE_PAGES) (
+ EFI_PHYSICAL_ADDRESS Memory,
+ UINTN NoPages);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_MEMORY_MAP) (
+ UINTN *MemoryMapSize,
+ EFI_MEMORY_DESCRIPTOR *MemoryMap,
+ UINTN *MapKey,
+ UINTN *DescriptorSize,
+ UINT32 *DescriptorVersion);
+
+#define NextMemoryDescriptor(Ptr,Size) ((EFI_MEMORY_DESCRIPTOR *) (((UINT8 *) Ptr) + Size))
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ALLOCATE_POOL) (
+ EFI_MEMORY_TYPE PoolType,
+ UINTN Size,
+ VOID **Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_FREE_POOL) (
+ VOID *Buffer);
+
+
+/*
+ * Protocol handler functions
+ */
+typedef enum {
+ EFI_NATIVE_INTERFACE,
+ EFI_PCODE_INTERFACE
+} EFI_INTERFACE_TYPE;
+
+typedef enum {
+ AllHandles,
+ ByRegisterNotify,
+ ByProtocol
+} EFI_LOCATE_SEARCH_TYPE;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE) (
+ EFI_HANDLE *Handle,
+ EFI_GUID *Protocol,
+ EFI_INTERFACE_TYPE InterfaceType,
+ VOID *Interface);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REINSTALL_PROTOCOL_INTERFACE) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ VOID *OldInterface,
+ VOID *NewInterface);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UNINSTALL_PROTOCOL_INTERFACE) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ VOID *Interface);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_HANDLE_PROTOCOL) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ VOID **Interface);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_REGISTER_PROTOCOL_NOTIFY) (
+ EFI_GUID *Protocol,
+ EFI_EVENT Event,
+ VOID **Registration);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_HANDLE) (
+ EFI_LOCATE_SEARCH_TYPE SearchType,
+ EFI_GUID *Protocol,
+ VOID *SearchKey,
+ UINTN *BufferSize,
+ EFI_HANDLE *Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_DEVICE_PATH) (
+ EFI_GUID *Protocol,
+ EFI_DEVICE_PATH **DevicePath,
+ EFI_HANDLE *Device);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INSTALL_CONFIGURATION_TABLE) (
+ EFI_GUID *Guid,
+ VOID *Table);
+
+#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001
+#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
+#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004
+#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008
+#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010
+#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_OPEN_PROTOCOL) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ VOID **Interface,
+ EFI_HANDLE AgentHandle,
+ EFI_HANDLE ControllerHandle,
+ UINT32 Attributes);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CLOSE_PROTOCOL) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ EFI_HANDLE AgentHandle,
+ EFI_HANDLE ControllerHandle);
+
+typedef struct {
+ EFI_HANDLE AgentHandle;
+ EFI_HANDLE ControllerHandle;
+ UINT32 Attributes;
+ UINT32 OpenCount;
+} EFI_OPEN_PROTOCOL_INFORMATION_ENTRY;
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION) (
+ EFI_HANDLE Handle,
+ EFI_GUID *Protocol,
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer,
+ UINTN *EntryCount);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_PROTOCOLS_PER_HANDLE) (
+ EFI_HANDLE Handle,
+ EFI_GUID ***ProtocolBuffer,
+ UINTN *ProtocolBufferCount);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
+ EFI_LOCATE_SEARCH_TYPE SearchType,
+ EFI_GUID *Protocol,
+ VOID *SearchKey,
+ UINTN *NoHandles,
+ EFI_HANDLE **Buffer);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LOCATE_PROTOCOL) (
+ EFI_GUID *Protocol,
+ VOID *Registration,
+ VOID **Interface);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
+ EFI_HANDLE *Handle,
+ ...);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
+ EFI_HANDLE Handle,
+ ...);
+
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CALCULATE_CRC32) (
+ VOID *Data,
+ UINTN DataSize,
+ UINT32 *Crc32);
+
+typedef
+VOID
+(EFIAPI *EFI_COPY_MEM) (
+ VOID *Destination,
+ VOID *Source,
+ UINTN Length);
+
+typedef
+VOID
+(EFIAPI *EFI_SET_MEM) (
+ VOID *Buffer,
+ UINTN Size,
+ UINT8 Value);
+
+/*
+ * EFI Boot Services Table
+ */
+#define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
+#define EFI_BOOT_SERVICES_REVISION (EFI_SPECIFICATION_MAJOR_REVISION<<16) | (EFI_SPECIFICATION_MINOR_REVISION)
+
+typedef struct _EFI_BOOT_SERVICES {
+ EFI_TABLE_HEADER Hdr;
+
+#if 0
+ EFI_RAISE_TPL RaiseTPL;
+ EFI_RESTORE_TPL RestoreTPL;
+#else
+ EFI_UNKNOWN_INTERFACE RaiseTPL;
+ EFI_UNKNOWN_INTERFACE RestoreTPL;
+#endif
+
+ EFI_ALLOCATE_PAGES AllocatePages;
+ EFI_FREE_PAGES FreePages;
+ EFI_GET_MEMORY_MAP GetMemoryMap;
+ EFI_ALLOCATE_POOL AllocatePool;
+ EFI_FREE_POOL FreePool;
+
+#if 0
+ EFI_CREATE_EVENT CreateEvent;
+ EFI_SET_TIMER SetTimer;
+ EFI_WAIT_FOR_EVENT WaitForEvent;
+ EFI_SIGNAL_EVENT SignalEvent;
+ EFI_CLOSE_EVENT CloseEvent;
+ EFI_CHECK_EVENT CheckEvent;
+#else
+ EFI_UNKNOWN_INTERFACE CreateEvent;
+ EFI_UNKNOWN_INTERFACE SetTimer;
+ EFI_UNKNOWN_INTERFACE WaitForEvent;
+ EFI_UNKNOWN_INTERFACE SignalEvent;
+ EFI_UNKNOWN_INTERFACE CloseEvent;
+ EFI_UNKNOWN_INTERFACE CheckEvent;
+#endif
+
+ EFI_INSTALL_PROTOCOL_INTERFACE InstallProtocolInterface;
+ EFI_REINSTALL_PROTOCOL_INTERFACE ReinstallProtocolInterface;
+ EFI_UNINSTALL_PROTOCOL_INTERFACE UninstallProtocolInterface;
+ EFI_HANDLE_PROTOCOL HandleProtocol;
+ EFI_HANDLE_PROTOCOL PCHandleProtocol;
+ EFI_REGISTER_PROTOCOL_NOTIFY RegisterProtocolNotify;
+ EFI_LOCATE_HANDLE LocateHandle;
+ EFI_LOCATE_DEVICE_PATH LocateDevicePath;
+ EFI_INSTALL_CONFIGURATION_TABLE InstallConfigurationTable;
+
+ EFI_IMAGE_LOAD LoadImage;
+ EFI_IMAGE_START StartImage;
+ EFI_EXIT Exit;
+ EFI_IMAGE_UNLOAD UnloadImage;
+
+#if 0
+ EFI_EXIT_BOOT_SERVICES ExitBootServices;
+ EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount;
+ EFI_STALL Stall;
+ EFI_SET_WATCHDOG_TIMER SetWatchdogTimer;
+#else
+ EFI_UNKNOWN_INTERFACE ExitBootServices;
+ EFI_UNKNOWN_INTERFACE GetNextMonotonicCount;
+ EFI_UNKNOWN_INTERFACE Stall;
+ EFI_UNKNOWN_INTERFACE SetWatchdogTimer;
+#endif
+
+#if 0
+ EFI_CONNECT_CONTROLLER ConnectController;
+ EFI_DISCONNECT_CONTROLLER DisconnectController;
+#else
+ EFI_UNKNOWN_INTERFACE ConnectController;
+ EFI_UNKNOWN_INTERFACE DisconnectController;
+#endif
+
+ EFI_OPEN_PROTOCOL OpenProtocol;
+ EFI_CLOSE_PROTOCOL CloseProtocol;
+ EFI_OPEN_PROTOCOL_INFORMATION OpenProtocolInformation;
+ EFI_PROTOCOLS_PER_HANDLE ProtocolsPerHandle;
+ EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer;
+ EFI_LOCATE_PROTOCOL LocateProtocol;
+ EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES InstallMultipleProtocolInterfaces;
+ EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES UninstallMultipleProtocolInterfaces;
+
+ EFI_CALCULATE_CRC32 CalculateCrc32;
+
+ EFI_COPY_MEM CopyMem;
+ EFI_SET_MEM SetMem;
+
+#if 0
+ EFI_CREATE_EVENT_EX CreateEventEx;
+#else
+ EFI_UNKNOWN_INTERFACE CreateEventEx;
+#endif
+} EFI_BOOT_SERVICES;
+
+
+/*
+ * EFI System Table
+ */
+
+/*
+ * EFI Configuration Table and GUID definitions
+ */
+#define ACPI_TABLE_GUID \
+ { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
+#define ACPI_20_TABLE_GUID \
+ { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81} }
+
+typedef struct _EFI_CONFIGURATION_TABLE {
+ EFI_GUID VendorGuid;
+ VOID *VendorTable;
+} EFI_CONFIGURATION_TABLE;
+
+
+#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249
+#define EFI_SYSTEM_TABLE_REVISION (EFI_SPECIFICATION_MAJOR_REVISION<<16) | (EFI_SPECIFICATION_MINOR_REVISION)
+
+typedef struct _EFI_SYSTEM_TABLE {
+ EFI_TABLE_HEADER Hdr;
+
+ CHAR16 *FirmwareVendor;
+ UINT32 FirmwareRevision;
+
+ EFI_HANDLE ConsoleInHandle;
+ SIMPLE_INPUT_INTERFACE *ConIn;
+
+ EFI_HANDLE ConsoleOutHandle;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *ConOut;
+
+ EFI_HANDLE StandardErrorHandle;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *StdErr;
+
+#if 0
+ EFI_RUNTIME_SERVICES *RuntimeServices;
+#else
+ EFI_HANDLE *RuntimeServices;
+#endif
+ EFI_BOOT_SERVICES *BootServices;
+
+ UINTN NumberOfTableEntries;
+ EFI_CONFIGURATION_TABLE *ConfigurationTable;
+
+} EFI_SYSTEM_TABLE;
+
+
+/* GNU EFI definitions */
+
+#if defined(_GNU_EFI)
+
+/*
+ * This is needed to hide platform specific code from ACPICA
+ */
+UINT64
+DivU64x32 (
+ UINT64 Dividend,
+ UINTN Divisor,
+ UINTN *Remainder);
+
+/*
+ * EFI specific prototypes
+ */
+EFI_STATUS
+efi_main (
+ EFI_HANDLE Image,
+ EFI_SYSTEM_TABLE *SystemTab);
+
+int
+acpi_main (
+ int argc,
+ char *argv[]);
+
+
+#endif
+
+extern EFI_GUID AcpiGbl_LoadedImageProtocol;
+extern EFI_GUID AcpiGbl_TextInProtocol;
+extern EFI_GUID AcpiGbl_TextOutProtocol;
+extern EFI_GUID AcpiGbl_FileSystemProtocol;
+
+#endif /* __ACEFIEX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acenv.h b/usr/src/uts/intel/sys/acpi/platform/acenv.h
index 7f893c13ff..99adf5f491 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acenv.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acenv.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,19 +67,32 @@
*
*****************************************************************************/
+/* Common application configuration. All single threaded except for AcpiExec. */
+
+#if (defined ACPI_ASL_COMPILER) || \
+ (defined ACPI_BIN_APP) || \
+ (defined ACPI_DUMP_APP) || \
+ (defined ACPI_HELP_APP) || \
+ (defined ACPI_NAMES_APP) || \
+ (defined ACPI_SRC_APP) || \
+ (defined ACPI_XTRACT_APP) || \
+ (defined ACPI_EXAMPLE_APP)
+#define ACPI_APPLICATION
+#define ACPI_SINGLE_THREADED
+#endif
+
/* iASL configuration */
#ifdef ACPI_ASL_COMPILER
-#define ACPI_APPLICATION
-#define ACPI_DISASSEMBLER
#define ACPI_DEBUG_OUTPUT
#define ACPI_CONSTANT_EVAL_ONLY
#define ACPI_LARGE_NAMESPACE_NODE
#define ACPI_DATA_TABLE_DISASSEMBLY
-#define ACPI_SINGLE_THREADED
+#define ACPI_32BIT_PHYSICAL_ADDRESS
+#define ACPI_DISASSEMBLER 1
#endif
-/* AcpiExec and AcpiBin configuration */
+/* AcpiExec configuration. Multithreaded with full AML debugger */
#ifdef ACPI_EXEC_APP
#define ACPI_APPLICATION
@@ -88,15 +101,50 @@
#define ACPI_DBG_TRACK_ALLOCATIONS
#endif
-#ifdef ACPI_BIN_APP
-#define ACPI_APPLICATION
-#define ACPI_SINGLE_THREADED
+/* AcpiHelp configuration. Error messages disabled. */
+
+#ifdef ACPI_HELP_APP
+#define ACPI_NO_ERROR_MESSAGES
+#endif
+
+/* AcpiNames configuration. Debug output enabled. */
+
+#ifdef ACPI_NAMES_APP
+#define ACPI_DEBUG_OUTPUT
+#endif
+
+/* AcpiExec/AcpiNames/Example configuration. Native RSDP used. */
+
+#if (defined ACPI_EXEC_APP) || \
+ (defined ACPI_EXAMPLE_APP) || \
+ (defined ACPI_NAMES_APP)
+#define ACPI_USE_NATIVE_RSDP_POINTER
+#endif
+
+/* AcpiDump configuration. Native mapping used if provided by the host */
+
+#ifdef ACPI_DUMP_APP
+#define ACPI_USE_NATIVE_MEMORY_MAPPING
+#define USE_NATIVE_ALLOCATE_ZEROED
+#endif
+
+/* AcpiNames/Example configuration. Hardware disabled */
+
+#if (defined ACPI_EXAMPLE_APP) || \
+ (defined ACPI_NAMES_APP)
+#define ACPI_REDUCED_HARDWARE 1
#endif
-/* Linkable ACPICA library */
+/* Linkable ACPICA library. Two versions, one with full debug. */
#ifdef ACPI_LIBRARY
#define ACPI_USE_LOCAL_CACHE
+#define ACPI_DEBUGGER 1
+#define ACPI_DISASSEMBLER 1
+
+#ifdef _DEBUG
+#define ACPI_DEBUG_OUTPUT
+#endif
#endif
/* Common for all ACPICA applications */
@@ -106,15 +154,14 @@
#define ACPI_USE_LOCAL_CACHE
#endif
-/* Common debug support */
+/* Common debug/disassembler support */
#ifdef ACPI_FULL_DEBUG
-#define ACPI_DEBUGGER
#define ACPI_DEBUG_OUTPUT
-#define ACPI_DISASSEMBLER
+#define ACPI_DEBUGGER 1
+#define ACPI_DISASSEMBLER 1
#endif
-
/*! [Begin] no source code translation */
/******************************************************************************
@@ -127,6 +174,12 @@
#if defined(_LINUX) || defined(__linux__)
#include "aclinux.h"
+#elif defined(_APPLE) || defined(__APPLE__)
+#include "acmacosx.h"
+
+#elif defined(__DragonFly__)
+#include "acdragonfly.h"
+
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include "acfreebsd.h"
@@ -160,6 +213,15 @@
#elif defined(_AED_EFI)
#include "acefi.h"
+#elif defined(_GNU_EFI)
+#include "acefi.h"
+
+#elif defined(__HAIKU__)
+#include "achaiku.h"
+
+#elif defined(__QNX__)
+#include "acqnx.h"
+
#else
/* Unknown environment */
@@ -239,6 +301,7 @@
#define ACPI_INTERNAL_VAR_XFACE
#endif
+
/*
* Debugger threading model
* Use single threaded if the entire subsystem is contained in an application
@@ -248,11 +311,11 @@
* multi-threaded if ACPI_APPLICATION is not set.
*/
#ifndef DEBUGGER_THREADING
-#ifdef ACPI_APPLICATION
-#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED
+#if !defined (ACPI_APPLICATION) || defined (ACPI_EXEC_APP)
+#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED
#else
-#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED
+#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED
#endif
#endif /* !DEBUGGER_THREADING */
@@ -269,15 +332,15 @@
* ACPI_USE_STANDARD_HEADERS - Define this if linking to a C library and
* the standard header files may be used.
*
- * The ACPICA subsystem only uses low level C library functions that do not call
- * operating system services and may therefore be inlined in the code.
+ * The ACPICA subsystem only uses low level C library functions that do not
+ * call operating system services and may therefore be inlined in the code.
*
* It may be necessary to tailor these include files to the target
* generation environment.
*/
#ifdef ACPI_USE_SYSTEM_CLIBRARY
-/* Use the standard C library headers. We want to keep these to a minimum */
+/* Use the standard C library headers. We want to keep these to a minimum. */
#ifdef ACPI_USE_STANDARD_HEADERS
@@ -292,28 +355,6 @@
/* We will be linking to the standard Clib functions */
-#define ACPI_STRSTR(s1,s2) strstr((s1), (s2))
-#define ACPI_STRCHR(s1,c) strchr((s1), (c))
-#define ACPI_STRLEN(s) (ACPI_SIZE) strlen((s))
-#define ACPI_STRCPY(d,s) (void) strcpy((d), (s))
-#define ACPI_STRNCPY(d,s,n) (void) strncpy((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRNCMP(d,s,n) strncmp((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRCMP(d,s) strcmp((d), (s))
-#define ACPI_STRCAT(d,s) (void) strcat((d), (s))
-#define ACPI_STRNCAT(d,s,n) strncat((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRTOUL(d,s,n) strtoul((d), (s), (ACPI_SIZE)(n))
-#define ACPI_MEMCMP(s1,s2,n) memcmp((const char *)(s1), (const char *)(s2), (ACPI_SIZE)(n))
-#define ACPI_MEMCPY(d,s,n) (void) memcpy((d), (s), (ACPI_SIZE)(n))
-#define ACPI_MEMSET(d,s,n) (void) memset((d), (s), (ACPI_SIZE)(n))
-#define ACPI_TOUPPER(i) toupper((int) (i))
-#define ACPI_TOLOWER(i) tolower((int) (i))
-#define ACPI_IS_XDIGIT(i) isxdigit((int) (i))
-#define ACPI_IS_DIGIT(i) isdigit((int) (i))
-#define ACPI_IS_SPACE(i) isspace((int) (i))
-#define ACPI_IS_UPPER(i) isupper((int) (i))
-#define ACPI_IS_PRINT(i) isprint((int) (i))
-#define ACPI_IS_ALPHA(i) isalpha((int) (i))
-
#else
/******************************************************************************
@@ -344,29 +385,26 @@ typedef char *va_list;
#define _Bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_Bnd (T, _AUPBND))) - (_Bnd (T,_ADNBND))))
-#define va_end(ap) (void) 0
+#define va_end(ap) (ap = (va_list) NULL)
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_Bnd (A,_AUPBND))))
#endif /* va_arg */
/* Use the local (ACPICA) definitions of the clib functions */
-#define ACPI_STRSTR(s1,s2) AcpiUtStrstr ((s1), (s2))
-#define ACPI_STRCHR(s1,c) AcpiUtStrchr ((s1), (c))
-#define ACPI_STRLEN(s) (ACPI_SIZE) AcpiUtStrlen ((s))
-#define ACPI_STRCPY(d,s) (void) AcpiUtStrcpy ((d), (s))
-#define ACPI_STRNCPY(d,s,n) (void) AcpiUtStrncpy ((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRNCMP(d,s,n) AcpiUtStrncmp ((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRCMP(d,s) AcpiUtStrcmp ((d), (s))
-#define ACPI_STRCAT(d,s) (void) AcpiUtStrcat ((d), (s))
-#define ACPI_STRNCAT(d,s,n) AcpiUtStrncat ((d), (s), (ACPI_SIZE)(n))
-#define ACPI_STRTOUL(d,s,n) AcpiUtStrtoul ((d), (s), (ACPI_SIZE)(n))
-#define ACPI_MEMCMP(s1,s2,n) AcpiUtMemcmp((const char *)(s1), (const char *)(s2), (ACPI_SIZE)(n))
-#define ACPI_MEMCPY(d,s,n) (void) AcpiUtMemcpy ((d), (s), (ACPI_SIZE)(n))
-#define ACPI_MEMSET(d,v,n) (void) AcpiUtMemset ((d), (v), (ACPI_SIZE)(n))
-#define ACPI_TOUPPER(c) AcpiUtToUpper ((int) (c))
-#define ACPI_TOLOWER(c) AcpiUtToLower ((int) (c))
-
#endif /* ACPI_USE_SYSTEM_CLIBRARY */
+#ifndef ACPI_FILE
+#ifdef ACPI_APPLICATION
+#include <stdio.h>
+#define ACPI_FILE FILE *
+#define ACPI_FILE_OUT stdout
+#define ACPI_FILE_ERR stderr
+#else
+#define ACPI_FILE void *
+#define ACPI_FILE_OUT NULL
+#define ACPI_FILE_ERR NULL
+#endif /* ACPI_APPLICATION */
+#endif /* ACPI_FILE */
+
#endif /* __ACENV_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acenvex.h b/usr/src/uts/intel/sys/acpi/platform/acenvex.h
new file mode 100644
index 0000000000..02a46f148b
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acenvex.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ * Name: acenvex.h - Extra host and compiler configuration
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACENVEX_H__
+#define __ACENVEX_H__
+
+/*! [Begin] no source code translation */
+
+/******************************************************************************
+ *
+ * Extra host configuration files. All ACPICA headers are included before
+ * including these files.
+ *
+ *****************************************************************************/
+
+#if defined(_LINUX) || defined(__linux__)
+#include "aclinuxex.h"
+
+#elif defined(WIN32)
+#include "acwinex.h"
+
+#elif defined(_AED_EFI)
+#include "acefiex.h"
+
+#elif defined(_GNU_EFI)
+#include "acefiex.h"
+
+#elif defined(__DragonFly__)
+#include "acdragonflyex.h"
+
+#endif
+
+/*! [End] no source code translation !*/
+
+#endif /* __ACENVEX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acfreebsd.h b/usr/src/uts/intel/sys/acpi/platform/acfreebsd.h
index ad106ecf5e..a968e12cc2 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acfreebsd.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acfreebsd.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,12 +49,21 @@
#include "acgcc.h"
#include <sys/types.h>
-#include <machine/acpica_machdep.h>
+
+#ifdef __LP64__
+#define ACPI_MACHINE_WIDTH 64
+#else
+#define ACPI_MACHINE_WIDTH 32
+#endif
+
+#define COMPILER_DEPENDENT_INT64 int64_t
+#define COMPILER_DEPENDENT_UINT64 uint64_t
#define ACPI_UINTPTR_T uintptr_t
#define ACPI_USE_DO_WHILE_0
#define ACPI_USE_LOCAL_CACHE
+#define ACPI_USE_NATIVE_DIVIDE
#define ACPI_USE_SYSTEM_CLIBRARY
#ifdef _KERNEL
@@ -63,6 +72,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/libkern.h>
+#include <machine/acpica_machdep.h>
#include <machine/stdarg.h>
#include "opt_acpi.h"
diff --git a/usr/src/uts/intel/sys/acpi/platform/acgcc.h b/usr/src/uts/intel/sys/acpi/platform/acgcc.h
index 4b197ff63a..7d703da3dc 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acgcc.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acgcc.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,7 +48,7 @@
/* Function name is used for debug output. Non-ANSI, compiler-dependent */
-#define ACPI_GET_FUNCTION_NAME __FUNCTION__
+#define ACPI_GET_FUNCTION_NAME __func__
/*
* This macro is used to tag functions as "printf-like" because
@@ -59,9 +59,24 @@
/*
* Some compilers complain about unused variables. Sometimes we don't want to
* use all the variables (for example, _AcpiModuleName). This allows us
- * to to tell the compiler warning in a per-variable manner that a variable
+ * to tell the compiler warning in a per-variable manner that a variable
* is unused.
*/
#define ACPI_UNUSED_VAR __attribute__ ((unused))
+/*
+ * Some versions of gcc implement strchr() with a buggy macro. So,
+ * undef it here. Prevents error messages of this form (usually from the
+ * file getopt.c):
+ *
+ * error: logical '&&' with non-zero constant will always evaluate as true
+ */
+#ifdef strchr
+#undef strchr
+#endif
+
+/* GCC supports __VA_ARGS__ in macros */
+
+#define COMPILER_VA_MACRO 1
+
#endif /* __ACGCC_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/achaiku.h b/usr/src/uts/intel/sys/acpi/platform/achaiku.h
new file mode 100644
index 0000000000..10aa6a3f4a
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/achaiku.h
@@ -0,0 +1,106 @@
+/******************************************************************************
+ *
+ * Name: achaiku.h - OS specific defines, etc. for Haiku (www.haiku-os.org)
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACHAIKU_H__
+#define __ACHAIKU_H__
+
+#include "acgcc.h"
+#include <KernelExport.h>
+
+struct mutex;
+
+
+/* Host-dependent types and defines for user- and kernel-space ACPICA */
+
+#define ACPI_USE_SYSTEM_CLIBRARY
+#define ACPI_USE_STANDARD_HEADERS
+
+#define ACPI_MUTEX_TYPE ACPI_OSL_MUTEX
+#define ACPI_MUTEX struct mutex *
+
+#define ACPI_USE_NATIVE_DIVIDE
+
+/* #define ACPI_THREAD_ID thread_id */
+
+#define ACPI_SEMAPHORE sem_id
+#define ACPI_SPINLOCK spinlock *
+#define ACPI_CPU_FLAGS cpu_status
+
+#define COMPILER_DEPENDENT_INT64 int64
+#define COMPILER_DEPENDENT_UINT64 uint64
+
+
+#ifdef B_HAIKU_64_BIT
+#define ACPI_MACHINE_WIDTH 64
+#else
+#define ACPI_MACHINE_WIDTH 32
+#endif
+
+
+#ifdef _KERNEL_MODE
+/* Host-dependent types and defines for in-kernel ACPICA */
+
+/* ACPICA cache implementation is adequate. */
+#define ACPI_USE_LOCAL_CACHE
+
+#define ACPI_FLUSH_CPU_CACHE() __asm __volatile("wbinvd");
+
+/* Based on FreeBSD's due to lack of documentation */
+extern int AcpiOsAcquireGlobalLock(uint32 *lock);
+extern int AcpiOsReleaseGlobalLock(uint32 *lock);
+
+#define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) do { \
+ (Acq) = AcpiOsAcquireGlobalLock(&((GLptr)->GlobalLock)); \
+} while (0)
+
+#define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) do { \
+ (Acq) = AcpiOsReleaseGlobalLock(&((GLptr)->GlobalLock)); \
+} while (0)
+
+#else /* _KERNEL_MODE */
+/* Host-dependent types and defines for user-space ACPICA */
+
+#error "We only support kernel mode ACPI atm."
+
+#endif /* _KERNEL_MODE */
+#endif /* __ACHAIKU_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acintel.h b/usr/src/uts/intel/sys/acpi/platform/acintel.h
index cf7e0bc224..b8dda571f2 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acintel.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acintel.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/usr/src/uts/intel/sys/acpi/platform/aclinux.h b/usr/src/uts/intel/sys/acpi/platform/aclinux.h
index 3fc3d97cb8..bd45cdb14e 100644
--- a/usr/src/uts/intel/sys/acpi/platform/aclinux.h
+++ b/usr/src/uts/intel/sys/acpi/platform/aclinux.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,27 +44,81 @@
#ifndef __ACLINUX_H__
#define __ACLINUX_H__
+#ifdef __KERNEL__
+
+/* ACPICA external files should not include ACPICA headers directly. */
+
+#if !defined(BUILDING_ACPICA) && !defined(_LINUX_ACPI_H)
+#error "Please don't include <acpi/acpi.h> directly, include <linux/acpi.h> instead."
+#endif
+
+#endif
+
/* Common (in-kernel/user-space) ACPICA configuration */
#define ACPI_USE_SYSTEM_CLIBRARY
#define ACPI_USE_DO_WHILE_0
-#define ACPI_MUTEX_TYPE ACPI_BINARY_SEMAPHORE
#ifdef __KERNEL__
+#define ACPI_USE_SYSTEM_INTTYPES
+
+/* Kernel specific ACPICA configuration */
+
+#ifdef CONFIG_ACPI_REDUCED_HARDWARE_ONLY
+#define ACPI_REDUCED_HARDWARE 1
+#endif
+
+#ifdef CONFIG_ACPI_DEBUGGER
+#define ACPI_DEBUGGER
+#endif
+
#include <linux/string.h>
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/sched.h>
-#include <asm/system.h>
-#include <asm/atomic.h>
-#include <asm/div64.h>
-#include <asm/acpi.h>
+#include <linux/atomic.h>
+#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>
-#include <asm/current.h>
+#ifdef EXPORT_ACPI_INTERFACES
+#include <linux/export.h>
+#endif
+#ifdef CONFIG_ACPI
+#include <asm/acenv.h>
+#endif
+
+#ifndef CONFIG_ACPI
+
+/* External globals for __KERNEL__, stubs is needed */
+
+#define ACPI_GLOBAL(t,a)
+#define ACPI_INIT_GLOBAL(t,a,b)
+
+/* Generating stubs for configurable ACPICA macros */
+
+#define ACPI_NO_MEM_ALLOCATIONS
+
+/* Generating stubs for configurable ACPICA functions */
+
+#define ACPI_NO_ERROR_MESSAGES
+#undef ACPI_DEBUG_OUTPUT
+
+/* External interface for __KERNEL__, stub is needed */
+
+#define ACPI_EXTERNAL_RETURN_STATUS(Prototype) \
+ static ACPI_INLINE Prototype {return(AE_NOT_CONFIGURED);}
+#define ACPI_EXTERNAL_RETURN_OK(Prototype) \
+ static ACPI_INLINE Prototype {return(AE_OK);}
+#define ACPI_EXTERNAL_RETURN_VOID(Prototype) \
+ static ACPI_INLINE Prototype {return;}
+#define ACPI_EXTERNAL_RETURN_UINT32(Prototype) \
+ static ACPI_INLINE Prototype {return(0);}
+#define ACPI_EXTERNAL_RETURN_PTR(Prototype) \
+ static ACPI_INLINE Prototype {return(NULL);}
+
+#endif /* CONFIG_ACPI */
/* Host-dependent types and defines for in-kernel ACPICA */
@@ -76,6 +130,47 @@
#define ACPI_SPINLOCK spinlock_t *
#define ACPI_CPU_FLAGS unsigned long
+/* Use native linux version of AcpiOsAllocateZeroed */
+
+#define USE_NATIVE_ALLOCATE_ZEROED
+
+/*
+ * Overrides for in-kernel ACPICA
+ */
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsInitialize
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsTerminate
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAllocate
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAllocateZeroed
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsFree
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsAcquireObject
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetThreadId
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCreateLock
+
+/*
+ * OSL interfaces used by debugger/disassembler
+ */
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsReadable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsWritable
+
+/*
+ * OSL interfaces used by utilities
+ */
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsRedirectOutput
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByName
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByIndex
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetTableByAddress
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsOpenDirectory
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsGetNextFilename
+#define ACPI_USE_ALTERNATE_PROTOTYPE_AcpiOsCloseDirectory
+
+#define ACPI_MSG_ERROR KERN_ERR "ACPI Error: "
+#define ACPI_MSG_EXCEPTION KERN_ERR "ACPI Exception: "
+#define ACPI_MSG_WARNING KERN_WARNING "ACPI Warning: "
+#define ACPI_MSG_INFO KERN_INFO "ACPI: "
+
+#define ACPI_MSG_BIOS_ERROR KERN_ERR "ACPI BIOS Error (bug): "
+#define ACPI_MSG_BIOS_WARNING KERN_WARNING "ACPI BIOS Warning (bug): "
+
#else /* !__KERNEL__ */
#include <stdarg.h>
@@ -84,12 +179,19 @@
#include <ctype.h>
#include <unistd.h>
+/* Define/disable kernel-specific declarators */
+
+#ifndef __init
+#define __init
+#endif
+
/* Host-dependent types and defines for user-space ACPICA */
#define ACPI_FLUSH_CPU_CACHE()
-#define ACPI_CAST_PTHREAD_T(pthread) ((ACPI_THREAD_ID) (pthread))
+#define ACPI_CAST_PTHREAD_T(Pthread) ((ACPI_THREAD_ID) (Pthread))
-#if defined(__ia64__) || defined(__x86_64__)
+#if defined(__ia64__) || defined(__x86_64__) ||\
+ defined(__aarch64__) || defined(__PPC64__)
#define ACPI_MACHINE_WIDTH 64
#define COMPILER_DEPENDENT_INT64 long
#define COMPILER_DEPENDENT_UINT64 unsigned long
@@ -110,51 +212,4 @@
#include "acgcc.h"
-
-#ifdef __KERNEL__
-/*
- * Overrides for in-kernel ACPICA
- */
-static inline acpi_thread_id acpi_os_get_thread_id(void)
-{
- return current;
-}
-
-/*
- * The irqs_disabled() check is for resume from RAM.
- * Interrupts are off during resume, just like they are for boot.
- * However, boot has (system_state != SYSTEM_RUNNING)
- * to quiet __might_sleep() in kmalloc() and resume does not.
- */
-#include <acpi/actypes.h>
-static inline void *acpi_os_allocate(acpi_size size)
-{
- return kmalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
-}
-
-static inline void *acpi_os_allocate_zeroed(acpi_size size)
-{
- return kzalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
-}
-
-static inline void *acpi_os_acquire_object(acpi_cache_t * cache)
-{
- return kmem_cache_zalloc(cache,
- irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
-}
-
-#define ACPI_ALLOCATE(a) acpi_os_allocate(a)
-#define ACPI_ALLOCATE_ZEROED(a) acpi_os_allocate_zeroed(a)
-#define ACPI_FREE(a) kfree(a)
-
-/* Used within ACPICA to show where it is safe to preempt execution */
-
-#define ACPI_PREEMPTION_POINT() \
- do { \
- if (!irqs_disabled()) \
- cond_resched(); \
- } while (0)
-
-#endif /* __KERNEL__ */
-
#endif /* __ACLINUX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/aclinuxex.h b/usr/src/uts/intel/sys/acpi/platform/aclinuxex.h
new file mode 100644
index 0000000000..c262e40819
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/aclinuxex.h
@@ -0,0 +1,158 @@
+/******************************************************************************
+ *
+ * Name: aclinuxex.h - Extra OS specific defines, etc. for Linux
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACLINUXEX_H__
+#define __ACLINUXEX_H__
+
+#ifdef __KERNEL__
+
+#ifndef ACPI_USE_NATIVE_DIVIDE
+
+#ifndef ACPI_DIV_64_BY_32
+#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \
+ do { \
+ UINT64 (__n) = ((UINT64) n_hi) << 32 | (n_lo); \
+ (r32) = do_div ((__n), (d32)); \
+ (q32) = (UINT32) (__n); \
+ } while (0)
+#endif
+
+#ifndef ACPI_SHIFT_RIGHT_64
+#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \
+ do { \
+ (n_lo) >>= 1; \
+ (n_lo) |= (((n_hi) & 1) << 31); \
+ (n_hi) >>= 1; \
+ } while (0)
+#endif
+
+#endif
+
+/*
+ * Overrides for in-kernel ACPICA
+ */
+ACPI_STATUS __init AcpiOsInitialize (
+ void);
+
+ACPI_STATUS AcpiOsTerminate (
+ void);
+
+/*
+ * The irqs_disabled() check is for resume from RAM.
+ * Interrupts are off during resume, just like they are for boot.
+ * However, boot has (system_state != SYSTEM_RUNNING)
+ * to quiet __might_sleep() in kmalloc() and resume does not.
+ */
+static inline void *
+AcpiOsAllocate (
+ ACPI_SIZE Size)
+{
+ return kmalloc (Size, irqs_disabled () ? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline void *
+AcpiOsAllocateZeroed (
+ ACPI_SIZE Size)
+{
+ return kzalloc (Size, irqs_disabled () ? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline void
+AcpiOsFree (
+ void *Memory)
+{
+ kfree (Memory);
+}
+
+static inline void *
+AcpiOsAcquireObject (
+ ACPI_CACHE_T *Cache)
+{
+ return kmem_cache_zalloc (Cache,
+ irqs_disabled () ? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline ACPI_THREAD_ID
+AcpiOsGetThreadId (
+ void)
+{
+ return (ACPI_THREAD_ID) (unsigned long) current;
+}
+
+/*
+ * When lockdep is enabled, the spin_lock_init() macro stringifies it's
+ * argument and uses that as a name for the lock in debugging.
+ * By executing spin_lock_init() in a macro the key changes from "lock" for
+ * all locks to the name of the argument of acpi_os_create_lock(), which
+ * prevents lockdep from reporting false positives for ACPICA locks.
+ */
+#define AcpiOsCreateLock(__Handle) \
+ ({ \
+ spinlock_t *Lock = ACPI_ALLOCATE(sizeof(*Lock)); \
+ if (Lock) { \
+ *(__Handle) = Lock; \
+ spin_lock_init(*(__Handle)); \
+ } \
+ Lock ? AE_OK : AE_NO_MEMORY; \
+ })
+
+static inline BOOLEAN
+AcpiOsReadable (
+ void *Pointer,
+ ACPI_SIZE Length)
+{
+ return TRUE;
+}
+
+
+/*
+ * OSL interfaces added by Linux
+ */
+void
+EarlyAcpiOsUnmapMemory (
+ void __iomem *Virt,
+ ACPI_SIZE Size);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ACLINUXEX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acmacosx.h b/usr/src/uts/intel/sys/acpi/platform/acmacosx.h
new file mode 100644
index 0000000000..5d2ba41e82
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acmacosx.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Name: acmacosx.h - OS specific defines, etc. for Mac OS X
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACMACOSX_H__
+#define __ACMACOSX_H__
+
+#include "aclinux.h"
+
+#ifdef __APPLE__
+#define sem_destroy sem_close
+#define ACPI_USE_ALTERNATE_TIMEOUT
+#endif /* __APPLE__ */
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+
+#endif /* __ACMACOSX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acmsvc.h b/usr/src/uts/intel/sys/acpi/platform/acmsvc.h
index 8590d95fd0..e8c801f568 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acmsvc.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acmsvc.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,35 @@
#ifndef __ACMSVC_H__
#define __ACMSVC_H__
+/* Note: do not include any C library headers here */
+
+/*
+ * Note: MSVC project files should define ACPI_DEBUGGER and ACPI_DISASSEMBLER
+ * as appropriate to enable editor functions like "Find all references".
+ * The editor isn't smart enough to dig through the include files to find
+ * out if these are actually defined.
+ */
+
+/*
+ * Map low I/O functions for MS. This allows us to disable MS language
+ * extensions for maximum portability.
+ */
+#define open _open
+#define read _read
+#define write _write
+#define close _close
+#define stat _stat
+#define fstat _fstat
+#define mkdir _mkdir
+#define O_RDONLY _O_RDONLY
+#define O_BINARY _O_BINARY
+#define O_CREAT _O_CREAT
+#define O_WRONLY _O_WRONLY
+#define O_TRUNC _O_TRUNC
+#define S_IREAD _S_IREAD
+#define S_IWRITE _S_IWRITE
+#define S_IFDIR _S_IFDIR
+
/* Eliminate warnings for "old" (non-secure) versions of clib functions */
#ifndef _CRT_SECURE_NO_WARNINGS
@@ -124,4 +153,43 @@
#pragma warning( disable : 4295 ) /* needed for acpredef.h array */
#endif
+
+/* Debug support. */
+
+#ifdef _DEBUG
+
+/*
+ * Debugging memory corruption issues with windows:
+ * Add #include <crtdbg.h> to accommon.h if necessary.
+ * Add _ASSERTE(_CrtCheckMemory()); where needed to test memory integrity.
+ * This can quickly localize the memory corruption.
+ */
+#define ACPI_DEBUG_INITIALIZE() \
+ _CrtSetDbgFlag (\
+ _CRTDBG_CHECK_ALWAYS_DF | \
+ _CRTDBG_ALLOC_MEM_DF | \
+ _CRTDBG_DELAY_FREE_MEM_DF | \
+ _CRTDBG_LEAK_CHECK_DF | \
+ _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
+
+#if 0
+/*
+ * _CrtSetBreakAlloc can be used to set a breakpoint at a particular
+ * memory leak, add to the macro above.
+ */
+Detected memory leaks!
+Dumping objects ->
+..\..\source\os_specific\service_layers\oswinxf.c(701) : {937} normal block at 0x002E9190, 40 bytes long.
+ Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+_CrtSetBreakAlloc (937);
+#endif
+
+#endif
+
+#if _MSC_VER > 1200 /* Versions above VC++ 6 */
+#define COMPILER_VA_MACRO 1
+#else
+#endif
+
#endif /* __ACMSVC_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acmsvcex.h b/usr/src/uts/intel/sys/acpi/platform/acmsvcex.h
new file mode 100644
index 0000000000..f99a034a47
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acmsvcex.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Name: acmsvcex.h - Extra VC specific defines, etc.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACMSVCEX_H__
+#define __ACMSVCEX_H__
+
+/* Debug support. */
+
+#ifdef _DEBUG
+#define _CRTDBG_MAP_ALLOC /* Enables specific file/lineno for leaks */
+#include <crtdbg.h>
+#endif
+
+#endif /* __ACMSVCEX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acnetbsd.h b/usr/src/uts/intel/sys/acpi/platform/acnetbsd.h
index 7c7a63ec94..09259af3aa 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acnetbsd.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acnetbsd.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,6 +48,10 @@
#include "acgcc.h"
+#define ACPI_UINTPTR_T uintptr_t
+#define ACPI_USE_LOCAL_CACHE
+#define ACPI_CAST_PTHREAD_T(x) ((ACPI_THREAD_ID) ACPI_TO_INTEGER (x))
+
#ifdef _LP64
#define ACPI_MACHINE_WIDTH 64
#else
@@ -57,8 +61,10 @@
#define COMPILER_DEPENDENT_INT64 int64_t
#define COMPILER_DEPENDENT_UINT64 uint64_t
-#ifdef _KERNEL
+#if defined(_KERNEL) || defined(_STANDALONE)
+#ifdef _KERNEL_OPT
#include "opt_acpi.h" /* collect build-time options here */
+#endif /* _KERNEL_OPT */
#include <sys/param.h>
#include <sys/systm.h>
@@ -88,26 +94,19 @@
#endif /* DDB */
#endif /* ACPI_DEBUG */
-static __inline int
-isprint(int ch)
-{
- return(isspace(ch) || isascii(ch));
-}
-
-#else /* _KERNEL */
+#else /* defined(_KERNEL) || defined(_STANDALONE) */
#include <ctype.h>
+#include <stdint.h>
/* Not building kernel code, so use libc */
#define ACPI_USE_STANDARD_HEADERS
#define __cli()
#define __sti()
+#define __cdecl
-/* XXX */
-#define __inline inline
-
-#endif /* _KERNEL */
+#endif /* defined(_KERNEL) || defined(_STANDALONE) */
/* Always use NetBSD code over our local versions */
#define ACPI_USE_SYSTEM_CLIBRARY
diff --git a/usr/src/uts/intel/sys/acpi/platform/acos2.h b/usr/src/uts/intel/sys/acpi/platform/acos2.h
index f01d697e31..419579bac0 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acos2.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acos2.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/usr/src/uts/intel/sys/acpi/platform/acqnx.h b/usr/src/uts/intel/sys/acpi/platform/acqnx.h
new file mode 100644
index 0000000000..76252f8668
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acqnx.h
@@ -0,0 +1,74 @@
+/******************************************************************************
+ *
+ * Name: acqnx.h - OS specific defines, etc.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACQNX_H__
+#define __ACQNX_H__
+
+/* QNX uses GCC */
+
+#include "acgcc.h"
+
+#define ACPI_UINTPTR_T uintptr_t
+#define ACPI_USE_LOCAL_CACHE
+#define ACPI_CAST_PTHREAD_T(x) ((ACPI_THREAD_ID) ACPI_TO_INTEGER (x))
+
+/* At present time (QNX 6.6) all supported architectures are 32 bits. */
+#define ACPI_MACHINE_WIDTH 32
+
+#define COMPILER_DEPENDENT_INT64 int64_t
+#define COMPILER_DEPENDENT_UINT64 uint64_t
+
+#include <ctype.h>
+#include <stdint.h>
+#include <sys/neutrino.h>
+
+#define ACPI_USE_STANDARD_HEADERS
+
+#define __cli() InterruptDisable();
+#define __sti() InterruptEnable();
+#define __cdecl
+
+#define ACPI_USE_SYSTEM_CLIBRARY
+#define ACPI_USE_NATIVE_DIVIDE
+
+#endif /* __ACQNX_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acsolaris.h b/usr/src/uts/intel/sys/acpi/platform/acsolaris.h
index b0cd8bfcf3..b702976fb3 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acsolaris.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acsolaris.h
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2016 Joyent, Inc.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -37,12 +38,21 @@ extern "C" {
#include <sys/cpu.h>
#include <sys/thread.h>
+#ifdef _KERNEL
+#include <sys/ctype.h>
+#else
+#include <ctype.h>
+#include <strings.h>
+#include <stdlib.h>
+#endif
+
/* Function name used for debug output. */
#define ACPI_GET_FUNCTION_NAME __func__
uint32_t __acpi_acquire_global_lock(void *);
uint32_t __acpi_release_global_lock(void *);
void __acpi_wbinvd(void);
+uint32_t acpi_strtoul(const char *, char **, int);
#ifdef _ILP32
#define ACPI_MACHINE_WIDTH 32
@@ -76,6 +86,19 @@ void __acpi_wbinvd(void);
#define ACPI_INTERNAL_XFACE
#define ACPI_INTERNAL_VAR_XFACE
+#ifdef _KERNEL
+#define strtoul(s, r, b) acpi_strtoul(s, r, b)
+#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x))
+#define tolower(x) (isupper(x) ? (x) - 'A' + 'a' : (x))
+
+/*
+ * The ACPI headers shipped from Intel defines a bunch of functions which are
+ * already provided by the kernel. The variable below prevents those from
+ * being loaded as part of accommon.h.
+ */
+#define ACPI_USE_SYSTEM_CLIBRARY
+#endif
+
#define ACPI_ASM_MACROS
#define BREAKPOINT3
#define ACPI_DISABLE_IRQS() cli()
diff --git a/usr/src/uts/intel/sys/acpi/platform/acwin.h b/usr/src/uts/intel/sys/acpi/platform/acwin.h
index 1355827ccb..dd5b334b23 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acwin.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acwin.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/usr/src/uts/intel/sys/acpi/platform/acwin64.h b/usr/src/uts/intel/sys/acpi/platform/acwin64.h
index b4d184294c..6481f46114 100644
--- a/usr/src/uts/intel/sys/acpi/platform/acwin64.h
+++ b/usr/src/uts/intel/sys/acpi/platform/acwin64.h
@@ -5,7 +5,7 @@
*****************************************************************************/
/*
- * Copyright (C) 2000 - 2011, Intel Corp.
+ * Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -79,5 +79,6 @@
#endif
+/*! [End] no source code translation !*/
#endif /* __ACWIN_H__ */
diff --git a/usr/src/uts/intel/sys/acpi/platform/acwinex.h b/usr/src/uts/intel/sys/acpi/platform/acwinex.h
new file mode 100644
index 0000000000..8c8e21b250
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/platform/acwinex.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Name: acwinex.h - Extra OS specific defines, etc.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACWINEX_H__
+#define __ACWINEX_H__
+
+/* Windows uses VC */
+#ifdef _MSC_VER
+#include "acmsvcex.h"
+#endif
+
+#endif /* __ACWINEX_H__ */
diff --git a/usr/src/uts/intel/sys/machbrand.h b/usr/src/uts/intel/sys/machbrand.h
index 3f9ebdb6b7..ad7f631649 100644
--- a/usr/src/uts/intel/sys/machbrand.h
+++ b/usr/src/uts/intel/sys/machbrand.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_MACHBRAND_H
@@ -32,20 +33,25 @@ extern "C" {
#ifndef _ASM
#include <sys/model.h>
+#include <sys/thread.h>
struct brand_mach_ops {
void (*b_sysenter)(void);
+ void (*b_int80)(void);
void (*b_int91)(void);
void (*b_syscall)(void);
void (*b_syscall32)(void);
+ greg_t (*b_fixsegreg)(greg_t, model_t);
+ uintptr_t (*b_fsbase)(klwp_t *, uintptr_t);
};
#endif /* _ASM */
#define BRAND_CB_SYSENTER 0
-#define BRAND_CB_INT91 1
-#define BRAND_CB_SYSCALL 2
-#define BRAND_CB_SYSCALL32 3
+#define BRAND_CB_INT80 1
+#define BRAND_CB_INT91 2
+#define BRAND_CB_SYSCALL 3
+#define BRAND_CB_SYSCALL32 4
#ifdef __cplusplus
}
diff --git a/usr/src/uts/intel/sys/machlock.h b/usr/src/uts/intel/sys/machlock.h
index d740bc6212..e362ca9aba 100644
--- a/usr/src/uts/intel/sys/machlock.h
+++ b/usr/src/uts/intel/sys/machlock.h
@@ -21,13 +21,12 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_MACHLOCK_H
#define _SYS_MACHLOCK_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifndef _ASM
#include <sys/types.h>
#include <sys/time.h>
@@ -76,7 +75,7 @@ typedef lock_t disp_lock_t; /* dispatcher lock type */
/*
* Externs for CLOCK_LOCK and clock resolution
*/
-extern volatile int hres_lock;
+extern volatile uint32_t hres_lock;
extern hrtime_t hrtime_base;
extern int clock_res;
diff --git a/usr/src/uts/intel/sys/segments.h b/usr/src/uts/intel/sys/segments.h
index c4b194fcd8..5368f80735 100644
--- a/usr/src/uts/intel/sys/segments.h
+++ b/usr/src/uts/intel/sys/segments.h
@@ -2,7 +2,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_SEGMENTS_H
@@ -522,6 +522,7 @@ void init_boot_gdt(user_desc_t *);
#define GDT_B16DATA 4 /* bios call 16 bit data descriptor */
#define GDT_B64CODE 5 /* dboot 64 bit code descriptor */
#define GDT_BGSTMP 7 /* kmdb descriptor only used early in boot */
+#define GDT_CPUID 16 /* store numeric id of current CPU */
#if defined(__amd64)
@@ -530,8 +531,8 @@ void init_boot_gdt(user_desc_t *);
#define GDT_U32CODE 8 /* 32-bit process on 64-bit kernel %cs */
#define GDT_UDATA 9 /* user data seg %ds (32 and 64 bit) */
#define GDT_UCODE 10 /* native user code seg %cs */
-#define GDT_LDT 12 /* LDT for current process */
-#define GDT_KTSS 14 /* kernel tss */
+#define GDT_LDT 12 /* (12-13) LDT for current process */
+#define GDT_KTSS 14 /* (14-15) kernel tss */
#define GDT_FS GDT_NULL /* kernel %fs segment selector */
#define GDT_GS GDT_NULL /* kernel %gs segment selector */
#define GDT_LWPFS 55 /* lwp private %fs segment selector (32-bit) */
@@ -683,6 +684,8 @@ extern void _start(), cmnint();
extern void achktrap(), mcetrap();
extern void xmtrap();
extern void fasttrap();
+extern void sys_int80();
+extern void brand_sys_int80();
extern void dtrace_ret();
#if !defined(__amd64)
diff --git a/usr/src/uts/intel/sys/ucontext.h b/usr/src/uts/intel/sys/ucontext.h
index 5f64fa9393..adbb01a301 100644
--- a/usr/src/uts/intel/sys/ucontext.h
+++ b/usr/src/uts/intel/sys/ucontext.h
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
@@ -84,9 +85,16 @@ struct __ucontext {
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
- long uc_filler[5]; /* see ABI spec for Intel386 */
+ /*
+ * The Intel386 ABI specification includes a 5-element array of longs
+ * called "uc_filler", padding the size of the struct to 512 bytes. To
+ * allow zone brands to communicate extra data right the way through
+ * the signal handling process, from sigacthandler to setcontext, we
+ * steal the first three of these longs as a brand-private member.
+ */
+ void *uc_brand_data[3];
+ long uc_filler[2];
};
-
#if defined(_SYSCALL32)
/* Kernel view of user ILP32 ucontext structure */
@@ -97,7 +105,8 @@ typedef struct ucontext32 {
sigset_t uc_sigmask;
stack32_t uc_stack;
mcontext32_t uc_mcontext;
- int32_t uc_filler[5];
+ caddr32_t uc_brand_data[3];
+ int32_t uc_filler[2];
} ucontext32_t;
#if defined(_KERNEL)
diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h
index 8dba3414ae..5ab5974ead 100644
--- a/usr/src/uts/intel/sys/x86_archext.h
+++ b/usr/src/uts/intel/sys/x86_archext.h
@@ -28,7 +28,7 @@
* All rights reserved.
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2012 Jens Elkner <jel+illumos@cs.uni-magdeburg.de>
* Copyright 2012 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
* Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
@@ -91,7 +91,7 @@ extern "C" {
#define CPUID_INTC_ECX_SSE3 0x00000001 /* Yet more SSE extensions */
#define CPUID_INTC_ECX_PCLMULQDQ 0x00000002 /* PCLMULQDQ insn */
- /* 0x00000004 - reserved */
+#define CPUID_INTC_ECX_DTES64 0x00000004 /* 64-bit DS area */
#define CPUID_INTC_ECX_MON 0x00000008 /* MONITOR/MWAIT */
#define CPUID_INTC_ECX_DSCPL 0x00000010 /* CPL-qualified debug store */
#define CPUID_INTC_ECX_VMX 0x00000020 /* Hardware VM extensions */
@@ -104,15 +104,16 @@ extern "C" {
#define CPUID_INTC_ECX_FMA 0x00001000 /* Fused Multiply Add */
#define CPUID_INTC_ECX_CX16 0x00002000 /* cmpxchg16 */
#define CPUID_INTC_ECX_ETPRD 0x00004000 /* extended task pri messages */
- /* 0x00008000 - reserved */
+#define CPUID_INTC_ECX_PDCM 0x00008000 /* Perf/Debug Capability MSR */
/* 0x00010000 - reserved */
- /* 0x00020000 - reserved */
+#define CPUID_INTC_ECX_PCID 0x00020000 /* process-context ids */
#define CPUID_INTC_ECX_DCA 0x00040000 /* direct cache access */
#define CPUID_INTC_ECX_SSE4_1 0x00080000 /* SSE4.1 insns */
#define CPUID_INTC_ECX_SSE4_2 0x00100000 /* SSE4.2 insns */
#define CPUID_INTC_ECX_X2APIC 0x00200000 /* x2APIC */
#define CPUID_INTC_ECX_MOVBE 0x00400000 /* MOVBE insn */
#define CPUID_INTC_ECX_POPCNT 0x00800000 /* POPCNT insn */
+#define CPUID_INTC_ECX_TSCDL 0x01000000 /* Deadline TSC */
#define CPUID_INTC_ECX_AES 0x02000000 /* AES insns */
#define CPUID_INTC_ECX_XSAVE 0x04000000 /* XSAVE/XRESTOR insns */
#define CPUID_INTC_ECX_OSXSAVE 0x08000000 /* OS supports XSAVE insns */
@@ -170,9 +171,17 @@ extern "C" {
#define CPUID_AMD_ECX_3DNP 0x00000100 /* AMD: 3DNowPrefectch */
#define CPUID_AMD_ECX_OSVW 0x00000200 /* AMD: OSVW */
#define CPUID_AMD_ECX_IBS 0x00000400 /* AMD: IBS */
-#define CPUID_AMD_ECX_SSE5 0x00000800 /* AMD: SSE5 */
+#define CPUID_AMD_ECX_SSE5 0x00000800 /* AMD: Extended AVX */
#define CPUID_AMD_ECX_SKINIT 0x00001000 /* AMD: SKINIT */
#define CPUID_AMD_ECX_WDT 0x00002000 /* AMD: WDT */
+ /* 0x00004000 - reserved */
+#define CPUID_AMD_ECX_LWP 0x00008000 /* AMD: Lightweight profiling */
+#define CPUID_AMD_ECX_FMA4 0x00010000 /* AMD: 4-operand FMA support */
+ /* 0x00020000 - reserved */
+ /* 0x00040000 - reserved */
+#define CPUID_AMD_ECX_NIDMSR 0x00080000 /* AMD: Node ID MSR */
+ /* 0x00100000 - reserved */
+#define CPUID_AMD_ECX_TBM 0x00200000 /* AMD: trailing bit manips. */
#define CPUID_AMD_ECX_TOPOEXT 0x00400000 /* AMD: Topology Extensions */
/*
diff --git a/usr/src/uts/intel/udmf/Makefile b/usr/src/uts/intel/udmf/Makefile
new file mode 100644
index 0000000000..1e9f0e5400
--- /dev/null
+++ b/usr/src/uts/intel/udmf/Makefile
@@ -0,0 +1,74 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = udmf
+OBJECTS = $(UDMF_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(UDMF_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CPPFLAGS += -I$(UTSBASE)/common/io/usbgem
+CPPFLAGS += -DVERSION=\"2.0.0\"
+CPPFLAGS += -DUSBGEM_CONFIG_GLDv3
+LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem
+
+CERRWARN += -_gcc=-Wno-unused-value
+CERRWARN += -_gcc=-Wno-unused-function
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/upf/Makefile b/usr/src/uts/intel/upf/Makefile
new file mode 100644
index 0000000000..5698051c51
--- /dev/null
+++ b/usr/src/uts/intel/upf/Makefile
@@ -0,0 +1,74 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = upf
+OBJECTS = $(UPF_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(UPF_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CPPFLAGS += -I$(UTSBASE)/common/io/usbgem
+CPPFLAGS += -DVERSION=\"2.0.1\"
+CPPFLAGS += -DUSBGEM_CONFIG_GLDv3
+LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem
+
+CERRWARN += -_gcc=-Wno-type-limits
+CERRWARN += -_gcc=-Wno-unused-function
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/urf/Makefile b/usr/src/uts/intel/urf/Makefile
new file mode 100644
index 0000000000..947cdac9e5
--- /dev/null
+++ b/usr/src/uts/intel/urf/Makefile
@@ -0,0 +1,74 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = urf
+OBJECTS = $(URF_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(URF_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CPPFLAGS += -I$(UTSBASE)/common/io/usbgem
+CPPFLAGS += -DVERSION=\"2.0.0\"
+CPPFLAGS += -DUSBGEM_CONFIG_GLDv3
+CERRWARN += -_gcc=-Wno-uninitialized
+LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem
+
+CERRWARN += -_gcc=-Wno-unused-function
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/usbgem/Makefile b/usr/src/uts/intel/usbgem/Makefile
new file mode 100644
index 0000000000..4465fd6c92
--- /dev/null
+++ b/usr/src/uts/intel/usbgem/Makefile
@@ -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 (c) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = usbgem
+OBJECTS = $(USBGEM_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(USBGEM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# The USBGEM has support for various different features. We use
+# these pre-processor macros to define the set we care about.
+#
+CPPFLAGS += \
+ -DUSBGEM_CONFIG_GLDv3 \
+ -DMODULE \
+ -DGEN_CONFIG_FMA \
+ -DUSBGEM_CONFIG_MAC_PROP \
+ -DVERSION=\"1.6\"
+
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+CERRWARN += -_gcc=-Wno-unused-function
+
+LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/vmxnet/Makefile b/usr/src/uts/intel/vmxnet/Makefile
new file mode 100644
index 0000000000..4f3ebcf5af
--- /dev/null
+++ b/usr/src/uts/intel/vmxnet/Makefile
@@ -0,0 +1,93 @@
+# 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) 2012, Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# VMware Ethernet Adapter b module
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+UTSCLOSED = ../../../../closed/uts
+
+#
+# Define the module and object file sets.
+#
+MODULE = vmxnet
+#
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/intel/io/vmxnet
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# The list of object files is defined here, rather than in Makefile.files,
+# because the "$(CLOSED_BUILD)" macro has not been defined at the time
+# Makefile.files is processed.
+#
+VMXNET_OBJS += vmxnet.o
+
+OBJECTS = $(VMXNET_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(VMXNET_OBJS:%.o=$(LINTS_DIR)/%.ln)
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET = $(LINT_MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/i86pc
+LDFLAGS += -dy -N misc/gld
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+#
+# Intentionally don't build lint libraries to minimize divergence with
+# the upstream source.
+#
+lint:
+modlintlib:
+clean.lint:
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include ../Makefile.targ
diff --git a/usr/src/uts/intel/vnd/Makefile b/usr/src/uts/intel/vnd/Makefile
new file mode 100644
index 0000000000..f66f89062c
--- /dev/null
+++ b/usr/src/uts/intel/vnd/Makefile
@@ -0,0 +1,56 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+UTSBASE = ../..
+
+MODULE = vnd
+OBJECTS = $(VND_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(VND_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+CONF_SRCDIR = $(UTSBASE)/common/io/vnd
+
+LDFLAGS += -dy -Nmisc/neti -Nmisch/hook -Nfs/dev -Nmisc/gsqueue
+
+#
+# We use <sys/ctype.h> which causes gcc to think that all of its inline
+# functions are defined and unused.
+#
+CERRWARN += -_gcc=-Wno-unused-function
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/vxlan/Makefile b/usr/src/uts/intel/vxlan/Makefile
new file mode 100644
index 0000000000..89a24c17f5
--- /dev/null
+++ b/usr/src/uts/intel/vxlan/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 2015 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = vxlan
+OBJECTS = $(OVERLAY_VXLAN_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(OVERLAY_VXLAN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_OVERLAY_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+LDFLAGS += -dy -Ndrv/overlay -Ndrv/ip
+
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/zfd/Makefile b/usr/src/uts/intel/zfd/Makefile
new file mode 100644
index 0000000000..c270466d08
--- /dev/null
+++ b/usr/src/uts/intel/zfd/Makefile
@@ -0,0 +1,48 @@
+#
+# 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 2014 Joyent, Inc. All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE = ../..
+
+MODULE = zfd
+OBJECTS = $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(ZFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sparc/Makefile.sparc b/usr/src/uts/sparc/Makefile.sparc
index abea00c625..4fd54a12ef 100644
--- a/usr/src/uts/sparc/Makefile.sparc
+++ b/usr/src/uts/sparc/Makefile.sparc
@@ -219,7 +219,7 @@ DRV_KMODS += log logindmux kssl mm nca physmem pm poll pool
DRV_KMODS += pseudo ptc ptm pts ptsl ramdisk random rsm rts sad
DRV_KMODS += simnet softmac sppp sppptun sy sysevent sysmsg
DRV_KMODS += spdsock
-DRV_KMODS += tcp tcp6 timerfd tl tnf ttymux udp udp6 wc winlock zcons
+DRV_KMODS += tcp tcp6 timerfd tl tnf ttymux udp udp6 wc winlock zcons zfd
DRV_KMODS += ippctl
DRV_KMODS += dld
DRV_KMODS += ipd
@@ -239,8 +239,10 @@ DRV_KMODS += nulldriver
DRV_KMODS += bridge trill
DRV_KMODS += bpf
DRV_KMODS += dca
+DRV_KMODS += inotify
DRV_KMODS += eventfd
DRV_KMODS += signalfd
+DRV_KMODS += timerfd
#
# Hardware Drivers in common space
@@ -494,6 +496,7 @@ SOCKET_KMODS += socksctp
SOCKET_KMODS += socksdp
SOCKET_KMODS += sockrds
SOCKET_KMODS += ksslf
+SOCKET_KMODS += datafilt
#
# kiconv modules (/kernel/kiconv):
diff --git a/usr/src/uts/sparc/bpf/Makefile b/usr/src/uts/sparc/bpf/Makefile
index 7fa0e7d76c..8661ccd96e 100644
--- a/usr/src/uts/sparc/bpf/Makefile
+++ b/usr/src/uts/sparc/bpf/Makefile
@@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
+LDFLAGS += -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
INC_PATH += -I$(UTSBASE)/common/io/bpf
#
diff --git a/usr/src/uts/sparc/datafilt/Makefile b/usr/src/uts/sparc/datafilt/Makefile
new file mode 100644
index 0000000000..80969500ef
--- /dev/null
+++ b/usr/src/uts/sparc/datafilt/Makefile
@@ -0,0 +1,73 @@
+#
+# 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) 2011, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2012, Nexenta Systems, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = datafilt
+OBJECTS = $(DATAFILT_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(DATAFILT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement and OS version
+#
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -dy -Nfs/sockfs -Ndrv/ip
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/dld/Makefile b/usr/src/uts/sparc/dld/Makefile
index b4b1c63377..2e6b29b719 100644
--- a/usr/src/uts/sparc/dld/Makefile
+++ b/usr/src/uts/sparc/dld/Makefile
@@ -57,7 +57,6 @@ CFLAGS += $(CCVERBOSE)
$(RELEASE_BUILD)CFLAGS += -xinline=auto -xcrossfile
$(RELEASE_BUILD)COPTIMIZE = -xO5
LDFLAGS += -dy -N misc/dls -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/sparc/dls/Makefile b/usr/src/uts/sparc/dls/Makefile
index a7c278e543..3f15600549 100644
--- a/usr/src/uts/sparc/dls/Makefile
+++ b/usr/src/uts/sparc/dls/Makefile
@@ -55,7 +55,6 @@ CFLAGS += $(CCVERBOSE)
$(RELEASE_BUILD)CFLAGS += -xinline=auto -xcrossfile
$(RELEASE_BUILD)COPTIMIZE = -xO5
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these lint checks; maintainers should endeavor
diff --git a/usr/src/uts/sparc/icmp/Makefile b/usr/src/uts/sparc/icmp/Makefile
index 55c11a1ea0..a35e66abd6 100644
--- a/usr/src/uts/sparc/icmp/Makefile
+++ b/usr/src/uts/sparc/icmp/Makefile
@@ -22,6 +22,7 @@
# uts/sparc/icmp/Makefile
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the icmp IP driver
#
@@ -62,6 +63,8 @@ ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
LINT_TARGET = $(MODULE).lint
INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+INC_PATH += -I$(UTSBASE)/common/io/bpf
+
#
# lint pass one enforcement
#
diff --git a/usr/src/uts/sparc/inotify/Makefile b/usr/src/uts/sparc/inotify/Makefile
new file mode 100644
index 0000000000..ce2b956955
--- /dev/null
+++ b/usr/src/uts/sparc/inotify/Makefile
@@ -0,0 +1,70 @@
+#
+# 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) 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = inotify
+OBJECTS = $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(INOTIFY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+LINTTAGS += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
+CERRWARN += -_gcc=-Wno-parentheses
+LDFLAGS += -dy -Nfs/specfs
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/ipf/ipf.global-objs.debug64 b/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
index 663613cee3..cb3e5485b2 100644
--- a/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
+++ b/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
@@ -25,6 +25,10 @@
# Copyright 2013 Joyent, Inc. All rights reserved
#
+hook4_vnd_in
+hook4_vnd_out
+hook6_vnd_in
+hook6_vnd_out
fr_availfuncs
fr_features
fr_objbytes
diff --git a/usr/src/uts/sparc/iptun/Makefile b/usr/src/uts/sparc/iptun/Makefile
index fc09008ab2..d63689a37a 100644
--- a/usr/src/uts/sparc/iptun/Makefile
+++ b/usr/src/uts/sparc/iptun/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -Ndrv/dld -Nmisc/dls -Nmisc/mac -Ndrv/ip
-INC_PATH += -I$(UTSBASE)/common/io/bpf
LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
diff --git a/usr/src/uts/sparc/mac/Makefile b/usr/src/uts/sparc/mac/Makefile
index 5a2fd6834c..4ff3033b55 100644
--- a/usr/src/uts/sparc/mac/Makefile
+++ b/usr/src/uts/sparc/mac/Makefile
@@ -60,7 +60,6 @@ CFLAGS += $(CCVERBOSE)
$(RELEASE_BUILD)CFLAGS += -xinline=auto -xcrossfile
$(RELEASE_BUILD)COPTIMIZE = -xO5
LDFLAGS += -dy
-INC_PATH += -I$(UTSBASE)/common/io/bpf
LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
diff --git a/usr/src/uts/sparc/mac_ether/Makefile b/usr/src/uts/sparc/mac_ether/Makefile
index 3ec066d4c6..b0acd20b30 100644
--- a/usr/src/uts/sparc/mac_ether/Makefile
+++ b/usr/src/uts/sparc/mac_ether/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/mac_ib/Makefile b/usr/src/uts/sparc/mac_ib/Makefile
index 339b88fb6b..7d4250d54f 100644
--- a/usr/src/uts/sparc/mac_ib/Makefile
+++ b/usr/src/uts/sparc/mac_ib/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/mac_wifi/Makefile b/usr/src/uts/sparc/mac_wifi/Makefile
index 66dcd3ac3f..0120e8404b 100644
--- a/usr/src/uts/sparc/mac_wifi/Makefile
+++ b/usr/src/uts/sparc/mac_wifi/Makefile
@@ -58,7 +58,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -dy -Nmisc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/sockpfp/Makefile b/usr/src/uts/sparc/sockpfp/Makefile
index 007eea8053..d01d241549 100644
--- a/usr/src/uts/sparc/sockpfp/Makefile
+++ b/usr/src/uts/sparc/sockpfp/Makefile
@@ -58,7 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/bpf -Ndrv/ip
+LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/ip
INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
#
diff --git a/usr/src/uts/sparc/spdsock/Makefile b/usr/src/uts/sparc/spdsock/Makefile
index 85adf6a5d3..6ed0ae89d2 100644
--- a/usr/src/uts/sparc/spdsock/Makefile
+++ b/usr/src/uts/sparc/spdsock/Makefile
@@ -61,11 +61,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
LDFLAGS += -dy -Ndrv/ip
#
-# Overrides
-#
-INC_PATH += -I$(UTSBASE)/common/io/bpf
-
-#
# lint pass one enforcement
#
CFLAGS += $(CCVERBOSE)
diff --git a/usr/src/uts/sparc/syscall/getcontext.c b/usr/src/uts/sparc/syscall/getcontext.c
index c9adb54b86..3c8dbf9190 100644
--- a/usr/src/uts/sparc/syscall/getcontext.c
+++ b/usr/src/uts/sparc/syscall/getcontext.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -110,10 +113,15 @@ savecontext(ucontext_t *ucp, const k_sigset_t *mask)
ucp->uc_flags &= ~UC_FPU;
ucp->uc_mcontext.gwins = (gwindows_t *)NULL;
- /*
- * Save signal mask.
- */
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
}
@@ -412,11 +420,16 @@ savecontext32(ucontext32_t *ucp, const k_sigset_t *mask, struct fq32 *dfq)
ucp->uc_flags &= ~UC_FPU;
ucp->uc_mcontext.gwins = (caddr32_t)NULL;
- /*
- * Save signal mask (the 32- and 64-bit sigset_t structures are
- * identical).
- */
- sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask (the 32- and 64-bit sigset_t structures are
+ * identical).
+ */
+ sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
}
int
diff --git a/usr/src/uts/sparc/zfd/Makefile b/usr/src/uts/sparc/zfd/Makefile
new file mode 100644
index 0000000000..ebdba686b4
--- /dev/null
+++ b/usr/src/uts/sparc/zfd/Makefile
@@ -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 2014 Joyent, Inc. All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE = ../..
+
+MODULE = zfd
+OBJECTS = $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(ZFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/sparc/Makefile.sparc
+
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sun4/brand/common/brand_solaris.s b/usr/src/uts/sun4/brand/common/brand_solaris.s
index 889218bc5f..9097273036 100644
--- a/usr/src/uts/sun4/brand/common/brand_solaris.s
+++ b/usr/src/uts/sun4/brand/common/brand_solaris.s
@@ -236,7 +236,7 @@ _emulation_check:
#endif /* sun4v */
ldn [%g2 + CPU_THREAD], %g3; /* get thread ptr */
ldn [%g3 + T_PROCP], %g4; /* get proc ptr */
- ldn [%g4 + P_BRAND_DATA], %g5; /* get brand data ptr */
+ ldn [%g4 + __P_BRAND_DATA], %g5; /* get brand data ptr */
ldn [%g5 + SPD_HANDLER], %g5; /* get userland brnd hdlr ptr */
brz %g5, _exit; /* has it been set? */
nop;
diff --git a/usr/src/uts/sun4/ml/offsets.in b/usr/src/uts/sun4/ml/offsets.in
index 4f6d19ba01..de214274ee 100644
--- a/usr/src/uts/sun4/ml/offsets.in
+++ b/usr/src/uts/sun4/ml/offsets.in
@@ -109,7 +109,7 @@ proc PROCSIZE
p_utraps
p_agenttp
p_brand
- p_brand_data
+ __p_brand_data
\#define P_UTRAP4 (UT_ILLTRAP_INSTRUCTION * CPTRSIZE)
\#define P_UTRAP7 (UT_FP_DISABLED * CPTRSIZE)
diff --git a/usr/src/uts/sun4/os/machdep.c b/usr/src/uts/sun4/os/machdep.c
index e6e4875da6..b9c01971ac 100644
--- a/usr/src/uts/sun4/os/machdep.c
+++ b/usr/src/uts/sun4/os/machdep.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -893,3 +894,13 @@ lbolt_softint_post(void)
{
setsoftint(lbolt_softint_inum);
}
+
+void
+thread_splitstack_run(caddr_t addr, void (*func)(void *), void *)
+{
+ panic("thread_splitstack() not supported on SPARC");
+}
+
+void
+thread_splitstack_cleanup(void)
+{}
diff --git a/usr/src/uts/sun4u/io/pci/pcisch.c b/usr/src/uts/sun4u/io/pci/pcisch.c
index 27916bcc74..2a2bd05970 100644
--- a/usr/src/uts/sun4u/io/pci/pcisch.c
+++ b/usr/src/uts/sun4u/io/pci/pcisch.c
@@ -2973,11 +2973,12 @@ iommu_tlb_scrub(iommu_t *iommu_p, int scrub)
"\tContext=%lx %sWritable %sStreamable\n"
"\tPCI Page Size=%sk Address in page %lx\n",
ddi_driver_name(dip), ddi_get_instance(dip), errstat, i,
- (tag & TLBTAG_CONTEXT_BITS) >> TLBTAG_CONTEXT_SHIFT,
+ (uint64_t)(tag & TLBTAG_CONTEXT_BITS) >>
+ TLBTAG_CONTEXT_SHIFT,
(tag & TLBTAG_WRITABLE_BIT) ? "" : neg,
(tag & TLBTAG_STREAM_BIT) ? "" : neg,
(tag & TLBTAG_PGSIZE_BIT) ? "64" : "8",
- (tag & TLBTAG_PCIVPN_BITS) << 13);
+ (uint64_t)(tag & TLBTAG_PCIVPN_BITS) << 13);
cmn_err(CE_CONT, "Memory: %sValid %sCacheable Page Frame=%lx\n",
(data & TLBDATA_VALID_BIT) ? "" : neg,
(data & TLBDATA_CACHE_BIT) ? "" : neg, pfn);